user-order-items.integration.test.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  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 { userOrderItemsRoutes } from '../../src/routes/user/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 userOrderItemsRoutes>>;
  20. let userToken: string;
  21. let otherUserToken: string;
  22. let testUser: UserEntity;
  23. let otherUser: UserEntity;
  24. let testOrder: Order;
  25. let otherUserOrder: Order;
  26. let testGoods: Goods;
  27. let testSupplier: Supplier;
  28. let testFile: File;
  29. beforeEach(async () => {
  30. // 创建测试客户端
  31. client = testClient(userOrderItemsRoutes);
  32. // 获取数据源
  33. const dataSource = await IntegrationTestDatabase.getDataSource();
  34. // 创建测试用户
  35. const userRepository = dataSource.getRepository(UserEntity);
  36. testUser = userRepository.create({
  37. username: `test_user_${Math.floor(Math.random() * 100000)}`,
  38. password: 'test_password',
  39. nickname: '测试用户',
  40. registrationSource: 'web'
  41. });
  42. await userRepository.save(testUser);
  43. // 创建其他用户
  44. otherUser = userRepository.create({
  45. username: `other_user_${Math.floor(Math.random() * 100000)}`,
  46. password: 'other_password',
  47. nickname: '其他用户',
  48. registrationSource: 'web'
  49. });
  50. await userRepository.save(otherUser);
  51. // 生成测试用户的token
  52. userToken = JWTUtil.generateToken({
  53. id: testUser.id,
  54. username: testUser.username,
  55. roles: [{name:'user'}]
  56. });
  57. // 生成其他用户的token
  58. otherUserToken = JWTUtil.generateToken({
  59. id: otherUser.id,
  60. username: otherUser.username,
  61. roles: [{name:'user'}]
  62. });
  63. // 创建测试商品
  64. const goodsRepository = dataSource.getRepository(Goods);
  65. testGoods = goodsRepository.create({
  66. name: '测试商品',
  67. price: 100.00,
  68. costPrice: 80.00,
  69. categoryId1: 1,
  70. categoryId2: 1,
  71. categoryId3: 1,
  72. goodsType: 1,
  73. supplierId: 1,
  74. state: 1,
  75. stock: 100,
  76. lowestBuy: 1,
  77. createdBy: testUser.id
  78. });
  79. await goodsRepository.save(testGoods);
  80. // 创建测试供应商
  81. const supplierRepository = dataSource.getRepository(Supplier);
  82. testSupplier = supplierRepository.create({
  83. name: '测试供应商',
  84. username: `test_supplier_${Math.floor(Math.random() * 100000)}`,
  85. password: 'password123',
  86. phone: '13800138000',
  87. realname: '测试供应商',
  88. state: 1,
  89. createdBy: testUser.id
  90. });
  91. await supplierRepository.save(testSupplier);
  92. // 创建测试文件
  93. const fileRepository = dataSource.getRepository(File);
  94. testFile = fileRepository.create({
  95. name: 'test_image.jpg',
  96. type: 'image/jpeg',
  97. size: 102400,
  98. path: 'images/test_image.jpg',
  99. uploadUserId: testUser.id,
  100. uploadTime: new Date(),
  101. createdAt: new Date(),
  102. updatedAt: new Date()
  103. });
  104. await fileRepository.save(testFile);
  105. // 创建测试用户的订单
  106. const orderRepository = dataSource.getRepository(Order);
  107. testOrder = orderRepository.create({
  108. orderNo: `ORDER_${Math.floor(Math.random() * 100000)}`,
  109. userId: testUser.id,
  110. amount: 100.00,
  111. costAmount: 80.00,
  112. payAmount: 100.00,
  113. orderType: 1,
  114. payType: 1,
  115. payState: 2,
  116. state: 0,
  117. createdBy: testUser.id
  118. });
  119. await orderRepository.save(testOrder);
  120. // 创建其他用户的订单
  121. otherUserOrder = orderRepository.create({
  122. orderNo: `ORDER_${Math.floor(Math.random() * 100000)}`,
  123. userId: otherUser.id,
  124. amount: 200.00,
  125. costAmount: 160.00,
  126. payAmount: 200.00,
  127. orderType: 1,
  128. payType: 1,
  129. payState: 2,
  130. state: 0,
  131. createdBy: otherUser.id
  132. });
  133. await orderRepository.save(otherUserOrder);
  134. });
  135. describe('GET /order-items', () => {
  136. it('应该返回当前用户订单的商品列表', async () => {
  137. // 为测试用户的订单创建一些商品
  138. const dataSource = await IntegrationTestDatabase.getDataSource();
  139. const orderGoodsRepository = dataSource.getRepository(OrderGoods);
  140. const userOrderGoods1 = orderGoodsRepository.create({
  141. orderId: testOrder.id,
  142. goodsId: testGoods.id,
  143. goodsName: '测试商品1',
  144. price: 50.00,
  145. num: 2,
  146. state: 0,
  147. supplierId: testSupplier.id,
  148. imageFileId: testFile.id,
  149. createdBy: testUser.id
  150. });
  151. await orderGoodsRepository.save(userOrderGoods1);
  152. const userOrderGoods2 = orderGoodsRepository.create({
  153. orderId: testOrder.id,
  154. goodsId: testGoods.id,
  155. goodsName: '测试商品2',
  156. price: 25.00,
  157. num: 4,
  158. state: 0,
  159. supplierId: testSupplier.id,
  160. imageFileId: testFile.id,
  161. createdBy: testUser.id
  162. });
  163. await orderGoodsRepository.save(userOrderGoods2);
  164. // 为其他用户的订单创建一个商品,确保不会返回
  165. const otherUserOrderGoods = orderGoodsRepository.create({
  166. orderId: otherUserOrder.id,
  167. goodsId: testGoods.id,
  168. goodsName: '其他用户商品',
  169. price: 100.00,
  170. num: 1,
  171. state: 0,
  172. supplierId: testSupplier.id,
  173. imageFileId: testFile.id,
  174. createdBy: otherUser.id
  175. });
  176. await orderGoodsRepository.save(otherUserOrderGoods);
  177. const response = await client.index.$get({
  178. query: {
  179. page: 1,
  180. pageSize: 10
  181. }
  182. }, {
  183. headers: {
  184. 'Authorization': `Bearer ${userToken}`
  185. }
  186. });
  187. console.debug('用户订单商品列表响应状态:', response.status);
  188. if (response.status !== 200) {
  189. const errorData = await response.json();
  190. console.debug('用户订单商品列表错误响应:', errorData);
  191. }
  192. expect(response.status).toBe(200);
  193. if (response.status === 200) {
  194. const data = await response.json();
  195. expect(data).toHaveProperty('data');
  196. expect(Array.isArray(data.data)).toBe(true);
  197. // 验证只返回当前用户订单的商品
  198. data.data.forEach((orderGoods: any) => {
  199. expect(orderGoods.order.userId).toBe(testUser.id);
  200. });
  201. // 验证不包含其他用户订单的商品
  202. const otherUserOrderGoodsInResponse = data.data.find((orderGoods: any) =>
  203. orderGoods.order && orderGoods.order.userId === otherUser.id
  204. );
  205. expect(otherUserOrderGoodsInResponse).toBeUndefined();
  206. }
  207. });
  208. it('应该拒绝未认证用户的访问', async () => {
  209. const response = await client.index.$get({
  210. query: {
  211. page: 1,
  212. pageSize: 10
  213. }
  214. });
  215. expect(response.status).toBe(401);
  216. });
  217. });
  218. describe('POST /order-items', () => {
  219. it('应该成功创建订单商品并自动设置当前用户权限', async () => {
  220. const createData = {
  221. orderId: testOrder.id,
  222. goodsId: testGoods.id,
  223. goodsName: '用户创建商品',
  224. price: 75.00,
  225. num: 3,
  226. state: 0,
  227. supplierId: testSupplier.id,
  228. imageFileId: testFile.id
  229. };
  230. const response = await client.index.$post({
  231. json: createData
  232. }, {
  233. headers: {
  234. 'Authorization': `Bearer ${userToken}`
  235. }
  236. });
  237. console.debug('用户创建订单商品响应状态:', response.status);
  238. if (response.status !== 201) {
  239. const errorData = await response.json();
  240. console.debug('用户创建订单商品错误响应:', errorData);
  241. }
  242. expect(response.status).toBe(201);
  243. if (response.status === 201) {
  244. const data = await response.json();
  245. expect(data).toHaveProperty('id');
  246. expect(data.goodsName).toBe(createData.goodsName);
  247. expect(parseFloat(data.price)).toBe(createData.price);
  248. expect(data.num).toBe(createData.num);
  249. expect(data.createdBy).toBe(testUser.id); // 验证自动设置创建用户
  250. }
  251. });
  252. it('应该验证创建订单商品的必填字段', async () => {
  253. const invalidData = {
  254. // 缺少必填字段
  255. price: -1,
  256. num: -1
  257. };
  258. const response = await client.index.$post({
  259. json: invalidData
  260. }, {
  261. headers: {
  262. 'Authorization': `Bearer ${userToken}`
  263. }
  264. });
  265. expect(response.status).toBe(400);
  266. });
  267. it('应该拒绝为其他用户的订单创建商品', async () => {
  268. const createData = {
  269. orderId: otherUserOrder.id,
  270. goodsId: testGoods.id,
  271. goodsName: '尝试为其他用户订单创建商品',
  272. price: 75.00,
  273. num: 3,
  274. state: 0,
  275. supplierId: testSupplier.id,
  276. imageFileId: testFile.id
  277. };
  278. const response = await client.index.$post({
  279. json: createData
  280. }, {
  281. headers: {
  282. 'Authorization': `Bearer ${userToken}`
  283. }
  284. });
  285. expect(response.status).toBe(403); // 数据权限控制返回403
  286. });
  287. });
  288. describe('GET /order-items/:id', () => {
  289. it('应该返回当前用户订单的商品详情', async () => {
  290. // 先为测试用户的订单创建一个商品
  291. const dataSource = await IntegrationTestDatabase.getDataSource();
  292. const orderGoodsRepository = dataSource.getRepository(OrderGoods);
  293. const testOrderGoods = orderGoodsRepository.create({
  294. orderId: testOrder.id,
  295. goodsId: testGoods.id,
  296. goodsName: '测试订单商品详情',
  297. price: 50.00,
  298. num: 2,
  299. state: 0,
  300. supplierId: testSupplier.id,
  301. imageFileId: testFile.id,
  302. createdBy: testUser.id
  303. });
  304. await orderGoodsRepository.save(testOrderGoods);
  305. const response = await client[':id'].$get({
  306. param: { id: testOrderGoods.id }
  307. }, {
  308. headers: {
  309. 'Authorization': `Bearer ${userToken}`
  310. }
  311. });
  312. console.debug('用户订单商品详情响应状态:', response.status);
  313. if (response.status !== 200) {
  314. const errorData = await response.json();
  315. console.debug('用户订单商品详情错误响应:', errorData);
  316. }
  317. expect(response.status).toBe(200);
  318. if (response.status === 200) {
  319. const data = await response.json();
  320. expect(data.id).toBe(testOrderGoods.id);
  321. expect(data.goodsName).toBe(testOrderGoods.goodsName);
  322. expect(data.order.userId).toBe(testUser.id); // 验证订单属于当前用户
  323. }
  324. });
  325. it('应该拒绝访问其他用户订单的商品', async () => {
  326. // 为其他用户的订单创建一个商品
  327. const dataSource = await IntegrationTestDatabase.getDataSource();
  328. const orderGoodsRepository = dataSource.getRepository(OrderGoods);
  329. const otherUserOrderGoods = orderGoodsRepository.create({
  330. orderId: otherUserOrder.id,
  331. goodsId: testGoods.id,
  332. goodsName: '其他用户订单商品',
  333. price: 100.00,
  334. num: 1,
  335. state: 0,
  336. supplierId: testSupplier.id,
  337. imageFileId: testFile.id,
  338. createdBy: otherUser.id
  339. });
  340. await orderGoodsRepository.save(otherUserOrderGoods);
  341. const response = await client[':id'].$get({
  342. param: { id: otherUserOrderGoods.id }
  343. }, {
  344. headers: {
  345. 'Authorization': `Bearer ${userToken}`
  346. }
  347. });
  348. expect(response.status).toBe(403); // 数据权限控制返回403(权限不足)
  349. });
  350. it('应该处理不存在的订单商品', async () => {
  351. const response = await client[':id'].$get({
  352. param: { id: 999999 }
  353. }, {
  354. headers: {
  355. 'Authorization': `Bearer ${userToken}`
  356. }
  357. });
  358. expect(response.status).toBe(404);
  359. });
  360. });
  361. describe('PUT /order-items/:id', () => {
  362. it('应该成功更新当前用户订单的商品', async () => {
  363. // 先为测试用户的订单创建一个商品
  364. const dataSource = await IntegrationTestDatabase.getDataSource();
  365. const orderGoodsRepository = dataSource.getRepository(OrderGoods);
  366. const testOrderGoods = orderGoodsRepository.create({
  367. orderId: testOrder.id,
  368. goodsId: testGoods.id,
  369. goodsName: '测试更新订单商品',
  370. price: 50.00,
  371. num: 2,
  372. state: 0,
  373. supplierId: testSupplier.id,
  374. imageFileId: testFile.id,
  375. createdBy: testUser.id
  376. });
  377. await orderGoodsRepository.save(testOrderGoods);
  378. const updateData = {
  379. num: 5,
  380. state: 1
  381. };
  382. const response = await client[':id'].$put({
  383. param: { id: testOrderGoods.id },
  384. json: updateData
  385. }, {
  386. headers: {
  387. 'Authorization': `Bearer ${userToken}`
  388. }
  389. });
  390. console.debug('用户更新订单商品响应状态:', response.status);
  391. expect(response.status).toBe(200);
  392. if (response.status === 200) {
  393. const data = await response.json();
  394. expect(data.num).toBe(updateData.num);
  395. expect(data.state).toBe(updateData.state);
  396. expect(data.updatedBy).toBe(testUser.id); // 验证自动设置更新用户
  397. }
  398. });
  399. it('应该拒绝更新其他用户订单的商品', async () => {
  400. // 为其他用户的订单创建一个商品
  401. const dataSource = await IntegrationTestDatabase.getDataSource();
  402. const orderGoodsRepository = dataSource.getRepository(OrderGoods);
  403. const otherUserOrderGoods = orderGoodsRepository.create({
  404. orderId: otherUserOrder.id,
  405. goodsId: testGoods.id,
  406. goodsName: '其他用户订单商品',
  407. price: 100.00,
  408. num: 1,
  409. state: 0,
  410. supplierId: testSupplier.id,
  411. imageFileId: testFile.id,
  412. createdBy: otherUser.id
  413. });
  414. await orderGoodsRepository.save(otherUserOrderGoods);
  415. const updateData = {
  416. num: 2
  417. };
  418. const response = await client[':id'].$put({
  419. param: { id: otherUserOrderGoods.id },
  420. json: updateData
  421. }, {
  422. headers: {
  423. 'Authorization': `Bearer ${userToken}`
  424. }
  425. });
  426. expect(response.status).toBe(403); // 数据权限控制返回403
  427. });
  428. });
  429. describe('DELETE /order-items/:id', () => {
  430. it('应该成功删除当前用户订单的商品', async () => {
  431. // 先为测试用户的订单创建一个商品
  432. const dataSource = await IntegrationTestDatabase.getDataSource();
  433. const orderGoodsRepository = dataSource.getRepository(OrderGoods);
  434. const testOrderGoods = orderGoodsRepository.create({
  435. orderId: testOrder.id,
  436. goodsId: testGoods.id,
  437. goodsName: '测试删除订单商品',
  438. price: 50.00,
  439. num: 2,
  440. state: 0,
  441. supplierId: testSupplier.id,
  442. imageFileId: testFile.id,
  443. createdBy: testUser.id
  444. });
  445. await orderGoodsRepository.save(testOrderGoods);
  446. const response = await client[':id'].$delete({
  447. param: { id: testOrderGoods.id }
  448. }, {
  449. headers: {
  450. 'Authorization': `Bearer ${userToken}`
  451. }
  452. });
  453. console.debug('用户删除订单商品响应状态:', response.status);
  454. expect(response.status).toBe(204);
  455. });
  456. it('应该拒绝删除其他用户订单的商品', async () => {
  457. // 为其他用户的订单创建一个商品
  458. const dataSource = await IntegrationTestDatabase.getDataSource();
  459. const orderGoodsRepository = dataSource.getRepository(OrderGoods);
  460. const otherUserOrderGoods = orderGoodsRepository.create({
  461. orderId: otherUserOrder.id,
  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: otherUser.id
  470. });
  471. await orderGoodsRepository.save(otherUserOrderGoods);
  472. const response = await client[':id'].$delete({
  473. param: { id: otherUserOrderGoods.id }
  474. }, {
  475. headers: {
  476. 'Authorization': `Bearer ${userToken}`
  477. }
  478. });
  479. expect(response.status).toBe(403); // 数据权限控制返回403
  480. });
  481. });
  482. describe('数据权限配置测试', () => {
  483. it('应该验证dataPermission配置正确工作', async () => {
  484. // 这个测试验证数据权限配置是否正常工作
  485. // 用户只能访问自己订单的商品
  486. const dataSource = await IntegrationTestDatabase.getDataSource();
  487. const orderGoodsRepository = dataSource.getRepository(OrderGoods);
  488. // 创建测试用户和其他用户订单的商品
  489. const userOrderGoods = orderGoodsRepository.create({
  490. orderId: testOrder.id,
  491. goodsId: testGoods.id,
  492. goodsName: '用户订单商品',
  493. price: 50.00,
  494. num: 2,
  495. state: 0,
  496. supplierId: testSupplier.id,
  497. imageFileId: testFile.id,
  498. createdBy: testUser.id
  499. });
  500. await orderGoodsRepository.save(userOrderGoods);
  501. const otherUserOrderGoods = orderGoodsRepository.create({
  502. orderId: otherUserOrder.id,
  503. goodsId: testGoods.id,
  504. goodsName: '其他用户订单商品',
  505. price: 100.00,
  506. num: 1,
  507. state: 0,
  508. supplierId: testSupplier.id,
  509. imageFileId: testFile.id,
  510. createdBy: otherUser.id
  511. });
  512. await orderGoodsRepository.save(otherUserOrderGoods);
  513. // 使用测试用户token获取列表
  514. const response = await client.index.$get({
  515. query: {
  516. page: 1,
  517. pageSize: 10
  518. }
  519. }, {
  520. headers: {
  521. 'Authorization': `Bearer ${userToken}`
  522. }
  523. });
  524. if (response.status !== 200) {
  525. const errorData = await response.json();
  526. console.debug('数据权限配置测试错误响应:', errorData);
  527. }
  528. expect(response.status).toBe(200);
  529. const data = await response.json();
  530. // 类型检查确保data属性存在
  531. if ('data' in data && Array.isArray(data.data)) {
  532. // 验证只返回测试用户订单的商品
  533. const userOrderGoodsInResponse = data.data.filter((orderGoods: any) =>
  534. orderGoods.order && orderGoods.order.userId === testUser.id
  535. );
  536. const otherUserOrderGoodsInResponse = data.data.filter((orderGoods: any) =>
  537. orderGoods.order && orderGoods.order.userId === otherUser.id
  538. );
  539. expect(userOrderGoodsInResponse.length).toBeGreaterThan(0);
  540. expect(otherUserOrderGoodsInResponse.length).toBe(0);
  541. } else {
  542. // 如果响应是错误格式,应该失败
  543. expect(data).toHaveProperty('data');
  544. }
  545. });
  546. });
  547. });