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

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