integration-test-db.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import { DataSource } from 'typeorm';
  2. import { beforeEach, afterEach, afterAll } from 'vitest';
  3. import { UserEntity } from '../modules/users/user.entity';
  4. import { Role } from '../modules/users/role.entity';
  5. /**
  6. * 集成测试数据库工具类 - 使用真实PostgreSQL数据库
  7. */
  8. export class IntegrationTestDatabase {
  9. private static dataSource: DataSource | null = null;
  10. /**
  11. * 初始化集成测试数据库
  12. */
  13. static async initialize(): Promise<DataSource> {
  14. if (this.dataSource?.isInitialized) {
  15. return this.dataSource;
  16. }
  17. // 使用环境变量中的测试数据库配置
  18. const databaseUrl = process.env.TEST_DATABASE_URL ||
  19. process.env.DATABASE_URL?.replace('d8dai', 'test_d8dai') ||
  20. 'postgresql://postgres:test_password@localhost:5432/test_d8dai';
  21. this.dataSource = new DataSource({
  22. type: 'postgres',
  23. url: databaseUrl,
  24. synchronize: true,
  25. dropSchema: true, // 每次测试都重新创建schema
  26. logging: false,
  27. entities: [UserEntity, Role]
  28. });
  29. await this.dataSource.initialize();
  30. return this.dataSource;
  31. }
  32. /**
  33. * 清理集成测试数据库
  34. */
  35. static async cleanup(): Promise<void> {
  36. if (this.dataSource?.isInitialized) {
  37. await this.dataSource.destroy();
  38. this.dataSource = null;
  39. }
  40. }
  41. /**
  42. * 获取当前数据源
  43. */
  44. static getDataSource(): DataSource | null {
  45. return this.dataSource;
  46. }
  47. /**
  48. * 清空所有表数据
  49. */
  50. static async clearAllData(): Promise<void> {
  51. if (!this.dataSource?.isInitialized) {
  52. return;
  53. }
  54. const queryRunner = this.dataSource.createQueryRunner();
  55. await queryRunner.connect();
  56. try {
  57. // 获取所有实体
  58. const entities = this.dataSource.entityMetadatas;
  59. // 按依赖关系排序(先删除子表,再删除父表)
  60. const sortedEntities = entities.sort((a, b) => {
  61. if (a.foreignKeys.some(fk => fk.referencedEntityMetadata.name === b.name)) {
  62. return 1; // a 依赖于 b,a 应该排在后面
  63. }
  64. if (b.foreignKeys.some(fk => fk.referencedEntityMetadata.name === a.name)) {
  65. return -1; // b 依赖于 a,a 应该排在前面
  66. }
  67. return 0;
  68. });
  69. // 清空所有表数据
  70. for (const entity of sortedEntities) {
  71. const repository = this.dataSource.getRepository(entity.name);
  72. await repository.clear();
  73. }
  74. } finally {
  75. await queryRunner.release();
  76. }
  77. }
  78. }
  79. /**
  80. * 测试数据工厂类
  81. */
  82. export class TestDataFactory {
  83. /**
  84. * 创建测试用户数据
  85. */
  86. static createUserData(overrides: Partial<UserEntity> = {}): Partial<UserEntity> {
  87. const timestamp = Date.now();
  88. return {
  89. username: `testuser_${timestamp}`,
  90. password: 'TestPassword123!',
  91. email: `test_${timestamp}@example.com`,
  92. phone: `138${timestamp.toString().slice(-8)}`,
  93. nickname: `Test User ${timestamp}`,
  94. name: `Test Name ${timestamp}`,
  95. isDisabled: 0,
  96. isDeleted: 0,
  97. ...overrides
  98. };
  99. }
  100. /**
  101. * 创建测试角色数据
  102. */
  103. static createRoleData(overrides: Partial<Role> = {}): Partial<Role> {
  104. const timestamp = Date.now();
  105. return {
  106. name: `test_role_${timestamp}`,
  107. description: `Test role description ${timestamp}`,
  108. ...overrides
  109. };
  110. }
  111. /**
  112. * 在数据库中创建测试用户
  113. */
  114. static async createTestUser(dataSource: DataSource, overrides: Partial<UserEntity> = {}): Promise<UserEntity> {
  115. const userData = this.createUserData(overrides);
  116. const userRepository = dataSource.getRepository(UserEntity);
  117. const user = userRepository.create(userData);
  118. return await userRepository.save(user);
  119. }
  120. /**
  121. * 在数据库中创建测试角色
  122. */
  123. static async createTestRole(dataSource: DataSource, overrides: Partial<Role> = {}): Promise<Role> {
  124. const roleData = this.createRoleData(overrides);
  125. const roleRepository = dataSource.getRepository(Role);
  126. const role = roleRepository.create(roleData);
  127. return await roleRepository.save(role);
  128. }
  129. }
  130. /**
  131. * 集成测试数据库生命周期钩子
  132. */
  133. export function setupIntegrationDatabaseHooks() {
  134. beforeEach(async () => {
  135. await IntegrationTestDatabase.initialize();
  136. });
  137. afterEach(async () => {
  138. await IntegrationTestDatabase.clearAllData();
  139. });
  140. afterAll(async () => {
  141. await IntegrationTestDatabase.cleanup();
  142. });
  143. }