orders.integration.test.ts 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041
  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. isDefault: testPassenger.isDefault
  389. },
  390. {
  391. name: '新乘客',
  392. idType: 'PASSPORT',
  393. idNumber: 'E12345678',
  394. phone: '13987654321',
  395. isDefault: false
  396. }
  397. ];
  398. const orderData = {
  399. routeId: testRoute.id,
  400. passengerCount: passengerSnapshots.length,
  401. totalAmount: testRoute.price * passengerSnapshots.length,
  402. passengerSnapshots: passengerSnapshots,
  403. routeSnapshot: {
  404. id: testRoute.id,
  405. name: testRoute.name,
  406. pickupPoint: testRoute.pickupPoint,
  407. dropoffPoint: testRoute.dropoffPoint,
  408. departureTime: testRoute.departureTime,
  409. price: testRoute.price,
  410. vehicleType: testRoute.vehicleType,
  411. travelMode: testRoute.travelMode
  412. }
  413. };
  414. const response = await client.orders.$post({
  415. json: orderData,
  416. },
  417. {
  418. headers: {
  419. 'Authorization': `Bearer ${testToken}`
  420. }
  421. });
  422. expect(response.status).toBe(201);
  423. if (response.status === 201) {
  424. const responseData = await response.json();
  425. expect(responseData.passengerSnapshots).toEqual(passengerSnapshots);
  426. // 验证数据库中的快照数据
  427. const order = await IntegrationTestAssertions.getOrderById(responseData.id);
  428. expect(order).not.toBeNull();
  429. expect(order!.passengerSnapshots).toEqual(passengerSnapshots);
  430. }
  431. });
  432. });
  433. describe('订单状态测试', () => {
  434. it('新创建的订单应该处于待支付状态', async () => {
  435. const orderData = {
  436. routeId: testRoute.id,
  437. passengerCount: 1,
  438. totalAmount: testRoute.price,
  439. passengerSnapshots: [],
  440. routeSnapshot: {
  441. id: testRoute.id,
  442. name: testRoute.name,
  443. pickupPoint: testRoute.pickupPoint,
  444. dropoffPoint: testRoute.dropoffPoint,
  445. departureTime: testRoute.departureTime,
  446. price: testRoute.price,
  447. vehicleType: testRoute.vehicleType,
  448. travelMode: testRoute.travelMode
  449. }
  450. };
  451. const response = await client.orders.$post({
  452. json: orderData,
  453. },
  454. {
  455. headers: {
  456. 'Authorization': `Bearer ${testToken}`
  457. }
  458. });
  459. expect(response.status).toBe(201);
  460. if (response.status === 201) {
  461. const responseData = await response.json();
  462. expect(responseData.status).toBe(OrderStatus.PENDING_PAYMENT);
  463. expect(responseData.paymentStatus).toBe(PaymentStatus.PENDING);
  464. }
  465. });
  466. });
  467. describe('订单列表API测试', () => {
  468. let testOrder: any;
  469. beforeEach(async () => {
  470. // 创建测试订单
  471. const orderData = {
  472. routeId: testRoute.id,
  473. passengerCount: 1,
  474. totalAmount: testRoute.price,
  475. passengerSnapshots: [
  476. {
  477. id: testPassenger.id,
  478. name: testPassenger.name,
  479. idType: testPassenger.idType,
  480. idNumber: testPassenger.idNumber,
  481. phone: testPassenger.phone
  482. }
  483. ],
  484. routeSnapshot: {
  485. id: testRoute.id,
  486. name: testRoute.name,
  487. pickupPoint: testRoute.pickupPoint,
  488. dropoffPoint: testRoute.dropoffPoint,
  489. departureTime: testRoute.departureTime,
  490. price: testRoute.price,
  491. vehicleType: testRoute.vehicleType,
  492. travelMode: testRoute.travelMode
  493. }
  494. };
  495. const response = await client.orders.$post({
  496. json: orderData,
  497. },
  498. {
  499. headers: {
  500. 'Authorization': `Bearer ${testToken}`
  501. }
  502. });
  503. if (response.status === 201) {
  504. testOrder = await response.json();
  505. }
  506. });
  507. it('应该成功获取订单列表', async () => {
  508. const response = await client.orders.$get({
  509. query: {
  510. page: 1,
  511. pageSize: 10
  512. }
  513. },
  514. {
  515. headers: {
  516. 'Authorization': `Bearer ${testToken}`
  517. }
  518. });
  519. expect(response.status).toBe(200);
  520. if (response.status === 200) {
  521. const responseData = await response.json();
  522. expect(responseData).toHaveProperty('data');
  523. expect(responseData).toHaveProperty('total');
  524. expect(responseData).toHaveProperty('page');
  525. expect(responseData).toHaveProperty('pageSize');
  526. expect(Array.isArray(responseData.data)).toBe(true);
  527. expect(responseData.data.length).toBeGreaterThan(0);
  528. // 验证订单数据格式
  529. const order = responseData.data[0];
  530. expect(order).toHaveProperty('id');
  531. expect(order).toHaveProperty('userId');
  532. expect(order).toHaveProperty('routeId');
  533. expect(order).toHaveProperty('status');
  534. expect(order).toHaveProperty('paymentStatus');
  535. }
  536. });
  537. it('应该支持按状态筛选订单', async () => {
  538. const response = await client.orders.$get({
  539. query: {
  540. page: 1,
  541. pageSize: 10,
  542. status: OrderStatus.PENDING_PAYMENT
  543. }
  544. },
  545. {
  546. headers: {
  547. 'Authorization': `Bearer ${testToken}`
  548. }
  549. });
  550. expect(response.status).toBe(200);
  551. if (response.status === 200) {
  552. const responseData = await response.json();
  553. expect(Array.isArray(responseData.data)).toBe(true);
  554. // 验证所有返回的订单都是待支付状态
  555. if (responseData.data.length > 0) {
  556. responseData.data.forEach((order: any) => {
  557. expect(order.status).toBe(OrderStatus.PENDING_PAYMENT);
  558. });
  559. }
  560. }
  561. });
  562. it('应该支持分页功能', async () => {
  563. const pageSize = 5;
  564. const response = await client.orders.$get({
  565. query: {
  566. page: 1,
  567. pageSize: pageSize
  568. }
  569. },
  570. {
  571. headers: {
  572. 'Authorization': `Bearer ${testToken}`
  573. }
  574. });
  575. expect(response.status).toBe(200);
  576. if (response.status === 200) {
  577. const responseData = await response.json();
  578. expect(responseData.page).toBe(1);
  579. expect(responseData.pageSize).toBe(pageSize);
  580. expect(responseData.data.length).toBeLessThanOrEqual(pageSize);
  581. }
  582. });
  583. it('应该拒绝未认证用户的订单列表请求', async () => {
  584. const response = await client.orders.$get({
  585. query: {
  586. page: 1,
  587. pageSize: 10
  588. }
  589. });
  590. expect(response.status).toBe(401);
  591. });
  592. });
  593. describe('订单详情API测试', () => {
  594. let testOrder: any;
  595. beforeEach(async () => {
  596. // 创建测试订单
  597. const orderData = {
  598. routeId: testRoute.id,
  599. passengerCount: 1,
  600. totalAmount: testRoute.price,
  601. passengerSnapshots: [
  602. {
  603. id: testPassenger.id,
  604. name: testPassenger.name,
  605. idType: testPassenger.idType,
  606. idNumber: testPassenger.idNumber,
  607. phone: testPassenger.phone
  608. }
  609. ],
  610. routeSnapshot: {
  611. id: testRoute.id,
  612. name: testRoute.name,
  613. pickupPoint: testRoute.pickupPoint,
  614. dropoffPoint: testRoute.dropoffPoint,
  615. departureTime: testRoute.departureTime,
  616. price: testRoute.price,
  617. vehicleType: testRoute.vehicleType,
  618. travelMode: testRoute.travelMode
  619. }
  620. };
  621. const response = await client.orders.$post({
  622. json: orderData,
  623. },
  624. {
  625. headers: {
  626. 'Authorization': `Bearer ${testToken}`
  627. }
  628. });
  629. if (response.status === 201) {
  630. testOrder = await response.json();
  631. }
  632. });
  633. it('应该成功获取订单详情', async () => {
  634. const response = await client.orders[':id'].$get({
  635. param: {
  636. id: testOrder.id
  637. }
  638. },
  639. {
  640. headers: {
  641. 'Authorization': `Bearer ${testToken}`
  642. }
  643. });
  644. expect(response.status).toBe(200);
  645. if (response.status === 200) {
  646. const responseData = await response.json();
  647. expect(responseData.id).toBe(testOrder.id);
  648. expect(responseData.userId).toBe(testUser.id);
  649. expect(responseData.routeId).toBe(testRoute.id);
  650. expect(responseData.status).toBe(OrderStatus.PENDING_PAYMENT);
  651. expect(responseData.passengerSnapshots).toEqual(testOrder.passengerSnapshots);
  652. expect(responseData.routeSnapshot).toEqual(testOrder.routeSnapshot);
  653. }
  654. });
  655. it('应该拒绝查看不存在的订单', async () => {
  656. const response = await client.orders[':id'].$get({
  657. param: {
  658. id: 999999
  659. }
  660. },
  661. {
  662. headers: {
  663. 'Authorization': `Bearer ${testToken}`
  664. }
  665. });
  666. expect(response.status).toBe(404);
  667. if (response.status === 404) {
  668. const responseData = await response.json();
  669. expect(responseData.message).toContain('订单不存在');
  670. }
  671. });
  672. it('应该拒绝查看其他用户的订单', async () => {
  673. // 创建另一个用户
  674. const dataSource = await IntegrationTestDatabase.getDataSource();
  675. const userService = new UserService(dataSource);
  676. const authService = new AuthService(userService);
  677. const otherUser = await TestDataFactory.createTestUser(dataSource, {
  678. username: 'other_user',
  679. phone: '13987654321'
  680. });
  681. const otherToken = authService.generateToken(otherUser);
  682. const response = await client.orders[':id'].$get({
  683. param: {
  684. id: testOrder.id
  685. }
  686. },
  687. {
  688. headers: {
  689. 'Authorization': `Bearer ${otherToken}`
  690. }
  691. });
  692. expect(response.status).toBe(403);
  693. if (response.status === 403) {
  694. const responseData = await response.json();
  695. expect(responseData.message).toContain('权限不足');
  696. }
  697. });
  698. it('应该拒绝未认证用户的订单详情请求', async () => {
  699. const response = await client.orders[':id'].$get({
  700. param: {
  701. id: testOrder.id
  702. }
  703. });
  704. expect(response.status).toBe(401);
  705. });
  706. });
  707. describe('订单取消API测试', () => {
  708. let testOrder: any;
  709. beforeEach(async () => {
  710. // 创建测试订单并更新为待出发状态
  711. const orderData = {
  712. routeId: testRoute.id,
  713. passengerCount: 1,
  714. totalAmount: testRoute.price,
  715. passengerSnapshots: [
  716. {
  717. id: testPassenger.id,
  718. name: testPassenger.name,
  719. idType: testPassenger.idType,
  720. idNumber: testPassenger.idNumber,
  721. phone: testPassenger.phone
  722. }
  723. ],
  724. routeSnapshot: {
  725. id: testRoute.id,
  726. name: testRoute.name,
  727. pickupPoint: testRoute.pickupPoint,
  728. dropoffPoint: testRoute.dropoffPoint,
  729. departureTime: new Date(Date.now() + 2 * 60 * 60 * 1000), // 2小时后出发
  730. price: testRoute.price,
  731. vehicleType: testRoute.vehicleType,
  732. travelMode: testRoute.travelMode
  733. }
  734. };
  735. const response = await client.orders.$post({
  736. json: orderData,
  737. },
  738. {
  739. headers: {
  740. 'Authorization': `Bearer ${testToken}`
  741. }
  742. });
  743. if (response.status === 201) {
  744. testOrder = await response.json();
  745. // 更新订单状态为待出发
  746. const dataSource = await IntegrationTestDatabase.getDataSource();
  747. const orderRepository = dataSource.getRepository('Order');
  748. await orderRepository.update(testOrder.id, {
  749. status: OrderStatus.WAITING_DEPARTURE,
  750. paymentStatus: PaymentStatus.PAID
  751. });
  752. }
  753. });
  754. it('应该成功取消待出发状态的订单', async () => {
  755. const response = await client.orders[':id'].cancel.$post({
  756. param: {
  757. id: testOrder.id
  758. }
  759. },
  760. {
  761. headers: {
  762. 'Authorization': `Bearer ${testToken}`
  763. }
  764. });
  765. expect(response.status).toBe(200);
  766. if (response.status === 200) {
  767. const responseData = await response.json();
  768. expect(responseData.id).toBe(testOrder.id);
  769. expect(responseData.status).toBe(OrderStatus.CANCELLED);
  770. expect(responseData.paymentStatus).toBe(PaymentStatus.REFUNDED);
  771. // 验证数据库中的状态更新
  772. const dataSource = await IntegrationTestDatabase.getDataSource();
  773. const orderRepository = dataSource.getRepository('Order');
  774. const updatedOrder = await orderRepository.findOne({
  775. where: { id: testOrder.id }
  776. });
  777. expect(updatedOrder?.status).toBe(OrderStatus.CANCELLED);
  778. expect(updatedOrder?.paymentStatus).toBe(PaymentStatus.REFUNDED);
  779. }
  780. });
  781. it('应该拒绝取消非待出发状态的订单', async () => {
  782. // 创建已完成状态的订单
  783. const orderData = {
  784. routeId: testRoute.id,
  785. passengerCount: 1,
  786. totalAmount: testRoute.price,
  787. passengerSnapshots: [],
  788. routeSnapshot: {
  789. id: testRoute.id,
  790. name: testRoute.name,
  791. pickupPoint: testRoute.pickupPoint,
  792. dropoffPoint: testRoute.dropoffPoint,
  793. departureTime: new Date(),
  794. price: testRoute.price,
  795. vehicleType: testRoute.vehicleType,
  796. travelMode: testRoute.travelMode
  797. }
  798. };
  799. const createResponse = await client.orders.$post({
  800. json: orderData,
  801. },
  802. {
  803. headers: {
  804. 'Authorization': `Bearer ${testToken}`
  805. }
  806. });
  807. if (createResponse.status === 201) {
  808. const completedOrder = await createResponse.json();
  809. // 更新订单状态为已完成
  810. const dataSource = await IntegrationTestDatabase.getDataSource();
  811. const orderRepository = dataSource.getRepository('Order');
  812. await orderRepository.update(completedOrder.id, {
  813. status: OrderStatus.COMPLETED,
  814. paymentStatus: PaymentStatus.PAID
  815. });
  816. const response = await client.orders[':id'].cancel.$post({
  817. param: {
  818. id: completedOrder.id
  819. }
  820. },
  821. {
  822. headers: {
  823. 'Authorization': `Bearer ${testToken}`
  824. }
  825. });
  826. expect(response.status).toBe(422);
  827. if (response.status === 422) {
  828. const responseData = await response.json();
  829. expect(responseData.message).toContain('只能取消待出发状态的订单');
  830. }
  831. }
  832. });
  833. it('应该拒绝取消距离出发时间不足1小时的订单', async () => {
  834. // 创建即将出发的订单
  835. const orderData = {
  836. routeId: testRoute.id,
  837. passengerCount: 1,
  838. totalAmount: testRoute.price,
  839. passengerSnapshots: [],
  840. routeSnapshot: {
  841. id: testRoute.id,
  842. name: testRoute.name,
  843. pickupPoint: testRoute.pickupPoint,
  844. dropoffPoint: testRoute.dropoffPoint,
  845. departureTime: new Date(Date.now() + 30 * 60 * 1000), // 30分钟后出发
  846. price: testRoute.price,
  847. vehicleType: testRoute.vehicleType,
  848. travelMode: testRoute.travelMode
  849. }
  850. };
  851. const createResponse = await client.orders.$post({
  852. json: orderData,
  853. },
  854. {
  855. headers: {
  856. 'Authorization': `Bearer ${testToken}`
  857. }
  858. });
  859. if (createResponse.status === 201) {
  860. const urgentOrder = await createResponse.json();
  861. // 更新订单状态为待出发
  862. const dataSource = await IntegrationTestDatabase.getDataSource();
  863. const orderRepository = dataSource.getRepository('Order');
  864. await orderRepository.update(urgentOrder.id, {
  865. status: OrderStatus.WAITING_DEPARTURE,
  866. paymentStatus: PaymentStatus.PAID
  867. });
  868. const response = await client.orders[':id'].cancel.$post({
  869. param: {
  870. id: urgentOrder.id
  871. }
  872. },
  873. {
  874. headers: {
  875. 'Authorization': `Bearer ${testToken}`
  876. }
  877. });
  878. expect(response.status).toBe(422);
  879. if (response.status === 422) {
  880. const responseData = await response.json();
  881. expect(responseData.message).toContain('距离出发时间不足1小时');
  882. }
  883. }
  884. });
  885. it('应该拒绝取消其他用户的订单', async () => {
  886. // 创建另一个用户
  887. const dataSource = await IntegrationTestDatabase.getDataSource();
  888. const userService = new UserService(dataSource);
  889. const authService = new AuthService(userService);
  890. const otherUser = await TestDataFactory.createTestUser(dataSource, {
  891. username: 'other_user',
  892. phone: '13987654321'
  893. });
  894. const otherToken = authService.generateToken(otherUser);
  895. const response = await client.orders[':id'].cancel.$post({
  896. param: {
  897. id: testOrder.id
  898. }
  899. },
  900. {
  901. headers: {
  902. 'Authorization': `Bearer ${otherToken}`
  903. }
  904. });
  905. expect(response.status).toBe(403);
  906. if (response.status === 403) {
  907. const responseData = await response.json();
  908. expect(responseData.message).toContain('权限不足');
  909. }
  910. });
  911. it('应该拒绝取消不存在的订单', async () => {
  912. const response = await client.orders[':id'].cancel.$post({
  913. param: {
  914. id: 999999
  915. }
  916. },
  917. {
  918. headers: {
  919. 'Authorization': `Bearer ${testToken}`
  920. }
  921. });
  922. expect(response.status).toBe(404);
  923. if (response.status === 404) {
  924. const responseData = await response.json();
  925. expect(responseData.message).toContain('订单不存在');
  926. }
  927. });
  928. it('应该拒绝未认证用户的订单取消请求', async () => {
  929. const response = await client.orders[':id'].cancel.$post({
  930. param: {
  931. id: testOrder.id
  932. }
  933. });
  934. expect(response.status).toBe(401);
  935. });
  936. });
  937. });