user-refunds.integration.test.ts 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  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 { UserEntity, Role } from '@d8d/user-module';
  6. import { File } from '@d8d/file-module';
  7. import { Merchant } from '@d8d/merchant-module';
  8. import { DeliveryAddress } from '@d8d/delivery-address-module';
  9. import { AreaEntity } from '@d8d/geo-areas';
  10. import { Supplier } from '@d8d/supplier-module';
  11. import userRefundsRoutes from '../../src/routes/user/refunds';
  12. import { Order, OrderRefund } from '../../src/entities';
  13. // 设置集成测试钩子
  14. setupIntegrationDatabaseHooksWithEntities([
  15. UserEntity, Role, Order, OrderRefund, File, Merchant, DeliveryAddress, AreaEntity, Supplier
  16. ])
  17. describe('用户退款管理API集成测试', () => {
  18. let client: ReturnType<typeof testClient<typeof userRefundsRoutes>>;
  19. let userToken: string;
  20. let otherUserToken: string;
  21. let testUser: UserEntity;
  22. let otherUser: UserEntity;
  23. let testOrder: Order;
  24. let otherUserOrder: Order;
  25. let testDeliveryAddress: DeliveryAddress;
  26. let testMerchant: Merchant;
  27. beforeEach(async () => {
  28. // 创建测试客户端
  29. client = testClient(userRefundsRoutes);
  30. // 获取数据源
  31. const dataSource = await IntegrationTestDatabase.getDataSource();
  32. // 创建测试用户
  33. const userRepository = dataSource.getRepository(UserEntity);
  34. testUser = userRepository.create({
  35. username: `test_user_${Math.floor(Math.random() * 100000)}`,
  36. password: 'test_password',
  37. nickname: '测试用户',
  38. registrationSource: 'web'
  39. });
  40. await userRepository.save(testUser);
  41. // 创建其他用户
  42. otherUser = userRepository.create({
  43. username: `other_user_${Math.floor(Math.random() * 100000)}`,
  44. password: 'other_password',
  45. nickname: '其他用户',
  46. registrationSource: 'web'
  47. });
  48. await userRepository.save(otherUser);
  49. // 生成测试用户的token
  50. userToken = JWTUtil.generateToken({
  51. id: testUser.id,
  52. username: testUser.username,
  53. roles: [{name:'user'}]
  54. });
  55. // 生成其他用户的token
  56. otherUserToken = JWTUtil.generateToken({
  57. id: otherUser.id,
  58. username: otherUser.username,
  59. roles: [{name:'user'}]
  60. });
  61. // 创建测试地区
  62. const areaRepository = dataSource.getRepository(AreaEntity);
  63. const testProvince = areaRepository.create({
  64. name: '广东省',
  65. level: 1,
  66. code: '440000',
  67. state: 1
  68. });
  69. await areaRepository.save(testProvince);
  70. const testCity = areaRepository.create({
  71. name: '深圳市',
  72. level: 2,
  73. code: '440300',
  74. state: 1
  75. });
  76. await areaRepository.save(testCity);
  77. const testDistrict = areaRepository.create({
  78. name: '南山区',
  79. level: 3,
  80. code: '440305',
  81. state: 1
  82. });
  83. await areaRepository.save(testDistrict);
  84. const testTown = areaRepository.create({
  85. name: '粤海街道',
  86. level: 4,
  87. code: '440305001',
  88. state: 1
  89. });
  90. await areaRepository.save(testTown);
  91. // 创建测试配送地址
  92. const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
  93. testDeliveryAddress = deliveryAddressRepository.create({
  94. userId: testUser.id,
  95. name: '收货人姓名',
  96. phone: '13800138000',
  97. receiverProvince: testProvince.id,
  98. receiverCity: testCity.id,
  99. receiverDistrict: testDistrict.id,
  100. receiverTown: testTown.id,
  101. address: '测试地址',
  102. isDefault: 1,
  103. state: 1,
  104. createdBy: testUser.id
  105. });
  106. await deliveryAddressRepository.save(testDeliveryAddress);
  107. // 创建测试商户
  108. const merchantRepository = dataSource.getRepository(Merchant);
  109. testMerchant = merchantRepository.create({
  110. name: '测试商户',
  111. username: `merchant_${Math.floor(Math.random() * 100000)}`,
  112. password: 'test_password',
  113. state: 1,
  114. createdBy: testUser.id
  115. });
  116. await merchantRepository.save(testMerchant);
  117. // 创建测试供应商
  118. const supplierRepository = dataSource.getRepository(Supplier);
  119. const testSupplier = supplierRepository.create({
  120. name: '测试供应商',
  121. username: `supplier_${Math.floor(Math.random() * 100000)}`,
  122. password: 'test_password',
  123. state: 1,
  124. createdBy: testUser.id
  125. });
  126. await supplierRepository.save(testSupplier);
  127. // 创建测试用户的订单
  128. const orderRepository = dataSource.getRepository(Order);
  129. testOrder = orderRepository.create({
  130. orderNo: `ORDER_${Math.floor(Math.random() * 100000)}`,
  131. userId: testUser.id,
  132. amount: 100.00,
  133. costAmount: 80.00,
  134. payAmount: 100.00,
  135. orderType: 1,
  136. payType: 1,
  137. payState: 2,
  138. state: 0,
  139. deliveryAddressId: testDeliveryAddress.id,
  140. addressId: testDeliveryAddress.id,
  141. merchantId: testMerchant.id,
  142. supplierId: testSupplier.id,
  143. recevierProvince: testProvince.id,
  144. recevierCity: testCity.id,
  145. recevierDistrict: testDistrict.id,
  146. recevierTown: testTown.id,
  147. createdBy: testUser.id
  148. });
  149. await orderRepository.save(testOrder);
  150. // 创建其他用户的订单
  151. otherUserOrder = orderRepository.create({
  152. orderNo: `ORDER_${Math.floor(Math.random() * 100000)}`,
  153. userId: otherUser.id,
  154. amount: 200.00,
  155. costAmount: 160.00,
  156. payAmount: 200.00,
  157. orderType: 1,
  158. payType: 1,
  159. payState: 2,
  160. state: 0,
  161. deliveryAddressId: testDeliveryAddress.id,
  162. addressId: testDeliveryAddress.id,
  163. merchantId: testMerchant.id,
  164. supplierId: testSupplier.id,
  165. recevierProvince: testProvince.id,
  166. recevierCity: testCity.id,
  167. recevierDistrict: testDistrict.id,
  168. recevierTown: testTown.id,
  169. createdBy: otherUser.id
  170. });
  171. await orderRepository.save(otherUserOrder);
  172. });
  173. describe('GET /refunds', () => {
  174. it('应该返回当前用户订单的退款列表', async () => {
  175. // 为测试用户的订单创建一些退款
  176. const dataSource = await IntegrationTestDatabase.getDataSource();
  177. const orderRefundRepository = dataSource.getRepository(OrderRefund);
  178. const userRefund1 = orderRefundRepository.create({
  179. orderNo: testOrder.orderNo,
  180. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  181. refundAmount: 50.00,
  182. state: 0,
  183. createdBy: testUser.id
  184. });
  185. await orderRefundRepository.save(userRefund1);
  186. const userRefund2 = orderRefundRepository.create({
  187. orderNo: testOrder.orderNo,
  188. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  189. refundAmount: 25.00,
  190. state: 1,
  191. createdBy: testUser.id
  192. });
  193. await orderRefundRepository.save(userRefund2);
  194. // 为其他用户的订单创建一个退款,确保不会返回
  195. const otherUserRefund = orderRefundRepository.create({
  196. orderNo: otherUserOrder.orderNo,
  197. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  198. refundAmount: 100.00,
  199. state: 0,
  200. createdBy: otherUser.id
  201. });
  202. await orderRefundRepository.save(otherUserRefund);
  203. const response = await client.index.$get({
  204. query: {
  205. page: 1,
  206. pageSize: 10
  207. }
  208. }, {
  209. headers: {
  210. 'Authorization': `Bearer ${userToken}`
  211. }
  212. });
  213. console.debug('用户退款列表响应状态:', response.status);
  214. if (response.status !== 200) {
  215. const errorData = await response.json();
  216. console.debug('用户退款列表错误响应:', errorData);
  217. }
  218. expect(response.status).toBe(200);
  219. if (response.status === 200) {
  220. const data = await response.json();
  221. expect(data).toHaveProperty('data');
  222. expect(Array.isArray(data.data)).toBe(true);
  223. // 验证只返回当前用户订单的退款
  224. data.data.forEach((refund: any) => {
  225. expect(refund.order.userId).toBe(testUser.id);
  226. });
  227. // 验证不包含其他用户订单的退款
  228. const otherUserRefundInResponse = data.data.find((refund: any) =>
  229. refund.order && refund.order.userId === otherUser.id
  230. );
  231. expect(otherUserRefundInResponse).toBeUndefined();
  232. }
  233. });
  234. it('应该拒绝未认证用户的访问', async () => {
  235. const response = await client.index.$get({
  236. query: {
  237. page: 1,
  238. pageSize: 10
  239. }
  240. });
  241. expect(response.status).toBe(401);
  242. });
  243. });
  244. describe('POST /refunds', () => {
  245. it('应该成功创建退款申请并自动设置当前用户权限', async () => {
  246. const createData = {
  247. orderNo: testOrder.orderNo,
  248. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  249. refundAmount: 75.00,
  250. state: 0
  251. };
  252. const response = await client.index.$post({
  253. json: createData
  254. }, {
  255. headers: {
  256. 'Authorization': `Bearer ${userToken}`
  257. }
  258. });
  259. console.debug('用户创建退款申请响应状态:', response.status);
  260. if (response.status !== 201) {
  261. const errorData = await response.json();
  262. console.debug('用户创建退款申请错误响应:', errorData);
  263. }
  264. expect(response.status).toBe(201);
  265. if (response.status === 201) {
  266. const data = await response.json();
  267. expect(data).toHaveProperty('id');
  268. expect(data.refundOrderNo).toBe(createData.refundOrderNo);
  269. expect(parseFloat(data.refundAmount)).toBe(createData.refundAmount);
  270. expect(data.state).toBe(createData.state);
  271. expect(data.createdBy).toBe(testUser.id); // 验证自动设置创建用户
  272. }
  273. });
  274. it('应该验证创建退款申请的必填字段', async () => {
  275. const invalidData = {
  276. // 缺少必填字段
  277. refundAmount: -1
  278. };
  279. const response = await client.index.$post({
  280. json: invalidData
  281. }, {
  282. headers: {
  283. 'Authorization': `Bearer ${userToken}`
  284. }
  285. });
  286. expect(response.status).toBe(400);
  287. });
  288. it('应该拒绝为其他用户的订单创建退款申请', async () => {
  289. const createData = {
  290. orderNo: otherUserOrder.orderNo,
  291. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  292. refundAmount: 75.00,
  293. state: 0
  294. };
  295. const response = await client.index.$post({
  296. json: createData
  297. }, {
  298. headers: {
  299. 'Authorization': `Bearer ${userToken}`
  300. }
  301. });
  302. expect(response.status).toBe(403); // 数据权限控制返回403
  303. });
  304. });
  305. describe('GET /refunds/:id', () => {
  306. it('应该返回当前用户订单的退款详情', async () => {
  307. // 先为测试用户的订单创建一个退款
  308. const dataSource = await IntegrationTestDatabase.getDataSource();
  309. const orderRefundRepository = dataSource.getRepository(OrderRefund);
  310. const testRefund = orderRefundRepository.create({
  311. orderNo: testOrder.orderNo,
  312. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  313. refundAmount: 50.00,
  314. state: 0,
  315. createdBy: testUser.id
  316. });
  317. await orderRefundRepository.save(testRefund);
  318. const response = await client[':id'].$get({
  319. param: { id: testRefund.id }
  320. }, {
  321. headers: {
  322. 'Authorization': `Bearer ${userToken}`
  323. }
  324. });
  325. console.debug('用户退款详情响应状态:', response.status);
  326. if (response.status !== 200) {
  327. const errorData = await response.json();
  328. console.debug('用户退款详情错误响应:', errorData);
  329. }
  330. expect(response.status).toBe(200);
  331. if (response.status === 200) {
  332. const data = await response.json();
  333. expect(data.id).toBe(testRefund.id);
  334. expect(data.refundOrderNo).toBe(testRefund.refundOrderNo);
  335. expect(data.order.userId).toBe(testUser.id); // 验证订单属于当前用户
  336. }
  337. });
  338. it('应该拒绝访问其他用户订单的退款', async () => {
  339. // 为其他用户的订单创建一个退款
  340. const dataSource = await IntegrationTestDatabase.getDataSource();
  341. const orderRefundRepository = dataSource.getRepository(OrderRefund);
  342. const otherUserRefund = orderRefundRepository.create({
  343. orderNo: otherUserOrder.orderNo,
  344. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  345. refundAmount: 100.00,
  346. state: 0,
  347. createdBy: otherUser.id
  348. });
  349. await orderRefundRepository.save(otherUserRefund);
  350. const response = await client[':id'].$get({
  351. param: { id: otherUserRefund.id }
  352. }, {
  353. headers: {
  354. 'Authorization': `Bearer ${userToken}`
  355. }
  356. });
  357. expect(response.status).toBe(403); // 数据权限控制返回403(权限不足)
  358. });
  359. it('应该处理不存在的退款', async () => {
  360. const response = await client[':id'].$get({
  361. param: { id: 999999 }
  362. }, {
  363. headers: {
  364. 'Authorization': `Bearer ${userToken}`
  365. }
  366. });
  367. expect(response.status).toBe(404);
  368. });
  369. });
  370. describe('PUT /refunds/:id', () => {
  371. it('应该成功更新当前用户订单的退款', async () => {
  372. // 先为测试用户的订单创建一个退款
  373. const dataSource = await IntegrationTestDatabase.getDataSource();
  374. const orderRefundRepository = dataSource.getRepository(OrderRefund);
  375. const testRefund = orderRefundRepository.create({
  376. orderNo: testOrder.orderNo,
  377. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  378. refundAmount: 50.00,
  379. state: 0,
  380. createdBy: testUser.id
  381. });
  382. await orderRefundRepository.save(testRefund);
  383. const updateData = {
  384. state: 1
  385. };
  386. const response = await client[':id'].$put({
  387. param: { id: testRefund.id },
  388. json: updateData
  389. }, {
  390. headers: {
  391. 'Authorization': `Bearer ${userToken}`
  392. }
  393. });
  394. console.debug('用户更新退款响应状态:', response.status);
  395. expect(response.status).toBe(200);
  396. if (response.status === 200) {
  397. const data = await response.json();
  398. expect(data.state).toBe(updateData.state);
  399. expect(data.updatedBy).toBe(testUser.id); // 验证自动设置更新用户
  400. }
  401. });
  402. it('应该拒绝更新其他用户订单的退款', async () => {
  403. // 为其他用户的订单创建一个退款
  404. const dataSource = await IntegrationTestDatabase.getDataSource();
  405. const orderRefundRepository = dataSource.getRepository(OrderRefund);
  406. const otherUserRefund = orderRefundRepository.create({
  407. orderNo: otherUserOrder.orderNo,
  408. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  409. refundAmount: 100.00,
  410. state: 0,
  411. createdBy: otherUser.id
  412. });
  413. await orderRefundRepository.save(otherUserRefund);
  414. const updateData = {
  415. state: 1
  416. };
  417. const response = await client[':id'].$put({
  418. param: { id: otherUserRefund.id },
  419. json: updateData
  420. }, {
  421. headers: {
  422. 'Authorization': `Bearer ${userToken}`
  423. }
  424. });
  425. expect(response.status).toBe(403); // 数据权限控制返回403
  426. });
  427. });
  428. describe('DELETE /refunds/:id', () => {
  429. it('应该成功删除当前用户订单的退款', async () => {
  430. // 先为测试用户的订单创建一个退款
  431. const dataSource = await IntegrationTestDatabase.getDataSource();
  432. const orderRefundRepository = dataSource.getRepository(OrderRefund);
  433. const testRefund = orderRefundRepository.create({
  434. orderNo: testOrder.orderNo,
  435. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  436. refundAmount: 50.00,
  437. state: 0,
  438. createdBy: testUser.id
  439. });
  440. await orderRefundRepository.save(testRefund);
  441. const response = await client[':id'].$delete({
  442. param: { id: testRefund.id }
  443. }, {
  444. headers: {
  445. 'Authorization': `Bearer ${userToken}`
  446. }
  447. });
  448. console.debug('用户删除退款响应状态:', response.status);
  449. expect(response.status).toBe(204);
  450. });
  451. it('应该拒绝删除其他用户订单的退款', async () => {
  452. // 为其他用户的订单创建一个退款
  453. const dataSource = await IntegrationTestDatabase.getDataSource();
  454. const orderRefundRepository = dataSource.getRepository(OrderRefund);
  455. const otherUserRefund = orderRefundRepository.create({
  456. orderNo: otherUserOrder.orderNo,
  457. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  458. refundAmount: 100.00,
  459. state: 0,
  460. createdBy: otherUser.id
  461. });
  462. await orderRefundRepository.save(otherUserRefund);
  463. const response = await client[':id'].$delete({
  464. param: { id: otherUserRefund.id }
  465. }, {
  466. headers: {
  467. 'Authorization': `Bearer ${userToken}`
  468. }
  469. });
  470. expect(response.status).toBe(403); // 数据权限控制返回403
  471. });
  472. });
  473. describe('数据权限配置测试', () => {
  474. it('应该验证dataPermission配置正确工作', async () => {
  475. // 这个测试验证数据权限配置是否正常工作
  476. // 用户只能访问自己订单的退款
  477. const dataSource = await IntegrationTestDatabase.getDataSource();
  478. const orderRefundRepository = dataSource.getRepository(OrderRefund);
  479. // 创建测试用户和其他用户订单的退款
  480. const userRefund = orderRefundRepository.create({
  481. orderNo: testOrder.orderNo,
  482. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  483. refundAmount: 50.00,
  484. state: 0,
  485. createdBy: testUser.id
  486. });
  487. await orderRefundRepository.save(userRefund);
  488. const otherUserRefund = orderRefundRepository.create({
  489. orderNo: otherUserOrder.orderNo,
  490. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  491. refundAmount: 100.00,
  492. state: 0,
  493. createdBy: otherUser.id
  494. });
  495. await orderRefundRepository.save(otherUserRefund);
  496. // 使用测试用户token获取列表
  497. const response = await client.index.$get({
  498. query: {
  499. page: 1,
  500. pageSize: 10
  501. }
  502. }, {
  503. headers: {
  504. 'Authorization': `Bearer ${userToken}`
  505. }
  506. });
  507. if (response.status !== 200) {
  508. const errorData = await response.json();
  509. console.debug('数据权限配置测试错误响应:', errorData);
  510. }
  511. expect(response.status).toBe(200);
  512. const data = await response.json();
  513. // 类型检查确保data属性存在
  514. if ('data' in data && Array.isArray(data.data)) {
  515. // 验证只返回测试用户订单的退款
  516. const userRefundInResponse = data.data.filter((refund: any) =>
  517. refund.order && refund.order.userId === testUser.id
  518. );
  519. const otherUserRefundInResponse = data.data.filter((refund: any) =>
  520. refund.order && refund.order.userId === otherUser.id
  521. );
  522. expect(userRefundInResponse.length).toBeGreaterThan(0);
  523. expect(otherUserRefundInResponse.length).toBe(0);
  524. } else {
  525. // 如果响应是错误格式,应该失败
  526. expect(data).toHaveProperty('data');
  527. }
  528. });
  529. });
  530. describe('退款状态管理测试', () => {
  531. it('应该正确处理退款状态变更', async () => {
  532. const dataSource = await IntegrationTestDatabase.getDataSource();
  533. const orderRefundRepository = dataSource.getRepository(OrderRefund);
  534. // 创建不同状态的退款
  535. const pendingRefund = orderRefundRepository.create({
  536. orderNo: testOrder.orderNo,
  537. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  538. refundAmount: 50.00,
  539. state: 0, // 未退款
  540. createdBy: testUser.id
  541. });
  542. await orderRefundRepository.save(pendingRefund);
  543. const processingRefund = orderRefundRepository.create({
  544. orderNo: testOrder.orderNo,
  545. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  546. refundAmount: 25.00,
  547. state: 1, // 退款中
  548. createdBy: testUser.id
  549. });
  550. await orderRefundRepository.save(processingRefund);
  551. const completedRefund = orderRefundRepository.create({
  552. orderNo: testOrder.orderNo,
  553. refundOrderNo: `REFUND_${Math.floor(Math.random() * 100000)}`,
  554. refundAmount: 75.00,
  555. state: 2, // 退款成功
  556. createdBy: testUser.id
  557. });
  558. await orderRefundRepository.save(completedRefund);
  559. // 验证状态过滤
  560. const response = await client.index.$get({
  561. query: { filters: JSON.stringify({ state: 0 }) }
  562. }, {
  563. headers: {
  564. 'Authorization': `Bearer ${userToken}`
  565. }
  566. });
  567. expect(response.status).toBe(200);
  568. const data = await response.json();
  569. // 类型检查确保data属性存在
  570. if ('data' in data && Array.isArray(data.data)) {
  571. // 应该只返回未退款状态的退款
  572. const pendingRefundsInResponse = data.data.filter((refund: any) => refund.state === 0);
  573. const processingRefundsInResponse = data.data.filter((refund: any) => refund.state === 1);
  574. const completedRefundsInResponse = data.data.filter((refund: any) => refund.state === 2);
  575. expect(pendingRefundsInResponse.length).toBeGreaterThan(0);
  576. expect(processingRefundsInResponse.length).toBe(0);
  577. expect(completedRefundsInResponse.length).toBe(0);
  578. } else {
  579. // 如果响应是错误格式,应该失败
  580. expect(data).toHaveProperty('data');
  581. }
  582. });
  583. });
  584. });