import { describe, it, expect, beforeEach } from 'vitest'; import { testClient } from 'hono/testing'; import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities, IntegrationTestAssertions } from '@d8d/shared-test-util'; import { areasRoutesMt } from '../../src/api/areas/index.mt'; import { AreaEntityMt, AreaLevel } from '../../src/modules/areas/area.entity.mt'; import { DisabledStatus } from '@d8d/shared-types'; import { TestDataFactory } from '../utils/test-data-factory'; import { TestQueryFactory } from '../utils/test-query-factory'; // 定义响应类型 interface SuccessResponse { success: boolean; data: { provinces: Array<{ id: number; name: string; code: string; level: number; parentId: number | null; }>; pagination: { page: number; pageSize: number; total: number; totalPages: number; }; }; message: string; } interface ErrorResponse { code: number; message: string; errors?: Array<{ path: string[]; message: string; }>; } // 设置集成测试钩子 setupIntegrationDatabaseHooksWithEntities([AreaEntityMt]) describe('区域API集成测试', () => { let client: ReturnType>; let testAreas: AreaEntityMt[]; beforeEach(async () => { // 创建测试客户端 client = testClient(areasRoutesMt); // 创建测试数据 const dataSource = await IntegrationTestDatabase.getDataSource(); // 创建启用状态的省份(租户1) const province1 = await TestDataFactory.createTestArea(dataSource, { name: '北京市', level: AreaLevel.PROVINCE, isDisabled: DisabledStatus.ENABLED, tenantId: 1 }); const province2 = await TestDataFactory.createTestArea(dataSource, { name: '上海市', level: AreaLevel.PROVINCE, isDisabled: DisabledStatus.ENABLED, tenantId: 1 }); const province3 = await TestDataFactory.createTestArea(dataSource, { name: '广东省', level: AreaLevel.PROVINCE, isDisabled: DisabledStatus.ENABLED, tenantId: 1 }); // 创建启用状态的城市 const city11 = await TestDataFactory.createTestArea(dataSource, { name: '北京市', level: AreaLevel.CITY, parentId: province1.id, isDisabled: DisabledStatus.ENABLED }); const city12 = await TestDataFactory.createTestArea(dataSource, { name: '朝阳区', level: AreaLevel.CITY, parentId: province1.id, isDisabled: DisabledStatus.ENABLED }); const city13 = await TestDataFactory.createTestArea(dataSource, { name: '海淀区', level: AreaLevel.CITY, parentId: province1.id, isDisabled: DisabledStatus.ENABLED }); const city21 = await TestDataFactory.createTestArea(dataSource, { name: '上海市', level: AreaLevel.CITY, parentId: province2.id, isDisabled: DisabledStatus.ENABLED }); const city22 = await TestDataFactory.createTestArea(dataSource, { name: '浦东新区', level: AreaLevel.CITY, parentId: province2.id, isDisabled: DisabledStatus.ENABLED }); // 创建启用状态的区县 const district101 = await TestDataFactory.createTestArea(dataSource, { name: '朝阳区', level: AreaLevel.DISTRICT, parentId: city12.id, isDisabled: DisabledStatus.ENABLED }); const district102 = await TestDataFactory.createTestArea(dataSource, { name: '海淀区', level: AreaLevel.DISTRICT, parentId: city13.id, isDisabled: DisabledStatus.ENABLED }); const district103 = await TestDataFactory.createTestArea(dataSource, { name: '西城区', level: AreaLevel.DISTRICT, parentId: city12.id, isDisabled: DisabledStatus.ENABLED }); const district201 = await TestDataFactory.createTestArea(dataSource, { name: '浦东新区', level: AreaLevel.DISTRICT, parentId: city22.id, isDisabled: DisabledStatus.ENABLED }); // 创建禁用状态的区域用于测试过滤 const disabledProvince = await TestDataFactory.createTestArea(dataSource, { name: '禁用省份', level: AreaLevel.PROVINCE, isDisabled: DisabledStatus.DISABLED }); const disabledCity = await TestDataFactory.createTestArea(dataSource, { name: '禁用城市', level: AreaLevel.CITY, parentId: province3.id, isDisabled: DisabledStatus.DISABLED }); const disabledDistrict = await TestDataFactory.createTestArea(dataSource, { name: '禁用区县', level: AreaLevel.DISTRICT, parentId: city12.id, isDisabled: DisabledStatus.DISABLED }); testAreas = [ province1, province2, province3, city11, city12, city13, city21, city22, district101, district102, district103, district201, disabledProvince, disabledCity, disabledDistrict ]; }); describe('GET /areas/provinces', () => { it('应该成功获取启用状态的省份列表', async () => { const response = await client.provinces.$get({ query: TestQueryFactory.createProvincesQuery() }); IntegrationTestAssertions.expectStatus(response, 200); if (response.status === 200) { const data = await response.json(); // 验证响应数据格式 expect(data).toHaveProperty('success', true); expect(data).toHaveProperty('data'); expect(data.data).toHaveProperty('provinces'); expect(data.data).toHaveProperty('pagination'); // 验证只返回启用状态的省份 const provinces = data.data.provinces; expect(provinces).toHaveLength(3); // 只返回3个启用状态的省份 // 验证不包含禁用状态的省份 const disabledProvince = provinces.find((p: any) => p.isDisabled === DisabledStatus.DISABLED); expect(disabledProvince).toBeUndefined(); // 验证分页信息 expect(data.data.pagination).toEqual({ page: 1, pageSize: 50, total: 3, totalPages: 1 }); } }); it('应该正确处理分页参数', async () => { const response = await client.provinces.$get({ query: TestQueryFactory.createPaginationQuery(1, 2) }); IntegrationTestAssertions.expectStatus(response, 200); if (response.status === 200) { const data = await response.json(); // 验证分页结果 expect(data.data.provinces).toHaveLength(2); expect(data.data.pagination).toEqual({ page: 1, pageSize: 2, total: 3, totalPages: 2 }); } }); }); describe('GET /areas/cities', () => { it('应该成功获取指定省份下启用状态的城市列表', async () => { const response = await client.cities.$get({ query: TestQueryFactory.createCitiesQuery(testAreas[0].id) }); IntegrationTestAssertions.expectStatus(response, 200); if (response.status === 200) { const data = await response.json(); // 验证响应数据格式 expect(data).toHaveProperty('success', true); expect(data).toHaveProperty('data'); expect(data.data).toHaveProperty('cities'); // 验证只返回启用状态的城市 const cities = data.data.cities; expect(cities).toHaveLength(3); // 北京市下有3个启用状态的城市 // 验证城市数据正确 const cityNames = cities.map((c: any) => c.name); expect(cityNames).toContain('北京市'); expect(cityNames).toContain('朝阳区'); expect(cityNames).toContain('海淀区'); // 验证不包含禁用状态的城市 const disabledCity = cities.find((c: any) => c.name === '禁用城市'); expect(disabledCity).toBeUndefined(); } }); it('应该处理不存在的省份ID', async () => { const response = await client.cities.$get({ query: TestQueryFactory.createCitiesQuery(999) }); IntegrationTestAssertions.expectStatus(response, 200); if (response.status === 200) { const data = await response.json(); // 不存在的省份应该返回空数组 expect(data.data.cities).toHaveLength(0); } }); it('应该验证省份ID参数', async () => { const response = await client.cities.$get({ query: TestQueryFactory.createCitiesQuery(0) }); // 参数验证应该返回400错误 IntegrationTestAssertions.expectStatus(response, 400); }); }); describe('GET /areas/districts', () => { it('应该成功获取指定城市下启用状态的区县列表', async () => { // 找到朝阳区城市对象 const chaoyangCity = testAreas.find(area => area.name === '朝阳区' && area.level === AreaLevel.CITY); expect(chaoyangCity).toBeDefined(); const response = await client.districts.$get({ query: TestQueryFactory.createDistrictsQuery(chaoyangCity!.id) }); IntegrationTestAssertions.expectStatus(response, 200); if (response.status === 200) { const data = await response.json(); // 验证响应数据格式 expect(data).toHaveProperty('success', true); expect(data).toHaveProperty('data'); expect(data.data).toHaveProperty('districts'); // 验证只返回启用状态的区县 const districts = data.data.districts; expect(districts).toHaveLength(2); // 朝阳区下有2个启用状态的区县 // 验证区县数据正确 const districtNames = districts.map((d: any) => d.name); expect(districtNames).toContain('朝阳区'); expect(districtNames).toContain('西城区'); // 验证不包含禁用状态的区县 const disabledDistrict = districts.find((d: any) => d.name === '禁用区县'); expect(disabledDistrict).toBeUndefined(); } }); it('应该处理不存在的城市ID', async () => { const response = await client.districts.$get({ query: TestQueryFactory.createDistrictsQuery(999) }); IntegrationTestAssertions.expectStatus(response, 200); if (response.status === 200) { const data = await response.json(); // 不存在的城市应该返回空数组 expect(data.data.districts).toHaveLength(0); } }); it('应该验证城市ID参数', async () => { const response = await client.districts.$get({ query: TestQueryFactory.createDistrictsQuery(0) }); // 参数验证应该返回400错误 IntegrationTestAssertions.expectStatus(response, 400); }); }); describe('过滤禁用状态验证', () => { it('应该确保所有API只返回启用状态的区域', async () => { // 测试省份API const provincesResponse = await client.provinces.$get({ query: TestQueryFactory.createProvincesQuery() }); IntegrationTestAssertions.expectStatus(provincesResponse, 200); const provincesData = await provincesResponse.json(); // 验证省份不包含禁用状态 if ('data' in provincesData) { const provinces = provincesData.data.provinces; const hasDisabledProvince = provinces.some((p: any) => p.isDisabled === DisabledStatus.DISABLED); expect(hasDisabledProvince).toBe(false); } // 测试城市API const citiesResponse = await client.cities.$get({ query: TestQueryFactory.createCitiesQuery(testAreas[0].id) }); IntegrationTestAssertions.expectStatus(citiesResponse, 200); const citiesData = await citiesResponse.json(); // 验证城市不包含禁用状态 if ('data' in citiesData) { const cities = citiesData.data.cities; const hasDisabledCity = cities.some((c: any) => c.isDisabled === DisabledStatus.DISABLED); expect(hasDisabledCity).toBe(false); } // 测试区县API const chaoyangCity = testAreas.find(area => area.name === '朝阳区' && area.level === AreaLevel.CITY); const districtsResponse = await client.districts.$get({ query: TestQueryFactory.createDistrictsQuery(chaoyangCity!.id) }); IntegrationTestAssertions.expectStatus(districtsResponse, 200); const districtsData = await districtsResponse.json(); // 验证区县不包含禁用状态 if ('data' in districtsData) { const districts = districtsData.data.districts; const hasDisabledDistrict = districts.some((d: any) => d.isDisabled === DisabledStatus.DISABLED); expect(hasDisabledDistrict).toBe(false); } }); }); describe('租户数据隔离测试', () => { beforeEach(async () => { // 为租户2创建测试数据 const dataSource = await IntegrationTestDatabase.getDataSource(); // 租户2的省份 await TestDataFactory.createTestArea(dataSource, { name: '租户2-北京市', level: AreaLevel.PROVINCE, isDisabled: DisabledStatus.ENABLED, tenantId: 2 }); // 租户2的城市 await TestDataFactory.createTestArea(dataSource, { name: '租户2-上海市', level: AreaLevel.PROVINCE, isDisabled: DisabledStatus.ENABLED, tenantId: 2 }); }); it('应该只返回指定租户的数据', async () => { // 测试租户1的数据 const response1 = await client.provinces.$get({ query: TestQueryFactory.createProvincesQuery() }); IntegrationTestAssertions.expectStatus(response1, 200); const data1 = await response1.json(); // 验证租户1只看到租户1的数据 const successData1 = data1 as SuccessResponse; if (successData1.success) { const tenant1Provinces = successData1.data.provinces; expect(tenant1Provinces).toHaveLength(3); // 租户1有3个省份 const hasTenant2Data = tenant1Provinces.some((p: any) => p.name.includes('租户2')); expect(hasTenant2Data).toBe(false); } else { throw new Error('租户1数据获取失败'); } // 测试租户2的数据 const response2 = await client.provinces.$get({ query: { tenantId: 2, page: 1, pageSize: 50 } }); IntegrationTestAssertions.expectStatus(response2, 200); const data2 = await response2.json(); // 验证租户2只看到租户2的数据 const successData2 = data2 as SuccessResponse; if (successData2.success) { const tenant2Provinces = successData2.data.provinces; expect(tenant2Provinces).toHaveLength(2); // 租户2有2个省份 const hasTenant1Data = tenant2Provinces.some((p: any) => p.name.includes('北京市') && !p.name.includes('租户2')); expect(hasTenant1Data).toBe(false); } else { throw new Error('租户2数据获取失败'); } }); it('不同租户的数据应该完全隔离', async () => { // 租户1查询省份 const response1 = await client.provinces.$get({ query: TestQueryFactory.createProvincesQuery() }); IntegrationTestAssertions.expectStatus(response1, 200); const data1 = await response1.json(); // 租户2查询省份 const response2 = await client.provinces.$get({ query: { tenantId: 2, page: 1, pageSize: 50 } }); IntegrationTestAssertions.expectStatus(response2, 200); const data2 = await response2.json(); // 验证两个租户的数据完全不同 const successData1 = data1 as SuccessResponse; const successData2 = data2 as SuccessResponse; if (successData1.success && successData2.success) { const tenant1Names = successData1.data.provinces.map((p: any) => p.name); const tenant2Names = successData2.data.provinces.map((p: any) => p.name); expect(tenant1Names).not.toEqual(tenant2Names); expect(tenant1Names).toContain('北京市'); expect(tenant1Names).toContain('上海市'); expect(tenant1Names).toContain('广东省'); expect(tenant2Names).toContain('租户2-北京市'); expect(tenant2Names).toContain('租户2-上海市'); } else { throw new Error('租户数据获取失败'); } }); it('应该验证tenantId参数', async () => { // 测试缺少tenantId参数 - 明确排除tenantId const response = await client.provinces.$get({ query: { page: 1, pageSize: 50 } as any // 不包含tenantId,使用any绕过类型检查 }); // 应该返回400错误,因为缺少必需的tenantId参数 IntegrationTestAssertions.expectStatus(response, 400); }); it('应该处理不存在的租户ID', async () => { const response = await client.provinces.$get({ query: { tenantId: 999, page: 1, pageSize: 50 } }); IntegrationTestAssertions.expectStatus(response, 200); const data = await response.json(); // 不存在的租户应该返回空数组 const successData = data as SuccessResponse; if (successData.success) { expect(successData.data.provinces).toHaveLength(0); } else { throw new Error('不存在的租户数据获取失败'); } }); }); });