index.test.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. import { describe, it, expect, beforeEach } from 'vitest';
  2. import { testClient } from 'hono/testing';
  3. import {
  4. IntegrationTestDatabase,
  5. setupIntegrationDatabaseHooks,
  6. TestDataFactory
  7. } from '~/utils/server/integration-test-db';
  8. import { areasUserRoutesExport as areaRoutes } from '@d8d/server/api';
  9. import { AreaLevel, AreaEntity } from '@d8d/server/modules/areas/area.entity';
  10. import { DisabledStatus } from '@d8d/server/share/types';
  11. // 设置集成测试钩子
  12. setupIntegrationDatabaseHooks()
  13. describe('区域API集成测试', () => {
  14. let client: ReturnType<typeof testClient<typeof areaRoutes>>['api']['v1']['areas'];
  15. let testAreas: AreaEntity[];
  16. beforeEach(async () => {
  17. // 创建测试客户端
  18. client = testClient(areaRoutes).api.v1.areas;
  19. // 创建测试数据
  20. const dataSource = await IntegrationTestDatabase.getDataSource();
  21. // 创建启用状态的省份
  22. const province1 = await TestDataFactory.createTestArea(dataSource, {
  23. name: '北京市',
  24. level: AreaLevel.PROVINCE,
  25. isDisabled: DisabledStatus.ENABLED
  26. });
  27. const province2 = await TestDataFactory.createTestArea(dataSource, {
  28. name: '上海市',
  29. level: AreaLevel.PROVINCE,
  30. isDisabled: DisabledStatus.ENABLED
  31. });
  32. const province3 = await TestDataFactory.createTestArea(dataSource, {
  33. name: '广东省',
  34. level: AreaLevel.PROVINCE,
  35. isDisabled: DisabledStatus.ENABLED
  36. });
  37. // 创建启用状态的城市
  38. const city11 = await TestDataFactory.createTestArea(dataSource, {
  39. name: '北京市',
  40. level: AreaLevel.CITY,
  41. parentId: province1.id,
  42. isDisabled: DisabledStatus.ENABLED
  43. });
  44. const city12 = await TestDataFactory.createTestArea(dataSource, {
  45. name: '朝阳区',
  46. level: AreaLevel.CITY,
  47. parentId: province1.id,
  48. isDisabled: DisabledStatus.ENABLED
  49. });
  50. const city13 = await TestDataFactory.createTestArea(dataSource, {
  51. name: '海淀区',
  52. level: AreaLevel.CITY,
  53. parentId: province1.id,
  54. isDisabled: DisabledStatus.ENABLED
  55. });
  56. const city21 = await TestDataFactory.createTestArea(dataSource, {
  57. name: '上海市',
  58. level: AreaLevel.CITY,
  59. parentId: province2.id,
  60. isDisabled: DisabledStatus.ENABLED
  61. });
  62. const city22 = await TestDataFactory.createTestArea(dataSource, {
  63. name: '浦东新区',
  64. level: AreaLevel.CITY,
  65. parentId: province2.id,
  66. isDisabled: DisabledStatus.ENABLED
  67. });
  68. // 创建启用状态的区县
  69. const district101 = await TestDataFactory.createTestArea(dataSource, {
  70. name: '朝阳区',
  71. level: AreaLevel.DISTRICT,
  72. parentId: city12.id,
  73. isDisabled: DisabledStatus.ENABLED
  74. });
  75. const district102 = await TestDataFactory.createTestArea(dataSource, {
  76. name: '海淀区',
  77. level: AreaLevel.DISTRICT,
  78. parentId: city13.id,
  79. isDisabled: DisabledStatus.ENABLED
  80. });
  81. const district103 = await TestDataFactory.createTestArea(dataSource, {
  82. name: '西城区',
  83. level: AreaLevel.DISTRICT,
  84. parentId: city12.id,
  85. isDisabled: DisabledStatus.ENABLED
  86. });
  87. const district201 = await TestDataFactory.createTestArea(dataSource, {
  88. name: '浦东新区',
  89. level: AreaLevel.DISTRICT,
  90. parentId: city22.id,
  91. isDisabled: DisabledStatus.ENABLED
  92. });
  93. // 创建禁用状态的区域用于测试过滤
  94. const disabledProvince = await TestDataFactory.createTestArea(dataSource, {
  95. name: '禁用省份',
  96. level: AreaLevel.PROVINCE,
  97. isDisabled: DisabledStatus.DISABLED
  98. });
  99. const disabledCity = await TestDataFactory.createTestArea(dataSource, {
  100. name: '禁用城市',
  101. level: AreaLevel.CITY,
  102. parentId: province3.id,
  103. isDisabled: DisabledStatus.DISABLED
  104. });
  105. const disabledDistrict = await TestDataFactory.createTestArea(dataSource, {
  106. name: '禁用区县',
  107. level: AreaLevel.DISTRICT,
  108. parentId: city12.id,
  109. isDisabled: DisabledStatus.DISABLED
  110. });
  111. testAreas = [
  112. province1, province2, province3,
  113. city11, city12, city13, city21, city22,
  114. district101, district102, district103, district201,
  115. disabledProvince, disabledCity, disabledDistrict
  116. ];
  117. });
  118. describe('GET /areas/provinces', () => {
  119. it('应该成功获取启用状态的省份列表', async () => {
  120. const response = await client.provinces.$get({
  121. query: { page: 1, pageSize: 50 }
  122. });
  123. expect(response.status).toBe(200);
  124. if (response.status === 200) {
  125. const data = await response.json();
  126. // 验证响应数据格式
  127. expect(data).toHaveProperty('success', true);
  128. expect(data).toHaveProperty('data');
  129. expect(data.data).toHaveProperty('provinces');
  130. expect(data.data).toHaveProperty('pagination');
  131. // 验证只返回启用状态的省份
  132. const provinces = data.data.provinces;
  133. expect(provinces).toHaveLength(3); // 只返回3个启用状态的省份
  134. // 验证不包含禁用状态的省份
  135. const disabledProvince = provinces.find((p: any) => p.id === 4);
  136. expect(disabledProvince).toBeUndefined();
  137. // 验证分页信息
  138. expect(data.data.pagination).toEqual({
  139. page: 1,
  140. pageSize: 50,
  141. total: 3,
  142. totalPages: 1
  143. });
  144. }
  145. });
  146. it('应该正确处理分页参数', async () => {
  147. const response = await client.provinces.$get({
  148. query: { page: 1, pageSize: 2 }
  149. });
  150. expect(response.status).toBe(200);
  151. if (response.status === 200) {
  152. const data = await response.json();
  153. // 验证分页结果
  154. expect(data.data.provinces).toHaveLength(2);
  155. expect(data.data.pagination).toEqual({
  156. page: 1,
  157. pageSize: 2,
  158. total: 3,
  159. totalPages: 2
  160. });
  161. }
  162. });
  163. });
  164. describe('GET /areas/cities', () => {
  165. it('应该成功获取指定省份下启用状态的城市列表', async () => {
  166. const response = await client.cities.$get({
  167. query: { provinceId: testAreas[0].id, page: 1, pageSize: 50 }
  168. });
  169. expect(response.status).toBe(200);
  170. if (response.status === 200) {
  171. const data = await response.json();
  172. // 验证响应数据格式
  173. expect(data).toHaveProperty('success', true);
  174. expect(data).toHaveProperty('data');
  175. expect(data.data).toHaveProperty('cities');
  176. // 验证只返回启用状态的城市
  177. const cities = data.data.cities;
  178. expect(cities).toHaveLength(3); // 北京市下有3个启用状态的城市
  179. // 验证城市数据正确
  180. const cityNames = cities.map((c: any) => c.name);
  181. expect(cityNames).toContain('北京市');
  182. expect(cityNames).toContain('朝阳区');
  183. expect(cityNames).toContain('海淀区');
  184. // 验证不包含禁用状态的城市
  185. const disabledCity = cities.find((c: any) => c.name === '禁用城市');
  186. expect(disabledCity).toBeUndefined();
  187. }
  188. });
  189. it('应该处理不存在的省份ID', async () => {
  190. const response = await client.cities.$get({
  191. query: { provinceId: 999, page: 1, pageSize: 50 }
  192. });
  193. expect(response.status).toBe(200);
  194. if (response.status === 200) {
  195. const data = await response.json();
  196. // 不存在的省份应该返回空数组
  197. expect(data.data.cities).toHaveLength(0);
  198. }
  199. });
  200. it('应该验证省份ID参数', async () => {
  201. const response = await client.cities.$get({
  202. query: { provinceId: 0, page: 1, pageSize: 50 }
  203. });
  204. // 参数验证应该返回400错误
  205. expect(response.status).toBe(400);
  206. });
  207. });
  208. describe('GET /areas/districts', () => {
  209. it('应该成功获取指定城市下启用状态的区县列表', async () => {
  210. // 找到朝阳区城市对象
  211. const chaoyangCity = testAreas.find(area => area.name === '朝阳区' && area.level === AreaLevel.CITY);
  212. expect(chaoyangCity).toBeDefined();
  213. const response = await client.districts.$get({
  214. query: { cityId: chaoyangCity!.id, page: 1, pageSize: 50 }
  215. });
  216. expect(response.status).toBe(200);
  217. if (response.status === 200) {
  218. const data = await response.json();
  219. // 验证响应数据格式
  220. expect(data).toHaveProperty('success', true);
  221. expect(data).toHaveProperty('data');
  222. expect(data.data).toHaveProperty('districts');
  223. // 验证只返回启用状态的区县
  224. const districts = data.data.districts;
  225. expect(districts).toHaveLength(2); // 朝阳区下有2个启用状态的区县
  226. // 验证区县数据正确
  227. const districtNames = districts.map((d: any) => d.name);
  228. expect(districtNames).toContain('朝阳区');
  229. expect(districtNames).toContain('西城区');
  230. // 验证不包含禁用状态的区县
  231. const disabledDistrict = districts.find((d: any) => d.name === '禁用区县');
  232. expect(disabledDistrict).toBeUndefined();
  233. }
  234. });
  235. it('应该处理不存在的城市ID', async () => {
  236. const response = await client.districts.$get({
  237. query: { cityId: 999, page: 1, pageSize: 50 }
  238. });
  239. expect(response.status).toBe(200);
  240. if (response.status === 200) {
  241. const data = await response.json();
  242. // 不存在的城市应该返回空数组
  243. expect(data.data.districts).toHaveLength(0);
  244. }
  245. });
  246. it('应该验证城市ID参数', async () => {
  247. const response = await client.districts.$get({
  248. query: { cityId: 0, page: 1, pageSize: 50 }
  249. });
  250. // 参数验证应该返回400错误
  251. expect(response.status).toBe(400);
  252. });
  253. });
  254. describe('过滤禁用状态验证', () => {
  255. it('应该确保所有API只返回启用状态的区域', async () => {
  256. // 测试省份API
  257. const provincesResponse = await client.provinces.$get({
  258. query: { page: 1, pageSize: 50 }
  259. });
  260. expect(provincesResponse.status).toBe(200);
  261. const provincesData = await provincesResponse.json();
  262. // 验证省份不包含禁用状态
  263. if ('data' in provincesData) {
  264. const provinces = provincesData.data.provinces;
  265. const hasDisabledProvince = provinces.some((p: any) => p.isDisabled === DisabledStatus.DISABLED);
  266. expect(hasDisabledProvince).toBe(false);
  267. }
  268. // 测试城市API
  269. const citiesResponse = await client.cities.$get({
  270. query: { provinceId: testAreas[0].id, page: 1, pageSize: 50 }
  271. });
  272. expect(citiesResponse.status).toBe(200);
  273. const citiesData = await citiesResponse.json();
  274. // 验证城市不包含禁用状态
  275. if ('data' in citiesData) {
  276. const cities = citiesData.data.cities;
  277. const hasDisabledCity = cities.some((c: any) => c.isDisabled === DisabledStatus.DISABLED);
  278. expect(hasDisabledCity).toBe(false);
  279. }
  280. // 测试区县API
  281. const chaoyangCity = testAreas.find(area => area.name === '朝阳区' && area.level === AreaLevel.CITY);
  282. const districtsResponse = await client.districts.$get({
  283. query: { cityId: chaoyangCity!.id, page: 1, pageSize: 50 }
  284. });
  285. expect(districtsResponse.status).toBe(200);
  286. const districtsData = await districtsResponse.json();
  287. // 验证区县不包含禁用状态
  288. if ('data' in districtsData) {
  289. const districts = districtsData.data.districts;
  290. const hasDisabledDistrict = districts.some((d: any) => d.isDisabled === DisabledStatus.DISABLED);
  291. expect(hasDisabledDistrict).toBe(false);
  292. }
  293. });
  294. });
  295. });