user-routes.integration.test.ts 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846
  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 { userSupplierRoutes } from '../../src/routes';
  8. import { SupplierMt } from '../../src/entities';
  9. // 设置集成测试钩子
  10. setupIntegrationDatabaseHooksWithEntities([UserEntityMt, RoleMt, SupplierMt, FileMt])
  11. describe('用户供应商管理API集成测试', () => {
  12. let client: ReturnType<typeof testClient<typeof userSupplierRoutes>>;
  13. let userToken: string;
  14. let otherUserToken: string;
  15. let testUser: UserEntityMt;
  16. let otherUser: UserEntityMt;
  17. beforeEach(async () => {
  18. // 创建测试客户端
  19. client = testClient(userSupplierRoutes);
  20. // 获取数据源
  21. const dataSource = await IntegrationTestDatabase.getDataSource();
  22. // 创建测试用户
  23. const userRepository = dataSource.getRepository(UserEntityMt);
  24. testUser = userRepository.create({
  25. username: `test_user_${Math.floor(Math.random() * 100000)}`,
  26. password: 'test_password',
  27. nickname: '测试用户',
  28. registrationSource: 'web',
  29. tenantId: 1
  30. });
  31. await userRepository.save(testUser);
  32. // 创建其他用户
  33. otherUser = userRepository.create({
  34. username: `other_user_${Math.floor(Math.random() * 100000)}`,
  35. password: 'other_password',
  36. nickname: '其他用户',
  37. registrationSource: 'web',
  38. tenantId: 1
  39. });
  40. await userRepository.save(otherUser);
  41. // 生成测试用户的token
  42. userToken = JWTUtil.generateToken({
  43. id: testUser.id,
  44. username: testUser.username,
  45. roles: [{name:'user'}],
  46. tenantId: testUser.tenantId
  47. });
  48. // 生成其他用户的token
  49. otherUserToken = JWTUtil.generateToken({
  50. id: otherUser.id,
  51. username: otherUser.username,
  52. roles: [{name:'user'}],
  53. tenantId: otherUser.tenantId
  54. });
  55. });
  56. describe('GET /suppliers', () => {
  57. it('应该返回当前用户的供应商列表', async () => {
  58. // 为测试用户创建一些供应商
  59. const dataSource = await IntegrationTestDatabase.getDataSource();
  60. const supplierRepository = dataSource.getRepository(SupplierMt);
  61. const userSupplier1 = supplierRepository.create({
  62. tenantId: 1,
  63. name: '用户供应商1',
  64. username: `user_supplier1_${Math.floor(Math.random() * 100000)}`,
  65. password: 'password123',
  66. phone: '13800138001',
  67. realname: '用户供应商1',
  68. loginNum: 0,
  69. loginTime: null,
  70. loginIp: null,
  71. lastLoginTime: null,
  72. lastLoginIp: null,
  73. state: 1,
  74. createdBy: testUser.id
  75. });
  76. await supplierRepository.save(userSupplier1);
  77. const userSupplier2 = supplierRepository.create({
  78. tenantId: 1,
  79. name: '用户供应商2',
  80. username: `user_supplier2_${Math.floor(Math.random() * 100000)}`,
  81. password: 'password123',
  82. phone: '13800138002',
  83. realname: '用户供应商2',
  84. loginNum: 0,
  85. loginTime: null,
  86. loginIp: null,
  87. lastLoginTime: null,
  88. lastLoginIp: null,
  89. state: 1,
  90. createdBy: testUser.id
  91. });
  92. await supplierRepository.save(userSupplier2);
  93. // 为其他用户创建一个供应商,确保不会返回
  94. const otherUserSupplier = supplierRepository.create({
  95. tenantId: 1,
  96. name: '其他用户供应商',
  97. username: `other_supplier_${Math.floor(Math.random() * 100000)}`,
  98. password: 'password123',
  99. phone: '13800138003',
  100. realname: '其他用户供应商',
  101. loginNum: 0,
  102. loginTime: null,
  103. loginIp: null,
  104. lastLoginTime: null,
  105. lastLoginIp: null,
  106. state: 1,
  107. createdBy: otherUser.id
  108. });
  109. await supplierRepository.save(otherUserSupplier);
  110. const response = await client.index.$get({
  111. query: {}
  112. }, {
  113. headers: {
  114. 'Authorization': `Bearer ${userToken}`
  115. }
  116. });
  117. console.debug('用户供应商列表响应状态:', response.status);
  118. expect(response.status).toBe(200);
  119. if (response.status === 200) {
  120. const data = await response.json();
  121. if (data && 'data' in data) {
  122. expect(Array.isArray(data.data)).toBe(true);
  123. // 应该只返回当前用户的供应商
  124. data.data.forEach((supplier: any) => {
  125. expect(supplier.createdBy).toBe(testUser.id);
  126. });
  127. }
  128. }
  129. });
  130. it('应该拒绝未认证用户的访问', async () => {
  131. const response = await client.index.$get({
  132. query: {}
  133. });
  134. expect(response.status).toBe(401);
  135. });
  136. });
  137. describe('POST /suppliers', () => {
  138. it('应该成功创建供应商并自动使用当前用户ID', async () => {
  139. const createData = {
  140. name: '测试供应商',
  141. username: `test_supplier_${Math.floor(Math.random() * 100000)}`,
  142. password: 'password123',
  143. phone: '13800138000',
  144. realname: '测试供应商',
  145. state: 1
  146. };
  147. const response = await client.index.$post({
  148. json: createData
  149. }, {
  150. headers: {
  151. 'Authorization': `Bearer ${userToken}`
  152. }
  153. });
  154. console.debug('用户创建供应商响应状态:', response.status);
  155. expect(response.status).toBe(201);
  156. if (response.status === 201) {
  157. const data = await response.json();
  158. console.debug('用户创建供应商响应数据:', JSON.stringify(data, null, 2));
  159. expect(data).toHaveProperty('id');
  160. expect(data.createdBy).toBe(testUser.id); // 自动使用当前用户ID
  161. expect(data.name).toBe(createData.name);
  162. expect(data.username).toBe(createData.username);
  163. expect(data.phone).toBe(createData.phone);
  164. expect(data.realname).toBe(createData.realname);
  165. }
  166. });
  167. it('应该验证创建供应商的必填字段', async () => {
  168. const invalidData = {
  169. // 缺少必填字段
  170. name: '',
  171. username: '',
  172. password: '',
  173. phone: '',
  174. realname: ''
  175. };
  176. const response = await client.index.$post({
  177. json: invalidData
  178. }, {
  179. headers: {
  180. 'Authorization': `Bearer ${userToken}`
  181. }
  182. });
  183. expect(response.status).toBe(400);
  184. });
  185. });
  186. describe('GET /suppliers/:id', () => {
  187. it('应该返回当前用户的供应商详情', async () => {
  188. // 先为当前用户创建一个供应商
  189. const dataSource = await IntegrationTestDatabase.getDataSource();
  190. const supplierRepository = dataSource.getRepository(SupplierMt);
  191. const testSupplier = supplierRepository.create({
  192. tenantId: 1,
  193. name: '测试供应商详情',
  194. username: `test_supplier_detail_${Math.floor(Math.random() * 100000)}`,
  195. password: 'password123',
  196. phone: '13600136000',
  197. realname: '测试供应商详情',
  198. loginNum: 5,
  199. loginTime: new Date('2024-01-01T12:00:00Z'),
  200. loginIp: '192.168.1.1',
  201. lastLoginTime: new Date('2024-01-01T12:00:00Z'),
  202. lastLoginIp: '192.168.1.1',
  203. state: 1,
  204. createdBy: testUser.id
  205. });
  206. await supplierRepository.save(testSupplier);
  207. const response = await client[':id'].$get({
  208. param: { id: testSupplier.id }
  209. }, {
  210. headers: {
  211. 'Authorization': `Bearer ${userToken}`
  212. }
  213. });
  214. console.debug('用户供应商详情响应状态:', response.status);
  215. expect(response.status).toBe(200);
  216. if (response.status === 200) {
  217. const data = await response.json();
  218. expect(data.id).toBe(testSupplier.id);
  219. expect(data.createdBy).toBe(testUser.id);
  220. expect(data.name).toBe(testSupplier.name);
  221. expect(data.username).toBe(testSupplier.username);
  222. expect(data.phone).toBe(testSupplier.phone);
  223. expect(data.realname).toBe(testSupplier.realname);
  224. }
  225. });
  226. it('应该拒绝访问其他用户的供应商', async () => {
  227. // 为其他用户创建一个供应商
  228. const dataSource = await IntegrationTestDatabase.getDataSource();
  229. const supplierRepository = dataSource.getRepository(SupplierMt);
  230. const otherUserSupplier = supplierRepository.create({
  231. tenantId: 1,
  232. name: '其他用户供应商',
  233. username: `other_supplier_detail_${Math.floor(Math.random() * 100000)}`,
  234. password: 'password123',
  235. phone: '13600136001',
  236. realname: '其他用户供应商',
  237. loginNum: 0,
  238. loginTime: null,
  239. loginIp: null,
  240. lastLoginTime: null,
  241. lastLoginIp: null,
  242. state: 1,
  243. createdBy: otherUser.id
  244. });
  245. await supplierRepository.save(otherUserSupplier);
  246. // 当前用户尝试访问其他用户的供应商
  247. const response = await client[':id'].$get({
  248. param: { id: otherUserSupplier.id }
  249. }, {
  250. headers: {
  251. 'Authorization': `Bearer ${userToken}`
  252. }
  253. });
  254. console.debug('用户访问其他用户供应商响应状态:', response.status);
  255. expect(response.status).toBe(404); // 应该返回404,而不是403
  256. });
  257. it('应该处理不存在的供应商', async () => {
  258. const response = await client[':id'].$get({
  259. param: { id: 999999 }
  260. }, {
  261. headers: {
  262. 'Authorization': `Bearer ${userToken}`
  263. }
  264. });
  265. expect(response.status).toBe(404);
  266. });
  267. });
  268. describe('PUT /suppliers/:id', () => {
  269. it('应该成功更新当前用户的供应商', async () => {
  270. // 先为当前用户创建一个供应商
  271. const dataSource = await IntegrationTestDatabase.getDataSource();
  272. const supplierRepository = dataSource.getRepository(SupplierMt);
  273. const testSupplier = supplierRepository.create({
  274. tenantId: 1,
  275. name: '原始供应商',
  276. username: `original_supplier_${Math.floor(Math.random() * 100000)}`,
  277. password: 'password123',
  278. phone: '13500135000',
  279. realname: '原始供应商',
  280. loginNum: 0,
  281. loginTime: null,
  282. loginIp: null,
  283. lastLoginTime: null,
  284. lastLoginIp: null,
  285. state: 1,
  286. createdBy: testUser.id
  287. });
  288. await supplierRepository.save(testSupplier);
  289. const updateData = {
  290. name: '更新后的供应商',
  291. phone: '13700137000',
  292. realname: '更新后的供应商',
  293. state: 2
  294. };
  295. const response = await client[':id'].$put({
  296. param: { id: testSupplier.id },
  297. json: updateData
  298. }, {
  299. headers: {
  300. 'Authorization': `Bearer ${userToken}`
  301. }
  302. });
  303. console.debug('用户更新供应商响应状态:', response.status);
  304. expect(response.status).toBe(200);
  305. if (response.status === 200) {
  306. const data = await response.json();
  307. expect(data.name).toBe(updateData.name);
  308. expect(data.phone).toBe(updateData.phone);
  309. expect(data.realname).toBe(updateData.realname);
  310. expect(data.state).toBe(updateData.state);
  311. }
  312. });
  313. it('应该拒绝更新其他用户的供应商', async () => {
  314. // 为其他用户创建一个供应商
  315. const dataSource = await IntegrationTestDatabase.getDataSource();
  316. const supplierRepository = dataSource.getRepository(SupplierMt);
  317. const otherUserSupplier = supplierRepository.create({
  318. tenantId: 1,
  319. name: '其他用户供应商',
  320. username: `other_supplier_update_${Math.floor(Math.random() * 100000)}`,
  321. password: 'password123',
  322. phone: '13500135001',
  323. realname: '其他用户供应商',
  324. loginNum: 0,
  325. loginTime: null,
  326. loginIp: null,
  327. lastLoginTime: null,
  328. lastLoginIp: null,
  329. state: 1,
  330. createdBy: otherUser.id
  331. });
  332. await supplierRepository.save(otherUserSupplier);
  333. const updateData = {
  334. name: '尝试更新的供应商',
  335. phone: '13700137001',
  336. realname: '尝试更新的供应商'
  337. };
  338. // 当前用户尝试更新其他用户的供应商
  339. const response = await client[':id'].$put({
  340. param: { id: otherUserSupplier.id },
  341. json: updateData
  342. }, {
  343. headers: {
  344. 'Authorization': `Bearer ${userToken}`
  345. }
  346. });
  347. console.debug('用户更新其他用户供应商响应状态:', response.status);
  348. expect(response.status).toBe(403); // 数据权限控制返回403
  349. });
  350. });
  351. describe('DELETE /suppliers/:id', () => {
  352. it('应该成功删除当前用户的供应商', async () => {
  353. // 先为当前用户创建一个供应商
  354. const dataSource = await IntegrationTestDatabase.getDataSource();
  355. const supplierRepository = dataSource.getRepository(SupplierMt);
  356. const testSupplier = supplierRepository.create({
  357. tenantId: 1,
  358. name: '待删除供应商',
  359. username: `delete_supplier_${Math.floor(Math.random() * 100000)}`,
  360. password: 'password123',
  361. phone: '13400134000',
  362. realname: '待删除供应商',
  363. loginNum: 0,
  364. loginTime: null,
  365. loginIp: null,
  366. lastLoginTime: null,
  367. lastLoginIp: null,
  368. state: 1,
  369. createdBy: testUser.id
  370. });
  371. await supplierRepository.save(testSupplier);
  372. const response = await client[':id'].$delete({
  373. param: { id: testSupplier.id }
  374. }, {
  375. headers: {
  376. 'Authorization': `Bearer ${userToken}`
  377. }
  378. });
  379. console.debug('用户删除供应商响应状态:', response.status);
  380. expect(response.status).toBe(204);
  381. // 验证供应商确实被删除
  382. const deletedSupplier = await supplierRepository.findOne({
  383. where: { id: testSupplier.id }
  384. });
  385. expect(deletedSupplier).toBeNull();
  386. });
  387. it('应该拒绝删除其他用户的供应商', async () => {
  388. // 为其他用户创建一个供应商
  389. const dataSource = await IntegrationTestDatabase.getDataSource();
  390. const supplierRepository = dataSource.getRepository(SupplierMt);
  391. const otherUserSupplier = supplierRepository.create({
  392. tenantId: 1,
  393. name: '其他用户供应商',
  394. username: `other_supplier_delete_${Math.floor(Math.random() * 100000)}`,
  395. password: 'password123',
  396. phone: '13400134001',
  397. realname: '其他用户供应商',
  398. loginNum: 0,
  399. loginTime: null,
  400. loginIp: null,
  401. lastLoginTime: null,
  402. lastLoginIp: null,
  403. state: 1,
  404. createdBy: otherUser.id
  405. });
  406. await supplierRepository.save(otherUserSupplier);
  407. // 当前用户尝试删除其他用户的供应商
  408. const response = await client[':id'].$delete({
  409. param: { id: otherUserSupplier.id }
  410. }, {
  411. headers: {
  412. 'Authorization': `Bearer ${userToken}`
  413. }
  414. });
  415. console.debug('用户删除其他用户供应商响应状态:', response.status);
  416. expect(response.status).toBe(403); // 数据权限控制返回403
  417. });
  418. });
  419. describe('数据权限验证', () => {
  420. it('用户应该只能访问和操作自己的数据', async () => {
  421. // 为两个用户都创建供应商
  422. const dataSource = await IntegrationTestDatabase.getDataSource();
  423. const supplierRepository = dataSource.getRepository(SupplierMt);
  424. const userSupplier = supplierRepository.create({
  425. tenantId: 1,
  426. name: '用户供应商',
  427. username: `user_supplier_perm_${Math.floor(Math.random() * 100000)}`,
  428. password: 'password123',
  429. phone: '13800138004',
  430. realname: '用户供应商',
  431. loginNum: 0,
  432. loginTime: null,
  433. loginIp: null,
  434. lastLoginTime: null,
  435. lastLoginIp: null,
  436. state: 1,
  437. createdBy: testUser.id
  438. });
  439. await supplierRepository.save(userSupplier);
  440. const otherUserSupplier = supplierRepository.create({
  441. tenantId: 1,
  442. name: '其他用户供应商',
  443. username: `other_supplier_perm_${Math.floor(Math.random() * 100000)}`,
  444. password: 'password123',
  445. phone: '13800138005',
  446. realname: '其他用户供应商',
  447. loginNum: 0,
  448. loginTime: null,
  449. loginIp: null,
  450. lastLoginTime: null,
  451. lastLoginIp: null,
  452. state: 1,
  453. createdBy: otherUser.id
  454. });
  455. await supplierRepository.save(otherUserSupplier);
  456. // 当前用户应该只能看到自己的供应商
  457. const listResponse = await client.index.$get({
  458. query: {}
  459. }, {
  460. headers: {
  461. 'Authorization': `Bearer ${userToken}`
  462. }
  463. });
  464. expect(listResponse.status).toBe(200);
  465. const listData = await listResponse.json();
  466. if (listData && 'data' in listData) {
  467. expect(Array.isArray(listData.data)).toBe(true);
  468. // 应该只包含当前用户的供应商
  469. listData.data.forEach((supplier: any) => {
  470. expect(supplier.createdBy).toBe(testUser.id);
  471. });
  472. }
  473. // 当前用户应该无法访问其他用户的供应商详情
  474. const getResponse = await client[':id'].$get({
  475. param: { id: otherUserSupplier.id }
  476. }, {
  477. headers: {
  478. 'Authorization': `Bearer ${userToken}`
  479. }
  480. });
  481. expect(getResponse.status).toBe(404);
  482. // 当前用户应该无法更新其他用户的供应商
  483. const updateResponse = await client[':id'].$put({
  484. param: { id: otherUserSupplier.id },
  485. json: { name: '尝试更新' }
  486. }, {
  487. headers: {
  488. 'Authorization': `Bearer ${userToken}`
  489. }
  490. });
  491. expect(updateResponse.status).toBe(403);
  492. // 当前用户应该无法删除其他用户的供应商
  493. const deleteResponse = await client[':id'].$delete({
  494. param: { id: otherUserSupplier.id }
  495. }, {
  496. headers: {
  497. 'Authorization': `Bearer ${userToken}`
  498. }
  499. });
  500. expect(deleteResponse.status).toBe(403);
  501. });
  502. });
  503. describe('供应商状态管理', () => {
  504. it('应该支持供应商状态管理', async () => {
  505. // 创建启用状态的供应商
  506. const createData = {
  507. name: '状态测试供应商',
  508. username: `status_test_supplier_${Math.floor(Math.random() * 100000)}`,
  509. password: 'password123',
  510. phone: '13800138006',
  511. realname: '状态测试供应商',
  512. state: 1 // 启用状态
  513. };
  514. const createResponse = await client.index.$post({
  515. json: createData
  516. }, {
  517. headers: {
  518. 'Authorization': `Bearer ${userToken}`
  519. }
  520. });
  521. expect(createResponse.status).toBe(201);
  522. const createdData = await createResponse.json();
  523. // 更新为禁用状态
  524. if (typeof createdData === 'object' && createdData !== null && 'id' in createdData) {
  525. const updateResponse = await client[':id'].$put({
  526. param: { id: createdData.id },
  527. json: { state: 2 } // 禁用状态
  528. }, {
  529. headers: {
  530. 'Authorization': `Bearer ${userToken}`
  531. }
  532. });
  533. expect(updateResponse.status).toBe(200);
  534. const updatedData = await updateResponse.json();
  535. if (typeof updatedData === 'object' && updatedData !== null && 'state' in updatedData) {
  536. expect(updatedData.state).toBe(2);
  537. }
  538. }
  539. });
  540. });
  541. describe('供应商登录统计', () => {
  542. it('应该支持供应商登录统计功能', async () => {
  543. // 创建供应商
  544. const createData = {
  545. name: '登录统计供应商',
  546. username: `login_stat_supplier_${Math.floor(Math.random() * 100000)}`,
  547. password: 'password123',
  548. phone: '13800138007',
  549. realname: '登录统计供应商',
  550. state: 1
  551. };
  552. const createResponse = await client.index.$post({
  553. json: createData
  554. }, {
  555. headers: {
  556. 'Authorization': `Bearer ${userToken}`
  557. }
  558. });
  559. expect(createResponse.status).toBe(201);
  560. const createdData = await createResponse.json();
  561. // 验证初始登录统计
  562. if (typeof createdData === 'object' && createdData !== null) {
  563. if ('loginNum' in createdData) expect(createdData.loginNum).toBe(0);
  564. if ('loginTime' in createdData) expect(createdData.loginTime).toBeNull();
  565. if ('lastLoginTime' in createdData) expect(createdData.lastLoginTime).toBeNull();
  566. if ('loginIp' in createdData) expect(createdData.loginIp).toBeNull();
  567. if ('lastLoginIp' in createdData) expect(createdData.lastLoginIp).toBeNull();
  568. }
  569. // 获取供应商详情验证字段存在
  570. if (typeof createdData === 'object' && createdData !== null && 'id' in createdData) {
  571. const getResponse = await client[':id'].$get({
  572. param: { id: createdData.id }
  573. }, {
  574. headers: {
  575. 'Authorization': `Bearer ${userToken}`
  576. }
  577. });
  578. expect(getResponse.status).toBe(200);
  579. const supplierData = await getResponse.json();
  580. if (typeof supplierData === 'object' && supplierData !== null) {
  581. expect(supplierData).toHaveProperty('loginNum');
  582. expect(supplierData).toHaveProperty('loginTime');
  583. expect(supplierData).toHaveProperty('lastLoginTime');
  584. expect(supplierData).toHaveProperty('loginIp');
  585. expect(supplierData).toHaveProperty('lastLoginIp');
  586. }
  587. }
  588. });
  589. });
  590. describe('跨租户数据隔离', () => {
  591. let tenant2UserToken: string;
  592. let tenant2User: UserEntityMt;
  593. beforeEach(async () => {
  594. // 获取数据源
  595. const dataSource = await IntegrationTestDatabase.getDataSource();
  596. const userRepository = dataSource.getRepository(UserEntityMt);
  597. // 创建租户2的用户
  598. tenant2User = userRepository.create({
  599. username: `tenant2_user_${Math.floor(Math.random() * 100000)}`,
  600. password: 'test_password',
  601. nickname: '租户2用户',
  602. registrationSource: 'web',
  603. tenantId: 2
  604. });
  605. await userRepository.save(tenant2User);
  606. // 生成租户2用户的token
  607. tenant2UserToken = JWTUtil.generateToken({
  608. id: tenant2User.id,
  609. username: tenant2User.username,
  610. roles: [],
  611. tenantId: tenant2User.tenantId
  612. });
  613. // 为租户1创建一些供应商
  614. const supplierRepository = dataSource.getRepository(SupplierMt);
  615. const tenant1Supplier1 = supplierRepository.create({
  616. name: '租户1供应商1',
  617. username: `tenant1_supplier1_${Math.floor(Math.random() * 100000)}`,
  618. password: 'password123',
  619. phone: '13800138008',
  620. realname: '租户1供应商1',
  621. state: 1,
  622. tenantId: 1,
  623. createdBy: testUser.id
  624. });
  625. await supplierRepository.save(tenant1Supplier1);
  626. // 为租户2创建一些供应商
  627. const tenant2Supplier1 = supplierRepository.create({
  628. name: '租户2供应商1',
  629. username: `tenant2_supplier1_${Math.floor(Math.random() * 100000)}`,
  630. password: 'password123',
  631. phone: '13800138009',
  632. realname: '租户2供应商1',
  633. state: 1,
  634. tenantId: 2,
  635. createdBy: tenant2User.id
  636. });
  637. await supplierRepository.save(tenant2Supplier1);
  638. });
  639. it('应该实现租户数据隔离 - 租户1用户只能看到租户1的供应商', async () => {
  640. const response = await client.index.$get({
  641. query: {}
  642. }, {
  643. headers: {
  644. 'Authorization': `Bearer ${userToken}`
  645. }
  646. });
  647. expect(response.status).toBe(200);
  648. const data = await response.json();
  649. // 验证租户1用户只能看到租户1的供应商
  650. if (data.data && Array.isArray(data.data)) {
  651. data.data.forEach((supplier: any) => {
  652. expect(supplier.tenantId).toBe(1);
  653. expect(supplier.createdBy).toBe(testUser.id);
  654. });
  655. }
  656. });
  657. it('应该实现租户数据隔离 - 租户2用户只能看到租户2的供应商', async () => {
  658. const response = await client.index.$get({
  659. query: {}
  660. }, {
  661. headers: {
  662. 'Authorization': `Bearer ${tenant2UserToken}`
  663. }
  664. });
  665. expect(response.status).toBe(200);
  666. const data = await response.json();
  667. // 验证租户2用户只能看到租户2的供应商
  668. if (data.data && Array.isArray(data.data)) {
  669. data.data.forEach((supplier: any) => {
  670. expect(supplier.tenantId).toBe(2);
  671. expect(supplier.createdBy).toBe(tenant2User.id);
  672. });
  673. }
  674. });
  675. it('应该阻止跨租户访问供应商详情', async () => {
  676. // 获取租户1的供应商列表
  677. const listResponse = await client.index.$get({
  678. query: {}
  679. }, {
  680. headers: {
  681. 'Authorization': `Bearer ${userToken}`
  682. }
  683. });
  684. expect(listResponse.status).toBe(200);
  685. const listData = await listResponse.json();
  686. if (listData.data && Array.isArray(listData.data) && listData.data.length > 0) {
  687. const tenant1SupplierId = listData.data[0].id;
  688. // 租户2用户尝试访问租户1的供应商
  689. const getResponse = await client[':id'].$get({
  690. param: { id: tenant1SupplierId }
  691. }, {
  692. headers: {
  693. 'Authorization': `Bearer ${tenant2UserToken}`
  694. }
  695. });
  696. // 跨租户访问应该返回404
  697. expect(getResponse.status).toBe(404);
  698. }
  699. });
  700. it('应该阻止跨租户更新供应商', async () => {
  701. // 获取租户1的供应商列表
  702. const listResponse = await client.index.$get({
  703. query: {}
  704. }, {
  705. headers: {
  706. 'Authorization': `Bearer ${userToken}`
  707. }
  708. });
  709. expect(listResponse.status).toBe(200);
  710. const listData = await listResponse.json();
  711. if (listData.data && Array.isArray(listData.data) && listData.data.length > 0) {
  712. const tenant1SupplierId = listData.data[0].id;
  713. // 租户2用户尝试更新租户1的供应商
  714. const updateResponse = await client[':id'].$put({
  715. param: { id: tenant1SupplierId },
  716. json: { name: '跨租户更新测试' }
  717. }, {
  718. headers: {
  719. 'Authorization': `Bearer ${tenant2UserToken}`
  720. }
  721. });
  722. // 跨租户更新应该返回404
  723. expect(updateResponse.status).toBe(404);
  724. }
  725. });
  726. it('应该阻止跨租户删除供应商', async () => {
  727. // 获取租户1的供应商列表
  728. const listResponse = await client.index.$get({
  729. query: {}
  730. }, {
  731. headers: {
  732. 'Authorization': `Bearer ${userToken}`
  733. }
  734. });
  735. expect(listResponse.status).toBe(200);
  736. const listData = await listResponse.json();
  737. if (listData.data && Array.isArray(listData.data) && listData.data.length > 0) {
  738. const tenant1SupplierId = listData.data[0].id;
  739. // 租户2用户尝试删除租户1的供应商
  740. const deleteResponse = await client[':id'].$delete({
  741. param: { id: tenant1SupplierId }
  742. }, {
  743. headers: {
  744. 'Authorization': `Bearer ${tenant2UserToken}`
  745. }
  746. });
  747. // 跨租户删除应该返回404
  748. expect(deleteResponse.status).toBe(404);
  749. }
  750. });
  751. });
  752. });