import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'; import { testClient } from 'hono/testing'; import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities, TestDataFactory } from '@d8d/shared-test-util'; import { JWTUtil } from '@d8d/shared-utils'; import { UserEntityMt, RoleMt } from '@d8d/user-module-mt'; import { AreaEntityMt, AreaLevel } from '@d8d/geo-areas-mt'; import { FileMt } from '@d8d/file-module-mt'; import { userDeliveryAddressRoutesMt } from '../../src/routes'; import { DeliveryAddressMt } from '../../src/entities'; // 设置集成测试钩子 setupIntegrationDatabaseHooksWithEntities([UserEntityMt, RoleMt, AreaEntityMt, DeliveryAddressMt, FileMt]) describe('用户配送地址管理API集成测试', () => { let client: ReturnType>; let userToken: string; let otherUserToken: string; let testUser: UserEntityMt; let otherUser: UserEntityMt; let testProvince: AreaEntityMt; let testCity: AreaEntityMt; let testDistrict: AreaEntityMt; beforeEach(async () => { // 创建测试客户端 client = testClient(userDeliveryAddressRoutesMt); // 使用测试数据工厂创建完整测试数据集 const testData = await TestDataFactory.createTestDataSet(1); testUser = testData.user; otherUser = testData.otherUser; testProvince = testData.province; testCity = testData.city; testDistrict = testData.district; // 生成测试用户的token userToken = JWTUtil.generateToken({ id: testUser.id, username: testUser.username, roles: [{name:'user'}] }); // 生成其他用户的token otherUserToken = JWTUtil.generateToken({ id: otherUser.id, username: otherUser.username, roles: [{name:'user'}] }); }); describe('GET /delivery-address', () => { it('应该返回当前用户的配送地址列表', async () => { // 为测试用户创建一些地址 const userAddress1 = await TestDataFactory.createTestDeliveryAddress( testUser.id, testProvince.id, testCity.id, testDistrict.id, { name: '用户地址1', phone: '13800138001', address: '用户地址1' } ); const userAddress2 = await TestDataFactory.createTestDeliveryAddress( testUser.id, testProvince.id, testCity.id, testDistrict.id, { name: '用户地址2', phone: '13800138002', address: '用户地址2' } ); // 为其他用户创建一个地址,确保不会返回 const otherUserAddress = await TestDataFactory.createTestDeliveryAddress( otherUser.id, testProvince.id, testCity.id, testDistrict.id, { name: '其他用户地址', phone: '13800138003', address: '其他用户地址' } ); const response = await client.index.$get({ query: {} }, { headers: { 'Authorization': `Bearer ${userToken}` } }); console.debug('用户配送地址列表响应状态:', response.status); expect(response.status).toBe(200); if (response.status === 200) { const data = await response.json(); if (data && 'data' in data) { expect(Array.isArray(data.data)).toBe(true); // 应该只返回当前用户的地址 data.data.forEach((address: any) => { expect(address.user?.id).toBe(testUser.id); }); } } }); it('应该拒绝未认证用户的访问', async () => { const response = await client.index.$get({ query: {} }); expect(response.status).toBe(401); }); }); describe('POST /delivery-address', () => { it('应该成功创建配送地址并自动使用当前用户ID', async () => { const createData = { name: '张三', phone: '13800138000', address: '朝阳区建国路88号', receiverProvince: testProvince.id, receiverCity: testCity.id, receiverDistrict: testDistrict.id, receiverTown: 1, state: 1, isDefault: 1 }; const response = await client.index.$post({ json: createData }, { headers: { 'Authorization': `Bearer ${userToken}` } }); console.debug('用户创建配送地址响应状态:', response.status); expect(response.status).toBe(201); if (response.status === 201) { const data = await response.json(); console.debug('用户创建地址响应数据:', JSON.stringify(data, null, 2)); expect(data).toHaveProperty('id'); expect(data.userId).toBe(testUser.id); // 自动使用当前用户ID expect(data.name).toBe(createData.name); expect(data.phone).toBe(createData.phone); expect(data.address).toBe(createData.address); } }); it('应该验证创建配送地址的必填字段', async () => { const invalidData = { // 缺少必填字段 name: '', phone: '', address: '', receiverProvince: 0, receiverCity: 0, receiverDistrict: 0 }; const response = await client.index.$post({ json: invalidData }, { headers: { 'Authorization': `Bearer ${userToken}` } }); expect(response.status).toBe(400); }); }); describe('GET /delivery-address/:id', () => { it('应该返回当前用户的配送地址详情', async () => { // 先为当前用户创建一个配送地址 const testDeliveryAddress = await TestDataFactory.createTestDeliveryAddress( testUser.id, testProvince.id, testCity.id, testDistrict.id, { name: '王五', phone: '13600136000', address: '海淀区中关村大街1号' } ); const response = await client[':id'].$get({ param: { id: testDeliveryAddress.id } }, { headers: { 'Authorization': `Bearer ${userToken}` } }); console.debug('用户配送地址详情响应状态:', response.status); expect(response.status).toBe(200); if (response.status === 200) { const data = await response.json(); expect(data.id).toBe(testDeliveryAddress.id); expect(data.user?.id).toBe(testUser.id); expect(data.name).toBe(testDeliveryAddress.name); expect(data.phone).toBe(testDeliveryAddress.phone); expect(data.address).toBe(testDeliveryAddress.address); } }); it('应该拒绝访问其他用户的配送地址', async () => { // 为其他用户创建一个配送地址 const otherUserAddress = await TestDataFactory.createTestDeliveryAddress( otherUser.id, testProvince.id, testCity.id, testDistrict.id, { name: '其他用户地址', phone: '13600136001', address: '其他用户地址' } ); // 当前用户尝试访问其他用户的地址 const response = await client[':id'].$get({ param: { id: otherUserAddress.id } }, { headers: { 'Authorization': `Bearer ${userToken}` } }); console.debug('用户访问其他用户地址响应状态:', response.status); expect(response.status).toBe(403); // 数据权限控制返回403 }); it('应该处理不存在的配送地址', async () => { const response = await client[':id'].$get({ param: { id: 999999 } }, { headers: { 'Authorization': `Bearer ${userToken}` } }); expect(response.status).toBe(404); }); }); describe('PUT /delivery-address/:id', () => { it('应该成功更新当前用户的配送地址', async () => { // 先为当前用户创建一个配送地址 const testDeliveryAddress = await TestDataFactory.createTestDeliveryAddress( testUser.id, testProvince.id, testCity.id, testDistrict.id, { name: '原始姓名', phone: '13500135000', address: '原始地址' } ); const updateData = { name: '更新后的姓名', phone: '13700137000', address: '更新后的地址', isDefault: 1 }; const response = await client[':id'].$put({ param: { id: testDeliveryAddress.id }, json: updateData }, { headers: { 'Authorization': `Bearer ${userToken}` } }); console.debug('用户更新配送地址响应状态:', response.status); expect(response.status).toBe(200); if (response.status === 200) { const data = await response.json(); expect(data.name).toBe(updateData.name); expect(data.phone).toBe(updateData.phone); expect(data.address).toBe(updateData.address); expect(data.isDefault).toBe(updateData.isDefault); } }); it('应该拒绝更新其他用户的配送地址', async () => { // 为其他用户创建一个配送地址 const otherUserAddress = await TestDataFactory.createTestDeliveryAddress( otherUser.id, testProvince.id, testCity.id, testDistrict.id, { name: '其他用户地址', phone: '13500135001', address: '其他用户地址' } ); const updateData = { name: '尝试更新的姓名', phone: '13700137001', address: '尝试更新的地址' }; // 当前用户尝试更新其他用户的地址 const response = await client[':id'].$put({ param: { id: otherUserAddress.id }, json: updateData }, { headers: { 'Authorization': `Bearer ${userToken}` } }); console.debug('用户更新其他用户地址响应状态:', response.status); expect(response.status).toBe(403); // 数据权限控制返回403 }); }); describe('DELETE /delivery-address/:id', () => { it('应该成功删除当前用户的配送地址', async () => { // 先为当前用户创建一个配送地址 const testDeliveryAddress = await TestDataFactory.createTestDeliveryAddress( testUser.id, testProvince.id, testCity.id, testDistrict.id, { name: '待删除地址', phone: '13400134000', address: '待删除地址' } ); const response = await client[':id'].$delete({ param: { id: testDeliveryAddress.id } }, { headers: { 'Authorization': `Bearer ${userToken}` } }); console.debug('用户删除配送地址响应状态:', response.status); expect(response.status).toBe(204); // 验证配送地址确实被删除 const dataSource = await IntegrationTestDatabase.getDataSource(); const deliveryAddressRepository = dataSource.getRepository(DeliveryAddressMt); const deletedDeliveryAddress = await deliveryAddressRepository.findOne({ where: { id: testDeliveryAddress.id } }); expect(deletedDeliveryAddress).toBeNull(); }); it('应该拒绝删除其他用户的配送地址', async () => { // 为其他用户创建一个配送地址 const otherUserAddress = await TestDataFactory.createTestDeliveryAddress( otherUser.id, testProvince.id, testCity.id, testDistrict.id, { name: '其他用户地址', phone: '13400134001', address: '其他用户地址' } ); // 当前用户尝试删除其他用户的地址 const response = await client[':id'].$delete({ param: { id: otherUserAddress.id } }, { headers: { 'Authorization': `Bearer ${userToken}` } }); console.debug('用户删除其他用户地址响应状态:', response.status); expect(response.status).toBe(403); // 数据权限控制返回403 }); }); describe('数据权限验证', () => { it('用户应该只能访问和操作自己的数据', async () => { // 为两个用户都创建地址 const userAddress = await TestDataFactory.createTestDeliveryAddress( testUser.id, testProvince.id, testCity.id, testDistrict.id, { name: '用户地址', phone: '13800138004', address: '用户地址' } ); const otherUserAddress = await TestDataFactory.createTestDeliveryAddress( otherUser.id, testProvince.id, testCity.id, testDistrict.id, { name: '其他用户地址', phone: '13800138005', address: '其他用户地址' } ); // 当前用户应该只能看到自己的地址 const listResponse = await client.index.$get({ query: {} }, { headers: { 'Authorization': `Bearer ${userToken}` } }); expect(listResponse.status).toBe(200); const listData = await listResponse.json(); if (listData && 'data' in listData) { expect(Array.isArray(listData.data)).toBe(true); // 应该只包含当前用户的地址 listData.data.forEach((address: any) => { expect(address.user?.id).toBe(testUser.id); }); } // 当前用户应该无法访问其他用户的地址详情 const getResponse = await client[':id'].$get({ param: { id: otherUserAddress.id } }, { headers: { 'Authorization': `Bearer ${userToken}` } }); expect(getResponse.status).toBe(403); // 当前用户应该无法更新其他用户的地址 const updateResponse = await client[':id'].$put({ param: { id: otherUserAddress.id }, json: { name: '尝试更新' } }, { headers: { 'Authorization': `Bearer ${userToken}` } }); expect(updateResponse.status).toBe(403); // 当前用户应该无法删除其他用户的地址 const deleteResponse = await client[':id'].$delete({ param: { id: otherUserAddress.id } }, { headers: { 'Authorization': `Bearer ${userToken}` } }); expect(deleteResponse.status).toBe(403); }); }); describe('用户租户隔离测试', () => { let tenant1UserToken: string; let tenant2UserToken: string; let tenant1User: UserEntityMt; let tenant2User: UserEntityMt; let tenant1Province: AreaEntityMt; let tenant1City: AreaEntityMt; let tenant1District: AreaEntityMt; let tenant2Province: AreaEntityMt; let tenant2City: AreaEntityMt; let tenant2District: AreaEntityMt; beforeEach(async () => { // 创建租户1的测试数据 const tenant1Data = await TestDataFactory.createTestDataSet(1); tenant1User = tenant1Data.user; tenant1Province = tenant1Data.province; tenant1City = tenant1Data.city; tenant1District = tenant1Data.district; // 创建租户2的测试数据 const tenant2Data = await TestDataFactory.createTestDataSet(2); tenant2User = tenant2Data.user; tenant2Province = tenant2Data.province; tenant2City = tenant2Data.city; tenant2District = tenant2Data.district; // 生成各租户用户的token tenant1UserToken = JWTUtil.generateToken({ id: tenant1User.id, username: tenant1User.username, roles: [{name:'user'}], tenantId: 1 }); tenant2UserToken = JWTUtil.generateToken({ id: tenant2User.id, username: tenant2User.username, roles: [{name:'user'}], tenantId: 2 }); // 为两个租户都创建一些配送地址 await TestDataFactory.createTestDeliveryAddress( tenant1User.id, tenant1Province.id, tenant1City.id, tenant1District.id, { name: '租户1地址1', phone: '13800138001', address: '租户1地址1' } ); await TestDataFactory.createTestDeliveryAddress( tenant2User.id, tenant2Province.id, tenant2City.id, tenant2District.id, { name: '租户2地址1', phone: '13800138002', address: '租户2地址1' } ); }); it('用户应该只能看到自己租户的地址', async () => { // 租户1用户访问地址列表 const tenant1Response = await client.index.$get({ query: {} }, { headers: { 'Authorization': `Bearer ${tenant1UserToken}` } }); expect(tenant1Response.status).toBe(200); const tenant1Data = await tenant1Response.json(); if (tenant1Data && 'data' in tenant1Data) { // 租户1用户应该只能看到租户1的地址 tenant1Data.data.forEach((address: any) => { expect(address.tenantId).toBe(1); expect(address.userId).toBe(tenant1User.id); }); } // 租户2用户访问地址列表 const tenant2Response = await client.index.$get({ query: {} }, { headers: { 'Authorization': `Bearer ${tenant2UserToken}` } }); expect(tenant2Response.status).toBe(200); const tenant2Data = await tenant2Response.json(); if (tenant2Data && 'data' in tenant2Data) { // 租户2用户应该只能看到租户2的地址 tenant2Data.data.forEach((address: any) => { expect(address.tenantId).toBe(2); expect(address.userId).toBe(tenant2User.id); }); } }); it('用户应该无法访问其他租户的地址详情', async () => { // 为租户1和租户2都创建地址 const tenant1Address = await TestDataFactory.createTestDeliveryAddress( tenant1User.id, tenant1Province.id, tenant1City.id, tenant1District.id, { name: '租户1专用地址', phone: '13800138003', address: '租户1专用地址', tenantId: 1 } ); const tenant2Address = await TestDataFactory.createTestDeliveryAddress( tenant2User.id, tenant2Province.id, tenant2City.id, tenant2District.id, { name: '租户2专用地址', phone: '13800138004', address: '租户2专用地址', tenantId: 2 } ); // 租户1用户尝试访问租户2的地址 const crossTenantResponse = await client[':id'].$get({ param: { id: tenant2Address.id } }, { headers: { 'Authorization': `Bearer ${tenant1UserToken}` } }); // 应该返回404,因为租户隔离机制应该阻止跨租户访问 expect(crossTenantResponse.status).toBe(404); // 租户2用户尝试访问租户1的地址 const crossTenantResponse2 = await client[':id'].$get({ param: { id: tenant1Address.id } }, { headers: { 'Authorization': `Bearer ${tenant2UserToken}` } }); // 应该返回404 expect(crossTenantResponse2.status).toBe(404); }); it('用户应该无法操作其他租户的地址', async () => { // 为租户2创建一个地址 const tenant2Address = await TestDataFactory.createTestDeliveryAddress( tenant2User.id, tenant2Province.id, tenant2City.id, tenant2District.id, { name: '租户2地址', phone: '13800138005', address: '租户2地址', tenantId: 2 } ); // 租户1用户尝试更新租户2的地址 const updateResponse = await client[':id'].$put({ param: { id: tenant2Address.id }, json: { name: '尝试更新' } }, { headers: { 'Authorization': `Bearer ${tenant1UserToken}` } }); // 应该返回404 expect(updateResponse.status).toBe(404); // 租户1用户尝试删除租户2的地址 const deleteResponse = await client[':id'].$delete({ param: { id: tenant2Address.id } }, { headers: { 'Authorization': `Bearer ${tenant1UserToken}` } }); // 应该返回404 expect(deleteResponse.status).toBe(404); }); describe('地区数据租户隔离', () => { it('应该使用正确的租户地区数据', async () => { // 租户1用户创建地址,应该使用租户1的地区数据 const createData = { name: '租户1地区测试', phone: '13800138009', address: '租户1地区测试', receiverProvince: tenant1Province.id, receiverCity: tenant1City.id, receiverDistrict: tenant1District.id, receiverTown: 1, state: 1, isDefault: 0 }; const response = await client.index.$post({ json: createData }, { headers: { 'Authorization': `Bearer ${tenant1UserToken}` } }); expect(response.status).toBe(201); // 租户1用户尝试使用租户2的地区数据创建地址 const crossTenantAreaData = { name: '跨租户地区测试', phone: '13800138010', address: '跨租户地区测试', receiverProvince: tenant2Province.id, // 租户2的省份 receiverCity: tenant1City.id, receiverDistrict: tenant1District.id, receiverTown: 1, state: 1, isDefault: 0 }; const crossTenantResponse = await client.index.$post({ json: crossTenantAreaData }, { headers: { 'Authorization': `Bearer ${tenant1UserToken}` } }); // 应该返回400,因为地区数据不属于当前租户 expect(crossTenantResponse.status).toBe(400); }); }); }); });