2
0

orders.integration.test.ts 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039
  1. import { describe, it, expect, beforeEach } from 'vitest';
  2. import { testClient } from 'hono/testing';
  3. import {
  4. IntegrationTestDatabase,
  5. setupIntegrationDatabaseHooks,
  6. TestDataFactory
  7. } from '~/utils/server/integration-test-db';
  8. import { IntegrationTestAssertions } from '~/utils/server/integration-test-utils';
  9. import { ordersRoutesExport } from '@d8d/server/api';
  10. import { AuthService } from '@d8d/server/modules/auth/auth.service';
  11. import { UserService } from '@d8d/server/modules/users/user.service';
  12. import { OrderStatus, PaymentStatus } from '@d8d/server/share/order.types';
  13. // 设置集成测试钩子
  14. setupIntegrationDatabaseHooks()
  15. describe('用户端订单API集成测试', () => {
  16. let client: ReturnType<typeof testClient<typeof ordersRoutesExport>>['api']['v1'];
  17. let testToken: string;
  18. let testUser: any;
  19. let testRoute: any;
  20. let testPassenger: any;
  21. beforeEach(async () => {
  22. // 创建测试客户端
  23. client = testClient(ordersRoutesExport).api.v1;
  24. // 创建测试用户并生成token
  25. const dataSource = await IntegrationTestDatabase.getDataSource();
  26. const userService = new UserService(dataSource);
  27. const authService = new AuthService(userService);
  28. // 创建测试用户
  29. testUser = await TestDataFactory.createTestUser(dataSource);
  30. // 生成测试用户的token
  31. testToken = authService.generateToken(testUser);
  32. // 创建测试路线
  33. testRoute = await TestDataFactory.createTestRoute(dataSource);
  34. // 创建测试乘客
  35. testPassenger = await TestDataFactory.createTestPassenger(dataSource, {
  36. userId: testUser.id,
  37. name: '测试乘客'
  38. });
  39. });
  40. describe('订单创建测试', () => {
  41. it('应该成功创建订单', async () => {
  42. const orderData = {
  43. routeId: testRoute.id,
  44. passengerCount: 1,
  45. totalAmount: testRoute.price,
  46. passengerSnapshots: [
  47. {
  48. id: testPassenger.id,
  49. name: testPassenger.name,
  50. idType: testPassenger.idType,
  51. idNumber: testPassenger.idNumber,
  52. phone: testPassenger.phone
  53. }
  54. ],
  55. routeSnapshot: {
  56. id: testRoute.id,
  57. name: testRoute.name,
  58. pickupPoint: testRoute.pickupPoint,
  59. dropoffPoint: testRoute.dropoffPoint,
  60. departureTime: testRoute.departureTime,
  61. price: testRoute.price,
  62. vehicleType: testRoute.vehicleType,
  63. travelMode: testRoute.travelMode
  64. }
  65. };
  66. const response = await client.orders.$post({
  67. json: orderData,
  68. },
  69. {
  70. headers: {
  71. 'Authorization': `Bearer ${testToken}`
  72. }
  73. });
  74. // 断言响应
  75. expect(response.status).toBe(201);
  76. if (response.status === 201) {
  77. const responseData = await response.json();
  78. expect(responseData).toHaveProperty('id');
  79. expect(responseData.userId).toBe(testUser.id);
  80. expect(responseData.routeId).toBe(orderData.routeId);
  81. expect(responseData.passengerCount).toBe(orderData.passengerCount);
  82. expect(responseData.totalAmount).toBe(orderData.totalAmount);
  83. expect(responseData.status).toBe(OrderStatus.PENDING_PAYMENT);
  84. expect(responseData.paymentStatus).toBe(PaymentStatus.PENDING);
  85. expect(responseData.passengerSnapshots).toEqual(orderData.passengerSnapshots);
  86. // 由于parseWithAwait会将Date转换为字符串,我们需要比较字符串格式
  87. const expectedRouteSnapshot = {
  88. ...orderData.routeSnapshot,
  89. departureTime: orderData.routeSnapshot.departureTime.toISOString()
  90. };
  91. expect(responseData.routeSnapshot).toEqual(expectedRouteSnapshot);
  92. // 断言数据库中存在订单
  93. await IntegrationTestAssertions.expectOrderToExist(responseData.id);
  94. } else {
  95. // 调试信息
  96. const errorData = await response.json();
  97. console.debug('订单创建失败:', errorData);
  98. }
  99. });
  100. it('应该拒绝创建不存在的路线订单', async () => {
  101. const orderData = {
  102. routeId: 999999, // 不存在的路线ID
  103. passengerCount: 1,
  104. totalAmount: 100,
  105. passengerSnapshots: [],
  106. routeSnapshot: {
  107. id: 999999,
  108. name: '不存在的路线',
  109. pickupPoint: '起点',
  110. dropoffPoint: '终点',
  111. departureTime: new Date(),
  112. price: 100,
  113. vehicleType: 'CAR',
  114. travelMode: 'DRIVING'
  115. }
  116. };
  117. const response = await client.orders.$post({
  118. json: orderData,
  119. },
  120. {
  121. headers: {
  122. 'Authorization': `Bearer ${testToken}`
  123. }
  124. });
  125. // 应该返回404错误
  126. expect(response.status).toBe(404);
  127. if (response.status === 404) {
  128. const responseData = await response.json();
  129. expect(responseData.message).toContain('路线不存在');
  130. }
  131. });
  132. it('应该拒绝创建超过座位数的订单', async () => {
  133. const orderData = {
  134. routeId: testRoute.id,
  135. passengerCount: testRoute.availableSeats + 1, // 超过可用座位数
  136. totalAmount: testRoute.price * (testRoute.availableSeats + 1),
  137. passengerSnapshots: [],
  138. routeSnapshot: {
  139. id: testRoute.id,
  140. name: testRoute.name,
  141. pickupPoint: testRoute.pickupPoint,
  142. dropoffPoint: testRoute.dropoffPoint,
  143. departureTime: testRoute.departureTime,
  144. price: testRoute.price,
  145. vehicleType: testRoute.vehicleType,
  146. travelMode: testRoute.travelMode
  147. }
  148. };
  149. const response = await client.orders.$post({
  150. json: orderData,
  151. },
  152. {
  153. headers: {
  154. 'Authorization': `Bearer ${testToken}`
  155. }
  156. });
  157. // 应该返回422业务逻辑错误
  158. expect(response.status).toBe(422);
  159. if (response.status === 422) {
  160. const responseData = await response.json();
  161. expect(responseData.message).toContain('乘客数量超过路线座位数');
  162. }
  163. });
  164. it('应该验证乘客快照信息', async () => {
  165. const orderData = {
  166. routeId: testRoute.id,
  167. passengerCount: 1,
  168. totalAmount: testRoute.price,
  169. passengerSnapshots: [
  170. {
  171. id: 999999, // 不存在的乘客ID
  172. name: '不存在的乘客',
  173. idType: 'ID_CARD',
  174. idNumber: '110101199001011234',
  175. phone: '13812345678'
  176. }
  177. ],
  178. routeSnapshot: {
  179. id: testRoute.id,
  180. name: testRoute.name,
  181. pickupPoint: testRoute.pickupPoint,
  182. dropoffPoint: testRoute.dropoffPoint,
  183. departureTime: testRoute.departureTime,
  184. price: testRoute.price,
  185. vehicleType: testRoute.vehicleType,
  186. travelMode: testRoute.travelMode
  187. }
  188. };
  189. const response = await client.orders.$post({
  190. json: orderData,
  191. },
  192. {
  193. headers: {
  194. 'Authorization': `Bearer ${testToken}`
  195. }
  196. });
  197. // 应该返回404错误
  198. expect(response.status).toBe(404);
  199. if (response.status === 404) {
  200. const responseData = await response.json();
  201. expect(responseData.message).toContain('乘客ID 999999 不存在');
  202. }
  203. });
  204. it('应该拒绝创建缺少必填字段的订单', async () => {
  205. const orderData = {
  206. // 缺少routeId
  207. routeId: undefined as any, // 故意不提供routeId来测试验证
  208. passengerCount: 1,
  209. totalAmount: 100,
  210. passengerSnapshots: [],
  211. routeSnapshot: {
  212. id: testRoute.id,
  213. name: testRoute.name,
  214. pickupPoint: testRoute.pickupPoint,
  215. dropoffPoint: testRoute.dropoffPoint,
  216. departureTime: testRoute.departureTime,
  217. price: testRoute.price,
  218. vehicleType: testRoute.vehicleType,
  219. travelMode: testRoute.travelMode
  220. }
  221. };
  222. const response = await client.orders.$post({
  223. json: orderData,
  224. },
  225. {
  226. headers: {
  227. 'Authorization': `Bearer ${testToken}`
  228. }
  229. });
  230. // 应该返回验证错误
  231. expect([400, 500]).toContain(response.status);
  232. });
  233. it('应该正确计算订单金额', async () => {
  234. const passengerCount = 2;
  235. const expectedTotalAmount = testRoute.price * passengerCount;
  236. const orderData = {
  237. routeId: testRoute.id,
  238. passengerCount: passengerCount,
  239. totalAmount: expectedTotalAmount,
  240. passengerSnapshots: [
  241. {
  242. id: testPassenger.id,
  243. name: testPassenger.name,
  244. idType: testPassenger.idType,
  245. idNumber: testPassenger.idNumber,
  246. phone: testPassenger.phone
  247. },
  248. {
  249. name: '第二个乘客',
  250. idType: 'ID_CARD',
  251. idNumber: '110101199001012345',
  252. phone: '13987654321'
  253. }
  254. ],
  255. routeSnapshot: {
  256. id: testRoute.id,
  257. name: testRoute.name,
  258. pickupPoint: testRoute.pickupPoint,
  259. dropoffPoint: testRoute.dropoffPoint,
  260. departureTime: testRoute.departureTime,
  261. price: testRoute.price,
  262. vehicleType: testRoute.vehicleType,
  263. travelMode: testRoute.travelMode
  264. }
  265. };
  266. const response = await client.orders.$post({
  267. json: orderData,
  268. },
  269. {
  270. headers: {
  271. 'Authorization': `Bearer ${testToken}`
  272. }
  273. });
  274. expect(response.status).toBe(201);
  275. if (response.status === 201) {
  276. const responseData = await response.json();
  277. expect(responseData.totalAmount).toBe(expectedTotalAmount);
  278. expect(responseData.passengerCount).toBe(passengerCount);
  279. }
  280. });
  281. });
  282. describe('权限控制测试', () => {
  283. it('应该拒绝未认证用户的订单创建', async () => {
  284. const orderData = {
  285. routeId: testRoute.id,
  286. passengerCount: 1,
  287. totalAmount: testRoute.price,
  288. passengerSnapshots: [],
  289. routeSnapshot: {
  290. id: testRoute.id,
  291. name: testRoute.name,
  292. pickupPoint: testRoute.pickupPoint,
  293. dropoffPoint: testRoute.dropoffPoint,
  294. departureTime: testRoute.departureTime,
  295. price: testRoute.price,
  296. vehicleType: testRoute.vehicleType,
  297. travelMode: testRoute.travelMode
  298. }
  299. };
  300. const response = await client.orders.$post({
  301. json: orderData,
  302. });
  303. expect(response.status).toBe(401);
  304. });
  305. it('应该拒绝无效token的订单创建', async () => {
  306. const orderData = {
  307. routeId: testRoute.id,
  308. passengerCount: 1,
  309. totalAmount: testRoute.price,
  310. passengerSnapshots: [],
  311. routeSnapshot: {
  312. id: testRoute.id,
  313. name: testRoute.name,
  314. pickupPoint: testRoute.pickupPoint,
  315. dropoffPoint: testRoute.dropoffPoint,
  316. departureTime: testRoute.departureTime,
  317. price: testRoute.price,
  318. vehicleType: testRoute.vehicleType,
  319. travelMode: testRoute.travelMode
  320. }
  321. };
  322. const response = await client.orders.$post({
  323. json: orderData,
  324. },
  325. {
  326. headers: {
  327. 'Authorization': 'Bearer invalid_token'
  328. }
  329. });
  330. expect(response.status).toBe(401);
  331. });
  332. });
  333. describe('快照机制测试', () => {
  334. it('应该正确保存路线快照信息', async () => {
  335. const routeSnapshot = {
  336. id: testRoute.id,
  337. name: testRoute.name,
  338. pickupPoint: testRoute.pickupPoint,
  339. dropoffPoint: testRoute.dropoffPoint,
  340. departureTime: testRoute.departureTime,
  341. price: testRoute.price,
  342. vehicleType: testRoute.vehicleType,
  343. travelMode: testRoute.travelMode
  344. };
  345. const orderData = {
  346. routeId: testRoute.id,
  347. passengerCount: 1,
  348. totalAmount: testRoute.price,
  349. passengerSnapshots: [],
  350. routeSnapshot: routeSnapshot
  351. };
  352. const response = await client.orders.$post({
  353. json: orderData,
  354. },
  355. {
  356. headers: {
  357. 'Authorization': `Bearer ${testToken}`
  358. }
  359. });
  360. expect(response.status).toBe(201);
  361. if (response.status === 201) {
  362. const responseData = await response.json();
  363. // 由于parseWithAwait会将Date转换为字符串,我们需要比较字符串格式
  364. const expectedRouteSnapshot = {
  365. ...routeSnapshot,
  366. departureTime: routeSnapshot.departureTime.toISOString()
  367. };
  368. expect(responseData.routeSnapshot).toEqual(expectedRouteSnapshot);
  369. // 验证数据库中的快照数据
  370. const order = await IntegrationTestAssertions.getOrderById(responseData.id);
  371. expect(order).not.toBeNull();
  372. // 数据库中的routeSnapshot应该包含字符串格式的时间
  373. const expectedDbRouteSnapshot = {
  374. ...routeSnapshot,
  375. departureTime: routeSnapshot.departureTime.toISOString()
  376. };
  377. expect(order!.routeSnapshot).toEqual(expectedDbRouteSnapshot);
  378. }
  379. });
  380. it('应该正确保存乘客快照信息', async () => {
  381. const passengerSnapshots = [
  382. {
  383. id: testPassenger.id,
  384. name: testPassenger.name,
  385. idType: testPassenger.idType,
  386. idNumber: testPassenger.idNumber,
  387. phone: testPassenger.phone
  388. },
  389. {
  390. name: '新乘客',
  391. idType: 'PASSPORT',
  392. idNumber: 'E12345678',
  393. phone: '13987654321'
  394. }
  395. ];
  396. const orderData = {
  397. routeId: testRoute.id,
  398. passengerCount: passengerSnapshots.length,
  399. totalAmount: testRoute.price * passengerSnapshots.length,
  400. passengerSnapshots: passengerSnapshots,
  401. routeSnapshot: {
  402. id: testRoute.id,
  403. name: testRoute.name,
  404. pickupPoint: testRoute.pickupPoint,
  405. dropoffPoint: testRoute.dropoffPoint,
  406. departureTime: testRoute.departureTime,
  407. price: testRoute.price,
  408. vehicleType: testRoute.vehicleType,
  409. travelMode: testRoute.travelMode
  410. }
  411. };
  412. const response = await client.orders.$post({
  413. json: orderData,
  414. },
  415. {
  416. headers: {
  417. 'Authorization': `Bearer ${testToken}`
  418. }
  419. });
  420. expect(response.status).toBe(201);
  421. if (response.status === 201) {
  422. const responseData = await response.json();
  423. expect(responseData.passengerSnapshots).toEqual(passengerSnapshots);
  424. // 验证数据库中的快照数据
  425. const order = await IntegrationTestAssertions.getOrderById(responseData.id);
  426. expect(order).not.toBeNull();
  427. expect(order!.passengerSnapshots).toEqual(passengerSnapshots);
  428. }
  429. });
  430. });
  431. describe('订单状态测试', () => {
  432. it('新创建的订单应该处于待支付状态', async () => {
  433. const orderData = {
  434. routeId: testRoute.id,
  435. passengerCount: 1,
  436. totalAmount: testRoute.price,
  437. passengerSnapshots: [],
  438. routeSnapshot: {
  439. id: testRoute.id,
  440. name: testRoute.name,
  441. pickupPoint: testRoute.pickupPoint,
  442. dropoffPoint: testRoute.dropoffPoint,
  443. departureTime: testRoute.departureTime,
  444. price: testRoute.price,
  445. vehicleType: testRoute.vehicleType,
  446. travelMode: testRoute.travelMode
  447. }
  448. };
  449. const response = await client.orders.$post({
  450. json: orderData,
  451. },
  452. {
  453. headers: {
  454. 'Authorization': `Bearer ${testToken}`
  455. }
  456. });
  457. expect(response.status).toBe(201);
  458. if (response.status === 201) {
  459. const responseData = await response.json();
  460. expect(responseData.status).toBe(OrderStatus.PENDING_PAYMENT);
  461. expect(responseData.paymentStatus).toBe(PaymentStatus.PENDING);
  462. }
  463. });
  464. });
  465. describe('订单列表API测试', () => {
  466. let testOrder: any;
  467. beforeEach(async () => {
  468. // 创建测试订单
  469. const orderData = {
  470. routeId: testRoute.id,
  471. passengerCount: 1,
  472. totalAmount: testRoute.price,
  473. passengerSnapshots: [
  474. {
  475. id: testPassenger.id,
  476. name: testPassenger.name,
  477. idType: testPassenger.idType,
  478. idNumber: testPassenger.idNumber,
  479. phone: testPassenger.phone
  480. }
  481. ],
  482. routeSnapshot: {
  483. id: testRoute.id,
  484. name: testRoute.name,
  485. pickupPoint: testRoute.pickupPoint,
  486. dropoffPoint: testRoute.dropoffPoint,
  487. departureTime: testRoute.departureTime,
  488. price: testRoute.price,
  489. vehicleType: testRoute.vehicleType,
  490. travelMode: testRoute.travelMode
  491. }
  492. };
  493. const response = await client.orders.$post({
  494. json: orderData,
  495. },
  496. {
  497. headers: {
  498. 'Authorization': `Bearer ${testToken}`
  499. }
  500. });
  501. if (response.status === 201) {
  502. testOrder = await response.json();
  503. }
  504. });
  505. it('应该成功获取订单列表', async () => {
  506. const response = await client.orders.$get({
  507. query: {
  508. page: 1,
  509. pageSize: 10
  510. }
  511. },
  512. {
  513. headers: {
  514. 'Authorization': `Bearer ${testToken}`
  515. }
  516. });
  517. expect(response.status).toBe(200);
  518. if (response.status === 200) {
  519. const responseData = await response.json();
  520. expect(responseData).toHaveProperty('data');
  521. expect(responseData).toHaveProperty('total');
  522. expect(responseData).toHaveProperty('page');
  523. expect(responseData).toHaveProperty('pageSize');
  524. expect(Array.isArray(responseData.data)).toBe(true);
  525. expect(responseData.data.length).toBeGreaterThan(0);
  526. // 验证订单数据格式
  527. const order = responseData.data[0];
  528. expect(order).toHaveProperty('id');
  529. expect(order).toHaveProperty('userId');
  530. expect(order).toHaveProperty('routeId');
  531. expect(order).toHaveProperty('status');
  532. expect(order).toHaveProperty('paymentStatus');
  533. }
  534. });
  535. it('应该支持按状态筛选订单', async () => {
  536. const response = await client.orders.$get({
  537. query: {
  538. page: 1,
  539. pageSize: 10,
  540. status: OrderStatus.PENDING_PAYMENT
  541. }
  542. },
  543. {
  544. headers: {
  545. 'Authorization': `Bearer ${testToken}`
  546. }
  547. });
  548. expect(response.status).toBe(200);
  549. if (response.status === 200) {
  550. const responseData = await response.json();
  551. expect(Array.isArray(responseData.data)).toBe(true);
  552. // 验证所有返回的订单都是待支付状态
  553. if (responseData.data.length > 0) {
  554. responseData.data.forEach((order: any) => {
  555. expect(order.status).toBe(OrderStatus.PENDING_PAYMENT);
  556. });
  557. }
  558. }
  559. });
  560. it('应该支持分页功能', async () => {
  561. const pageSize = 5;
  562. const response = await client.orders.$get({
  563. query: {
  564. page: 1,
  565. pageSize: pageSize
  566. }
  567. },
  568. {
  569. headers: {
  570. 'Authorization': `Bearer ${testToken}`
  571. }
  572. });
  573. expect(response.status).toBe(200);
  574. if (response.status === 200) {
  575. const responseData = await response.json();
  576. expect(responseData.page).toBe(1);
  577. expect(responseData.pageSize).toBe(pageSize);
  578. expect(responseData.data.length).toBeLessThanOrEqual(pageSize);
  579. }
  580. });
  581. it('应该拒绝未认证用户的订单列表请求', async () => {
  582. const response = await client.orders.$get({
  583. query: {
  584. page: 1,
  585. pageSize: 10
  586. }
  587. });
  588. expect(response.status).toBe(401);
  589. });
  590. });
  591. describe('订单详情API测试', () => {
  592. let testOrder: any;
  593. beforeEach(async () => {
  594. // 创建测试订单
  595. const orderData = {
  596. routeId: testRoute.id,
  597. passengerCount: 1,
  598. totalAmount: testRoute.price,
  599. passengerSnapshots: [
  600. {
  601. id: testPassenger.id,
  602. name: testPassenger.name,
  603. idType: testPassenger.idType,
  604. idNumber: testPassenger.idNumber,
  605. phone: testPassenger.phone
  606. }
  607. ],
  608. routeSnapshot: {
  609. id: testRoute.id,
  610. name: testRoute.name,
  611. pickupPoint: testRoute.pickupPoint,
  612. dropoffPoint: testRoute.dropoffPoint,
  613. departureTime: testRoute.departureTime,
  614. price: testRoute.price,
  615. vehicleType: testRoute.vehicleType,
  616. travelMode: testRoute.travelMode
  617. }
  618. };
  619. const response = await client.orders.$post({
  620. json: orderData,
  621. },
  622. {
  623. headers: {
  624. 'Authorization': `Bearer ${testToken}`
  625. }
  626. });
  627. if (response.status === 201) {
  628. testOrder = await response.json();
  629. }
  630. });
  631. it('应该成功获取订单详情', async () => {
  632. const response = await client.orders[':id'].$get({
  633. param: {
  634. id: testOrder.id
  635. }
  636. },
  637. {
  638. headers: {
  639. 'Authorization': `Bearer ${testToken}`
  640. }
  641. });
  642. expect(response.status).toBe(200);
  643. if (response.status === 200) {
  644. const responseData = await response.json();
  645. expect(responseData.id).toBe(testOrder.id);
  646. expect(responseData.userId).toBe(testUser.id);
  647. expect(responseData.routeId).toBe(testRoute.id);
  648. expect(responseData.status).toBe(OrderStatus.PENDING_PAYMENT);
  649. expect(responseData.passengerSnapshots).toEqual(testOrder.passengerSnapshots);
  650. expect(responseData.routeSnapshot).toEqual(testOrder.routeSnapshot);
  651. }
  652. });
  653. it('应该拒绝查看不存在的订单', async () => {
  654. const response = await client.orders[':id'].$get({
  655. param: {
  656. id: 999999
  657. }
  658. },
  659. {
  660. headers: {
  661. 'Authorization': `Bearer ${testToken}`
  662. }
  663. });
  664. expect(response.status).toBe(404);
  665. if (response.status === 404) {
  666. const responseData = await response.json();
  667. expect(responseData.message).toContain('订单不存在');
  668. }
  669. });
  670. it('应该拒绝查看其他用户的订单', async () => {
  671. // 创建另一个用户
  672. const dataSource = await IntegrationTestDatabase.getDataSource();
  673. const userService = new UserService(dataSource);
  674. const authService = new AuthService(userService);
  675. const otherUser = await TestDataFactory.createTestUser(dataSource, {
  676. username: 'other_user',
  677. phone: '13987654321'
  678. });
  679. const otherToken = authService.generateToken(otherUser);
  680. const response = await client.orders[':id'].$get({
  681. param: {
  682. id: testOrder.id
  683. }
  684. },
  685. {
  686. headers: {
  687. 'Authorization': `Bearer ${otherToken}`
  688. }
  689. });
  690. expect(response.status).toBe(403);
  691. if (response.status === 403) {
  692. const responseData = await response.json();
  693. expect(responseData.message).toContain('权限不足');
  694. }
  695. });
  696. it('应该拒绝未认证用户的订单详情请求', async () => {
  697. const response = await client.orders[':id'].$get({
  698. param: {
  699. id: testOrder.id
  700. }
  701. });
  702. expect(response.status).toBe(401);
  703. });
  704. });
  705. describe('订单取消API测试', () => {
  706. let testOrder: any;
  707. beforeEach(async () => {
  708. // 创建测试订单并更新为待出发状态
  709. const orderData = {
  710. routeId: testRoute.id,
  711. passengerCount: 1,
  712. totalAmount: testRoute.price,
  713. passengerSnapshots: [
  714. {
  715. id: testPassenger.id,
  716. name: testPassenger.name,
  717. idType: testPassenger.idType,
  718. idNumber: testPassenger.idNumber,
  719. phone: testPassenger.phone
  720. }
  721. ],
  722. routeSnapshot: {
  723. id: testRoute.id,
  724. name: testRoute.name,
  725. pickupPoint: testRoute.pickupPoint,
  726. dropoffPoint: testRoute.dropoffPoint,
  727. departureTime: new Date(Date.now() + 2 * 60 * 60 * 1000), // 2小时后出发
  728. price: testRoute.price,
  729. vehicleType: testRoute.vehicleType,
  730. travelMode: testRoute.travelMode
  731. }
  732. };
  733. const response = await client.orders.$post({
  734. json: orderData,
  735. },
  736. {
  737. headers: {
  738. 'Authorization': `Bearer ${testToken}`
  739. }
  740. });
  741. if (response.status === 201) {
  742. testOrder = await response.json();
  743. // 更新订单状态为待出发
  744. const dataSource = await IntegrationTestDatabase.getDataSource();
  745. const orderRepository = dataSource.getRepository('Order');
  746. await orderRepository.update(testOrder.id, {
  747. status: OrderStatus.WAITING_DEPARTURE,
  748. paymentStatus: PaymentStatus.PAID
  749. });
  750. }
  751. });
  752. it('应该成功取消待出发状态的订单', async () => {
  753. const response = await client.orders[':id'].cancel.$post({
  754. param: {
  755. id: testOrder.id
  756. }
  757. },
  758. {
  759. headers: {
  760. 'Authorization': `Bearer ${testToken}`
  761. }
  762. });
  763. expect(response.status).toBe(200);
  764. if (response.status === 200) {
  765. const responseData = await response.json();
  766. expect(responseData.id).toBe(testOrder.id);
  767. expect(responseData.status).toBe(OrderStatus.CANCELLED);
  768. expect(responseData.paymentStatus).toBe(PaymentStatus.REFUNDED);
  769. // 验证数据库中的状态更新
  770. const dataSource = await IntegrationTestDatabase.getDataSource();
  771. const orderRepository = dataSource.getRepository('Order');
  772. const updatedOrder = await orderRepository.findOne({
  773. where: { id: testOrder.id }
  774. });
  775. expect(updatedOrder?.status).toBe(OrderStatus.CANCELLED);
  776. expect(updatedOrder?.paymentStatus).toBe(PaymentStatus.REFUNDED);
  777. }
  778. });
  779. it('应该拒绝取消非待出发状态的订单', async () => {
  780. // 创建已完成状态的订单
  781. const orderData = {
  782. routeId: testRoute.id,
  783. passengerCount: 1,
  784. totalAmount: testRoute.price,
  785. passengerSnapshots: [],
  786. routeSnapshot: {
  787. id: testRoute.id,
  788. name: testRoute.name,
  789. pickupPoint: testRoute.pickupPoint,
  790. dropoffPoint: testRoute.dropoffPoint,
  791. departureTime: new Date(),
  792. price: testRoute.price,
  793. vehicleType: testRoute.vehicleType,
  794. travelMode: testRoute.travelMode
  795. }
  796. };
  797. const createResponse = await client.orders.$post({
  798. json: orderData,
  799. },
  800. {
  801. headers: {
  802. 'Authorization': `Bearer ${testToken}`
  803. }
  804. });
  805. if (createResponse.status === 201) {
  806. const completedOrder = await createResponse.json();
  807. // 更新订单状态为已完成
  808. const dataSource = await IntegrationTestDatabase.getDataSource();
  809. const orderRepository = dataSource.getRepository('Order');
  810. await orderRepository.update(completedOrder.id, {
  811. status: OrderStatus.COMPLETED,
  812. paymentStatus: PaymentStatus.PAID
  813. });
  814. const response = await client.orders[':id'].cancel.$post({
  815. param: {
  816. id: completedOrder.id
  817. }
  818. },
  819. {
  820. headers: {
  821. 'Authorization': `Bearer ${testToken}`
  822. }
  823. });
  824. expect(response.status).toBe(422);
  825. if (response.status === 422) {
  826. const responseData = await response.json();
  827. expect(responseData.message).toContain('只能取消待出发状态的订单');
  828. }
  829. }
  830. });
  831. it('应该拒绝取消距离出发时间不足1小时的订单', async () => {
  832. // 创建即将出发的订单
  833. const orderData = {
  834. routeId: testRoute.id,
  835. passengerCount: 1,
  836. totalAmount: testRoute.price,
  837. passengerSnapshots: [],
  838. routeSnapshot: {
  839. id: testRoute.id,
  840. name: testRoute.name,
  841. pickupPoint: testRoute.pickupPoint,
  842. dropoffPoint: testRoute.dropoffPoint,
  843. departureTime: new Date(Date.now() + 30 * 60 * 1000), // 30分钟后出发
  844. price: testRoute.price,
  845. vehicleType: testRoute.vehicleType,
  846. travelMode: testRoute.travelMode
  847. }
  848. };
  849. const createResponse = await client.orders.$post({
  850. json: orderData,
  851. },
  852. {
  853. headers: {
  854. 'Authorization': `Bearer ${testToken}`
  855. }
  856. });
  857. if (createResponse.status === 201) {
  858. const urgentOrder = await createResponse.json();
  859. // 更新订单状态为待出发
  860. const dataSource = await IntegrationTestDatabase.getDataSource();
  861. const orderRepository = dataSource.getRepository('Order');
  862. await orderRepository.update(urgentOrder.id, {
  863. status: OrderStatus.WAITING_DEPARTURE,
  864. paymentStatus: PaymentStatus.PAID
  865. });
  866. const response = await client.orders[':id'].cancel.$post({
  867. param: {
  868. id: urgentOrder.id
  869. }
  870. },
  871. {
  872. headers: {
  873. 'Authorization': `Bearer ${testToken}`
  874. }
  875. });
  876. expect(response.status).toBe(422);
  877. if (response.status === 422) {
  878. const responseData = await response.json();
  879. expect(responseData.message).toContain('距离出发时间不足1小时');
  880. }
  881. }
  882. });
  883. it('应该拒绝取消其他用户的订单', async () => {
  884. // 创建另一个用户
  885. const dataSource = await IntegrationTestDatabase.getDataSource();
  886. const userService = new UserService(dataSource);
  887. const authService = new AuthService(userService);
  888. const otherUser = await TestDataFactory.createTestUser(dataSource, {
  889. username: 'other_user',
  890. phone: '13987654321'
  891. });
  892. const otherToken = authService.generateToken(otherUser);
  893. const response = await client.orders[':id'].cancel.$post({
  894. param: {
  895. id: testOrder.id
  896. }
  897. },
  898. {
  899. headers: {
  900. 'Authorization': `Bearer ${otherToken}`
  901. }
  902. });
  903. expect(response.status).toBe(403);
  904. if (response.status === 403) {
  905. const responseData = await response.json();
  906. expect(responseData.message).toContain('权限不足');
  907. }
  908. });
  909. it('应该拒绝取消不存在的订单', async () => {
  910. const response = await client.orders[':id'].cancel.$post({
  911. param: {
  912. id: 999999
  913. }
  914. },
  915. {
  916. headers: {
  917. 'Authorization': `Bearer ${testToken}`
  918. }
  919. });
  920. expect(response.status).toBe(404);
  921. if (response.status === 404) {
  922. const responseData = await response.json();
  923. expect(responseData.message).toContain('订单不存在');
  924. }
  925. });
  926. it('应该拒绝未认证用户的订单取消请求', async () => {
  927. const response = await client.orders[':id'].cancel.$post({
  928. param: {
  929. id: testOrder.id
  930. }
  931. });
  932. expect(response.status).toBe(401);
  933. });
  934. });
  935. });