Przeglądaj źródła

✅ test(payment): 添加支付测试数据工厂并优化集成测试

- 创建PaymentTestFactory类,提供完整的测试数据生成功能
- 实现createTestUser、createTestMerchant等方法创建各类测试实体
- 添加createCompleteTestData方法生成完整测试数据链
- 实现createMultiTenantTestData方法支持多租户测试场景
- 重构支付回调集成测试,使用测试工厂替代硬编码数据创建逻辑
- 优化测试用例,提高代码可维护性和可读性
yourname 2 miesięcy temu
rodzic
commit
79ef954058

+ 247 - 0
packages/mini-payment-mt/tests/factories/payment-test.factory.ts

@@ -0,0 +1,247 @@
+import { DataSource } from 'typeorm';
+import { UserEntityMt } from '@d8d/user-module-mt';
+import { MerchantMt } from '@d8d/merchant-module-mt';
+import { SupplierMt } from '@d8d/supplier-module-mt';
+import { DeliveryAddressMt } from '@d8d/delivery-address-module-mt';
+import { OrderMt } from '@d8d/orders-module-mt';
+import { AreaEntityMt } from '@d8d/geo-areas-mt';
+import { PaymentMtEntity } from '../../src/entities/payment.mt.entity.js';
+import { PaymentStatus } from '../../src/entities/payment.types.js';
+
+/**
+ * 支付测试数据工厂
+ */
+export class PaymentTestFactory {
+  constructor(private dataSource: DataSource) {}
+
+  /**
+   * 创建测试用户
+   */
+  async createTestUser(tenantId: number = 1, options: Partial<UserEntityMt> = {}): Promise<UserEntityMt> {
+    const userRepository = this.dataSource.getRepository(UserEntityMt);
+    const timestamp = Date.now();
+
+    const user = userRepository.create({
+      username: `test_user_${timestamp}`,
+      password: 'test_password',
+      nickname: '测试用户',
+      openid: 'oJy1-16IIG18XZLl7G32k1hHMUFg',
+      tenantId,
+      ...options
+    });
+
+    return await userRepository.save(user);
+  }
+
+  /**
+   * 创建测试商户
+   */
+  async createTestMerchant(tenantId: number = 1, userId: number, options: Partial<MerchantMt> = {}): Promise<MerchantMt> {
+    const merchantRepository = this.dataSource.getRepository(MerchantMt);
+    const timestamp = Date.now();
+
+    const merchant = merchantRepository.create({
+      tenantId,
+      name: '测试商户',
+      username: `m${timestamp}`.slice(-19), // 确保不超过20字符
+      password: 'test_password',
+      state: 1,
+      createdBy: userId,
+      updatedBy: userId,
+      ...options
+    });
+
+    return await merchantRepository.save(merchant);
+  }
+
+  /**
+   * 创建测试供货商
+   */
+  async createTestSupplier(tenantId: number = 1, userId: number, options: Partial<SupplierMt> = {}): Promise<SupplierMt> {
+    const supplierRepository = this.dataSource.getRepository(SupplierMt);
+    const timestamp = Date.now();
+
+    const supplier = supplierRepository.create({
+      tenantId,
+      name: '测试供货商',
+      username: `s${timestamp}`.slice(-49), // 确保不超过50字符
+      password: 'test_password',
+      state: 1,
+      createdBy: userId,
+      updatedBy: userId,
+      ...options
+    });
+
+    return await supplierRepository.save(supplier);
+  }
+
+  /**
+   * 创建测试地区记录
+   */
+  async createTestArea(id: number, name: string, level: number, parentId: number = 0): Promise<AreaEntityMt> {
+    const areaRepository = this.dataSource.getRepository(AreaEntityMt);
+
+    const area = areaRepository.create({
+      id,
+      name,
+      level,
+      parentId,
+      state: 1
+    });
+
+    return await areaRepository.save(area);
+  }
+
+  /**
+   * 创建测试配送地址
+   */
+  async createTestDeliveryAddress(tenantId: number = 1, userId: number, options: Partial<DeliveryAddressMt> = {}): Promise<DeliveryAddressMt> {
+    const addressRepository = this.dataSource.getRepository(DeliveryAddressMt);
+
+    // 创建地区记录
+    await this.createTestArea(110000, '北京市', 1);
+    await this.createTestArea(110100, '北京市', 2, 110000);
+    await this.createTestArea(110101, '东城区', 3, 110100);
+    await this.createTestArea(110101001, '东华门街道', 4, 110101);
+
+    const address = addressRepository.create({
+      tenantId,
+      userId,
+      name: '测试收货人',
+      phone: '13800138000',
+      receiverProvince: 110000,
+      receiverCity: 110100,
+      receiverDistrict: 110101,
+      receiverTown: 110101001,
+      address: '测试地址',
+      isDefault: 1,
+      state: 1,
+      createdBy: userId,
+      updatedBy: userId,
+      ...options
+    });
+
+    return await addressRepository.save(address);
+  }
+
+  /**
+   * 创建测试订单
+   */
+  async createTestOrder(
+    tenantId: number = 1,
+    userId: number,
+    merchantId: number,
+    supplierId: number,
+    addressId: number,
+    options: Partial<OrderMt> = {}
+  ): Promise<OrderMt> {
+    const orderRepository = this.dataSource.getRepository(OrderMt);
+    const timestamp = Date.now();
+
+    const order = orderRepository.create({
+      tenantId,
+      orderNo: `ORD${timestamp}`,
+      userId,
+      amount: 1,
+      costAmount: 0.5,
+      payAmount: 1,
+      orderType: 1,
+      payType: 2,
+      payState: 0, // 未支付
+      state: 0,
+      addressId,
+      merchantId,
+      supplierId,
+      createdBy: userId,
+      updatedBy: userId,
+      ...options
+    });
+
+    return await orderRepository.save(order);
+  }
+
+  /**
+   * 创建测试支付记录
+   */
+  async createTestPayment(
+    externalOrderId: number,
+    userId: number,
+    tenantId: number = 1,
+    options: Partial<PaymentMtEntity> = {}
+  ): Promise<PaymentMtEntity> {
+    const paymentRepository = this.dataSource.getRepository(PaymentMtEntity);
+    const timestamp = Date.now();
+
+    const payment = paymentRepository.create({
+      externalOrderId,
+      userId,
+      tenantId,
+      totalAmount: 1,
+      description: '测试支付',
+      paymentStatus: PaymentStatus.PROCESSING,
+      openid: 'oJy1-16IIG18XZLl7G32k1hHMUFg',
+      outTradeNo: `ORDER_${externalOrderId}_${timestamp}`,
+      ...options
+    });
+
+    return await paymentRepository.save(payment);
+  }
+
+  /**
+   * 创建完整的测试环境数据
+   */
+  async createCompleteTestData(tenantId: number = 1): Promise<{
+    user: UserEntityMt;
+    merchant: MerchantMt;
+    supplier: SupplierMt;
+    address: DeliveryAddressMt;
+    order: OrderMt;
+    payment: PaymentMtEntity;
+  }> {
+    const user = await this.createTestUser(tenantId);
+    const merchant = await this.createTestMerchant(tenantId, user.id);
+    const supplier = await this.createTestSupplier(tenantId, user.id);
+    const address = await this.createTestDeliveryAddress(tenantId, user.id);
+    const order = await this.createTestOrder(tenantId, user.id, merchant.id, supplier.id, address.id);
+    const payment = await this.createTestPayment(order.id, user.id, tenantId);
+
+    return {
+      user,
+      merchant,
+      supplier,
+      address,
+      order,
+      payment
+    };
+  }
+
+  /**
+   * 创建多租户测试数据
+   */
+  async createMultiTenantTestData(): Promise<{
+    tenant1: {
+      user: UserEntityMt;
+      merchant: MerchantMt;
+      supplier: SupplierMt;
+      address: DeliveryAddressMt;
+      order: OrderMt;
+      payment: PaymentMtEntity;
+    };
+    tenant2: {
+      user: UserEntityMt;
+      merchant: MerchantMt;
+      supplier: SupplierMt;
+      address: DeliveryAddressMt;
+      order: OrderMt;
+      payment: PaymentMtEntity;
+    };
+  }> {
+    const tenant1 = await this.createCompleteTestData(1);
+    const tenant2 = await this.createCompleteTestData(2);
+
+    return {
+      tenant1,
+      tenant2
+    };
+  }
+}

+ 23 - 183
packages/mini-payment-mt/tests/integration/payment-callback.integration.test.ts

@@ -19,6 +19,8 @@ import { config } from 'dotenv';
 import { resolve } from 'path';
 // 导入微信支付SDK用于模拟
 import WxPay from 'wechatpay-node-v3';
+// 导入测试数据工厂
+import { PaymentTestFactory } from '../factories/payment-test.factory.js';
 
 // 在测试环境中加载环境变量
 config({ path: resolve(process.cwd(), '.env.test') });
@@ -30,9 +32,15 @@ setupIntegrationDatabaseHooksWithEntities([PaymentMtEntity, UserEntityMt, FileMt
 
 describe('支付回调API集成测试 - 多租户版本', () => {
   let client: ReturnType<typeof testClient<typeof PaymentMtRoutes>>;
-  let testUser: UserEntityMt;
-  let testPayment: PaymentMtEntity;
-  let testOrder: OrderMt;
+  let testFactory: PaymentTestFactory;
+  let testData: {
+    user: UserEntityMt;
+    merchant: MerchantMt;
+    supplier: SupplierMt;
+    address: DeliveryAddressMt;
+    order: OrderMt;
+    payment: PaymentMtEntity;
+  };
 
   // 使用真实的微信支付回调数据 - 直接使用原始请求体字符串
   const rawBody = '{"id":"495e231b-9fd8-54a1-8a30-2a38a807744c","create_time":"2025-10-25T12:48:11+08:00","resource_type":"encrypt-resource","event_type":"TRANSACTION.SUCCESS","summary":"支付成功","resource":{"original_type":"transaction","algorithm":"AEAD_AES_256_GCM","ciphertext":"tl1/8FRRn6g0gRq8IoVR8+95VuIADYBDOt6N9PKiHVhiD6l++W5g/wg6VlsCRIZJ+KWMYTaf5FzQHMjCs8o9otIkLLuJA2aZC+kCQtGxNfyVBwxool/tLT9mHd0dFGThqbj8vb/lm+jjNcmmiWHz+J1ZRvGl7mH4I714vudok7JRt5Q0u0tYaLWr76TTXuQErlA7T4KbeVeGAj8iMpu2ErCpR9QRif36Anc5ARjNYrIWfraXlmUXVbXermDyJ8r4o/4QCFfGk8L1u1WqNYASrRTQvQ8OPqj/J21OkDxbPPrOiEmAX1jOvONvIVEe9Lbkm6rdhW4aLRoZYtiusAk/Vm7MI/UYPwRZbyuc4wwdA1T1D4RdJd/m2I4KSvZHQgs0DM0tLqlb0z3880XYNr8iPFnyu2r8Z8LGcXD+COm06vc7bvNWh3ODwmMrmZQkym/Y/T3X/h/4MZj7+1h2vYHqnnrsgtNPHc/2IwWC/fQlPwtSrLh6iUxSd0betFpKLSq08CaJZvnenpDf1ORRMvd8EhTtIJJ4mV4v+VzCOYNhIcBhKp9XwsuhxIdkpGGmNPpow2c2BXY=","associated_data":"transaction","nonce":"sTnWce32BTQP"}}';
@@ -47,97 +55,12 @@ describe('支付回调API集成测试 - 多租户版本', () => {
     // 创建测试客户端
     client = testClient(PaymentMtRoutes);
 
-    // 创建测试用户
+    // 创建测试数据工厂
     const dataSource = await IntegrationTestDatabase.getDataSource();
+    testFactory = new PaymentTestFactory(dataSource);
 
-    const userRepository = dataSource.getRepository(UserEntityMt);
-    testUser = userRepository.create({
-      username: `test_user_${Date.now()}`,
-      password: 'test_password',
-      nickname: '测试用户',
-      openid: 'oJy1-16IIG18XZLl7G32k1hHMUFg',
-      tenantId: 1
-    });
-    await userRepository.save(testUser);
-
-    // 创建商户记录
-    const merchantRepository = dataSource.getRepository(MerchantMt);
-    const testMerchant = merchantRepository.create({
-      tenantId: 1,
-      name: '测试商户',
-      username: `m${Date.now()}`.slice(-19), // 确保不超过20字符
-      password: 'test_password',
-      state: 1,
-      createdBy: testUser.id,
-      updatedBy: testUser.id
-    });
-    await merchantRepository.save(testMerchant);
-
-    // 创建供货商记录
-    const supplierRepository = dataSource.getRepository(SupplierMt);
-    const testSupplier = supplierRepository.create({
-      tenantId: 1,
-      name: '测试供货商',
-      username: `s${Date.now()}`.slice(-49), // 确保不超过50字符
-      password: 'test_password',
-      state: 1,
-      createdBy: testUser.id,
-      updatedBy: testUser.id
-    });
-    await supplierRepository.save(testSupplier);
-
-    // 创建配送地址记录
-    const addressRepository = dataSource.getRepository(DeliveryAddressMt);
-    const testAddress = addressRepository.create({
-      tenantId: 1,
-      userId: testUser.id,
-      name: '测试收货人',
-      mobile: '13800138000',
-      province: 110000,
-      city: 110100,
-      district: 110101,
-      town: 110101001,
-      address: '测试地址',
-      isDefault: 1,
-      createdBy: testUser.id,
-      updatedBy: testUser.id
-    });
-    await addressRepository.save(testAddress);
-
-    // 创建测试订单
-    const orderRepository = dataSource.getRepository(OrderMt);
-    testOrder = orderRepository.create({
-      tenantId: 1,
-      orderNo: `ORD${Date.now()}`,
-      userId: testUser.id,
-      amount: 1,
-      costAmount: 0.5,
-      payAmount: 1,
-      orderType: 1,
-      payType: 2,
-      payState: 0, // 未支付
-      state: 0,
-      addressId: testAddress.id,
-      merchantId: testMerchant.id,
-      supplierId: testSupplier.id,
-      createdBy: testUser.id,
-      updatedBy: testUser.id
-    });
-    await orderRepository.save(testOrder);
-
-    // 创建测试支付记录,使用与真实回调数据一致的金额
-    const paymentRepository = dataSource.getRepository(PaymentMtEntity);
-    testPayment = paymentRepository.create({
-      externalOrderId: testOrder.id, // 使用订单ID作为外部订单ID
-      userId: testUser.id,
-      totalAmount: 1, // 1分钱,与真实回调数据一致
-      description: '测试支付',
-      paymentStatus: PaymentStatus.PROCESSING, // 设置为处理中状态,模拟已发起支付
-      openid: testUser.openid!,
-      outTradeNo: `ORDER_${testOrder.id}_${Date.now()}`,
-      tenantId: 1
-    });
-    await paymentRepository.save(testPayment);
+    // 创建完整的测试数据
+    testData = await testFactory.createCompleteTestData(1);
 
     // 设置微信支付SDK的全局mock
     const mockWxPay = {
@@ -150,7 +73,7 @@ describe('支付回调API集成测试 - 多租户版本', () => {
       }),
       verifySign: vi.fn().mockResolvedValue(true),
       decipher_gcm: vi.fn().mockReturnValue(JSON.stringify({
-        out_trade_no: testPayment.outTradeNo, // 使用数据库中保存的 outTradeNo
+        out_trade_no: testData.payment.outTradeNo, // 使用数据库中保存的 outTradeNo
         trade_state: 'SUCCESS',
         transaction_id: 'test_transaction_id',
         amount: {
@@ -286,94 +209,9 @@ describe('支付回调API集成测试 - 多租户版本', () => {
 
     it('应该验证多租户数据隔离', async () => {
       // 创建第二个租户的测试数据
-      const dataSource = await IntegrationTestDatabase.getDataSource();
-
-      const userRepository = dataSource.getRepository(UserEntityMt);
-      const testUser2 = userRepository.create({
-        username: `test_user2_${Date.now()}`,
-        password: 'test_password',
-        nickname: '测试用户2',
-        openid: 'oJy1-16IIG18XZLl7G32k1hHMUFg2',
-        tenantId: 2
-      });
-      await userRepository.save(testUser2);
-
-      // 创建第二个租户的商户记录
-      const merchantRepository = dataSource.getRepository(MerchantMt);
-      const testMerchant2 = merchantRepository.create({
-        tenantId: 2,
-        name: '测试商户2',
-        username: `test_merchant2_${Date.now()}`,
-        password: 'test_password',
-        state: 1,
-        createdBy: testUser2.id,
-        updatedBy: testUser2.id
-      });
-      await merchantRepository.save(testMerchant2);
-
-      // 创建第二个租户的供货商记录
-      const supplierRepository = dataSource.getRepository(SupplierMt);
-      const testSupplier2 = supplierRepository.create({
-        tenantId: 2,
-        name: '测试供货商2',
-        username: `test_supplier2_${Date.now()}`,
-        password: 'test_password',
-        state: 1,
-        createdBy: testUser2.id,
-        updatedBy: testUser2.id
-      });
-      await supplierRepository.save(testSupplier2);
-
-      // 创建第二个租户的配送地址记录
-      const addressRepository = dataSource.getRepository(DeliveryAddressMt);
-      const testAddress2 = addressRepository.create({
-        tenantId: 2,
-        userId: testUser2.id,
-        name: '测试收货人2',
-        mobile: '13800138001',
-        province: 110000,
-        city: 110100,
-        district: 110101,
-        town: 110101001,
-        address: '测试地址2',
-        isDefault: 1,
-        createdBy: testUser2.id,
-        updatedBy: testUser2.id
-      });
-      await addressRepository.save(testAddress2);
-
-      const orderRepository = dataSource.getRepository(OrderMt);
-      const testOrder2 = orderRepository.create({
-        tenantId: 2,
-        orderNo: `ORD${Date.now()}_2`,
-        userId: testUser2.id,
-        amount: 1,
-        costAmount: 0.5,
-        payAmount: 1,
-        orderType: 1,
-        payType: 2,
-        payState: 0,
-        state: 0,
-        addressId: testAddress2.id,
-        merchantId: testMerchant2.id,
-        supplierId: testSupplier2.id,
-        createdBy: testUser2.id,
-        updatedBy: testUser2.id
-      });
-      await orderRepository.save(testOrder2);
-
-      const paymentRepository = dataSource.getRepository(PaymentMtEntity);
-      const testPayment2 = paymentRepository.create({
-        externalOrderId: testOrder2.id,
-        userId: testUser2.id,
-        totalAmount: 1,
-        description: '测试支付2',
-        paymentStatus: PaymentStatus.PROCESSING,
-        openid: testUser2.openid!,
-        outTradeNo: `ORDER_${testOrder2.id}_${Date.now()}`,
-        tenantId: 2
-      });
-      await paymentRepository.save(testPayment2);
+      const multiTenantData = await testFactory.createMultiTenantTestData();
+      const tenant1Data = multiTenantData.tenant1;
+      const tenant2Data = multiTenantData.tenant2;
 
       // 处理租户1的支付回调
       const response = await client.payment.callback.$post({
@@ -388,14 +226,16 @@ describe('支付回调API集成测试 - 多租户版本', () => {
       expect(response.status).toBe(200);
 
       // 验证租户1的订单状态已更新
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const orderRepository = dataSource.getRepository(OrderMt);
       const updatedOrder1 = await orderRepository.findOne({
-        where: { id: testOrder.id, tenantId: 1 }
+        where: { id: tenant1Data.order.id, tenantId: 1 }
       });
       expect(updatedOrder1?.payState).toBe(2); // 已支付
 
       // 验证租户2的订单状态未受影响
       const updatedOrder2 = await orderRepository.findOne({
-        where: { id: testOrder2.id, tenantId: 2 }
+        where: { id: tenant2Data.order.id, tenantId: 2 }
       });
       expect(updatedOrder2?.payState).toBe(0); // 仍为未支付
     });