user-goods-routes.integration.test.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  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 { Supplier } from '@d8d/supplier-module';
  8. import { Merchant } from '@d8d/merchant-module';
  9. import { userGoodsRoutes } from '../../src/routes';
  10. import { Goods, GoodsCategory } from '../../src/entities';
  11. // 设置集成测试钩子
  12. setupIntegrationDatabaseHooksWithEntities([
  13. UserEntity, Role, Goods, GoodsCategory, File, Supplier, Merchant
  14. ])
  15. describe('用户商品管理API集成测试', () => {
  16. let client: ReturnType<typeof testClient<typeof userGoodsRoutes>>;
  17. let userToken: string;
  18. let otherUserToken: string;
  19. let testUser: UserEntity;
  20. let otherUser: UserEntity;
  21. let testCategory: GoodsCategory;
  22. let testSupplier: Supplier;
  23. let testMerchant: Merchant;
  24. beforeEach(async () => {
  25. // 创建测试客户端
  26. client = testClient(userGoodsRoutes);
  27. // 获取数据源
  28. const dataSource = await IntegrationTestDatabase.getDataSource();
  29. // 创建测试用户
  30. const userRepository = dataSource.getRepository(UserEntity);
  31. testUser = userRepository.create({
  32. username: `test_user_${Math.floor(Math.random() * 100000)}`,
  33. password: 'test_password',
  34. nickname: '测试用户',
  35. registrationSource: 'web'
  36. });
  37. await userRepository.save(testUser);
  38. // 创建其他用户
  39. otherUser = userRepository.create({
  40. username: `other_user_${Math.floor(Math.random() * 100000)}`,
  41. password: 'other_password',
  42. nickname: '其他用户',
  43. registrationSource: 'web'
  44. });
  45. await userRepository.save(otherUser);
  46. // 生成测试用户的token
  47. userToken = JWTUtil.generateToken({
  48. id: testUser.id,
  49. username: testUser.username,
  50. roles: [{name:'user'}]
  51. });
  52. // 生成其他用户的token
  53. otherUserToken = JWTUtil.generateToken({
  54. id: otherUser.id,
  55. username: otherUser.username,
  56. roles: [{name:'user'}]
  57. });
  58. // 创建测试商品分类
  59. const categoryRepository = dataSource.getRepository(GoodsCategory);
  60. testCategory = categoryRepository.create({
  61. name: '测试分类',
  62. parentId: 0,
  63. level: 1,
  64. state: 1,
  65. createdBy: testUser.id
  66. });
  67. await categoryRepository.save(testCategory);
  68. // 创建测试供应商
  69. const supplierRepository = dataSource.getRepository(Supplier);
  70. testSupplier = supplierRepository.create({
  71. name: '测试供应商',
  72. username: `test_supplier_${Math.floor(Math.random() * 100000)}`,
  73. password: 'password123',
  74. phone: '13800138000',
  75. realname: '测试供应商',
  76. state: 1,
  77. createdBy: testUser.id
  78. });
  79. await supplierRepository.save(testSupplier);
  80. // 创建测试商户
  81. const merchantRepository = dataSource.getRepository(Merchant);
  82. testMerchant = merchantRepository.create({
  83. name: '测试商户',
  84. username: `test_merchant_${Math.floor(Math.random() * 100000)}`,
  85. password: 'password123',
  86. phone: '13800138001',
  87. realname: '测试商户',
  88. state: 1,
  89. createdBy: testUser.id
  90. });
  91. await merchantRepository.save(testMerchant);
  92. });
  93. describe('GET /goods', () => {
  94. it('应该返回当前用户的商品列表', async () => {
  95. // 为测试用户创建一些商品
  96. const dataSource = await IntegrationTestDatabase.getDataSource();
  97. const goodsRepository = dataSource.getRepository(Goods);
  98. const userGoods1 = goodsRepository.create({
  99. name: '用户商品1',
  100. price: 100.00,
  101. costPrice: 80.00,
  102. categoryId1: testCategory.id,
  103. categoryId2: 0,
  104. categoryId3: 0,
  105. goodsType: 1,
  106. supplierId: testSupplier.id,
  107. merchantId: testMerchant.id,
  108. state: 1,
  109. stock: 100,
  110. lowestBuy: 1,
  111. createdBy: testUser.id
  112. });
  113. await goodsRepository.save(userGoods1);
  114. const userGoods2 = goodsRepository.create({
  115. name: '用户商品2',
  116. price: 200.00,
  117. costPrice: 160.00,
  118. categoryId1: testCategory.id,
  119. categoryId2: 0,
  120. categoryId3: 0,
  121. goodsType: 1,
  122. supplierId: testSupplier.id,
  123. merchantId: testMerchant.id,
  124. state: 1,
  125. stock: 50,
  126. lowestBuy: 1,
  127. createdBy: testUser.id
  128. });
  129. await goodsRepository.save(userGoods2);
  130. // 为其他用户创建一个商品,确保不会返回
  131. const otherUserGoods = goodsRepository.create({
  132. name: '其他用户商品',
  133. price: 300.00,
  134. costPrice: 240.00,
  135. categoryId1: testCategory.id,
  136. categoryId2: 0,
  137. categoryId3: 0,
  138. goodsType: 1,
  139. supplierId: testSupplier.id,
  140. merchantId: testMerchant.id,
  141. state: 1,
  142. stock: 30,
  143. lowestBuy: 1,
  144. createdBy: otherUser.id
  145. });
  146. await goodsRepository.save(otherUserGoods);
  147. const response = await client.index.$get({
  148. query: {}
  149. }, {
  150. headers: {
  151. 'Authorization': `Bearer ${userToken}`
  152. }
  153. });
  154. console.debug('用户商品列表响应状态:', response.status);
  155. expect(response.status).toBe(200);
  156. if (response.status === 200) {
  157. const data = await response.json();
  158. expect(data).toHaveProperty('data');
  159. expect(Array.isArray(data.data)).toBe(true);
  160. // 验证只返回当前用户的商品
  161. data.data.forEach((goods: any) => {
  162. expect(goods.createdBy).toBe(testUser.id);
  163. });
  164. // 验证不包含其他用户的商品
  165. const otherUserGoodsInResponse = data.data.find((goods: any) => goods.createdBy === otherUser.id);
  166. expect(otherUserGoodsInResponse).toBeUndefined();
  167. }
  168. });
  169. it('应该拒绝未认证用户的访问', async () => {
  170. const response = await client.index.$get({
  171. query: {}
  172. });
  173. expect(response.status).toBe(401);
  174. });
  175. });
  176. describe('POST /goods', () => {
  177. it('应该成功创建商品并自动设置当前用户权限', async () => {
  178. const createData = {
  179. name: '用户创建商品',
  180. price: 150.00,
  181. costPrice: 120.00,
  182. categoryId1: testCategory.id,
  183. categoryId2: 0,
  184. categoryId3: 0,
  185. goodsType: 1,
  186. supplierId: testSupplier.id,
  187. merchantId: testMerchant.id,
  188. state: 1,
  189. stock: 80,
  190. lowestBuy: 1
  191. };
  192. const response = await client.index.$post({
  193. json: createData
  194. }, {
  195. headers: {
  196. 'Authorization': `Bearer ${userToken}`
  197. }
  198. });
  199. console.debug('用户创建商品响应状态:', response.status);
  200. if (response.status !== 201) {
  201. const errorData = await response.json();
  202. console.debug('用户创建商品错误响应:', errorData);
  203. }
  204. expect(response.status).toBe(201);
  205. if (response.status === 201) {
  206. const data = await response.json();
  207. expect(data).toHaveProperty('id');
  208. expect(data.name).toBe(createData.name);
  209. expect(data.price).toBe(createData.price);
  210. expect(data.createdBy).toBe(testUser.id); // 验证自动设置当前用户权限
  211. }
  212. });
  213. it('应该验证创建商品的必填字段', async () => {
  214. const invalidData = {
  215. // 缺少必填字段
  216. name: '',
  217. price: -1,
  218. categoryId1: -1
  219. };
  220. const response = await client.index.$post({
  221. json: invalidData
  222. }, {
  223. headers: {
  224. 'Authorization': `Bearer ${userToken}`
  225. }
  226. });
  227. expect(response.status).toBe(400);
  228. });
  229. });
  230. describe('GET /goods/:id', () => {
  231. it('应该返回当前用户的商品详情', async () => {
  232. // 先为测试用户创建一个商品
  233. const dataSource = await IntegrationTestDatabase.getDataSource();
  234. const goodsRepository = dataSource.getRepository(Goods);
  235. const testGoods = goodsRepository.create({
  236. name: '测试用户商品详情',
  237. price: 100.00,
  238. costPrice: 80.00,
  239. categoryId1: testCategory.id,
  240. categoryId2: 0,
  241. categoryId3: 0,
  242. goodsType: 1,
  243. supplierId: testSupplier.id,
  244. merchantId: testMerchant.id,
  245. state: 1,
  246. stock: 100,
  247. lowestBuy: 1,
  248. createdBy: testUser.id
  249. });
  250. await goodsRepository.save(testGoods);
  251. const response = await client[':id'].$get({
  252. param: { id: testGoods.id }
  253. }, {
  254. headers: {
  255. 'Authorization': `Bearer ${userToken}`
  256. }
  257. });
  258. console.debug('用户商品详情响应状态:', response.status);
  259. expect(response.status).toBe(200);
  260. if (response.status === 200) {
  261. const data = await response.json();
  262. expect(data.id).toBe(testGoods.id);
  263. expect(data.name).toBe(testGoods.name);
  264. expect(data.createdBy).toBe(testUser.id);
  265. }
  266. });
  267. it('应该拒绝访问其他用户的商品', async () => {
  268. // 为其他用户创建一个商品
  269. const dataSource = await IntegrationTestDatabase.getDataSource();
  270. const goodsRepository = dataSource.getRepository(Goods);
  271. const otherUserGoods = goodsRepository.create({
  272. name: '其他用户商品',
  273. price: 100.00,
  274. costPrice: 80.00,
  275. categoryId1: testCategory.id,
  276. categoryId2: 0,
  277. categoryId3: 0,
  278. goodsType: 1,
  279. supplierId: testSupplier.id,
  280. merchantId: testMerchant.id,
  281. state: 1,
  282. stock: 100,
  283. lowestBuy: 1,
  284. createdBy: otherUser.id
  285. });
  286. await goodsRepository.save(otherUserGoods);
  287. const response = await client[':id'].$get({
  288. param: { id: otherUserGoods.id }
  289. }, {
  290. headers: {
  291. 'Authorization': `Bearer ${userToken}`
  292. }
  293. });
  294. expect(response.status).toBe(404); // 数据权限控制应该返回404而不是403
  295. });
  296. it('应该处理不存在的商品', async () => {
  297. const response = await client[':id'].$get({
  298. param: { id: 999999 }
  299. }, {
  300. headers: {
  301. 'Authorization': `Bearer ${userToken}`
  302. }
  303. });
  304. expect(response.status).toBe(404);
  305. });
  306. });
  307. describe('PUT /goods/:id', () => {
  308. it('应该成功更新当前用户的商品', async () => {
  309. // 先为测试用户创建一个商品
  310. const dataSource = await IntegrationTestDatabase.getDataSource();
  311. const goodsRepository = dataSource.getRepository(Goods);
  312. const testGoods = goodsRepository.create({
  313. name: '测试更新商品',
  314. price: 100.00,
  315. costPrice: 80.00,
  316. categoryId1: testCategory.id,
  317. categoryId2: 0,
  318. categoryId3: 0,
  319. goodsType: 1,
  320. supplierId: testSupplier.id,
  321. merchantId: testMerchant.id,
  322. state: 1,
  323. stock: 100,
  324. lowestBuy: 1,
  325. createdBy: testUser.id
  326. });
  327. await goodsRepository.save(testGoods);
  328. const updateData = {
  329. name: '更新后的商品名称',
  330. price: 120.00,
  331. state: 2
  332. };
  333. const response = await client[':id'].$put({
  334. param: { id: testGoods.id },
  335. json: updateData
  336. }, {
  337. headers: {
  338. 'Authorization': `Bearer ${userToken}`
  339. }
  340. });
  341. console.debug('用户更新商品响应状态:', response.status);
  342. expect(response.status).toBe(200);
  343. if (response.status === 200) {
  344. const data = await response.json();
  345. expect(data.name).toBe(updateData.name);
  346. expect(data.price).toBe(updateData.price);
  347. expect(data.state).toBe(updateData.state);
  348. expect(data.updatedBy).toBe(testUser.id); // 验证自动设置更新用户
  349. }
  350. });
  351. it('应该拒绝更新其他用户的商品', async () => {
  352. // 为其他用户创建一个商品
  353. const dataSource = await IntegrationTestDatabase.getDataSource();
  354. const goodsRepository = dataSource.getRepository(Goods);
  355. const otherUserGoods = goodsRepository.create({
  356. name: '其他用户商品',
  357. price: 100.00,
  358. costPrice: 80.00,
  359. categoryId1: testCategory.id,
  360. categoryId2: 0,
  361. categoryId3: 0,
  362. goodsType: 1,
  363. supplierId: testSupplier.id,
  364. merchantId: testMerchant.id,
  365. state: 1,
  366. stock: 100,
  367. lowestBuy: 1,
  368. createdBy: otherUser.id
  369. });
  370. await goodsRepository.save(otherUserGoods);
  371. const updateData = {
  372. name: '尝试更新其他用户商品'
  373. };
  374. const response = await client[':id'].$put({
  375. param: { id: otherUserGoods.id },
  376. json: updateData
  377. }, {
  378. headers: {
  379. 'Authorization': `Bearer ${userToken}`
  380. }
  381. });
  382. expect(response.status).toBe(404); // 数据权限控制应该返回404而不是403
  383. });
  384. });
  385. describe('DELETE /goods/:id', () => {
  386. it('应该成功删除当前用户的商品', async () => {
  387. // 先为测试用户创建一个商品
  388. const dataSource = await IntegrationTestDatabase.getDataSource();
  389. const goodsRepository = dataSource.getRepository(Goods);
  390. const testGoods = goodsRepository.create({
  391. name: '测试删除商品',
  392. price: 100.00,
  393. costPrice: 80.00,
  394. categoryId1: testCategory.id,
  395. categoryId2: 0,
  396. categoryId3: 0,
  397. goodsType: 1,
  398. supplierId: testSupplier.id,
  399. merchantId: testMerchant.id,
  400. state: 1,
  401. stock: 100,
  402. lowestBuy: 1,
  403. createdBy: testUser.id
  404. });
  405. await goodsRepository.save(testGoods);
  406. const response = await client[':id'].$delete({
  407. param: { id: testGoods.id }
  408. }, {
  409. headers: {
  410. 'Authorization': `Bearer ${userToken}`
  411. }
  412. });
  413. console.debug('用户删除商品响应状态:', response.status);
  414. expect(response.status).toBe(204);
  415. });
  416. it('应该拒绝删除其他用户的商品', async () => {
  417. // 为其他用户创建一个商品
  418. const dataSource = await IntegrationTestDatabase.getDataSource();
  419. const goodsRepository = dataSource.getRepository(Goods);
  420. const otherUserGoods = goodsRepository.create({
  421. name: '其他用户商品',
  422. price: 100.00,
  423. costPrice: 80.00,
  424. categoryId1: testCategory.id,
  425. categoryId2: 0,
  426. categoryId3: 0,
  427. goodsType: 1,
  428. supplierId: testSupplier.id,
  429. merchantId: testMerchant.id,
  430. state: 1,
  431. stock: 100,
  432. lowestBuy: 1,
  433. createdBy: otherUser.id
  434. });
  435. await goodsRepository.save(otherUserGoods);
  436. const response = await client[':id'].$delete({
  437. param: { id: otherUserGoods.id }
  438. }, {
  439. headers: {
  440. 'Authorization': `Bearer ${userToken}`
  441. }
  442. });
  443. expect(response.status).toBe(404); // 数据权限控制应该返回404而不是403
  444. });
  445. });
  446. describe('数据权限配置测试', () => {
  447. it('应该验证dataPermission配置正确工作', async () => {
  448. // 这个测试验证数据权限配置是否正常工作
  449. // 用户只能访问自己创建的商品
  450. const dataSource = await IntegrationTestDatabase.getDataSource();
  451. const goodsRepository = dataSource.getRepository(Goods);
  452. // 创建测试用户和其他用户的商品
  453. const userGoods = goodsRepository.create({
  454. name: '用户商品',
  455. price: 100.00,
  456. costPrice: 80.00,
  457. categoryId1: testCategory.id,
  458. categoryId2: 0,
  459. categoryId3: 0,
  460. goodsType: 1,
  461. supplierId: testSupplier.id,
  462. merchantId: testMerchant.id,
  463. state: 1,
  464. stock: 100,
  465. lowestBuy: 1,
  466. createdBy: testUser.id
  467. });
  468. await goodsRepository.save(userGoods);
  469. const otherUserGoods = goodsRepository.create({
  470. name: '其他用户商品',
  471. price: 200.00,
  472. costPrice: 160.00,
  473. categoryId1: testCategory.id,
  474. categoryId2: 0,
  475. categoryId3: 0,
  476. goodsType: 1,
  477. supplierId: testSupplier.id,
  478. merchantId: testMerchant.id,
  479. state: 1,
  480. stock: 50,
  481. lowestBuy: 1,
  482. createdBy: otherUser.id
  483. });
  484. await goodsRepository.save(otherUserGoods);
  485. // 使用测试用户token获取列表
  486. const response = await client.index.$get({
  487. query: {}
  488. }, {
  489. headers: {
  490. 'Authorization': `Bearer ${userToken}`
  491. }
  492. });
  493. expect(response.status).toBe(200);
  494. const data = await response.json();
  495. // 验证只返回测试用户的商品
  496. const userGoodsInResponse = data.data.filter((goods: any) => goods.createdBy === testUser.id);
  497. const otherUserGoodsInResponse = data.data.filter((goods: any) => goods.createdBy === otherUser.id);
  498. expect(userGoodsInResponse.length).toBeGreaterThan(0);
  499. expect(otherUserGoodsInResponse.length).toBe(0);
  500. });
  501. });
  502. });