2
0

admin-order-items.integration.test.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. import { describe, it, expect, beforeEach } 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 { File } from '@d8d/file-module';
  7. import { Goods, GoodsCategory } from '@d8d/goods-module';
  8. import { Supplier } from '@d8d/supplier-module';
  9. import { Merchant } from '@d8d/merchant-module';
  10. import { DeliveryAddress } from '@d8d/delivery-address-module';
  11. import { AreaEntity } from '@d8d/geo-areas';
  12. import adminOrderItemsRoutes from '../../src/routes/admin/order-items';
  13. import { Order, OrderGoods } from '../../src/entities';
  14. // 设置集成测试钩子
  15. setupIntegrationDatabaseHooksWithEntities([
  16. UserEntity, Role, Order, OrderGoods, Goods, GoodsCategory, File, Supplier, Merchant, DeliveryAddress, AreaEntity
  17. ])
  18. describe('管理员订单商品管理API集成测试', () => {
  19. let client: ReturnType<typeof testClient<typeof adminOrderItemsRoutes>>;
  20. let adminToken: string;
  21. let testUser: UserEntity;
  22. let testAdmin: UserEntity;
  23. let testOrder: Order;
  24. let otherUserOrder: Order;
  25. let testGoods: Goods;
  26. let testSupplier: Supplier;
  27. let testFile: File;
  28. beforeEach(async () => {
  29. // 创建测试客户端
  30. client = testClient(adminOrderItemsRoutes);
  31. // 获取数据源
  32. const dataSource = await IntegrationTestDatabase.getDataSource();
  33. // 创建测试用户
  34. const userRepository = dataSource.getRepository(UserEntity);
  35. testUser = userRepository.create({
  36. username: `test_user_${Math.floor(Math.random() * 100000)}`,
  37. password: 'test_password',
  38. nickname: '测试用户',
  39. registrationSource: 'web'
  40. });
  41. await userRepository.save(testUser);
  42. // 创建测试管理员用户
  43. testAdmin = userRepository.create({
  44. username: `test_admin_${Math.floor(Math.random() * 100000)}`,
  45. password: 'admin_password',
  46. nickname: '测试管理员',
  47. registrationSource: 'web'
  48. });
  49. await userRepository.save(testAdmin);
  50. // 生成测试管理员的token
  51. adminToken = JWTUtil.generateToken({
  52. id: testAdmin.id,
  53. username: testAdmin.username,
  54. roles: [{name:'admin'}]
  55. });
  56. // 创建测试供应商
  57. const supplierRepository = dataSource.getRepository(Supplier);
  58. testSupplier = supplierRepository.create({
  59. name: '测试供应商',
  60. username: `test_supplier_${Math.floor(Math.random() * 100000)}`,
  61. password: 'password123',
  62. phone: '13800138000',
  63. realname: '测试供应商',
  64. state: 1,
  65. createdBy: testUser.id
  66. });
  67. await supplierRepository.save(testSupplier);
  68. // 创建测试文件
  69. const fileRepository = dataSource.getRepository(File);
  70. testFile = fileRepository.create({
  71. name: 'test_image.jpg',
  72. type: 'image/jpeg',
  73. size: 102400,
  74. path: 'images/test_image.jpg',
  75. uploadUserId: testUser.id,
  76. uploadTime: new Date(),
  77. createdAt: new Date(),
  78. updatedAt: new Date()
  79. });
  80. await fileRepository.save(testFile);
  81. // 创建测试商品分类
  82. const categoryRepository = dataSource.getRepository(GoodsCategory);
  83. const testCategory = categoryRepository.create({
  84. name: '测试分类',
  85. level: 1,
  86. sort: 1,
  87. state: 1,
  88. createdBy: testUser.id
  89. });
  90. await categoryRepository.save(testCategory);
  91. // 创建测试商品
  92. const goodsRepository = dataSource.getRepository(Goods);
  93. testGoods = goodsRepository.create({
  94. name: '测试商品',
  95. price: 100.00,
  96. costPrice: 80.00,
  97. categoryId1: testCategory.id,
  98. categoryId2: testCategory.id,
  99. categoryId3: testCategory.id,
  100. goodsType: 1,
  101. supplierId: testSupplier.id,
  102. imageFileId: testFile.id,
  103. state: 1,
  104. stock: 100,
  105. lowestBuy: 1,
  106. createdBy: testUser.id
  107. });
  108. await goodsRepository.save(testGoods);
  109. // 创建测试商户
  110. const merchantRepository = dataSource.getRepository(Merchant);
  111. const testMerchant = merchantRepository.create({
  112. name: '测试商户',
  113. username: `test_merchant_${Math.floor(Math.random() * 100000)}`,
  114. password: 'password123',
  115. phone: '13900139000',
  116. realname: '测试商户',
  117. state: 1,
  118. createdBy: testUser.id
  119. });
  120. await merchantRepository.save(testMerchant);
  121. // 创建测试地区数据
  122. const areaRepository = dataSource.getRepository(AreaEntity);
  123. const testProvince = areaRepository.create({
  124. name: '广东省',
  125. level: 1,
  126. code: '44',
  127. state: 1
  128. });
  129. await areaRepository.save(testProvince);
  130. const testCity = areaRepository.create({
  131. name: '广州市',
  132. level: 2,
  133. code: '4401',
  134. state: 1
  135. });
  136. await areaRepository.save(testCity);
  137. const testDistrict = areaRepository.create({
  138. name: '天河区',
  139. level: 3,
  140. code: '440106',
  141. state: 1
  142. });
  143. await areaRepository.save(testDistrict);
  144. const testTown = areaRepository.create({
  145. name: '天河南街道',
  146. level: 4,
  147. code: '440106001',
  148. state: 1
  149. });
  150. await areaRepository.save(testTown);
  151. // 创建测试配送地址
  152. const addressRepository = dataSource.getRepository(DeliveryAddress);
  153. const testDeliveryAddress = addressRepository.create({
  154. userId: testUser.id,
  155. name: '测试收货人',
  156. phone: '13800138000',
  157. address: '测试地址详情',
  158. receiverProvince: testProvince.id,
  159. receiverCity: testCity.id,
  160. receiverDistrict: testDistrict.id,
  161. receiverTown: testTown.id,
  162. isDefault: 1,
  163. state: 1,
  164. createdBy: testUser.id
  165. });
  166. await addressRepository.save(testDeliveryAddress);
  167. // 创建测试用户的订单
  168. const orderRepository = dataSource.getRepository(Order);
  169. testOrder = orderRepository.create({
  170. orderNo: `ORDER_${Math.floor(Math.random() * 100000)}`,
  171. userId: testUser.id,
  172. amount: 100.00,
  173. costAmount: 80.00,
  174. payAmount: 100.00,
  175. orderType: 1,
  176. payType: 1,
  177. payState: 2,
  178. state: 0,
  179. addressId: testDeliveryAddress.id,
  180. merchantId: testMerchant.id,
  181. supplierId: testSupplier.id,
  182. recevierProvince: testProvince.id,
  183. recevierCity: testCity.id,
  184. recevierDistrict: testDistrict.id,
  185. recevierTown: testTown.id,
  186. createdBy: testUser.id
  187. });
  188. await orderRepository.save(testOrder);
  189. // 创建其他用户的订单
  190. otherUserOrder = orderRepository.create({
  191. orderNo: `ORDER_${Math.floor(Math.random() * 100000)}`,
  192. userId: testAdmin.id,
  193. amount: 200.00,
  194. costAmount: 160.00,
  195. payAmount: 200.00,
  196. orderType: 1,
  197. payType: 1,
  198. payState: 2,
  199. state: 0,
  200. addressId: testDeliveryAddress.id,
  201. merchantId: testMerchant.id,
  202. supplierId: testSupplier.id,
  203. recevierProvince: testProvince.id,
  204. recevierCity: testCity.id,
  205. recevierDistrict: testDistrict.id,
  206. recevierTown: testTown.id,
  207. createdBy: testAdmin.id
  208. });
  209. await orderRepository.save(otherUserOrder);
  210. });
  211. describe('GET /order-items', () => {
  212. it('应该返回所有订单的商品列表', async () => {
  213. // 为不同用户的订单创建商品
  214. const dataSource = await IntegrationTestDatabase.getDataSource();
  215. const orderGoodsRepository = dataSource.getRepository(OrderGoods);
  216. const userOrderGoods = orderGoodsRepository.create({
  217. orderId: testOrder.id,
  218. orderNo: testOrder.orderNo,
  219. goodsId: testGoods.id,
  220. goodsName: '测试用户商品',
  221. price: 50.00,
  222. num: 2,
  223. state: 0,
  224. supplierId: testSupplier.id,
  225. imageFileId: testFile.id,
  226. createdBy: testUser.id
  227. });
  228. await orderGoodsRepository.save(userOrderGoods);
  229. const adminOrderGoods = orderGoodsRepository.create({
  230. orderId: otherUserOrder.id,
  231. orderNo: otherUserOrder.orderNo,
  232. goodsId: testGoods.id,
  233. goodsName: '管理员用户商品',
  234. price: 100.00,
  235. num: 1,
  236. state: 0,
  237. supplierId: testSupplier.id,
  238. imageFileId: testFile.id,
  239. createdBy: testAdmin.id
  240. });
  241. await orderGoodsRepository.save(adminOrderGoods);
  242. const response = await client.index.$get({
  243. query: {}
  244. }, {
  245. headers: {
  246. 'Authorization': `Bearer ${adminToken}`
  247. }
  248. });
  249. console.debug('管理员订单商品列表响应状态:', response.status);
  250. if (response.status !== 200) {
  251. const errorData = await response.json();
  252. console.debug('管理员订单商品列表错误响应:', JSON.stringify(errorData, null, 2));
  253. }
  254. expect(response.status).toBe(200);
  255. if (response.status === 200) {
  256. const data = await response.json();
  257. expect(data).toHaveProperty('data');
  258. expect(Array.isArray(data.data)).toBe(true);
  259. // 验证返回所有用户的订单商品(管理员可以访问所有数据)
  260. const userOrderGoodsCount = data.data.filter((orderGoods: any) =>
  261. orderGoods.order && orderGoods.order.userId === testUser.id
  262. ).length;
  263. const adminOrderGoodsCount = data.data.filter((orderGoods: any) =>
  264. orderGoods.order && orderGoods.order.userId === testAdmin.id
  265. ).length;
  266. expect(userOrderGoodsCount).toBeGreaterThan(0);
  267. expect(adminOrderGoodsCount).toBeGreaterThan(0);
  268. }
  269. });
  270. it('应该拒绝未认证用户的访问', async () => {
  271. const response = await client.index.$get({
  272. query: {}
  273. });
  274. expect(response.status).toBe(401);
  275. });
  276. });
  277. describe('POST /order-items', () => {
  278. it('应该成功创建订单商品并可以指定权限', async () => {
  279. const createData = {
  280. orderId: testOrder.id,
  281. goodsId: testGoods.id,
  282. goodsName: '管理员创建商品',
  283. price: 75.00,
  284. num: 3,
  285. state: 0,
  286. supplierId: testSupplier.id,
  287. imageFileId: testFile.id,
  288. createdBy: testAdmin.id // 管理员可以指定创建人
  289. };
  290. const response = await client.index.$post({
  291. json: createData
  292. }, {
  293. headers: {
  294. 'Authorization': `Bearer ${adminToken}`
  295. }
  296. });
  297. console.debug('管理员创建订单商品响应状态:', response.status);
  298. if (response.status !== 201) {
  299. const errorData = await response.json();
  300. console.debug('管理员创建订单商品错误响应:', errorData);
  301. }
  302. expect(response.status).toBe(201);
  303. if (response.status === 201) {
  304. const data = await response.json();
  305. expect(data).toHaveProperty('id');
  306. expect(data.goodsName).toBe(createData.goodsName);
  307. expect(parseFloat(data.price)).toBe(createData.price);
  308. expect(data.num).toBe(createData.num);
  309. expect(data.createdBy).toBe(testAdmin.id); // 验证可以指定创建人
  310. }
  311. });
  312. it('应该验证创建订单商品的必填字段', async () => {
  313. const invalidData = {
  314. // 缺少必填字段
  315. price: -1,
  316. num: -1
  317. };
  318. const response = await client.index.$post({
  319. json: invalidData
  320. }, {
  321. headers: {
  322. 'Authorization': `Bearer ${adminToken}`
  323. }
  324. });
  325. expect(response.status).toBe(400);
  326. });
  327. });
  328. describe('GET /order-items/:id', () => {
  329. it('应该返回指定订单商品的详情', async () => {
  330. // 先为测试用户的订单创建一个商品
  331. const dataSource = await IntegrationTestDatabase.getDataSource();
  332. const orderGoodsRepository = dataSource.getRepository(OrderGoods);
  333. const testOrderGoods = orderGoodsRepository.create({
  334. orderId: testOrder.id,
  335. orderNo: testOrder.orderNo,
  336. goodsId: testGoods.id,
  337. goodsName: '测试订单商品详情',
  338. price: 50.00,
  339. num: 2,
  340. state: 0,
  341. supplierId: testSupplier.id,
  342. imageFileId: testFile.id,
  343. createdBy: testUser.id
  344. });
  345. await orderGoodsRepository.save(testOrderGoods);
  346. const response = await client[':id'].$get({
  347. param: { id: testOrderGoods.id }
  348. }, {
  349. headers: {
  350. 'Authorization': `Bearer ${adminToken}`
  351. }
  352. });
  353. console.debug('管理员订单商品详情响应状态:', response.status);
  354. expect(response.status).toBe(200);
  355. if (response.status === 200) {
  356. const data = await response.json();
  357. expect(data.id).toBe(testOrderGoods.id);
  358. expect(data.goodsName).toBe(testOrderGoods.goodsName);
  359. expect(data.order.userId).toBe(testUser.id); // 验证可以访问其他用户的订单商品
  360. }
  361. });
  362. it('应该处理不存在的订单商品', async () => {
  363. const response = await client[':id'].$get({
  364. param: { id: 999999 }
  365. }, {
  366. headers: {
  367. 'Authorization': `Bearer ${adminToken}`
  368. }
  369. });
  370. expect(response.status).toBe(404);
  371. });
  372. });
  373. describe('PUT /order-items/:id', () => {
  374. it('应该成功更新任何订单商品', async () => {
  375. // 先为测试用户的订单创建一个商品
  376. const dataSource = await IntegrationTestDatabase.getDataSource();
  377. const orderGoodsRepository = dataSource.getRepository(OrderGoods);
  378. const testOrderGoods = orderGoodsRepository.create({
  379. orderId: testOrder.id,
  380. goodsId: testGoods.id,
  381. goodsName: '测试更新订单商品',
  382. price: 50.00,
  383. num: 2,
  384. state: 0,
  385. supplierId: testSupplier.id,
  386. imageFileId: testFile.id,
  387. createdBy: testUser.id
  388. });
  389. await orderGoodsRepository.save(testOrderGoods);
  390. const updateData = {
  391. num: 5,
  392. state: 1,
  393. updatedBy: testAdmin.id // 管理员可以指定更新人
  394. };
  395. const response = await client[':id'].$put({
  396. param: { id: testOrderGoods.id },
  397. json: updateData
  398. }, {
  399. headers: {
  400. 'Authorization': `Bearer ${adminToken}`
  401. }
  402. });
  403. console.debug('管理员更新订单商品响应状态:', response.status);
  404. expect(response.status).toBe(200);
  405. if (response.status === 200) {
  406. const data = await response.json();
  407. expect(data.num).toBe(updateData.num);
  408. expect(data.state).toBe(updateData.state);
  409. expect(data.updatedBy).toBe(testAdmin.id); // 验证可以指定更新人
  410. }
  411. });
  412. });
  413. describe('DELETE /order-items/:id', () => {
  414. it('应该成功删除任何订单商品', async () => {
  415. // 先为测试用户的订单创建一个商品
  416. const dataSource = await IntegrationTestDatabase.getDataSource();
  417. const orderGoodsRepository = dataSource.getRepository(OrderGoods);
  418. const testOrderGoods = orderGoodsRepository.create({
  419. orderId: testOrder.id,
  420. goodsId: testGoods.id,
  421. goodsName: '测试删除订单商品',
  422. price: 50.00,
  423. num: 2,
  424. state: 0,
  425. supplierId: testSupplier.id,
  426. imageFileId: testFile.id,
  427. createdBy: testUser.id
  428. });
  429. await orderGoodsRepository.save(testOrderGoods);
  430. const response = await client[':id'].$delete({
  431. param: { id: testOrderGoods.id }
  432. }, {
  433. headers: {
  434. 'Authorization': `Bearer ${adminToken}`
  435. }
  436. });
  437. console.debug('管理员删除订单商品响应状态:', response.status);
  438. expect(response.status).toBe(204);
  439. });
  440. });
  441. describe('管理员权限验证测试', () => {
  442. it('应该验证管理员可以访问所有数据', async () => {
  443. const dataSource = await IntegrationTestDatabase.getDataSource();
  444. const orderGoodsRepository = dataSource.getRepository(OrderGoods);
  445. // 创建不同用户的订单商品
  446. const userOrderGoods = orderGoodsRepository.create({
  447. orderId: testOrder.id,
  448. orderNo: testOrder.orderNo,
  449. goodsId: testGoods.id,
  450. goodsName: '用户订单商品',
  451. price: 50.00,
  452. num: 2,
  453. state: 0,
  454. supplierId: testSupplier.id,
  455. imageFileId: testFile.id,
  456. createdBy: testUser.id
  457. });
  458. await orderGoodsRepository.save(userOrderGoods);
  459. const adminOrderGoods = orderGoodsRepository.create({
  460. orderId: otherUserOrder.id,
  461. orderNo: otherUserOrder.orderNo,
  462. goodsId: testGoods.id,
  463. goodsName: '管理员订单商品',
  464. price: 100.00,
  465. num: 1,
  466. state: 0,
  467. supplierId: testSupplier.id,
  468. imageFileId: testFile.id,
  469. createdBy: testAdmin.id
  470. });
  471. await orderGoodsRepository.save(adminOrderGoods);
  472. // 使用管理员token获取列表
  473. const response = await client.index.$get({
  474. query: {}
  475. }, {
  476. headers: {
  477. 'Authorization': `Bearer ${adminToken}`
  478. }
  479. });
  480. expect(response.status).toBe(200);
  481. const data = await response.json();
  482. // 类型检查确保data属性存在
  483. if ('data' in data && Array.isArray(data.data)) {
  484. // 验证返回所有用户的订单商品
  485. const userOrderGoodsInResponse = data.data.filter((orderGoods: any) =>
  486. orderGoods.order && orderGoods.order.userId === testUser.id
  487. );
  488. const adminOrderGoodsInResponse = data.data.filter((orderGoods: any) =>
  489. orderGoods.order && orderGoods.order.userId === testAdmin.id
  490. );
  491. expect(userOrderGoodsInResponse.length).toBeGreaterThan(0);
  492. expect(adminOrderGoodsInResponse.length).toBeGreaterThan(0);
  493. } else {
  494. // 如果响应是错误格式,应该失败
  495. expect(data).toHaveProperty('data');
  496. }
  497. });
  498. });
  499. });