2
0

admin-goods-routes.integration.test.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  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 { UserEntityMt, RoleMt } from '@d8d/user-module-mt';
  6. import { FileMt } from '@d8d/file-module-mt';
  7. import { SupplierMt } from '@d8d/supplier-module-mt';
  8. import { MerchantMt } from '@d8d/merchant-module-mt';
  9. import { adminGoodsRoutesMt } from '../../src/routes/index.mt';
  10. import { GoodsMt, GoodsCategoryMt } from '../../src/entities/index.mt';
  11. import { GoodsTestFactory } from '../factories/goods-test-factory';
  12. // 设置集成测试钩子
  13. setupIntegrationDatabaseHooksWithEntities([
  14. UserEntityMt, RoleMt, GoodsMt, GoodsCategoryMt, FileMt, SupplierMt, MerchantMt
  15. ])
  16. describe('管理员商品管理API集成测试', () => {
  17. let client: ReturnType<typeof testClient<typeof adminGoodsRoutesMt>>;
  18. let adminToken: string;
  19. let testUser: UserEntityMt;
  20. let testAdmin: UserEntityMt;
  21. let testCategory: GoodsCategoryMt;
  22. let testSupplier: SupplierMt;
  23. let testMerchant: MerchantMt;
  24. let testFactory: GoodsTestFactory;
  25. beforeEach(async () => {
  26. // 创建测试客户端
  27. client = testClient(adminGoodsRoutesMt);
  28. // 获取数据源并创建测试工厂
  29. const dataSource = await IntegrationTestDatabase.getDataSource();
  30. testFactory = new GoodsTestFactory(dataSource);
  31. // 使用测试工厂创建测试数据
  32. testUser = await testFactory.createTestUser();
  33. testAdmin = await testFactory.createTestAdmin();
  34. testCategory = await testFactory.createTestCategory(testUser.id);
  35. testSupplier = await testFactory.createTestSupplier(testUser.id);
  36. testMerchant = await testFactory.createTestMerchant(testUser.id);
  37. // 生成测试管理员的token
  38. adminToken = JWTUtil.generateToken({
  39. id: testAdmin.id,
  40. username: testAdmin.username,
  41. roles: [{name:'admin'}]
  42. });
  43. });
  44. describe('GET /goods', () => {
  45. it('应该返回所有商品列表', async () => {
  46. // 创建多个用户的商品
  47. const userGoods1 = await testFactory.createTestGoods(testUser.id, {
  48. name: '用户商品1',
  49. price: 100.00,
  50. costPrice: 80.00,
  51. categoryId1: testCategory.id,
  52. categoryId2: testCategory.id,
  53. categoryId3: testCategory.id,
  54. supplierId: testSupplier.id,
  55. merchantId: testMerchant.id,
  56. stock: 100
  57. });
  58. const userGoods2 = await testFactory.createTestGoods(testAdmin.id, {
  59. name: '用户商品2',
  60. price: 200.00,
  61. costPrice: 160.00,
  62. categoryId1: testCategory.id,
  63. categoryId2: testCategory.id,
  64. categoryId3: testCategory.id,
  65. supplierId: testSupplier.id,
  66. merchantId: testMerchant.id,
  67. stock: 50
  68. });
  69. const response = await client.index.$get({
  70. query: {}
  71. }, {
  72. headers: {
  73. 'Authorization': `Bearer ${adminToken}`
  74. }
  75. });
  76. console.debug('管理员商品列表响应状态:', response.status);
  77. expect(response.status).toBe(200);
  78. if (response.status === 200) {
  79. const data = await response.json();
  80. expect(data).toHaveProperty('data');
  81. expect(Array.isArray(data.data)).toBe(true);
  82. // 验证返回所有用户的商品(管理员可以访问所有数据)
  83. const userGoodsCount = data.data.filter((goods: any) => goods.createdBy === testUser.id).length;
  84. const adminGoodsCount = data.data.filter((goods: any) => goods.createdBy === testAdmin.id).length;
  85. expect(userGoodsCount).toBeGreaterThan(0);
  86. expect(adminGoodsCount).toBeGreaterThan(0);
  87. }
  88. });
  89. it('应该拒绝未认证用户的访问', async () => {
  90. const response = await client.index.$get({
  91. query: {}
  92. });
  93. expect(response.status).toBe(401);
  94. });
  95. });
  96. describe('POST /goods', () => {
  97. it('应该成功创建商品并可以指定权限', async () => {
  98. const createData = {
  99. name: '管理员创建商品',
  100. price: 150.00,
  101. costPrice: 120.00,
  102. categoryId1: testCategory.id,
  103. categoryId2: testCategory.id,
  104. categoryId3: testCategory.id,
  105. goodsType: 1,
  106. supplierId: testSupplier.id,
  107. merchantId: testMerchant.id,
  108. state: 1,
  109. stock: 80,
  110. lowestBuy: 1,
  111. createdBy: testUser.id // 管理员可以指定创建人
  112. };
  113. const response = await client.index.$post({
  114. json: createData
  115. }, {
  116. headers: {
  117. 'Authorization': `Bearer ${adminToken}`
  118. }
  119. });
  120. console.debug('管理员创建商品响应状态:', response.status);
  121. if (response.status !== 201) {
  122. const errorData = await response.json();
  123. console.debug('管理员创建商品错误响应:', errorData);
  124. }
  125. expect(response.status).toBe(201);
  126. if (response.status === 201) {
  127. const data = await response.json();
  128. expect(data).toHaveProperty('id');
  129. expect(data.name).toBe(createData.name);
  130. expect(data.price).toBe(Number(createData.price));
  131. expect(data.createdBy).toBe(testUser.id); // 验证可以指定创建人
  132. }
  133. });
  134. it('应该验证创建商品的必填字段', async () => {
  135. const invalidData = {
  136. // 缺少必填字段
  137. name: '',
  138. price: -1,
  139. categoryId1: -1
  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. });
  151. describe('GET /goods/:id', () => {
  152. it('应该返回指定商品的详情', async () => {
  153. // 先创建一个商品
  154. const testGoods = await testFactory.createTestGoods(testUser.id, {
  155. name: '测试商品详情',
  156. price: 100.00,
  157. costPrice: 80.00,
  158. categoryId1: testCategory.id,
  159. categoryId2: testCategory.id,
  160. categoryId3: testCategory.id,
  161. supplierId: testSupplier.id,
  162. merchantId: testMerchant.id,
  163. stock: 100
  164. });
  165. const response = await client[':id'].$get({
  166. param: { id: testGoods.id }
  167. }, {
  168. headers: {
  169. 'Authorization': `Bearer ${adminToken}`
  170. }
  171. });
  172. console.debug('管理员商品详情响应状态:', response.status);
  173. expect(response.status).toBe(200);
  174. if (response.status === 200) {
  175. const data = await response.json();
  176. expect(data.id).toBe(testGoods.id);
  177. expect(data.name).toBe(testGoods.name);
  178. expect(data.createdBy).toBe(testUser.id);
  179. }
  180. });
  181. it('应该处理不存在的商品', async () => {
  182. const response = await client[':id'].$get({
  183. param: { id: 999999 }
  184. }, {
  185. headers: {
  186. 'Authorization': `Bearer ${adminToken}`
  187. }
  188. });
  189. expect(response.status).toBe(404);
  190. });
  191. });
  192. describe('PUT /goods/:id', () => {
  193. it('应该成功更新任何商品', async () => {
  194. // 先创建一个商品
  195. const testGoods = await testFactory.createTestGoods(testUser.id, {
  196. name: '测试更新商品',
  197. price: 100.00,
  198. costPrice: 80.00,
  199. categoryId1: testCategory.id,
  200. categoryId2: testCategory.id,
  201. categoryId3: testCategory.id,
  202. supplierId: testSupplier.id,
  203. merchantId: testMerchant.id,
  204. stock: 100
  205. });
  206. const updateData = {
  207. name: '管理员更新后的商品名称',
  208. price: 120.00,
  209. state: 2,
  210. updatedBy: testAdmin.id // 管理员可以指定更新人
  211. };
  212. const response = await client[':id'].$put({
  213. param: { id: testGoods.id },
  214. json: updateData
  215. }, {
  216. headers: {
  217. 'Authorization': `Bearer ${adminToken}`
  218. }
  219. });
  220. console.debug('管理员更新商品响应状态:', response.status);
  221. expect(response.status).toBe(200);
  222. if (response.status === 200) {
  223. const data = await response.json();
  224. expect(data.name).toBe(updateData.name);
  225. expect(data.price).toBe(Number(updateData.price));
  226. expect(data.state).toBe(updateData.state);
  227. expect(data.updatedBy).toBe(testAdmin.id); // 验证可以指定更新人
  228. }
  229. });
  230. });
  231. describe('DELETE /goods/:id', () => {
  232. it('应该成功删除任何商品', async () => {
  233. // 先创建一个商品
  234. const testGoods = await testFactory.createTestGoods(testUser.id, {
  235. name: '测试删除商品',
  236. price: 100.00,
  237. costPrice: 80.00,
  238. categoryId1: testCategory.id,
  239. categoryId2: testCategory.id,
  240. categoryId3: testCategory.id,
  241. supplierId: testSupplier.id,
  242. merchantId: testMerchant.id,
  243. stock: 100
  244. });
  245. const response = await client[':id'].$delete({
  246. param: { id: testGoods.id }
  247. }, {
  248. headers: {
  249. 'Authorization': `Bearer ${adminToken}`
  250. }
  251. });
  252. console.debug('管理员删除商品响应状态:', response.status);
  253. expect(response.status).toBe(204);
  254. });
  255. });
  256. describe('商品状态管理测试', () => {
  257. it('应该正确处理商品状态变更', async () => {
  258. // 创建可用状态的商品
  259. const activeGoods = await testFactory.createTestGoods(testUser.id, {
  260. name: '可用商品',
  261. price: 100.00,
  262. costPrice: 80.00,
  263. categoryId1: testCategory.id,
  264. categoryId2: testCategory.id,
  265. categoryId3: testCategory.id,
  266. supplierId: testSupplier.id,
  267. merchantId: testMerchant.id,
  268. state: 1,
  269. stock: 100
  270. });
  271. // 创建不可用状态的商品
  272. const inactiveGoods = await testFactory.createTestGoods(testUser.id, {
  273. name: '不可用商品',
  274. price: 200.00,
  275. costPrice: 160.00,
  276. categoryId1: testCategory.id,
  277. categoryId2: testCategory.id,
  278. categoryId3: testCategory.id,
  279. supplierId: testSupplier.id,
  280. merchantId: testMerchant.id,
  281. state: 2,
  282. stock: 50
  283. });
  284. // 验证状态过滤
  285. const response = await client.index.$get({
  286. query: { filters: JSON.stringify({ state: 1 }) }
  287. }, {
  288. headers: {
  289. 'Authorization': `Bearer ${adminToken}`
  290. }
  291. });
  292. expect(response.status).toBe(200);
  293. const data = await response.json();
  294. // 类型检查确保data属性存在
  295. if ('data' in data && Array.isArray(data.data)) {
  296. // 应该只返回可用状态的商品
  297. const activeGoodsInResponse = data.data.filter((goods: any) => goods.state === 1);
  298. const inactiveGoodsInResponse = data.data.filter((goods: any) => goods.state === 2);
  299. expect(activeGoodsInResponse.length).toBeGreaterThan(0);
  300. expect(inactiveGoodsInResponse.length).toBe(0);
  301. } else {
  302. // 如果响应是错误格式,应该失败
  303. expect(data).toHaveProperty('data');
  304. }
  305. });
  306. });
  307. describe('商品库存管理测试', () => {
  308. it('应该正确处理商品库存更新', async () => {
  309. // 创建一个商品
  310. const testGoods = await testFactory.createTestGoods(testUser.id, {
  311. name: '库存测试商品',
  312. price: 100.00,
  313. costPrice: 80.00,
  314. categoryId1: testCategory.id,
  315. categoryId2: testCategory.id,
  316. categoryId3: testCategory.id,
  317. supplierId: testSupplier.id,
  318. merchantId: testMerchant.id,
  319. stock: 100
  320. });
  321. // 更新库存
  322. const updateData = {
  323. stock: 50
  324. };
  325. const response = await client[':id'].$put({
  326. param: { id: testGoods.id },
  327. json: updateData
  328. }, {
  329. headers: {
  330. 'Authorization': `Bearer ${adminToken}`
  331. }
  332. });
  333. expect(response.status).toBe(200);
  334. if (response.status === 200) {
  335. const data = await response.json();
  336. expect(data.stock).toBe(Number(updateData.stock));
  337. }
  338. });
  339. });
  340. describe('管理员权限验证测试', () => {
  341. it('应该验证管理员可以访问所有数据', async () => {
  342. // 创建多个用户的商品
  343. const userGoods = await testFactory.createTestGoods(testUser.id, {
  344. name: '用户商品',
  345. price: 100.00,
  346. costPrice: 80.00,
  347. categoryId1: testCategory.id,
  348. categoryId2: testCategory.id,
  349. categoryId3: testCategory.id,
  350. supplierId: testSupplier.id,
  351. merchantId: testMerchant.id,
  352. stock: 100
  353. });
  354. const adminGoods = await testFactory.createTestGoods(testAdmin.id, {
  355. name: '管理员商品',
  356. price: 200.00,
  357. costPrice: 160.00,
  358. categoryId1: testCategory.id,
  359. categoryId2: testCategory.id,
  360. categoryId3: testCategory.id,
  361. supplierId: testSupplier.id,
  362. merchantId: testMerchant.id,
  363. stock: 50
  364. });
  365. // 使用管理员token获取列表
  366. const response = await client.index.$get({
  367. query: {}
  368. }, {
  369. headers: {
  370. 'Authorization': `Bearer ${adminToken}`
  371. }
  372. });
  373. expect(response.status).toBe(200);
  374. const data = await response.json();
  375. // 类型检查确保data属性存在
  376. if ('data' in data && Array.isArray(data.data)) {
  377. // 验证返回所有用户的商品
  378. const userGoodsInResponse = data.data.filter((goods: any) => goods.createdBy === testUser.id);
  379. const adminGoodsInResponse = data.data.filter((goods: any) => goods.createdBy === testAdmin.id);
  380. expect(userGoodsInResponse.length).toBeGreaterThan(0);
  381. expect(adminGoodsInResponse.length).toBeGreaterThan(0);
  382. } else {
  383. // 如果响应是错误格式,应该失败
  384. expect(data).toHaveProperty('data');
  385. }
  386. });
  387. });
  388. });