integration-test-db.ts 10 KB

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