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, adminDeliveryAddressRoutesMt } from '../../src/routes'; import { DeliveryAddressMt } from '../../src/entities'; // 设置集成测试钩子 setupIntegrationDatabaseHooksWithEntities([UserEntityMt, RoleMt, AreaEntityMt, DeliveryAddressMt, FileMt]) describe('多租户数据隔离测试', () => { let userClient: ReturnType>; let adminClient: ReturnType>; let tenant1UserToken: string; let tenant2UserToken: string; let tenant1AdminToken: string; let tenant2AdminToken: string; let tenant1User: UserEntityMt; let tenant2User: UserEntityMt; let tenant1Admin: UserEntityMt; let tenant2Admin: UserEntityMt; let tenant1Province: AreaEntityMt; let tenant1City: AreaEntityMt; let tenant1District: AreaEntityMt; let tenant2Province: AreaEntityMt; let tenant2City: AreaEntityMt; let tenant2District: AreaEntityMt; beforeEach(async () => { // 创建测试客户端 userClient = testClient(userDeliveryAddressRoutesMt); adminClient = testClient(adminDeliveryAddressRoutesMt); // 创建租户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; // 创建租户1的管理员 const dataSource = await IntegrationTestDatabase.getDataSource(); const userRepository = dataSource.getRepository(UserEntityMt); tenant1Admin = userRepository.create({ username: `test_admin_tenant1_${Date.now()}`, password: 'admin_password', nickname: '租户1管理员', registrationSource: 'web', tenantId: 1 }); await userRepository.save(tenant1Admin); // 创建租户2的管理员 tenant2Admin = userRepository.create({ username: `test_admin_tenant2_${Date.now()}`, password: 'admin_password', nickname: '租户2管理员', registrationSource: 'web', tenantId: 2 }); await userRepository.save(tenant2Admin); // 生成各租户用户的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 }); // 生成各租户管理员的token tenant1AdminToken = JWTUtil.generateToken({ id: tenant1Admin.id, username: tenant1Admin.username, roles: [{name:'admin'}], tenantId: 1 }); tenant2AdminToken = JWTUtil.generateToken({ id: tenant2Admin.id, username: tenant2Admin.username, roles: [{name:'admin'}], 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' } ); }); describe('用户路由租户隔离', () => { it('用户应该只能看到自己租户的地址', async () => { // 租户1用户访问地址列表 const tenant1Response = await userClient.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 userClient.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 userClient[':id'].$get({ param: { id: tenant2Address.id } }, { headers: { 'Authorization': `Bearer ${tenant1UserToken}` } }); // 应该返回404,因为租户隔离机制应该阻止跨租户访问 expect(crossTenantResponse.status).toBe(404); // 租户2用户尝试访问租户1的地址 const crossTenantResponse2 = await userClient[':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 userClient[':id'].$put({ param: { id: tenant2Address.id }, json: { name: '尝试更新' } }, { headers: { 'Authorization': `Bearer ${tenant1UserToken}` } }); // 应该返回404 expect(updateResponse.status).toBe(404); // 租户1用户尝试删除租户2的地址 const deleteResponse = await userClient[':id'].$delete({ param: { id: tenant2Address.id } }, { headers: { 'Authorization': `Bearer ${tenant1UserToken}` } }); // 应该返回404 expect(deleteResponse.status).toBe(404); }); }); describe('管理员路由租户隔离', () => { it('管理员应该只能管理自己租户的地址', async () => { // 租户1管理员访问地址列表 const tenant1Response = await adminClient.index.$get({ query: {} }, { headers: { 'Authorization': `Bearer ${tenant1AdminToken}` } }); 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); }); } // 租户2管理员访问地址列表 const tenant2Response = await adminClient.index.$get({ query: {} }, { headers: { 'Authorization': `Bearer ${tenant2AdminToken}` } }); 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); }); } }); it('管理员应该无法访问其他租户的地址详情', async () => { // 为租户2创建一个地址 const tenant2Address = await TestDataFactory.createTestDeliveryAddress( tenant2User.id, tenant2Province.id, tenant2City.id, tenant2District.id, { name: '租户2管理员测试地址', phone: '13800138006', address: '租户2管理员测试地址', tenantId: 2 } ); // 租户1管理员尝试访问租户2的地址 const crossTenantResponse = await adminClient[':id'].$get({ param: { id: tenant2Address.id } }, { headers: { 'Authorization': `Bearer ${tenant1AdminToken}` } }); // 应该返回404 expect(crossTenantResponse.status).toBe(404); }); it('管理员应该无法操作其他租户的地址', async () => { // 为租户2创建一个地址 const tenant2Address = await TestDataFactory.createTestDeliveryAddress( tenant2User.id, tenant2Province.id, tenant2City.id, tenant2District.id, { name: '租户2操作测试地址', phone: '13800138007', address: '租户2操作测试地址', tenantId: 2 } ); // 租户1管理员尝试更新租户2的地址 const updateResponse = await adminClient[':id'].$put({ param: { id: tenant2Address.id }, json: { name: '租户1尝试更新' } }, { headers: { 'Authorization': `Bearer ${tenant1AdminToken}` } }); // 应该返回404 expect(updateResponse.status).toBe(404); // 租户1管理员尝试删除租户2的地址 const deleteResponse = await adminClient[':id'].$delete({ param: { id: tenant2Address.id } }, { headers: { 'Authorization': `Bearer ${tenant1AdminToken}` } }); // 应该返回404 expect(deleteResponse.status).toBe(404); }); it('管理员应该只能在自己租户内创建地址', async () => { // 租户1管理员为租户1用户创建地址 const createData = { userId: tenant1User.id, name: '租户1管理员创建', phone: '13800138008', address: '租户1管理员创建', receiverProvince: tenant1Province.id, receiverCity: tenant1City.id, receiverDistrict: tenant1District.id, receiverTown: 1, state: 1, isDefault: 0 }; const response = await adminClient.index.$post({ json: createData }, { headers: { 'Authorization': `Bearer ${tenant1AdminToken}` } }); expect(response.status).toBe(201); // 验证创建的地址属于租户1 if (response.status === 201) { const data = await response.json(); expect(data.tenantId).toBe(1); } }); }); 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 userClient.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 userClient.index.$post({ json: crossTenantAreaData }, { headers: { 'Authorization': `Bearer ${tenant1UserToken}` } }); // 应该返回400,因为地区数据不属于当前租户 expect(crossTenantResponse.status).toBe(400); }); }); });