integration-test-db.ts 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. import { DataSource } from 'typeorm';
  2. import { beforeEach, afterEach } from 'vitest';
  3. import { UserEntity } from '@/server/modules/users/user.entity';
  4. import { Role } from '@/server/modules/users/role.entity';
  5. import { ActivityEntity, ActivityType } from '@/server/modules/activities/activity.entity';
  6. import { RouteEntity } from '@/server/modules/routes/route.entity';
  7. import { LocationEntity } from '@/server/modules/locations/location.entity';
  8. import { AreaEntity } from '@/server/modules/areas/area.entity';
  9. import { VehicleType } from '@/server/modules/routes/route.schema';
  10. import { AppDataSource } from '@/server/data-source';
  11. /**
  12. * 集成测试数据库工具类 - 使用真实PostgreSQL数据库
  13. */
  14. export class IntegrationTestDatabase {
  15. /**
  16. * 清理集成测试数据库
  17. */
  18. static async cleanup(): Promise<void> {
  19. if (AppDataSource.isInitialized) {
  20. await AppDataSource.destroy();
  21. }
  22. }
  23. /**
  24. * 获取当前数据源
  25. */
  26. static async getDataSource(): Promise<DataSource> {
  27. if(!AppDataSource.isInitialized) {
  28. await AppDataSource.initialize();
  29. }
  30. return AppDataSource
  31. }
  32. }
  33. /**
  34. * 测试数据工厂类
  35. */
  36. export class TestDataFactory {
  37. /**
  38. * 创建测试用户数据
  39. */
  40. static createUserData(overrides: Partial<UserEntity> = {}): Partial<UserEntity> {
  41. const timestamp = Date.now();
  42. return {
  43. username: `testuser_${timestamp}`,
  44. password: 'TestPassword123!',
  45. email: `test_${timestamp}@example.com`,
  46. phone: `138${timestamp.toString().slice(-8)}`,
  47. nickname: `Test User ${timestamp}`,
  48. name: `Test Name ${timestamp}`,
  49. isDisabled: 0,
  50. isDeleted: 0,
  51. ...overrides
  52. };
  53. }
  54. /**
  55. * 创建测试角色数据
  56. */
  57. static createRoleData(overrides: Partial<Role> = {}): Partial<Role> {
  58. const timestamp = Date.now();
  59. return {
  60. name: `test_role_${timestamp}`,
  61. description: `Test role description ${timestamp}`,
  62. ...overrides
  63. };
  64. }
  65. /**
  66. * 创建测试区域数据
  67. */
  68. static createAreaData(overrides: Partial<AreaEntity> = {}): Partial<AreaEntity> {
  69. const timestamp = Date.now();
  70. return {
  71. name: `测试区域_${timestamp}`,
  72. code: `area_${timestamp}`,
  73. level: 1,
  74. parentId: null,
  75. isDisabled: 0,
  76. isDeleted: 0,
  77. ...overrides
  78. };
  79. }
  80. /**
  81. * 创建测试地点数据
  82. */
  83. static createLocationData(overrides: Partial<LocationEntity> = {}): Partial<LocationEntity> {
  84. const timestamp = Date.now();
  85. return {
  86. name: `北京测试地点_${timestamp}`,
  87. address: `北京市测试地址_${timestamp}`,
  88. provinceId: 0, // 将在创建时自动设置
  89. cityId: 0, // 将在创建时自动设置
  90. districtId: 0, // 将在创建时自动设置
  91. latitude: 39.9042,
  92. longitude: 116.4074,
  93. isDisabled: 0,
  94. isDeleted: 0,
  95. ...overrides
  96. };
  97. }
  98. /**
  99. * 在数据库中创建测试用户
  100. */
  101. static async createTestUser(dataSource: DataSource, overrides: Partial<UserEntity> = {}): Promise<UserEntity> {
  102. const userData = this.createUserData(overrides);
  103. const userRepository = dataSource.getRepository(UserEntity);
  104. const user = userRepository.create(userData);
  105. return await userRepository.save(user);
  106. }
  107. /**
  108. * 在数据库中创建测试角色
  109. */
  110. static async createTestRole(dataSource: DataSource, overrides: Partial<Role> = {}): Promise<Role> {
  111. const roleData = this.createRoleData(overrides);
  112. const roleRepository = dataSource.getRepository(Role);
  113. const role = roleRepository.create(roleData);
  114. return await roleRepository.save(role);
  115. }
  116. /**
  117. * 在数据库中创建测试区域
  118. */
  119. static async createTestArea(dataSource: DataSource, overrides: Partial<AreaEntity> = {}): Promise<AreaEntity> {
  120. const areaData = this.createAreaData(overrides);
  121. const areaRepository = dataSource.getRepository(AreaEntity);
  122. // 对于顶级区域(省/直辖市),parentId应该为null
  123. if (areaData.level === 1) {
  124. areaData.parentId = null;
  125. }
  126. // 对于市级区域,确保有对应的省级区域
  127. else if (areaData.level === 2 && !areaData.parentId) {
  128. const province = await this.createTestArea(dataSource, { level: 1 });
  129. areaData.parentId = province.id;
  130. }
  131. // 对于区县级区域,确保有对应的市级区域
  132. else if (areaData.level === 3 && !areaData.parentId) {
  133. const city = await this.createTestArea(dataSource, { level: 2 });
  134. areaData.parentId = city.id;
  135. }
  136. const area = areaRepository.create(areaData);
  137. return await areaRepository.save(area);
  138. }
  139. /**
  140. * 在数据库中创建测试地点
  141. */
  142. static async createTestLocation(dataSource: DataSource, overrides: Partial<LocationEntity> = {}): Promise<LocationEntity> {
  143. const locationData = this.createLocationData(overrides);
  144. const locationRepository = dataSource.getRepository(LocationEntity);
  145. // 确保关联的区域存在 - 按层级顺序创建
  146. if (!locationData.provinceId) {
  147. const province = await this.createTestArea(dataSource, { level: 1 });
  148. locationData.provinceId = province.id;
  149. }
  150. if (!locationData.cityId) {
  151. const city = await this.createTestArea(dataSource, { level: 2, parentId: locationData.provinceId });
  152. locationData.cityId = city.id;
  153. }
  154. if (!locationData.districtId) {
  155. const district = await this.createTestArea(dataSource, { level: 3, parentId: locationData.cityId });
  156. locationData.districtId = district.id;
  157. }
  158. const location = locationRepository.create(locationData);
  159. return await locationRepository.save(location);
  160. }
  161. /**
  162. * 创建测试活动数据
  163. */
  164. static createActivityData(overrides: Partial<ActivityEntity> = {}): Partial<ActivityEntity> {
  165. const timestamp = Date.now();
  166. const now = new Date();
  167. const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
  168. return {
  169. name: `测试活动_${timestamp}`,
  170. description: `测试活动描述_${timestamp}`,
  171. type: ActivityType.DEPARTURE,
  172. startDate: now,
  173. endDate: tomorrow,
  174. venueLocationId: 0, // 将在创建时自动设置
  175. isDisabled: 0,
  176. isDeleted: 0,
  177. ...overrides
  178. };
  179. }
  180. /**
  181. * 创建测试路线数据
  182. */
  183. static createRouteData(overrides: Partial<RouteEntity> = {}): Partial<RouteEntity> {
  184. const timestamp = Date.now();
  185. const now = new Date();
  186. const departureTime = new Date(now.getTime() + 2 * 60 * 60 * 1000); // 2小时后
  187. return {
  188. name: `测试路线_${timestamp}`,
  189. startLocationId: 0, // 将在创建时自动设置
  190. endLocationId: 0, // 将在创建时自动设置
  191. pickupPoint: `上车点_${timestamp}`,
  192. dropoffPoint: `下车点_${timestamp}`,
  193. departureTime: departureTime,
  194. vehicleType: VehicleType.BUS,
  195. price: 100,
  196. seatCount: 40,
  197. availableSeats: 40,
  198. isDisabled: 0,
  199. isDeleted: 0,
  200. ...overrides
  201. };
  202. }
  203. /**
  204. * 在数据库中创建测试活动
  205. */
  206. static async createTestActivity(dataSource: DataSource, overrides: Partial<ActivityEntity> = {}): Promise<ActivityEntity> {
  207. const activityData = this.createActivityData(overrides);
  208. const activityRepository = dataSource.getRepository(ActivityEntity);
  209. // 如果没有提供venueLocationId,自动创建一个测试地点
  210. if (!activityData.venueLocationId || activityData.venueLocationId === 0) {
  211. const testLocation = await this.createTestLocation(dataSource);
  212. activityData.venueLocationId = testLocation.id;
  213. }
  214. const activity = activityRepository.create(activityData);
  215. return await activityRepository.save(activity);
  216. }
  217. /**
  218. * 在数据库中创建测试路线
  219. */
  220. static async createTestRoute(dataSource: DataSource, overrides: Partial<RouteEntity> = {}): Promise<RouteEntity> {
  221. const routeData = this.createRouteData(overrides);
  222. const routeRepository = dataSource.getRepository(RouteEntity);
  223. // 如果没有提供startLocationId,自动创建一个测试地点
  224. if (!routeData.startLocationId || routeData.startLocationId === 0) {
  225. const startLocation = await this.createTestLocation(dataSource);
  226. routeData.startLocationId = startLocation.id;
  227. }
  228. // 如果没有提供endLocationId,自动创建一个测试地点
  229. if (!routeData.endLocationId || routeData.endLocationId === 0) {
  230. const endLocation = await this.createTestLocation(dataSource);
  231. routeData.endLocationId = endLocation.id;
  232. }
  233. // 如果没有提供activityId,自动创建一个测试活动
  234. if (!routeData.activityId) {
  235. const testActivity = await this.createTestActivity(dataSource);
  236. routeData.activityId = testActivity.id;
  237. }
  238. const route = routeRepository.create(routeData);
  239. return await routeRepository.save(route);
  240. }
  241. }
  242. /**
  243. * 集成测试数据库生命周期钩子
  244. */
  245. export function setupIntegrationDatabaseHooks() {
  246. beforeEach(async () => {
  247. await IntegrationTestDatabase.getDataSource();
  248. });
  249. afterEach(async () => {
  250. await IntegrationTestDatabase.cleanup();
  251. });
  252. }