admin-goods-categories.integration.test.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  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 { adminGoodsCategoriesRoutes } from '../../src/routes/admin-goods-categories';
  8. import { GoodsCategory } from '../../src/entities';
  9. // 设置集成测试钩子
  10. setupIntegrationDatabaseHooksWithEntities([UserEntity, Role, GoodsCategory, File])
  11. describe('管理员商品分类管理API集成测试', () => {
  12. let client: ReturnType<typeof testClient<typeof adminGoodsCategoriesRoutes>>;
  13. let adminToken: string;
  14. let testUser: UserEntity;
  15. let testAdmin: UserEntity;
  16. beforeEach(async () => {
  17. // 创建测试客户端
  18. client = testClient(adminGoodsCategoriesRoutes);
  19. // 获取数据源
  20. const dataSource = await IntegrationTestDatabase.getDataSource();
  21. // 创建测试用户
  22. const userRepository = dataSource.getRepository(UserEntity);
  23. testUser = userRepository.create({
  24. username: `test_user_${Math.floor(Math.random() * 100000)}`,
  25. password: 'test_password',
  26. nickname: '测试用户',
  27. registrationSource: 'web'
  28. });
  29. await userRepository.save(testUser);
  30. // 创建测试管理员用户
  31. testAdmin = userRepository.create({
  32. username: `test_admin_${Math.floor(Math.random() * 100000)}`,
  33. password: 'admin_password',
  34. nickname: '测试管理员',
  35. registrationSource: 'web'
  36. });
  37. await userRepository.save(testAdmin);
  38. // 生成测试管理员的token
  39. adminToken = JWTUtil.generateToken({
  40. id: testAdmin.id,
  41. username: testAdmin.username,
  42. roles: [{name:'admin'}]
  43. });
  44. });
  45. describe('GET /goods-categories', () => {
  46. it('应该返回商品分类列表', async () => {
  47. const response = await client.index.$get({
  48. query: {}
  49. }, {
  50. headers: {
  51. 'Authorization': `Bearer ${adminToken}`
  52. }
  53. });
  54. console.debug('商品分类列表响应状态:', response.status);
  55. expect(response.status).toBe(200);
  56. if (response.status === 200) {
  57. const data = await response.json();
  58. expect(data).toHaveProperty('data');
  59. expect(Array.isArray(data.data)).toBe(true);
  60. }
  61. });
  62. it('应该拒绝未认证用户的访问', async () => {
  63. const response = await client.index.$get({
  64. query: {}
  65. });
  66. expect(response.status).toBe(401);
  67. });
  68. });
  69. describe('POST /goods-categories', () => {
  70. it('应该成功创建商品分类', async () => {
  71. const createData = {
  72. name: '管理员创建商品分类',
  73. parentId: 0,
  74. level: 1,
  75. state: 1
  76. };
  77. const response = await client.index.$post({
  78. json: createData
  79. }, {
  80. headers: {
  81. 'Authorization': `Bearer ${adminToken}`
  82. }
  83. });
  84. console.debug('创建商品分类响应状态:', response.status);
  85. if (response.status !== 201) {
  86. const errorData = await response.json();
  87. console.debug('创建商品分类错误响应:', errorData);
  88. }
  89. expect(response.status).toBe(201);
  90. if (response.status === 201) {
  91. const data = await response.json();
  92. expect(data).toHaveProperty('id');
  93. expect(data.name).toBe(createData.name);
  94. expect(data.parentId).toBe(createData.parentId);
  95. expect(data.level).toBe(createData.level);
  96. expect(data.state).toBe(createData.state);
  97. }
  98. });
  99. it('应该验证创建商品分类的必填字段', async () => {
  100. const invalidData = {
  101. // 缺少必填字段
  102. name: '',
  103. parentId: -1,
  104. level: -1
  105. };
  106. const response = await client.index.$post({
  107. json: invalidData
  108. }, {
  109. headers: {
  110. 'Authorization': `Bearer ${adminToken}`
  111. }
  112. });
  113. expect(response.status).toBe(400);
  114. });
  115. });
  116. describe('GET /goods-categories/:id', () => {
  117. it('应该返回指定商品分类的详情', async () => {
  118. // 先创建一个商品分类
  119. const dataSource = await IntegrationTestDatabase.getDataSource();
  120. const categoryRepository = dataSource.getRepository(GoodsCategory);
  121. const testCategory = categoryRepository.create({
  122. name: '测试商品分类详情',
  123. parentId: 0,
  124. level: 1,
  125. state: 1,
  126. createdBy: testUser.id
  127. });
  128. await categoryRepository.save(testCategory);
  129. const response = await client[':id'].$get({
  130. param: { id: testCategory.id }
  131. }, {
  132. headers: {
  133. 'Authorization': `Bearer ${adminToken}`
  134. }
  135. });
  136. console.debug('商品分类详情响应状态:', response.status);
  137. expect(response.status).toBe(200);
  138. if (response.status === 200) {
  139. const data = await response.json();
  140. expect(data.id).toBe(testCategory.id);
  141. expect(data.name).toBe(testCategory.name);
  142. expect(data.parentId).toBe(testCategory.parentId);
  143. expect(data.level).toBe(testCategory.level);
  144. expect(data.state).toBe(testCategory.state);
  145. }
  146. });
  147. it('应该处理不存在的商品分类', async () => {
  148. const response = await client[':id'].$get({
  149. param: { id: 999999 }
  150. }, {
  151. headers: {
  152. 'Authorization': `Bearer ${adminToken}`
  153. }
  154. });
  155. expect(response.status).toBe(404);
  156. });
  157. });
  158. describe('PUT /goods-categories/:id', () => {
  159. it('应该成功更新商品分类', async () => {
  160. // 先创建一个商品分类
  161. const dataSource = await IntegrationTestDatabase.getDataSource();
  162. const categoryRepository = dataSource.getRepository(GoodsCategory);
  163. const testCategory = categoryRepository.create({
  164. name: '测试更新商品分类',
  165. parentId: 0,
  166. level: 1,
  167. state: 1,
  168. createdBy: testUser.id
  169. });
  170. await categoryRepository.save(testCategory);
  171. const updateData = {
  172. name: '更新后的商品分类名称',
  173. state: 2
  174. };
  175. const response = await client[':id'].$put({
  176. param: { id: testCategory.id },
  177. json: updateData
  178. }, {
  179. headers: {
  180. 'Authorization': `Bearer ${adminToken}`
  181. }
  182. });
  183. console.debug('更新商品分类响应状态:', response.status);
  184. expect(response.status).toBe(200);
  185. if (response.status === 200) {
  186. const data = await response.json();
  187. expect(data.name).toBe(updateData.name);
  188. expect(data.state).toBe(updateData.state);
  189. }
  190. });
  191. });
  192. describe('DELETE /goods-categories/:id', () => {
  193. it('应该成功删除商品分类', async () => {
  194. // 先创建一个商品分类
  195. const dataSource = await IntegrationTestDatabase.getDataSource();
  196. const categoryRepository = dataSource.getRepository(GoodsCategory);
  197. const testCategory = categoryRepository.create({
  198. name: '测试删除商品分类',
  199. parentId: 0,
  200. level: 1,
  201. state: 1,
  202. createdBy: testUser.id
  203. });
  204. await categoryRepository.save(testCategory);
  205. const response = await client[':id'].$delete({
  206. param: { id: testCategory.id }
  207. }, {
  208. headers: {
  209. 'Authorization': `Bearer ${adminToken}`
  210. }
  211. });
  212. console.debug('删除商品分类响应状态:', response.status);
  213. expect(response.status).toBe(204);
  214. });
  215. });
  216. describe('商品分类树形结构测试', () => {
  217. it('应该支持多级分类结构', async () => {
  218. const dataSource = await IntegrationTestDatabase.getDataSource();
  219. const categoryRepository = dataSource.getRepository(GoodsCategory);
  220. // 创建一级分类
  221. const level1Category = categoryRepository.create({
  222. name: '一级分类',
  223. parentId: 0,
  224. level: 1,
  225. state: 1,
  226. createdBy: testUser.id
  227. });
  228. await categoryRepository.save(level1Category);
  229. // 创建二级分类
  230. const level2Category = categoryRepository.create({
  231. name: '二级分类',
  232. parentId: level1Category.id,
  233. level: 2,
  234. state: 1,
  235. createdBy: testUser.id
  236. });
  237. await categoryRepository.save(level2Category);
  238. // 创建三级分类
  239. const level3Category = categoryRepository.create({
  240. name: '三级分类',
  241. parentId: level2Category.id,
  242. level: 3,
  243. state: 1,
  244. createdBy: testUser.id
  245. });
  246. await categoryRepository.save(level3Category);
  247. // 验证分类层级关系
  248. const response = await client.index.$get({
  249. query: {}
  250. }, {
  251. headers: {
  252. 'Authorization': `Bearer ${adminToken}`
  253. }
  254. });
  255. expect(response.status).toBe(200);
  256. const data = await response.json();
  257. // 类型检查确保data属性存在
  258. if ('data' in data && Array.isArray(data.data)) {
  259. // 验证分类数量
  260. expect(data.data.length).toBeGreaterThanOrEqual(3);
  261. } else {
  262. // 如果响应是错误格式,应该失败
  263. expect(data).toHaveProperty('data');
  264. }
  265. });
  266. });
  267. describe('商品分类状态管理测试', () => {
  268. it('应该正确处理分类状态变更', async () => {
  269. const dataSource = await IntegrationTestDatabase.getDataSource();
  270. const categoryRepository = dataSource.getRepository(GoodsCategory);
  271. // 创建可用状态的分类
  272. const activeCategory = categoryRepository.create({
  273. name: '可用分类',
  274. parentId: 0,
  275. level: 1,
  276. state: 1,
  277. createdBy: testUser.id
  278. });
  279. await categoryRepository.save(activeCategory);
  280. // 创建不可用状态的分类
  281. const inactiveCategory = categoryRepository.create({
  282. name: '不可用分类',
  283. parentId: 0,
  284. level: 1,
  285. state: 2,
  286. createdBy: testUser.id
  287. });
  288. await categoryRepository.save(inactiveCategory);
  289. // 验证状态过滤
  290. const response = await client.index.$get({
  291. query: { filters: JSON.stringify({ state: 1 }) }
  292. }, {
  293. headers: {
  294. 'Authorization': `Bearer ${adminToken}`
  295. }
  296. });
  297. expect(response.status).toBe(200);
  298. const data = await response.json();
  299. // 类型检查确保data属性存在
  300. if ('data' in data && Array.isArray(data.data)) {
  301. // 应该只返回可用状态的分类
  302. const activeCategories = data.data.filter((category: any) => category.state === 1);
  303. expect(activeCategories.length).toBeGreaterThan(0);
  304. const inactiveCategories = data.data.filter((category: any) => category.state === 2);
  305. expect(inactiveCategories.length).toBe(0);
  306. } else {
  307. // 如果响应是错误格式,应该失败
  308. expect(data).toHaveProperty('data');
  309. }
  310. });
  311. });
  312. });