integration-test-utils.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import { OpenAPIHono } from '@hono/zod-openapi';
  2. import { testClient } from 'hono/testing';
  3. import { IntegrationTestDatabase } from './integration-test-db';
  4. import { DataSource } from 'typeorm';
  5. import { UserEntity } from '../modules/users/user.entity';
  6. import { Role } from '../modules/users/role.entity';
  7. import apiApp from '../api';
  8. /**
  9. * 集成测试配置选项
  10. */
  11. export interface IntegrationTestOptions {
  12. setupDatabase?: boolean;
  13. setupAuth?: boolean;
  14. setupMiddlewares?: boolean;
  15. }
  16. /**
  17. * 集成测试上下文
  18. */
  19. export interface IntegrationTestContext {
  20. app: OpenAPIHono;
  21. client: ReturnType<typeof testClient<typeof apiApp>>;
  22. dataSource: DataSource | null;
  23. }
  24. /**
  25. * 创建集成测试应用实例
  26. */
  27. export async function createIntegrationTestApp(
  28. routes: any[],
  29. options: IntegrationTestOptions = {}
  30. ): Promise<OpenAPIHono> {
  31. // 使用主API应用,确保所有路由已注册
  32. return apiApp;
  33. }
  34. /**
  35. * 创建集成测试客户端(使用hono/testing的testClient)
  36. */
  37. export function createIntegrationTestClient(
  38. app: OpenAPIHono,
  39. options: IntegrationTestOptions = {}
  40. ): ReturnType<typeof testClient<typeof apiApp>> {
  41. const client = testClient(app);
  42. // 设置默认认证头(如果需要)
  43. if (options.setupAuth !== false) {
  44. // testClient会自动处理header,这里不需要额外设置
  45. }
  46. return client;
  47. }
  48. /**
  49. * 设置集成测试环境
  50. */
  51. export async function setupIntegrationTestEnvironment(
  52. routes: any[],
  53. options: IntegrationTestOptions = {}
  54. ): Promise<IntegrationTestContext> {
  55. const {
  56. setupDatabase = true,
  57. setupAuth = true,
  58. setupMiddlewares = true
  59. } = options;
  60. // 创建测试应用
  61. const app = await createIntegrationTestApp(routes, options);
  62. // 初始化数据库(如果需要)
  63. let dataSource: DataSource | null = null;
  64. if (setupDatabase) {
  65. dataSource = await IntegrationTestDatabase.initialize();
  66. }
  67. // 创建API客户端
  68. const client = createIntegrationTestClient(app, { setupAuth });
  69. return {
  70. app,
  71. client,
  72. dataSource
  73. };
  74. }
  75. /**
  76. * 清理集成测试环境
  77. */
  78. export async function cleanupIntegrationTestEnvironment(): Promise<void> {
  79. await IntegrationTestDatabase.clearAllData();
  80. await IntegrationTestDatabase.cleanup();
  81. }
  82. /**
  83. * 测试数据工厂函数
  84. * 使用integration-test-db.ts中的TestDataFactory
  85. */
  86. export { TestDataFactory } from './integration-test-db';
  87. /**
  88. * 集成测试断言工具
  89. */
  90. export class IntegrationTestAssertions {
  91. /**
  92. * 断言响应状态码
  93. */
  94. static expectStatus(response: { status: number }, expectedStatus: number): void {
  95. if (response.status !== expectedStatus) {
  96. throw new Error(`Expected status ${expectedStatus}, but got ${response.status}`);
  97. }
  98. }
  99. /**
  100. * 断言响应包含特定字段
  101. */
  102. static expectResponseToHave(response: { data: any }, expectedFields: Record<string, any>): void {
  103. for (const [key, value] of Object.entries(expectedFields)) {
  104. if (response.data[key] !== value) {
  105. throw new Error(`Expected field ${key} to be ${value}, but got ${response.data[key]}`);
  106. }
  107. }
  108. }
  109. /**
  110. * 断言响应包含特定结构
  111. */
  112. static expectResponseStructure(response: { data: any }, structure: Record<string, any>): void {
  113. for (const key of Object.keys(structure)) {
  114. if (!(key in response.data)) {
  115. throw new Error(`Expected response to have key: ${key}`);
  116. }
  117. }
  118. }
  119. /**
  120. * 断言用户存在于数据库中
  121. */
  122. static async expectUserToExist(username: string): Promise<void> {
  123. const dataSource = IntegrationTestDatabase.getDataSource();
  124. if (!dataSource) {
  125. throw new Error('Database not initialized');
  126. }
  127. const userRepository = dataSource.getRepository(UserEntity);
  128. const user = await userRepository.findOne({ where: { username } });
  129. if (!user) {
  130. throw new Error(`Expected user ${username} to exist in database`);
  131. }
  132. }
  133. /**
  134. * 断言用户不存在于数据库中
  135. */
  136. static async expectUserNotToExist(username: string): Promise<void> {
  137. const dataSource = IntegrationTestDatabase.getDataSource();
  138. if (!dataSource) {
  139. throw new Error('Database not initialized');
  140. }
  141. const userRepository = dataSource.getRepository(UserEntity);
  142. const user = await userRepository.findOne({ where: { username } });
  143. if (user) {
  144. throw new Error(`Expected user ${username} not to exist in database`);
  145. }
  146. }
  147. }
  148. /**
  149. * 集成测试生命周期钩子
  150. */
  151. export function setupIntegrationTestHooks() {
  152. beforeEach(async () => {
  153. await IntegrationTestDatabase.initialize();
  154. });
  155. afterEach(async () => {
  156. await IntegrationTestDatabase.clearAllData();
  157. });
  158. afterAll(async () => {
  159. await IntegrationTestDatabase.cleanup();
  160. });
  161. }