integration-test-db.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  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. import { AppDataSource } from '../data-source';
  6. /**
  7. * 集成测试数据库工具类 - 使用真实PostgreSQL数据库
  8. */
  9. export class IntegrationTestDatabase {
  10. /**
  11. * 初始化集成测试数据库
  12. */
  13. static async initialize(): Promise<DataSource> {
  14. if (!AppDataSource.isInitialized) {
  15. await AppDataSource.initialize();
  16. }
  17. return AppDataSource;
  18. }
  19. /**
  20. * 清理集成测试数据库
  21. */
  22. static async cleanup(): Promise<void> {
  23. if (AppDataSource.isInitialized) {
  24. await AppDataSource.destroy();
  25. }
  26. }
  27. /**
  28. * 获取当前数据源
  29. */
  30. static getDataSource(): DataSource | null {
  31. return AppDataSource.isInitialized ? AppDataSource : null;
  32. }
  33. /**
  34. * 清空所有表数据
  35. */
  36. static async clearAllData(): Promise<void> {
  37. if (!AppDataSource.isInitialized) {
  38. return;
  39. }
  40. const queryRunner = AppDataSource.createQueryRunner();
  41. await queryRunner.connect();
  42. try {
  43. // 获取所有实体
  44. const entities = AppDataSource.entityMetadatas;
  45. // 按依赖关系排序(先删除子表,再删除父表)
  46. const sortedEntities = entities.sort((a, b) => {
  47. if (a.foreignKeys.some(fk => fk.referencedEntityMetadata.name === b.name)) {
  48. return 1; // a 依赖于 b,a 应该排在后面
  49. }
  50. if (b.foreignKeys.some(fk => fk.referencedEntityMetadata.name === a.name)) {
  51. return -1; // b 依赖于 a,a 应该排在前面
  52. }
  53. return 0;
  54. });
  55. // 使用TRUNCATE CASCADE来清空所有表数据(包括有外键约束的表)
  56. for (const entity of sortedEntities) {
  57. await queryRunner.query(`TRUNCATE TABLE "${entity.tableName}" CASCADE`);
  58. }
  59. } finally {
  60. await queryRunner.release();
  61. }
  62. }
  63. }
  64. /**
  65. * 测试数据工厂类
  66. */
  67. export class TestDataFactory {
  68. /**
  69. * 创建测试用户数据
  70. */
  71. static createUserData(overrides: Partial<UserEntity> = {}): Partial<UserEntity> {
  72. const timestamp = Date.now();
  73. return {
  74. username: `testuser_${timestamp}`,
  75. password: 'TestPassword123!',
  76. email: `test_${timestamp}@example.com`,
  77. phone: `138${timestamp.toString().slice(-8)}`,
  78. nickname: `Test User ${timestamp}`,
  79. name: `Test Name ${timestamp}`,
  80. isDisabled: 0,
  81. isDeleted: 0,
  82. ...overrides
  83. };
  84. }
  85. /**
  86. * 创建测试角色数据
  87. */
  88. static createRoleData(overrides: Partial<Role> = {}): Partial<Role> {
  89. const timestamp = Date.now();
  90. return {
  91. name: `test_role_${timestamp}`,
  92. description: `Test role description ${timestamp}`,
  93. ...overrides
  94. };
  95. }
  96. /**
  97. * 在数据库中创建测试用户
  98. */
  99. static async createTestUser(dataSource: DataSource, overrides: Partial<UserEntity> = {}): Promise<UserEntity> {
  100. const userData = this.createUserData(overrides);
  101. const userRepository = dataSource.getRepository(UserEntity);
  102. const user = userRepository.create(userData);
  103. return await userRepository.save(user);
  104. }
  105. /**
  106. * 在数据库中创建测试角色
  107. */
  108. static async createTestRole(dataSource: DataSource, overrides: Partial<Role> = {}): Promise<Role> {
  109. const roleData = this.createRoleData(overrides);
  110. const roleRepository = dataSource.getRepository(Role);
  111. const role = roleRepository.create(roleData);
  112. return await roleRepository.save(role);
  113. }
  114. }
  115. /**
  116. * 集成测试数据库生命周期钩子
  117. */
  118. export function setupIntegrationDatabaseHooks() {
  119. beforeEach(async () => {
  120. await IntegrationTestDatabase.initialize();
  121. });
  122. afterEach(async () => {
  123. await IntegrationTestDatabase.clearAllData();
  124. });
  125. afterAll(async () => {
  126. await IntegrationTestDatabase.cleanup();
  127. });
  128. }