2
0

admin-routes.integration.test.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  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 { adminSupplierRoutes } from '../../src/routes';
  8. import { Supplier } from '../../src/entities';
  9. // 设置集成测试钩子
  10. setupIntegrationDatabaseHooksWithEntities([UserEntity, Role, Supplier, File])
  11. describe('管理员供应商管理API集成测试', () => {
  12. let client: ReturnType<typeof testClient<typeof adminSupplierRoutes>>;
  13. let adminToken: string;
  14. let testUser: UserEntity;
  15. let testAdmin: UserEntity;
  16. beforeEach(async () => {
  17. // 创建测试客户端
  18. client = testClient(adminSupplierRoutes);
  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 /suppliers', () => {
  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 /suppliers', () => {
  70. it('应该成功创建供应商', async () => {
  71. const createData = {
  72. name: '管理员创建供应商',
  73. username: `admin_created_supplier_${Math.floor(Math.random() * 100000)}`,
  74. password: 'password123',
  75. phone: '13800138000',
  76. realname: '管理员创建供应商',
  77. state: 1
  78. };
  79. const response = await client.index.$post({
  80. json: createData
  81. }, {
  82. headers: {
  83. 'Authorization': `Bearer ${adminToken}`
  84. }
  85. });
  86. console.debug('创建供应商响应状态:', response.status);
  87. if (response.status !== 201) {
  88. const errorData = await response.json();
  89. console.debug('创建供应商错误响应:', errorData);
  90. }
  91. expect(response.status).toBe(201);
  92. if (response.status === 201) {
  93. const data = await response.json();
  94. expect(data).toHaveProperty('id');
  95. expect(data.name).toBe(createData.name);
  96. expect(data.username).toBe(createData.username);
  97. expect(data.phone).toBe(createData.phone);
  98. expect(data.realname).toBe(createData.realname);
  99. expect(data.state).toBe(createData.state);
  100. }
  101. });
  102. it('应该验证创建供应商的必填字段', async () => {
  103. const invalidData = {
  104. // 缺少必填字段
  105. name: '',
  106. username: '',
  107. password: '',
  108. phone: '',
  109. realname: ''
  110. };
  111. const response = await client.index.$post({
  112. json: invalidData
  113. }, {
  114. headers: {
  115. 'Authorization': `Bearer ${adminToken}`
  116. }
  117. });
  118. expect(response.status).toBe(400);
  119. });
  120. });
  121. describe('GET /suppliers/:id', () => {
  122. it('应该返回指定供应商的详情', async () => {
  123. // 先创建一个供应商
  124. const dataSource = await IntegrationTestDatabase.getDataSource();
  125. const supplierRepository = dataSource.getRepository(Supplier);
  126. const testSupplier = supplierRepository.create({
  127. name: '测试供应商详情',
  128. username: `test_supplier_detail_${Math.floor(Math.random() * 100000)}`,
  129. password: 'password123',
  130. phone: '13600136000',
  131. realname: '测试供应商详情',
  132. loginNum: 5,
  133. loginTime: Date.now(),
  134. loginIp: '192.168.1.1',
  135. lastLoginTime: Date.now(),
  136. lastLoginIp: '192.168.1.1',
  137. state: 1,
  138. createdBy: testUser.id
  139. });
  140. await supplierRepository.save(testSupplier);
  141. const response = await client[':id'].$get({
  142. param: { id: testSupplier.id }
  143. }, {
  144. headers: {
  145. 'Authorization': `Bearer ${adminToken}`
  146. }
  147. });
  148. console.debug('供应商详情响应状态:', response.status);
  149. expect(response.status).toBe(200);
  150. if (response.status === 200) {
  151. const data = await response.json();
  152. expect(data.id).toBe(testSupplier.id);
  153. expect(data.name).toBe(testSupplier.name);
  154. expect(data.username).toBe(testSupplier.username);
  155. expect(data.phone).toBe(testSupplier.phone);
  156. expect(data.realname).toBe(testSupplier.realname);
  157. }
  158. });
  159. it('应该处理不存在的供应商', async () => {
  160. const response = await client[':id'].$get({
  161. param: { id: 999999 }
  162. }, {
  163. headers: {
  164. 'Authorization': `Bearer ${adminToken}`
  165. }
  166. });
  167. expect(response.status).toBe(404);
  168. });
  169. });
  170. describe('PUT /suppliers/:id', () => {
  171. it('应该成功更新供应商', async () => {
  172. // 先创建一个供应商
  173. const dataSource = await IntegrationTestDatabase.getDataSource();
  174. const supplierRepository = dataSource.getRepository(Supplier);
  175. const testSupplier = supplierRepository.create({
  176. name: '原始供应商',
  177. username: `original_supplier_${Math.floor(Math.random() * 100000)}`,
  178. password: 'password123',
  179. phone: '13500135000',
  180. realname: '原始供应商',
  181. loginNum: 0,
  182. loginTime: 0,
  183. loginIp: null,
  184. lastLoginTime: 0,
  185. lastLoginIp: null,
  186. state: 1,
  187. createdBy: testUser.id
  188. });
  189. await supplierRepository.save(testSupplier);
  190. const updateData = {
  191. name: '更新后的供应商',
  192. phone: '13700137000',
  193. realname: '更新后的供应商',
  194. state: 2
  195. };
  196. const response = await client[':id'].$put({
  197. param: { id: testSupplier.id },
  198. json: updateData
  199. }, {
  200. headers: {
  201. 'Authorization': `Bearer ${adminToken}`
  202. }
  203. });
  204. console.debug('更新供应商响应状态:', response.status);
  205. expect(response.status).toBe(200);
  206. if (response.status === 200) {
  207. const data = await response.json();
  208. expect(data.name).toBe(updateData.name);
  209. expect(data.phone).toBe(updateData.phone);
  210. expect(data.realname).toBe(updateData.realname);
  211. expect(data.state).toBe(updateData.state);
  212. }
  213. });
  214. });
  215. describe('DELETE /suppliers/:id', () => {
  216. it('应该成功删除供应商', async () => {
  217. // 先创建一个供应商
  218. const dataSource = await IntegrationTestDatabase.getDataSource();
  219. const supplierRepository = dataSource.getRepository(Supplier);
  220. const testSupplier = supplierRepository.create({
  221. name: '待删除供应商',
  222. username: `delete_supplier_${Math.floor(Math.random() * 100000)}`,
  223. password: 'password123',
  224. phone: '13400134000',
  225. realname: '待删除供应商',
  226. loginNum: 0,
  227. loginTime: 0,
  228. loginIp: null,
  229. lastLoginTime: 0,
  230. lastLoginIp: null,
  231. state: 1,
  232. createdBy: testUser.id
  233. });
  234. await supplierRepository.save(testSupplier);
  235. const response = await client[':id'].$delete({
  236. param: { id: testSupplier.id }
  237. }, {
  238. headers: {
  239. 'Authorization': `Bearer ${adminToken}`
  240. }
  241. });
  242. console.debug('删除供应商响应状态:', response.status);
  243. expect(response.status).toBe(204);
  244. // 验证供应商确实被删除
  245. const deletedSupplier = await supplierRepository.findOne({
  246. where: { id: testSupplier.id }
  247. });
  248. expect(deletedSupplier).toBeNull();
  249. });
  250. });
  251. describe('管理员权限测试', () => {
  252. it('管理员应该可以为其他用户创建供应商', async () => {
  253. const createData = {
  254. name: '为其他用户创建供应商',
  255. username: `other_user_supplier_${Math.floor(Math.random() * 100000)}`,
  256. password: 'password123',
  257. phone: '13800138001',
  258. realname: '为其他用户创建供应商',
  259. state: 1,
  260. createdBy: testUser.id // 管理员可以指定创建者
  261. };
  262. const response = await client.index.$post({
  263. json: createData
  264. }, {
  265. headers: {
  266. 'Authorization': `Bearer ${adminToken}`
  267. }
  268. });
  269. console.debug('管理员为其他用户创建供应商响应状态:', response.status);
  270. expect(response.status).toBe(201);
  271. if (response.status === 201) {
  272. const data = await response.json();
  273. expect(data.createdBy).toBe(testUser.id); // 验证供应商确实属于其他用户
  274. expect(data.name).toBe(createData.name);
  275. }
  276. });
  277. it('管理员应该可以访问所有用户的供应商', async () => {
  278. // 为测试用户创建一些供应商
  279. const dataSource = await IntegrationTestDatabase.getDataSource();
  280. const supplierRepository = dataSource.getRepository(Supplier);
  281. const userSupplier1 = supplierRepository.create({
  282. name: '用户供应商1',
  283. username: `user_supplier1_${Math.floor(Math.random() * 100000)}`,
  284. password: 'password123',
  285. phone: '13800138002',
  286. realname: '用户供应商1',
  287. loginNum: 0,
  288. loginTime: 0,
  289. loginIp: null,
  290. lastLoginTime: 0,
  291. lastLoginIp: null,
  292. state: 1,
  293. createdBy: testUser.id
  294. });
  295. await supplierRepository.save(userSupplier1);
  296. const userSupplier2 = supplierRepository.create({
  297. name: '用户供应商2',
  298. username: `user_supplier2_${Math.floor(Math.random() * 100000)}`,
  299. password: 'password123',
  300. phone: '13800138003',
  301. realname: '用户供应商2',
  302. loginNum: 0,
  303. loginTime: 0,
  304. loginIp: null,
  305. lastLoginTime: 0,
  306. lastLoginIp: null,
  307. state: 1,
  308. createdBy: testUser.id
  309. });
  310. await supplierRepository.save(userSupplier2);
  311. // 管理员应该能看到所有供应商
  312. const response = await client.index.$get({
  313. query: {}
  314. }, {
  315. headers: {
  316. 'Authorization': `Bearer ${adminToken}`
  317. }
  318. });
  319. expect(response.status).toBe(200);
  320. const data = await response.json();
  321. if (data && 'data' in data) {
  322. expect(Array.isArray(data.data)).toBe(true);
  323. expect(data.data.length).toBeGreaterThanOrEqual(2); // 至少包含我们创建的两个供应商
  324. }
  325. });
  326. it('管理员应该可以更新其他用户的供应商', async () => {
  327. // 先为测试用户创建一个供应商
  328. const dataSource = await IntegrationTestDatabase.getDataSource();
  329. const supplierRepository = dataSource.getRepository(Supplier);
  330. const testSupplier = supplierRepository.create({
  331. name: '原始供应商',
  332. username: `original_supplier_admin_${Math.floor(Math.random() * 100000)}`,
  333. password: 'password123',
  334. phone: '13800138004',
  335. realname: '原始供应商',
  336. loginNum: 0,
  337. loginTime: 0,
  338. loginIp: null,
  339. lastLoginTime: 0,
  340. lastLoginIp: null,
  341. state: 1,
  342. createdBy: testUser.id
  343. });
  344. await supplierRepository.save(testSupplier);
  345. const updateData = {
  346. name: '管理员更新的供应商',
  347. phone: '13900139000',
  348. realname: '管理员更新的供应商'
  349. };
  350. const response = await client[':id'].$put({
  351. param: { id: testSupplier.id },
  352. json: updateData
  353. }, {
  354. headers: {
  355. 'Authorization': `Bearer ${adminToken}`
  356. }
  357. });
  358. console.debug('管理员更新其他用户供应商响应状态:', response.status);
  359. expect(response.status).toBe(200);
  360. if (response.status === 200) {
  361. const data = await response.json();
  362. expect(data.name).toBe(updateData.name);
  363. expect(data.phone).toBe(updateData.phone);
  364. expect(data.realname).toBe(updateData.realname);
  365. }
  366. });
  367. it('管理员应该可以删除其他用户的供应商', async () => {
  368. // 先为测试用户创建一个供应商
  369. const dataSource = await IntegrationTestDatabase.getDataSource();
  370. const supplierRepository = dataSource.getRepository(Supplier);
  371. const testSupplier = supplierRepository.create({
  372. name: '待删除供应商',
  373. username: `delete_supplier_admin_${Math.floor(Math.random() * 100000)}`,
  374. password: 'password123',
  375. phone: '13800138005',
  376. realname: '待删除供应商',
  377. loginNum: 0,
  378. loginTime: 0,
  379. loginIp: null,
  380. lastLoginTime: 0,
  381. lastLoginIp: null,
  382. state: 1,
  383. createdBy: testUser.id
  384. });
  385. await supplierRepository.save(testSupplier);
  386. const response = await client[':id'].$delete({
  387. param: { id: testSupplier.id }
  388. }, {
  389. headers: {
  390. 'Authorization': `Bearer ${adminToken}`
  391. }
  392. });
  393. console.debug('管理员删除其他用户供应商响应状态:', response.status);
  394. expect(response.status).toBe(204);
  395. // 验证供应商确实被删除
  396. const deletedSupplier = await supplierRepository.findOne({
  397. where: { id: testSupplier.id }
  398. });
  399. expect(deletedSupplier).toBeNull();
  400. });
  401. it('管理员应该可以查询指定用户的供应商', async () => {
  402. // 为测试用户创建一些供应商
  403. const dataSource = await IntegrationTestDatabase.getDataSource();
  404. const supplierRepository = dataSource.getRepository(Supplier);
  405. const userSupplier = supplierRepository.create({
  406. name: '指定用户供应商',
  407. username: `specified_user_supplier_${Math.floor(Math.random() * 100000)}`,
  408. password: 'password123',
  409. phone: '13800138006',
  410. realname: '指定用户供应商',
  411. loginNum: 0,
  412. loginTime: 0,
  413. loginIp: null,
  414. lastLoginTime: 0,
  415. lastLoginIp: null,
  416. state: 1,
  417. createdBy: testUser.id
  418. });
  419. await supplierRepository.save(userSupplier);
  420. // 管理员可以查询指定用户的供应商
  421. const response = await client.index.$get({
  422. query: { filters: JSON.stringify({ createdBy: testUser.id }) }
  423. }, {
  424. headers: {
  425. 'Authorization': `Bearer ${adminToken}`
  426. }
  427. });
  428. expect(response.status).toBe(200);
  429. const data = await response.json();
  430. if (data && 'data' in data) {
  431. expect(Array.isArray(data.data)).toBe(true);
  432. // 验证返回的供应商都属于指定用户
  433. if (data.data.length > 0) {
  434. data.data.forEach((supplier: any) => {
  435. expect(supplier.createdBy).toBe(testUser.id);
  436. });
  437. }
  438. }
  439. });
  440. });
  441. describe('供应商状态管理', () => {
  442. it('应该支持供应商状态管理', async () => {
  443. // 创建启用状态的供应商
  444. const createData = {
  445. name: '状态测试供应商',
  446. username: `status_test_supplier_${Math.floor(Math.random() * 100000)}`,
  447. password: 'password123',
  448. phone: '13800138007',
  449. realname: '状态测试供应商',
  450. state: 1 // 启用状态
  451. };
  452. const createResponse = await client.index.$post({
  453. json: createData
  454. }, {
  455. headers: {
  456. 'Authorization': `Bearer ${adminToken}`
  457. }
  458. });
  459. expect(createResponse.status).toBe(201);
  460. const createdData = await createResponse.json();
  461. // 更新为禁用状态
  462. if (typeof createdData === 'object' && createdData !== null && 'id' in createdData) {
  463. const updateResponse = await client[':id'].$put({
  464. param: { id: createdData.id },
  465. json: { state: 2 } // 禁用状态
  466. }, {
  467. headers: {
  468. 'Authorization': `Bearer ${adminToken}`
  469. }
  470. });
  471. expect(updateResponse.status).toBe(200);
  472. const updatedData = await updateResponse.json();
  473. if (typeof updatedData === 'object' && updatedData !== null && 'state' in updatedData) {
  474. expect(updatedData.state).toBe(2);
  475. }
  476. }
  477. });
  478. });
  479. describe('供应商登录统计', () => {
  480. it('应该支持供应商登录统计功能', async () => {
  481. // 创建供应商
  482. const createData = {
  483. name: '登录统计供应商',
  484. username: `login_stat_supplier_${Math.floor(Math.random() * 100000)}`,
  485. password: 'password123',
  486. phone: '13800138008',
  487. realname: '登录统计供应商',
  488. state: 1
  489. };
  490. const createResponse = await client.index.$post({
  491. json: createData
  492. }, {
  493. headers: {
  494. 'Authorization': `Bearer ${adminToken}`
  495. }
  496. });
  497. expect(createResponse.status).toBe(201);
  498. const createdData = await createResponse.json();
  499. // 验证初始登录统计
  500. if (typeof createdData === 'object' && createdData !== null) {
  501. if ('loginNum' in createdData) expect(createdData.loginNum).toBe(0);
  502. if ('loginTime' in createdData) expect(createdData.loginTime).toBe(0);
  503. if ('lastLoginTime' in createdData) expect(createdData.lastLoginTime).toBe(0);
  504. if ('loginIp' in createdData) expect(createdData.loginIp).toBeNull();
  505. if ('lastLoginIp' in createdData) expect(createdData.lastLoginIp).toBeNull();
  506. }
  507. // 获取供应商详情验证字段存在
  508. if (typeof createdData === 'object' && createdData !== null && 'id' in createdData) {
  509. const getResponse = await client[':id'].$get({
  510. param: { id: createdData.id }
  511. }, {
  512. headers: {
  513. 'Authorization': `Bearer ${adminToken}`
  514. }
  515. });
  516. expect(getResponse.status).toBe(200);
  517. const supplierData = await getResponse.json();
  518. if (typeof supplierData === 'object' && supplierData !== null) {
  519. expect(supplierData).toHaveProperty('loginNum');
  520. expect(supplierData).toHaveProperty('loginTime');
  521. expect(supplierData).toHaveProperty('lastLoginTime');
  522. expect(supplierData).toHaveProperty('loginIp');
  523. expect(supplierData).toHaveProperty('lastLoginIp');
  524. }
  525. }
  526. });
  527. });
  528. });