2
0

integration-test-db.ts 10 KB

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