user-orders-routes.integration.test.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  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 { DeliveryAddressMt } from '@d8d/delivery-address-module-mt';
  7. import { AreaEntityMt } from '@d8d/geo-areas-mt';
  8. import { MerchantMt } from '@d8d/merchant-module-mt';
  9. import { SupplierMt } from '@d8d/supplier-module-mt';
  10. import { FileMt } from '@d8d/file-module-mt';
  11. import { GoodsMt, GoodsCategoryMt } from '@d8d/goods-module-mt';
  12. import userOrderRoutes from '../../src/routes/user/orders.mt';
  13. import { OrderMt, OrderGoodsMt } from '../../src/entities';
  14. import { OrdersTestFactory } from '../factories/orders-test-factory';
  15. // 设置集成测试钩子
  16. setupIntegrationDatabaseHooksWithEntities([
  17. UserEntityMt, RoleMt, OrderMt, OrderGoodsMt, DeliveryAddressMt, MerchantMt, SupplierMt, FileMt, AreaEntityMt, GoodsMt, GoodsCategoryMt
  18. ])
  19. describe('多租户用户订单管理API集成测试', () => {
  20. let client: ReturnType<typeof testClient<typeof userOrderRoutes>>;
  21. let testFactory: OrdersTestFactory;
  22. let userToken: string;
  23. let otherUserToken: string;
  24. let otherTenantUserToken: string;
  25. let testUser: UserEntityMt;
  26. let otherUser: UserEntityMt;
  27. let otherTenantUser: UserEntityMt;
  28. beforeEach(async () => {
  29. // 创建测试客户端
  30. client = testClient(userOrderRoutes);
  31. // 获取数据源并创建测试工厂
  32. const dataSource = await IntegrationTestDatabase.getDataSource();
  33. testFactory = new OrdersTestFactory(dataSource);
  34. // 创建测试用户
  35. testUser = await testFactory.createTestUser(1);
  36. otherUser = await testFactory.createTestUser(1);
  37. otherTenantUser = await testFactory.createTestUser(2);
  38. // 生成JWT令牌
  39. userToken = JWTUtil.generateToken({ id: testUser.id, username: testUser.username, tenantId: 1 });
  40. otherUserToken = JWTUtil.generateToken({ id: otherUser.id, username: otherUser.username, tenantId: 1 });
  41. otherTenantUserToken = JWTUtil.generateToken({ id: otherTenantUser.id, username: otherTenantUser.username, tenantId: 2 });
  42. });
  43. describe('租户数据隔离验证', () => {
  44. it('应该只能访问自己租户的订单', async () => {
  45. // 创建租户1的订单
  46. const tenant1Order = await testFactory.createTestOrder(testUser.id, { tenantId: 1 });
  47. // 创建租户2的订单
  48. const tenant2Order = await testFactory.createTestOrder(otherTenantUser.id, { tenantId: 2 });
  49. // 使用租户1的用户查询订单列表
  50. const response = await client.index.$get({}, {
  51. headers: {
  52. 'Authorization': `Bearer ${userToken}`
  53. }
  54. });
  55. expect(response.status).toBe(200);
  56. const data = await response.json();
  57. // 应该只返回租户1的订单
  58. expect(data.data).toHaveLength(1);
  59. expect(data.data[0].tenantId).toBe(1);
  60. expect(data.data[0].id).toBe(tenant1Order.id);
  61. });
  62. it('不应该访问其他租户的订单详情', async () => {
  63. // 创建租户2的订单
  64. const otherTenantOrder = await testFactory.createTestOrder(otherTenantUser.id, { tenantId: 2 });
  65. // 使用租户1的用户尝试访问租户2的订单
  66. const response = await client[':id'].$get({
  67. param: { id: otherTenantOrder.id }
  68. }, {
  69. headers: {
  70. 'Authorization': `Bearer ${userToken}`
  71. }
  72. });
  73. // 应该返回404,因为订单不在当前租户
  74. expect(response.status).toBe(404);
  75. });
  76. it('应该正确过滤跨租户订单访问', async () => {
  77. // 创建租户1的订单
  78. const tenant1Order = await testFactory.createTestOrder(testUser.id, { tenantId: 1 });
  79. // 使用租户2的用户尝试访问租户1的订单
  80. const response = await client[':id'].$get({
  81. param: { id: tenant1Order.id }
  82. }, {
  83. headers: {
  84. 'Authorization': `Bearer ${otherTenantUserToken}`
  85. }
  86. });
  87. // 应该返回404,因为订单不在当前租户
  88. expect(response.status).toBe(404);
  89. });
  90. });
  91. describe('用户数据权限验证', () => {
  92. it('应该只能访问自己的订单', async () => {
  93. // 创建当前用户的订单
  94. const myOrder = await testFactory.createTestOrder(testUser.id, { tenantId: 1 });
  95. // 创建其他用户的订单(同一租户)
  96. const otherUserOrder = await testFactory.createTestOrder(otherUser.id, { tenantId: 1 });
  97. // 使用当前用户查询订单列表
  98. const response = await client.index.$get({}, {
  99. headers: {
  100. 'Authorization': `Bearer ${userToken}`
  101. }
  102. });
  103. expect(response.status).toBe(200);
  104. const data = await response.json();
  105. // 应该只返回当前用户的订单
  106. expect(data.data).toHaveLength(1);
  107. expect(data.data[0].userId).toBe(testUser.id);
  108. expect(data.data[0].id).toBe(myOrder.id);
  109. });
  110. it('不应该访问其他用户的订单详情', async () => {
  111. // 创建其他用户的订单
  112. const otherUserOrder = await testFactory.createTestOrder(otherUser.id, { tenantId: 1 });
  113. // 使用当前用户尝试访问其他用户的订单
  114. const response = await client[':id'].$get({
  115. param: { id: otherUserOrder.id }
  116. }, {
  117. headers: {
  118. 'Authorization': `Bearer ${userToken}`
  119. }
  120. });
  121. // 应该返回404,因为无权访问其他用户的订单(安全考虑,不暴露存在性)
  122. expect(response.status).toBe(404);
  123. });
  124. });
  125. describe('订单创建验证', () => {
  126. it('应该自动设置租户ID', async () => {
  127. // 创建必要的关联实体
  128. const testSupplier = await testFactory.createTestSupplier(testUser.id, { tenantId: 1 });
  129. const testMerchant = await testFactory.createTestMerchant(testUser.id, { tenantId: 1 });
  130. const testDeliveryAddress = await testFactory.createTestDeliveryAddress(testUser.id, { tenantId: 1 });
  131. const orderData = {
  132. orderNo: `ORD_${Date.now()}`,
  133. amount: 100.00,
  134. payAmount: 95.00,
  135. discountAmount: 5.00,
  136. merchantId: testMerchant.id,
  137. supplierId: testSupplier.id,
  138. addressId: testDeliveryAddress.id,
  139. orderType: 1,
  140. payType: 1,
  141. payState: 0,
  142. state: 0
  143. };
  144. const response = await client.index.$post({
  145. json: orderData
  146. }, {
  147. headers: {
  148. 'Authorization': `Bearer ${userToken}`
  149. }
  150. });
  151. expect(response.status).toBe(201);
  152. const createdOrder = await response.json();
  153. // 验证租户ID已正确设置
  154. expect(createdOrder.tenantId).toBe(1);
  155. expect(createdOrder.userId).toBe(testUser.id);
  156. });
  157. });
  158. describe('取消订单功能验证', () => {
  159. it('应该成功取消未支付订单', async () => {
  160. // 创建未支付订单
  161. const order = await testFactory.createTestOrder(testUser.id, {
  162. tenantId: 1,
  163. payState: 0, // 未支付
  164. state: 0
  165. });
  166. const cancelData = {
  167. orderId: order.id,
  168. reason: '用户主动取消'
  169. };
  170. const response = await client['cancel-order'].$post({
  171. json: cancelData
  172. }, {
  173. headers: {
  174. 'Authorization': `Bearer ${userToken}`
  175. }
  176. });
  177. expect(response.status).toBe(200);
  178. const result = await response.json();
  179. expect(result.success).toBe(true);
  180. expect(result.message).toBe('订单取消成功');
  181. // 验证订单状态已更新
  182. const dataSource = await IntegrationTestDatabase.getDataSource();
  183. const updatedOrder = await dataSource.getRepository(OrderMt).findOne({
  184. where: { id: order.id, tenantId: 1 }
  185. });
  186. expect(updatedOrder?.payState).toBe(5); // 订单关闭
  187. expect(updatedOrder?.cancelReason).toBe('用户主动取消');
  188. expect(updatedOrder?.cancelTime).toBeInstanceOf(Date);
  189. });
  190. it('应该成功取消已支付订单', async () => {
  191. // 创建已支付订单
  192. const order = await testFactory.createTestOrder(testUser.id, {
  193. tenantId: 1,
  194. payState: 2, // 支付成功
  195. state: 0
  196. });
  197. const cancelData = {
  198. orderId: order.id,
  199. reason: '用户主动取消(已支付)'
  200. };
  201. const response = await client['cancel-order'].$post({
  202. json: cancelData
  203. }, {
  204. headers: {
  205. 'Authorization': `Bearer ${userToken}`
  206. }
  207. });
  208. expect(response.status).toBe(200);
  209. const result = await response.json();
  210. expect(result.success).toBe(true);
  211. expect(result.message).toBe('订单取消成功');
  212. // 验证订单状态已更新
  213. const dataSource = await IntegrationTestDatabase.getDataSource();
  214. const updatedOrder = await dataSource.getRepository(OrderMt).findOne({
  215. where: { id: order.id, tenantId: 1 }
  216. });
  217. expect(updatedOrder?.payState).toBe(5); // 订单关闭
  218. expect(updatedOrder?.cancelReason).toBe('用户主动取消(已支付)');
  219. expect(updatedOrder?.cancelTime).toBeInstanceOf(Date);
  220. });
  221. it('应该拒绝取消不允许的订单状态', async () => {
  222. // 创建已发货订单(支付状态=2,订单状态=1)
  223. const order = await testFactory.createTestOrder(testUser.id, {
  224. tenantId: 1,
  225. payState: 2, // 支付成功
  226. state: 1 // 已发货
  227. });
  228. const cancelData = {
  229. orderId: order.id,
  230. reason: '尝试取消已发货订单'
  231. };
  232. const response = await client['cancel-order'].$post({
  233. json: cancelData
  234. }, {
  235. headers: {
  236. 'Authorization': `Bearer ${userToken}`
  237. }
  238. });
  239. // 应该返回403,因为已发货订单不允许取消
  240. expect(response.status).toBe(403);
  241. const result = await response.json();
  242. expect(result.error).toBe('当前订单状态不允许取消');
  243. });
  244. it('应该拒绝取消不存在的订单', async () => {
  245. const cancelData = {
  246. orderId: 99999, // 不存在的订单ID
  247. reason: '取消不存在的订单'
  248. };
  249. const response = await client['cancel-order'].$post({
  250. json: cancelData
  251. }, {
  252. headers: {
  253. 'Authorization': `Bearer ${userToken}`
  254. }
  255. });
  256. // 应该返回404
  257. expect(response.status).toBe(404);
  258. const result = await response.json();
  259. expect(result.error).toBe('订单不存在');
  260. });
  261. it('应该拒绝跨租户取消订单', async () => {
  262. // 创建租户2的订单
  263. const otherTenantOrder = await testFactory.createTestOrder(otherTenantUser.id, {
  264. tenantId: 2,
  265. payState: 0
  266. });
  267. const cancelData = {
  268. orderId: otherTenantOrder.id,
  269. reason: '跨租户取消尝试'
  270. };
  271. const response = await client['cancel-order'].$post({
  272. json: cancelData
  273. }, {
  274. headers: {
  275. 'Authorization': `Bearer ${userToken}`
  276. }
  277. });
  278. // 应该返回404,因为订单不在当前租户
  279. expect(response.status).toBe(404);
  280. const result = await response.json();
  281. expect(result.error).toBe('订单不存在');
  282. });
  283. it('应该拒绝跨用户取消订单', async () => {
  284. // 创建其他用户的订单(同一租户)
  285. const otherUserOrder = await testFactory.createTestOrder(otherUser.id, {
  286. tenantId: 1,
  287. payState: 0
  288. });
  289. const cancelData = {
  290. orderId: otherUserOrder.id,
  291. reason: '跨用户取消尝试'
  292. };
  293. const response = await client['cancel-order'].$post({
  294. json: cancelData
  295. }, {
  296. headers: {
  297. 'Authorization': `Bearer ${userToken}`
  298. }
  299. });
  300. // 应该返回404,因为无权访问其他用户的订单
  301. expect(response.status).toBe(404);
  302. const result = await response.json();
  303. expect(result.error).toBe('订单不存在');
  304. });
  305. });
  306. });