admin-routes.integration.test.ts 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  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. userId: testUser.id,
  101. name: '张三',
  102. phone: '13800138000',
  103. address: '朝阳区建国路88号',
  104. receiverProvince: testProvince.id,
  105. receiverCity: testCity.id,
  106. receiverDistrict: testDistrict.id,
  107. receiverTown: 0,
  108. state: 1,
  109. isDefault: 1
  110. };
  111. const response = await client.index.$post({
  112. json: createData
  113. }, {
  114. headers: {
  115. 'Authorization': `Bearer ${adminToken}`
  116. }
  117. });
  118. console.debug('创建配送地址响应状态:', response.status);
  119. expect(response.status).toBe(201);
  120. if (response.status === 201) {
  121. const data = await response.json();
  122. expect(data).toHaveProperty('id');
  123. expect(data.name).toBe(createData.name);
  124. expect(data.phone).toBe(createData.phone);
  125. expect(data.address).toBe(createData.address);
  126. expect(data.receiverProvince).toBe(createData.receiverProvince);
  127. expect(data.receiverCity).toBe(createData.receiverCity);
  128. expect(data.receiverDistrict).toBe(createData.receiverDistrict);
  129. expect(data.isDefault).toBe(createData.isDefault);
  130. }
  131. });
  132. it('应该验证创建配送地址的必填字段', async () => {
  133. const invalidData = {
  134. // 缺少必填字段
  135. userId: testUser.id,
  136. name: '',
  137. phone: '',
  138. address: '',
  139. receiverProvince: 0,
  140. receiverCity: 0,
  141. receiverDistrict: 0
  142. };
  143. const response = await client.index.$post({
  144. json: invalidData
  145. }, {
  146. headers: {
  147. 'Authorization': `Bearer ${adminToken}`
  148. }
  149. });
  150. expect(response.status).toBe(400);
  151. });
  152. it('应该验证地区层级关系', async () => {
  153. // 使用不存在的地区ID
  154. const invalidAreaData = {
  155. userId: testUser.id,
  156. name: '李四',
  157. phone: '13900139000',
  158. address: '测试地址',
  159. receiverProvince: 999999, // 不存在的省份
  160. receiverCity: testCity.id,
  161. receiverDistrict: testDistrict.id,
  162. receiverTown: 0,
  163. state: 1,
  164. isDefault: 0
  165. };
  166. const response = await client.index.$post({
  167. json: invalidAreaData
  168. }, {
  169. headers: {
  170. 'Authorization': `Bearer ${adminToken}`
  171. }
  172. });
  173. console.debug('地区验证响应状态:', response.status);
  174. expect(response.status).toBe(400);
  175. });
  176. });
  177. describe('GET /delivery-address/:id', () => {
  178. it('应该返回指定配送地址的详情', async () => {
  179. // 先创建一个配送地址
  180. const dataSource = await IntegrationTestDatabase.getDataSource();
  181. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  182. const testDeliveryAddress = deliveryAddressRepository.create({
  183. userId: testUser.id,
  184. name: '王五',
  185. phone: '13600136000',
  186. address: '海淀区中关村大街1号',
  187. receiverProvince: testProvince.id,
  188. receiverCity: testCity.id,
  189. receiverDistrict: testDistrict.id,
  190. receiverTown: 0,
  191. state: 1,
  192. isDefault: 0,
  193. createdBy: testUser.id
  194. });
  195. await deliveryAddressRepository.save(testDeliveryAddress);
  196. const response = await client[':id'].$get({
  197. param: { id: testDeliveryAddress.id }
  198. }, {
  199. headers: {
  200. 'Authorization': `Bearer ${adminToken}`
  201. }
  202. });
  203. console.debug('配送地址详情响应状态:', response.status);
  204. expect(response.status).toBe(200);
  205. if (response.status === 200) {
  206. const data = await response.json();
  207. expect(data.id).toBe(testDeliveryAddress.id);
  208. expect(data.name).toBe(testDeliveryAddress.name);
  209. expect(data.phone).toBe(testDeliveryAddress.phone);
  210. expect(data.address).toBe(testDeliveryAddress.address);
  211. // 验证地区数据关联
  212. if (data.province) {
  213. expect(data.province.id).toBe(testProvince.id);
  214. }
  215. if (data.city) {
  216. expect(data.city.id).toBe(testCity.id);
  217. }
  218. if (data.district) {
  219. expect(data.district.id).toBe(testDistrict.id);
  220. }
  221. }
  222. });
  223. it('应该处理不存在的配送地址', async () => {
  224. const response = await client[':id'].$get({
  225. param: { id: 999999 }
  226. }, {
  227. headers: {
  228. 'Authorization': `Bearer ${adminToken}`
  229. }
  230. });
  231. expect(response.status).toBe(404);
  232. });
  233. });
  234. describe('PUT /delivery-address/:id', () => {
  235. it('应该成功更新配送地址', async () => {
  236. // 先创建一个配送地址
  237. const dataSource = await IntegrationTestDatabase.getDataSource();
  238. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  239. const testDeliveryAddress = deliveryAddressRepository.create({
  240. userId: testUser.id,
  241. name: '原始姓名',
  242. phone: '13500135000',
  243. address: '原始地址',
  244. receiverProvince: testProvince.id,
  245. receiverCity: testCity.id,
  246. receiverDistrict: testDistrict.id,
  247. receiverTown: 0,
  248. state: 1,
  249. isDefault: 0,
  250. createdBy: testUser.id
  251. });
  252. await deliveryAddressRepository.save(testDeliveryAddress);
  253. const updateData = {
  254. name: '更新后的姓名',
  255. phone: '13700137000',
  256. address: '更新后的地址',
  257. isDefault: 1
  258. };
  259. const response = await client[':id'].$put({
  260. param: { id: testDeliveryAddress.id },
  261. json: updateData
  262. }, {
  263. headers: {
  264. 'Authorization': `Bearer ${adminToken}`
  265. }
  266. });
  267. console.debug('更新配送地址响应状态:', response.status);
  268. expect(response.status).toBe(200);
  269. if (response.status === 200) {
  270. const data = await response.json();
  271. expect(data.name).toBe(updateData.name);
  272. expect(data.phone).toBe(updateData.phone);
  273. expect(data.address).toBe(updateData.address);
  274. expect(data.isDefault).toBe(updateData.isDefault);
  275. }
  276. });
  277. });
  278. describe('DELETE /delivery-address/:id', () => {
  279. it('应该成功删除配送地址', async () => {
  280. // 先创建一个配送地址
  281. const dataSource = await IntegrationTestDatabase.getDataSource();
  282. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  283. const testDeliveryAddress = deliveryAddressRepository.create({
  284. userId: testUser.id,
  285. name: '待删除地址',
  286. phone: '13400134000',
  287. address: '待删除地址',
  288. receiverProvince: testProvince.id,
  289. receiverCity: testCity.id,
  290. receiverDistrict: testDistrict.id,
  291. receiverTown: 0,
  292. state: 1,
  293. isDefault: 0,
  294. createdBy: testUser.id
  295. });
  296. await deliveryAddressRepository.save(testDeliveryAddress);
  297. const response = await client[':id'].$delete({
  298. param: { id: testDeliveryAddress.id }
  299. }, {
  300. headers: {
  301. 'Authorization': `Bearer ${adminToken}`
  302. }
  303. });
  304. console.debug('删除配送地址响应状态:', response.status);
  305. expect(response.status).toBe(204);
  306. // 验证配送地址确实被删除
  307. const deletedDeliveryAddress = await deliveryAddressRepository.findOne({
  308. where: { id: testDeliveryAddress.id }
  309. });
  310. expect(deletedDeliveryAddress).toBeNull();
  311. });
  312. });
  313. describe('省市区关联测试', () => {
  314. it('应该正确关联省市区数据', async () => {
  315. // 创建配送地址
  316. const dataSource = await IntegrationTestDatabase.getDataSource();
  317. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  318. const testDeliveryAddress = deliveryAddressRepository.create({
  319. userId: testUser.id,
  320. name: '关联测试',
  321. phone: '13300133000',
  322. address: '关联测试地址',
  323. receiverProvince: testProvince.id,
  324. receiverCity: testCity.id,
  325. receiverDistrict: testDistrict.id,
  326. receiverTown: 0,
  327. state: 1,
  328. isDefault: 0,
  329. createdBy: testUser.id
  330. });
  331. await deliveryAddressRepository.save(testDeliveryAddress);
  332. // 查询配送地址详情,验证地区关联
  333. const response = await client[':id'].$get({
  334. param: { id: testDeliveryAddress.id }
  335. }, {
  336. headers: {
  337. 'Authorization': `Bearer ${adminToken}`
  338. }
  339. });
  340. expect(response.status).toBe(200);
  341. const data = await response.json();
  342. // 验证省市区关联数据
  343. if ('province' in data && data.province) {
  344. expect(data.province.name).toBe('北京市');
  345. expect(data.province.level).toBe(AreaLevel.PROVINCE);
  346. }
  347. if ('city' in data && data.city) {
  348. expect(data.city.name).toBe('北京市');
  349. expect(data.city.level).toBe(AreaLevel.CITY);
  350. }
  351. if ('district' in data && data.district) {
  352. expect(data.district.name).toBe('朝阳区');
  353. expect(data.district.level).toBe(AreaLevel.DISTRICT);
  354. }
  355. });
  356. it('应该验证地区层级关系', async () => {
  357. // 创建另一个区级地区,但父级不是市级
  358. const dataSource = await IntegrationTestDatabase.getDataSource();
  359. const areaRepository = dataSource.getRepository(AreaEntity);
  360. const invalidDistrict = areaRepository.create({
  361. name: '无效区',
  362. code: '999999',
  363. level: AreaLevel.DISTRICT,
  364. parentId: testProvince.id // 父级是省,不是市
  365. });
  366. await areaRepository.save(invalidDistrict);
  367. // 尝试使用无效的地区层级关系创建地址
  368. const createData = {
  369. userId: testUser.id,
  370. name: '层级测试',
  371. phone: '13200132000',
  372. address: '层级测试地址',
  373. receiverProvince: testProvince.id,
  374. receiverCity: testCity.id,
  375. receiverDistrict: invalidDistrict.id, // 这个区的父级不是testCity
  376. receiverTown: 0,
  377. state: 1,
  378. isDefault: 0
  379. };
  380. const response = await client.index.$post({
  381. json: createData
  382. }, {
  383. headers: {
  384. 'Authorization': `Bearer ${adminToken}`
  385. }
  386. });
  387. console.debug('地区层级验证响应状态:', response.status);
  388. // 这里期望返回400,因为地区层级关系不匹配
  389. expect(response.status).toBe(400);
  390. });
  391. });
  392. describe('管理员权限测试', () => {
  393. it('管理员应该可以为其他用户创建地址', async () => {
  394. const createData = {
  395. userId: testUser.id, // 为其他用户创建地址
  396. name: '其他用户地址',
  397. phone: '13800138001',
  398. address: '其他用户地址',
  399. receiverProvince: testProvince.id,
  400. receiverCity: testCity.id,
  401. receiverDistrict: testDistrict.id,
  402. receiverTown: 0,
  403. state: 1,
  404. isDefault: 0
  405. };
  406. const response = await client.index.$post({
  407. json: createData
  408. }, {
  409. headers: {
  410. 'Authorization': `Bearer ${adminToken}`
  411. }
  412. });
  413. console.debug('管理员为其他用户创建地址响应状态:', response.status);
  414. expect(response.status).toBe(201);
  415. if (response.status === 201) {
  416. const data = await response.json();
  417. expect(data.userId).toBe(testUser.id); // 验证地址确实属于其他用户
  418. expect(data.name).toBe(createData.name);
  419. }
  420. });
  421. it('管理员应该可以访问所有用户的地址', async () => {
  422. // 为测试用户创建一些地址
  423. const dataSource = await IntegrationTestDatabase.getDataSource();
  424. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  425. const userAddress1 = deliveryAddressRepository.create({
  426. userId: testUser.id,
  427. name: '用户地址1',
  428. phone: '13800138002',
  429. address: '用户地址1',
  430. receiverProvince: testProvince.id,
  431. receiverCity: testCity.id,
  432. receiverDistrict: testDistrict.id,
  433. receiverTown: 0,
  434. state: 1,
  435. isDefault: 0,
  436. createdBy: testUser.id
  437. });
  438. await deliveryAddressRepository.save(userAddress1);
  439. const userAddress2 = deliveryAddressRepository.create({
  440. userId: testUser.id,
  441. name: '用户地址2',
  442. phone: '13800138003',
  443. address: '用户地址2',
  444. receiverProvince: testProvince.id,
  445. receiverCity: testCity.id,
  446. receiverDistrict: testDistrict.id,
  447. receiverTown: 0,
  448. state: 1,
  449. isDefault: 0,
  450. createdBy: testUser.id
  451. });
  452. await deliveryAddressRepository.save(userAddress2);
  453. // 管理员应该能看到所有地址
  454. const response = await client.index.$get({
  455. query: {}
  456. }, {
  457. headers: {
  458. 'Authorization': `Bearer ${adminToken}`
  459. }
  460. });
  461. expect(response.status).toBe(200);
  462. const data = await response.json();
  463. if (data && 'data' in data) {
  464. expect(Array.isArray(data.data)).toBe(true);
  465. expect(data.data.length).toBeGreaterThanOrEqual(2); // 至少包含我们创建的两个地址
  466. }
  467. });
  468. it('管理员应该可以更新其他用户的地址', async () => {
  469. // 先为测试用户创建一个地址
  470. const dataSource = await IntegrationTestDatabase.getDataSource();
  471. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  472. const testDeliveryAddress = deliveryAddressRepository.create({
  473. userId: testUser.id,
  474. name: '原始地址',
  475. phone: '13800138004',
  476. address: '原始地址',
  477. receiverProvince: testProvince.id,
  478. receiverCity: testCity.id,
  479. receiverDistrict: testDistrict.id,
  480. receiverTown: 0,
  481. state: 1,
  482. isDefault: 0,
  483. createdBy: testUser.id
  484. });
  485. await deliveryAddressRepository.save(testDeliveryAddress);
  486. const updateData = {
  487. name: '管理员更新的地址',
  488. phone: '13900139000',
  489. address: '管理员更新的地址'
  490. };
  491. const response = await client[':id'].$put({
  492. param: { id: testDeliveryAddress.id },
  493. json: updateData
  494. }, {
  495. headers: {
  496. 'Authorization': `Bearer ${adminToken}`
  497. }
  498. });
  499. console.debug('管理员更新其他用户地址响应状态:', response.status);
  500. expect(response.status).toBe(200);
  501. if (response.status === 200) {
  502. const data = await response.json();
  503. expect(data.name).toBe(updateData.name);
  504. expect(data.phone).toBe(updateData.phone);
  505. expect(data.address).toBe(updateData.address);
  506. }
  507. });
  508. it('管理员应该可以删除其他用户的地址', async () => {
  509. // 先为测试用户创建一个地址
  510. const dataSource = await IntegrationTestDatabase.getDataSource();
  511. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  512. const testDeliveryAddress = deliveryAddressRepository.create({
  513. userId: testUser.id,
  514. name: '待删除地址',
  515. phone: '13800138005',
  516. address: '待删除地址',
  517. receiverProvince: testProvince.id,
  518. receiverCity: testCity.id,
  519. receiverDistrict: testDistrict.id,
  520. receiverTown: 0,
  521. state: 1,
  522. isDefault: 0,
  523. createdBy: testUser.id
  524. });
  525. await deliveryAddressRepository.save(testDeliveryAddress);
  526. const response = await client[':id'].$delete({
  527. param: { id: testDeliveryAddress.id }
  528. }, {
  529. headers: {
  530. 'Authorization': `Bearer ${adminToken}`
  531. }
  532. });
  533. console.debug('管理员删除其他用户地址响应状态:', response.status);
  534. expect(response.status).toBe(204);
  535. // 验证地址确实被删除
  536. const deletedDeliveryAddress = await deliveryAddressRepository.findOne({
  537. where: { id: testDeliveryAddress.id }
  538. });
  539. expect(deletedDeliveryAddress).toBeNull();
  540. });
  541. it('管理员应该可以查询指定用户的地址', async () => {
  542. // 为测试用户创建一些地址
  543. const dataSource = await IntegrationTestDatabase.getDataSource();
  544. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  545. const userAddress = deliveryAddressRepository.create({
  546. userId: testUser.id,
  547. name: '指定用户地址',
  548. phone: '13800138006',
  549. address: '指定用户地址',
  550. receiverProvince: testProvince.id,
  551. receiverCity: testCity.id,
  552. receiverDistrict: testDistrict.id,
  553. receiverTown: 0,
  554. state: 1,
  555. isDefault: 0,
  556. createdBy: testUser.id
  557. });
  558. await deliveryAddressRepository.save(userAddress);
  559. // 管理员可以查询指定用户的地址
  560. const response = await client.index.$get({
  561. query: { filters: JSON.stringify({ userId: testUser.id }) }
  562. }, {
  563. headers: {
  564. 'Authorization': `Bearer ${adminToken}`
  565. }
  566. });
  567. expect(response.status).toBe(200);
  568. const data = await response.json();
  569. if (data && 'data' in data) {
  570. expect(Array.isArray(data.data)).toBe(true);
  571. // 验证返回的地址都属于指定用户
  572. if (data.data.length > 0) {
  573. data.data.forEach((address: any) => {
  574. expect(address.userId).toBe(testUser.id);
  575. });
  576. }
  577. }
  578. });
  579. });
  580. });