| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- 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<typeof testClient<typeof areasRoutesMt>>;
- 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('不存在的租户数据获取失败');
- }
- });
- });
- });
|