import { DataSource } from 'typeorm'; import { beforeEach, afterEach } from 'vitest'; import { UserEntity } from '@d8d/server/modules/users/user.entity'; import { Role } from '@d8d/server/modules/users/role.entity'; import { ActivityEntity } from '@d8d/server/modules/activities/activity.entity'; import { RouteEntity } from '@d8d/server/modules/routes/route.entity'; import { LocationEntity } from '@d8d/server/modules/locations/location.entity'; import { AreaEntity } from '@d8d/server/modules/areas/area.entity'; import { VehicleType } from '@d8d/server/modules/routes/route.schema'; import { Passenger } from '@d8d/server/modules/passengers/passenger.entity'; import { IdType } from '@d8d/server/modules/passengers/passenger.schema'; import { Order } from '@d8d/server/modules/orders/order.entity'; import { OrderStatus, PaymentStatus } from '@d8d/server/share/order.types'; import { AppDataSource } from '@d8d/server/data-source'; /** * 集成测试数据库工具类 - 使用真实PostgreSQL数据库 */ export class IntegrationTestDatabase { /** * 清理集成测试数据库 */ static async cleanup(): Promise { if (AppDataSource.isInitialized) { await AppDataSource.destroy(); } } /** * 获取当前数据源 */ static async getDataSource(): Promise { if(!AppDataSource.isInitialized) { await AppDataSource.initialize(); } return AppDataSource } } /** * 测试数据工厂类 */ export class TestDataFactory { /** * 创建测试用户数据 */ static createUserData(overrides: Partial = {}): Partial { const timestamp = Date.now(); return { username: `testuser_${timestamp}`, password: 'TestPassword123!', email: `test_${timestamp}@example.com`, phone: `138${timestamp.toString().slice(-8)}`, nickname: `Test User ${timestamp}`, name: `Test Name ${timestamp}`, isDisabled: 0, isDeleted: 0, ...overrides }; } /** * 创建测试角色数据 */ static createRoleData(overrides: Partial = {}): Partial { const timestamp = Date.now(); return { name: `test_role_${timestamp}`, description: `Test role description ${timestamp}`, ...overrides }; } /** * 创建测试区域数据 */ static createAreaData(overrides: Partial = {}): Partial { const timestamp = Date.now(); return { name: `测试区域_${timestamp}`, code: `area_${timestamp}`, level: 1, parentId: null, isDisabled: 0, isDeleted: 0, ...overrides }; } /** * 创建测试地点数据 */ static createLocationData(overrides: Partial = {}): Partial { const timestamp = Date.now(); return { name: `北京测试地点_${timestamp}`, address: `北京市测试地址_${timestamp}`, provinceId: 0, // 将在创建时自动设置 cityId: 0, // 将在创建时自动设置 districtId: 0, // 将在创建时自动设置 latitude: 39.9042, longitude: 116.4074, isDisabled: 0, isDeleted: 0, ...overrides }; } /** * 在数据库中创建测试用户 */ static async createTestUser(dataSource: DataSource, overrides: Partial = {}): Promise { const userData = this.createUserData(overrides); const userRepository = dataSource.getRepository(UserEntity); const user = userRepository.create(userData); return await userRepository.save(user); } /** * 在数据库中创建测试角色 */ static async createTestRole(dataSource: DataSource, overrides: Partial = {}): Promise { const roleData = this.createRoleData(overrides); const roleRepository = dataSource.getRepository(Role); const role = roleRepository.create(roleData); return await roleRepository.save(role); } /** * 在数据库中创建测试区域 */ static async createTestArea(dataSource: DataSource, overrides: Partial = {}): Promise { const areaData = this.createAreaData(overrides); const areaRepository = dataSource.getRepository(AreaEntity); // 对于顶级区域(省/直辖市),parentId应该为null if (areaData.level === 1) { areaData.parentId = null; } // 对于市级区域,确保有对应的省级区域 else if (areaData.level === 2 && !areaData.parentId) { const province = await this.createTestArea(dataSource, { level: 1 }); areaData.parentId = province.id; } // 对于区县级区域,确保有对应的市级区域 else if (areaData.level === 3 && !areaData.parentId) { const city = await this.createTestArea(dataSource, { level: 2 }); areaData.parentId = city.id; } const area = areaRepository.create(areaData); return await areaRepository.save(area); } /** * 在数据库中创建测试地点 */ static async createTestLocation(dataSource: DataSource, overrides: Partial = {}): Promise { const locationData = this.createLocationData(overrides); const locationRepository = dataSource.getRepository(LocationEntity); // 确保关联的区域存在 - 按层级顺序创建 if (!locationData.provinceId) { const province = await this.createTestArea(dataSource, { level: 1 }); locationData.provinceId = province.id; } if (!locationData.cityId) { const city = await this.createTestArea(dataSource, { level: 2, parentId: locationData.provinceId }); locationData.cityId = city.id; } if (!locationData.districtId) { const district = await this.createTestArea(dataSource, { level: 3, parentId: locationData.cityId }); locationData.districtId = district.id; } const location = locationRepository.create(locationData); return await locationRepository.save(location); } /** * 创建测试活动数据 */ static createActivityData(overrides: Partial = {}): Partial { const timestamp = Date.now(); const now = new Date(); const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000); return { name: `测试活动_${timestamp}`, description: `测试活动描述_${timestamp}`, startDate: now, endDate: tomorrow, venueLocationId: 0, // 将在创建时自动设置 isDisabled: 0, isDeleted: 0, ...overrides }; } /** * 创建测试路线数据 */ static createRouteData(overrides: Partial = {}): Partial { const timestamp = Date.now(); const now = new Date(); const departureTime = new Date(now.getTime() + 2 * 60 * 60 * 1000); // 2小时后 return { name: `测试路线_${timestamp}`, startLocationId: 0, // 将在创建时自动设置 endLocationId: 0, // 将在创建时自动设置 pickupPoint: `上车点_${timestamp}`, dropoffPoint: `下车点_${timestamp}`, departureTime: departureTime, vehicleType: VehicleType.BUS, price: 100, seatCount: 40, availableSeats: 40, isDisabled: 0, isDeleted: 0, ...overrides }; } /** * 在数据库中创建测试活动 */ static async createTestActivity(dataSource: DataSource, overrides: Partial = {}): Promise { const activityData = this.createActivityData(overrides); const activityRepository = dataSource.getRepository(ActivityEntity); // 如果没有提供venueLocationId,自动创建一个测试地点 if (!activityData.venueLocationId || activityData.venueLocationId === 0) { const testLocation = await this.createTestLocation(dataSource); activityData.venueLocationId = testLocation.id; } const activity = activityRepository.create(activityData); return await activityRepository.save(activity); } /** * 在数据库中创建测试路线 */ static async createTestRoute(dataSource: DataSource, overrides: Partial = {}): Promise { const routeData = this.createRouteData(overrides); const routeRepository = dataSource.getRepository(RouteEntity); // 如果没有提供startLocationId,自动创建一个测试地点 if (!routeData.startLocationId || routeData.startLocationId === 0) { const startLocation = await this.createTestLocation(dataSource); routeData.startLocationId = startLocation.id; } // 如果没有提供endLocationId,自动创建一个测试地点 if (!routeData.endLocationId || routeData.endLocationId === 0) { const endLocation = await this.createTestLocation(dataSource); routeData.endLocationId = endLocation.id; } // 如果没有提供activityId,自动创建一个测试活动 if (!routeData.activityId) { const testActivity = await this.createTestActivity(dataSource); routeData.activityId = testActivity.id; } const route = routeRepository.create(routeData); return await routeRepository.save(route); } /** * 创建测试乘客数据 */ static createPassengerData(overrides: Partial = {}): Partial { const timestamp = Date.now(); return { name: `测试乘客_${timestamp}`, idType: IdType.ID_CARD, idNumber: `11010119900101${timestamp.toString().slice(-4)}`, phone: `138${timestamp.toString().slice(-8)}`, isDefault: false, ...overrides }; } /** * 在数据库中创建测试乘客 */ static async createTestPassenger(dataSource: DataSource, overrides: Partial = {}): Promise { const passengerData = this.createPassengerData(overrides); const passengerRepository = dataSource.getRepository(Passenger); // 如果没有提供userId,自动创建一个测试用户 if (!passengerData.userId) { const testUser = await this.createTestUser(dataSource); passengerData.userId = testUser.id; } const passenger = passengerRepository.create(passengerData); return await passengerRepository.save(passenger); } /** * 创建测试订单数据 */ static createOrderData(overrides: Partial = {}): Partial { const timestamp = Date.now(); return { userId: 0, // 将在创建时自动设置 routeId: 0, // 将在创建时自动设置 passengerCount: 2, totalAmount: 200.00, status: OrderStatus.PENDING_PAYMENT, paymentStatus: PaymentStatus.PENDING, passengerSnapshots: [ { name: '测试乘客1', idType: '身份证', idNumber: '110101199001011234' }, { name: '测试乘客2', idType: '身份证', idNumber: '110101199001011235' } ], routeSnapshot: { name: '测试路线快照', description: '测试路线描述', price: 100.00, departureTime: new Date(Date.now() + 24 * 60 * 60 * 1000) }, ...overrides }; } /** * 在数据库中创建测试订单 */ static async createTestOrder(dataSource: DataSource, overrides: Partial = {}): Promise { const orderData = this.createOrderData(overrides); const orderRepository = dataSource.getRepository(Order); // 如果没有提供userId,自动创建一个测试用户 if (!orderData.userId || orderData.userId === 0) { const testUser = await this.createTestUser(dataSource); orderData.userId = testUser.id; } // 如果没有提供routeId,自动创建一个测试路线 if (!orderData.routeId || orderData.routeId === 0) { const testRoute = await this.createTestRoute(dataSource); orderData.routeId = testRoute.id; } const order = orderRepository.create(orderData); return await orderRepository.save(order); } } /** * 集成测试数据库生命周期钩子 */ export function setupIntegrationDatabaseHooks() { beforeEach(async () => { await IntegrationTestDatabase.getDataSource(); }); afterEach(async () => { await IntegrationTestDatabase.cleanup(); }); }