admin-routes.integration.test.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
  2. import { testClient } from 'hono/testing';
  3. import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities } from '@d8d/shared-test-util';
  4. import { JWTUtil } from '@d8d/shared-utils';
  5. import { UserEntity, Role } from '@d8d/user-module';
  6. import { AreaEntity, AreaLevel } from '@d8d/geo-areas';
  7. import { adminDeliveryAddressRoutes } from '../../src/routes';
  8. import { DeliveryAddress } from '../../src/entities';
  9. // 设置集成测试钩子
  10. setupIntegrationDatabaseHooksWithEntities([UserEntity, Role, AreaEntity, DeliveryAddress])
  11. describe('管理员配送地址管理API集成测试', () => {
  12. let client: ReturnType<typeof testClient<typeof adminDeliveryAddressRoutes>>;
  13. let adminToken: string;
  14. let testUser: UserEntity;
  15. let testAdmin: UserEntity;
  16. let testProvince: AreaEntity;
  17. let testCity: AreaEntity;
  18. let testDistrict: AreaEntity;
  19. beforeEach(async () => {
  20. // 创建测试客户端
  21. client = testClient(adminDeliveryAddressRoutes);
  22. // 获取数据源
  23. const dataSource = await IntegrationTestDatabase.getDataSource();
  24. // 创建测试用户
  25. const userRepository = dataSource.getRepository(UserEntity);
  26. testUser = userRepository.create({
  27. username: `test_user_${Date.now()}`,
  28. password: 'test_password',
  29. nickname: '测试用户',
  30. registrationSource: 'web'
  31. });
  32. await userRepository.save(testUser);
  33. // 创建测试管理员用户
  34. testAdmin = userRepository.create({
  35. username: `test_admin_${Date.now()}`,
  36. password: 'admin_password',
  37. nickname: '测试管理员',
  38. registrationSource: 'web'
  39. });
  40. await userRepository.save(testAdmin);
  41. // 创建测试地区数据 - 省
  42. const areaRepository = dataSource.getRepository(AreaEntity);
  43. testProvince = areaRepository.create({
  44. name: '北京市',
  45. code: '110000',
  46. level: AreaLevel.PROVINCE,
  47. parentId: null
  48. });
  49. await areaRepository.save(testProvince);
  50. // 创建测试地区数据 - 市
  51. testCity = areaRepository.create({
  52. name: '北京市',
  53. code: '110100',
  54. level: AreaLevel.CITY,
  55. parentId: testProvince.id
  56. });
  57. await areaRepository.save(testCity);
  58. // 创建测试地区数据 - 区
  59. testDistrict = areaRepository.create({
  60. name: '朝阳区',
  61. code: '110105',
  62. level: AreaLevel.DISTRICT,
  63. parentId: testCity.id
  64. });
  65. await areaRepository.save(testDistrict);
  66. // 生成测试管理员的token
  67. adminToken = JWTUtil.generateToken({
  68. id: testAdmin.id,
  69. username: testAdmin.username,
  70. roles: [{name:'admin'}]
  71. });
  72. });
  73. describe('GET /delivery-address', () => {
  74. it('应该返回配送地址列表', async () => {
  75. const response = await client.index.$get({
  76. query: {}
  77. }, {
  78. headers: {
  79. 'Authorization': `Bearer ${adminToken}`
  80. }
  81. });
  82. console.debug('配送地址列表响应状态:', response.status);
  83. expect(response.status).toBe(200);
  84. if (response.status === 200) {
  85. const data = await response.json();
  86. expect(data).toHaveProperty('data');
  87. expect(Array.isArray(data.data)).toBe(true);
  88. }
  89. });
  90. it('应该拒绝未认证用户的访问', async () => {
  91. const response = await client.index.$get({
  92. query: {}
  93. });
  94. expect(response.status).toBe(401);
  95. });
  96. });
  97. describe('POST /delivery-address', () => {
  98. it('应该成功创建配送地址', async () => {
  99. const createData = {
  100. name: '张三',
  101. phone: '13800138000',
  102. address: '朝阳区建国路88号',
  103. receiverProvince: testProvince.id,
  104. receiverCity: testCity.id,
  105. receiverDistrict: testDistrict.id,
  106. receiverTown: 0,
  107. state: 1,
  108. isDefault: 1
  109. };
  110. const response = await client.index.$post({
  111. json: createData
  112. }, {
  113. headers: {
  114. 'Authorization': `Bearer ${adminToken}`
  115. }
  116. });
  117. console.debug('创建配送地址响应状态:', response.status);
  118. expect(response.status).toBe(201);
  119. if (response.status === 201) {
  120. const data = await response.json();
  121. expect(data).toHaveProperty('id');
  122. expect(data.name).toBe(createData.name);
  123. expect(data.phone).toBe(createData.phone);
  124. expect(data.address).toBe(createData.address);
  125. expect(data.receiverProvince).toBe(createData.receiverProvince);
  126. expect(data.receiverCity).toBe(createData.receiverCity);
  127. expect(data.receiverDistrict).toBe(createData.receiverDistrict);
  128. expect(data.isDefault).toBe(createData.isDefault);
  129. }
  130. });
  131. it('应该验证创建配送地址的必填字段', async () => {
  132. const invalidData = {
  133. // 缺少必填字段
  134. name: '',
  135. phone: '',
  136. address: '',
  137. receiverProvince: 0,
  138. receiverCity: 0,
  139. receiverDistrict: 0
  140. };
  141. const response = await client.index.$post({
  142. json: invalidData
  143. }, {
  144. headers: {
  145. 'Authorization': `Bearer ${adminToken}`
  146. }
  147. });
  148. expect(response.status).toBe(400);
  149. });
  150. it('应该验证地区层级关系', async () => {
  151. // 使用不存在的地区ID
  152. const invalidAreaData = {
  153. name: '李四',
  154. phone: '13900139000',
  155. address: '测试地址',
  156. receiverProvince: 999999, // 不存在的省份
  157. receiverCity: testCity.id,
  158. receiverDistrict: testDistrict.id,
  159. receiverTown: 0,
  160. state: 1,
  161. isDefault: 0
  162. };
  163. const response = await client.index.$post({
  164. json: invalidAreaData
  165. }, {
  166. headers: {
  167. 'Authorization': `Bearer ${adminToken}`
  168. }
  169. });
  170. console.debug('地区验证响应状态:', response.status);
  171. expect(response.status).toBe(400);
  172. });
  173. });
  174. describe('GET /delivery-address/:id', () => {
  175. it('应该返回指定配送地址的详情', async () => {
  176. // 先创建一个配送地址
  177. const dataSource = await IntegrationTestDatabase.getDataSource();
  178. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  179. const testDeliveryAddress = deliveryAddressRepository.create({
  180. userId: testUser.id,
  181. name: '王五',
  182. phone: '13600136000',
  183. address: '海淀区中关村大街1号',
  184. receiverProvince: testProvince.id,
  185. receiverCity: testCity.id,
  186. receiverDistrict: testDistrict.id,
  187. receiverTown: 0,
  188. state: 1,
  189. isDefault: 0,
  190. createdBy: testUser.id
  191. });
  192. await deliveryAddressRepository.save(testDeliveryAddress);
  193. const response = await client[':id'].$get({
  194. param: { id: testDeliveryAddress.id }
  195. }, {
  196. headers: {
  197. 'Authorization': `Bearer ${adminToken}`
  198. }
  199. });
  200. console.debug('配送地址详情响应状态:', response.status);
  201. expect(response.status).toBe(200);
  202. if (response.status === 200) {
  203. const data = await response.json();
  204. expect(data.id).toBe(testDeliveryAddress.id);
  205. expect(data.name).toBe(testDeliveryAddress.name);
  206. expect(data.phone).toBe(testDeliveryAddress.phone);
  207. expect(data.address).toBe(testDeliveryAddress.address);
  208. // 验证地区数据关联
  209. expect(data.province).toBeDefined();
  210. expect(data.province.id).toBe(testProvince.id);
  211. expect(data.city).toBeDefined();
  212. expect(data.city.id).toBe(testCity.id);
  213. expect(data.district).toBeDefined();
  214. expect(data.district.id).toBe(testDistrict.id);
  215. }
  216. });
  217. it('应该处理不存在的配送地址', async () => {
  218. const response = await client[':id'].$get({
  219. param: { id: 999999 }
  220. }, {
  221. headers: {
  222. 'Authorization': `Bearer ${adminToken}`
  223. }
  224. });
  225. expect(response.status).toBe(404);
  226. });
  227. });
  228. describe('PUT /delivery-address/:id', () => {
  229. it('应该成功更新配送地址', async () => {
  230. // 先创建一个配送地址
  231. const dataSource = await IntegrationTestDatabase.getDataSource();
  232. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  233. const testDeliveryAddress = deliveryAddressRepository.create({
  234. userId: testUser.id,
  235. name: '原始姓名',
  236. phone: '13500135000',
  237. address: '原始地址',
  238. receiverProvince: testProvince.id,
  239. receiverCity: testCity.id,
  240. receiverDistrict: testDistrict.id,
  241. receiverTown: 0,
  242. state: 1,
  243. isDefault: 0,
  244. createdBy: testUser.id
  245. });
  246. await deliveryAddressRepository.save(testDeliveryAddress);
  247. const updateData = {
  248. name: '更新后的姓名',
  249. phone: '13700137000',
  250. address: '更新后的地址',
  251. isDefault: 1
  252. };
  253. const response = await client[':id'].$put({
  254. param: { id: testDeliveryAddress.id },
  255. json: updateData
  256. }, {
  257. headers: {
  258. 'Authorization': `Bearer ${adminToken}`
  259. }
  260. });
  261. console.debug('更新配送地址响应状态:', response.status);
  262. expect(response.status).toBe(200);
  263. if (response.status === 200) {
  264. const data = await response.json();
  265. expect(data.name).toBe(updateData.name);
  266. expect(data.phone).toBe(updateData.phone);
  267. expect(data.address).toBe(updateData.address);
  268. expect(data.isDefault).toBe(updateData.isDefault);
  269. }
  270. });
  271. });
  272. describe('DELETE /delivery-address/:id', () => {
  273. it('应该成功删除配送地址', async () => {
  274. // 先创建一个配送地址
  275. const dataSource = await IntegrationTestDatabase.getDataSource();
  276. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  277. const testDeliveryAddress = deliveryAddressRepository.create({
  278. userId: testUser.id,
  279. name: '待删除地址',
  280. phone: '13400134000',
  281. address: '待删除地址',
  282. receiverProvince: testProvince.id,
  283. receiverCity: testCity.id,
  284. receiverDistrict: testDistrict.id,
  285. receiverTown: 0,
  286. state: 1,
  287. isDefault: 0,
  288. createdBy: testUser.id
  289. });
  290. await deliveryAddressRepository.save(testDeliveryAddress);
  291. const response = await client[':id'].$delete({
  292. param: { id: testDeliveryAddress.id }
  293. }, {
  294. headers: {
  295. 'Authorization': `Bearer ${adminToken}`
  296. }
  297. });
  298. console.debug('删除配送地址响应状态:', response.status);
  299. expect(response.status).toBe(204);
  300. // 验证配送地址确实被删除
  301. const deletedDeliveryAddress = await deliveryAddressRepository.findOne({
  302. where: { id: testDeliveryAddress.id }
  303. });
  304. expect(deletedDeliveryAddress).toBeNull();
  305. });
  306. });
  307. describe('省市区关联测试', () => {
  308. it('应该正确关联省市区数据', async () => {
  309. // 创建配送地址
  310. const dataSource = await IntegrationTestDatabase.getDataSource();
  311. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  312. const testDeliveryAddress = deliveryAddressRepository.create({
  313. userId: testUser.id,
  314. name: '关联测试',
  315. phone: '13300133000',
  316. address: '关联测试地址',
  317. receiverProvince: testProvince.id,
  318. receiverCity: testCity.id,
  319. receiverDistrict: testDistrict.id,
  320. receiverTown: 0,
  321. state: 1,
  322. isDefault: 0,
  323. createdBy: testUser.id
  324. });
  325. await deliveryAddressRepository.save(testDeliveryAddress);
  326. // 查询配送地址详情,验证地区关联
  327. const response = await client[':id'].$get({
  328. param: { id: testDeliveryAddress.id }
  329. }, {
  330. headers: {
  331. 'Authorization': `Bearer ${adminToken}`
  332. }
  333. });
  334. expect(response.status).toBe(200);
  335. const data = await response.json();
  336. // 验证省市区关联数据
  337. expect(data.province).toBeDefined();
  338. expect(data.province.name).toBe('北京市');
  339. expect(data.province.level).toBe(AreaLevel.PROVINCE);
  340. expect(data.city).toBeDefined();
  341. expect(data.city.name).toBe('北京市');
  342. expect(data.city.level).toBe(AreaLevel.CITY);
  343. expect(data.district).toBeDefined();
  344. expect(data.district.name).toBe('朝阳区');
  345. expect(data.district.level).toBe(AreaLevel.DISTRICT);
  346. });
  347. it('应该验证地区层级关系', async () => {
  348. // 创建另一个区级地区,但父级不是市级
  349. const dataSource = await IntegrationTestDatabase.getDataSource();
  350. const areaRepository = dataSource.getRepository(AreaEntity);
  351. const invalidDistrict = areaRepository.create({
  352. name: '无效区',
  353. code: '999999',
  354. level: AreaLevel.DISTRICT,
  355. parentId: testProvince.id // 父级是省,不是市
  356. });
  357. await areaRepository.save(invalidDistrict);
  358. // 尝试使用无效的地区层级关系创建地址
  359. const createData = {
  360. name: '层级测试',
  361. phone: '13200132000',
  362. address: '层级测试地址',
  363. receiverProvince: testProvince.id,
  364. receiverCity: testCity.id,
  365. receiverDistrict: invalidDistrict.id, // 这个区的父级不是testCity
  366. receiverTown: 0,
  367. state: 1,
  368. isDefault: 0
  369. };
  370. const response = await client.index.$post({
  371. json: createData
  372. }, {
  373. headers: {
  374. 'Authorization': `Bearer ${adminToken}`
  375. }
  376. });
  377. console.debug('地区层级验证响应状态:', response.status);
  378. // 这里期望返回400,因为地区层级关系不匹配
  379. expect(response.status).toBe(400);
  380. });
  381. });
  382. describe('管理员权限测试', () => {
  383. it('管理员应该可以为其他用户创建地址', async () => {
  384. const createData = {
  385. userId: testUser.id, // 为其他用户创建地址
  386. name: '其他用户地址',
  387. phone: '13800138001',
  388. address: '其他用户地址',
  389. receiverProvince: testProvince.id,
  390. receiverCity: testCity.id,
  391. receiverDistrict: testDistrict.id,
  392. receiverTown: 0,
  393. state: 1,
  394. isDefault: 0
  395. };
  396. const response = await client.index.$post({
  397. json: createData
  398. }, {
  399. headers: {
  400. 'Authorization': `Bearer ${adminToken}`
  401. }
  402. });
  403. console.debug('管理员为其他用户创建地址响应状态:', response.status);
  404. expect(response.status).toBe(201);
  405. if (response.status === 201) {
  406. const data = await response.json();
  407. expect(data.userId).toBe(testUser.id); // 验证地址确实属于其他用户
  408. expect(data.name).toBe(createData.name);
  409. }
  410. });
  411. it('管理员应该可以访问所有用户的地址', async () => {
  412. // 为测试用户创建一些地址
  413. const dataSource = await IntegrationTestDatabase.getDataSource();
  414. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  415. const userAddress1 = deliveryAddressRepository.create({
  416. userId: testUser.id,
  417. name: '用户地址1',
  418. phone: '13800138002',
  419. address: '用户地址1',
  420. receiverProvince: testProvince.id,
  421. receiverCity: testCity.id,
  422. receiverDistrict: testDistrict.id,
  423. receiverTown: 0,
  424. state: 1,
  425. isDefault: 0,
  426. createdBy: testUser.id
  427. });
  428. await deliveryAddressRepository.save(userAddress1);
  429. const userAddress2 = deliveryAddressRepository.create({
  430. userId: testUser.id,
  431. name: '用户地址2',
  432. phone: '13800138003',
  433. address: '用户地址2',
  434. receiverProvince: testProvince.id,
  435. receiverCity: testCity.id,
  436. receiverDistrict: testDistrict.id,
  437. receiverTown: 0,
  438. state: 1,
  439. isDefault: 0,
  440. createdBy: testUser.id
  441. });
  442. await deliveryAddressRepository.save(userAddress2);
  443. // 管理员应该能看到所有地址
  444. const response = await client.index.$get({
  445. query: {}
  446. }, {
  447. headers: {
  448. 'Authorization': `Bearer ${adminToken}`
  449. }
  450. });
  451. expect(response.status).toBe(200);
  452. const data = await response.json();
  453. expect(Array.isArray(data.data)).toBe(true);
  454. expect(data.data.length).toBeGreaterThanOrEqual(2); // 至少包含我们创建的两个地址
  455. });
  456. it('管理员应该可以更新其他用户的地址', async () => {
  457. // 先为测试用户创建一个地址
  458. const dataSource = await IntegrationTestDatabase.getDataSource();
  459. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  460. const testDeliveryAddress = deliveryAddressRepository.create({
  461. userId: testUser.id,
  462. name: '原始地址',
  463. phone: '13800138004',
  464. address: '原始地址',
  465. receiverProvince: testProvince.id,
  466. receiverCity: testCity.id,
  467. receiverDistrict: testDistrict.id,
  468. receiverTown: 0,
  469. state: 1,
  470. isDefault: 0,
  471. createdBy: testUser.id
  472. });
  473. await deliveryAddressRepository.save(testDeliveryAddress);
  474. const updateData = {
  475. name: '管理员更新的地址',
  476. phone: '13900139000',
  477. address: '管理员更新的地址'
  478. };
  479. const response = await client[':id'].$put({
  480. param: { id: testDeliveryAddress.id },
  481. json: updateData
  482. }, {
  483. headers: {
  484. 'Authorization': `Bearer ${adminToken}`
  485. }
  486. });
  487. console.debug('管理员更新其他用户地址响应状态:', response.status);
  488. expect(response.status).toBe(200);
  489. if (response.status === 200) {
  490. const data = await response.json();
  491. expect(data.name).toBe(updateData.name);
  492. expect(data.phone).toBe(updateData.phone);
  493. expect(data.address).toBe(updateData.address);
  494. }
  495. });
  496. it('管理员应该可以删除其他用户的地址', async () => {
  497. // 先为测试用户创建一个地址
  498. const dataSource = await IntegrationTestDatabase.getDataSource();
  499. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  500. const testDeliveryAddress = deliveryAddressRepository.create({
  501. userId: testUser.id,
  502. name: '待删除地址',
  503. phone: '13800138005',
  504. address: '待删除地址',
  505. receiverProvince: testProvince.id,
  506. receiverCity: testCity.id,
  507. receiverDistrict: testDistrict.id,
  508. receiverTown: 0,
  509. state: 1,
  510. isDefault: 0,
  511. createdBy: testUser.id
  512. });
  513. await deliveryAddressRepository.save(testDeliveryAddress);
  514. const response = await client[':id'].$delete({
  515. param: { id: testDeliveryAddress.id }
  516. }, {
  517. headers: {
  518. 'Authorization': `Bearer ${adminToken}`
  519. }
  520. });
  521. console.debug('管理员删除其他用户地址响应状态:', response.status);
  522. expect(response.status).toBe(204);
  523. // 验证地址确实被删除
  524. const deletedDeliveryAddress = await deliveryAddressRepository.findOne({
  525. where: { id: testDeliveryAddress.id }
  526. });
  527. expect(deletedDeliveryAddress).toBeNull();
  528. });
  529. it('管理员应该可以查询指定用户的地址', async () => {
  530. // 为测试用户创建一些地址
  531. const dataSource = await IntegrationTestDatabase.getDataSource();
  532. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  533. const userAddress = deliveryAddressRepository.create({
  534. userId: testUser.id,
  535. name: '指定用户地址',
  536. phone: '13800138006',
  537. address: '指定用户地址',
  538. receiverProvince: testProvince.id,
  539. receiverCity: testCity.id,
  540. receiverDistrict: testDistrict.id,
  541. receiverTown: 0,
  542. state: 1,
  543. isDefault: 0,
  544. createdBy: testUser.id
  545. });
  546. await deliveryAddressRepository.save(userAddress);
  547. // 管理员可以查询指定用户的地址
  548. const response = await client.index.$get({
  549. query: { userId: testUser.id }
  550. }, {
  551. headers: {
  552. 'Authorization': `Bearer ${adminToken}`
  553. }
  554. });
  555. expect(response.status).toBe(200);
  556. const data = await response.json();
  557. expect(Array.isArray(data.data)).toBe(true);
  558. // 验证返回的地址都属于指定用户
  559. if (data.data.length > 0) {
  560. data.data.forEach((address: any) => {
  561. expect(address.userId).toBe(testUser.id);
  562. });
  563. }
  564. });
  565. });
  566. });