Skip to content

Task 3-1: Integration Test - Rental Lifecycle

Phase: 3 Priority: Medium Module: rental Depends on: task-1-1 Reference: docs/bountyhunter-blockchain-p2/details/feature-nft-rental/SPEC.md

Background

Sau khi migrate sang async queue (task-1-1), cần có integration test toàn bộ rental lifecycle: create → rent → return/cancel. Mỗi operation cần test cả EVM path và IMX path (riêng createRentNft). Đặc biệt cần test error codes granular (task-1-2) và chain validation (task-2-1) đã được implement đúng.

Tasks

Note: Sau task-1-1, controller trả HTTP 202 thay vì blocking response. Test cần waitForJobCompletion để kiểm tra webhook. Mock BlockchainUtilImmutableUtil để tránh gọi thật lên chain. Đảm bảo task-1-1 và task-1-2 đã hoàn thành trước khi viết test.

  • [ ] Tạo test file rental.integration.spec.ts
  • [ ] Test case 1 — Create Rent NFT (EVM):
    it('POST /rental/create-rent-nft (EVM) → queued → tx executed → webhook called', async () => {
      mockBlockchainUtil.createRentNft.mockResolvedValue('0xrent123');
    
      const res = await request(app.getHttpServer())
        .post('/rental/create-rent-nft')
        .send({ chainId: EVM_CHAIN_ID, nftId: '1', duration: 7, webhookUrl: 'http://backend/webhook' });
    
      expect(res.status).toBe(202);
      expect(res.body).toHaveProperty('queued', true);
      expect(res.body).toHaveProperty('jobId');
    
      await waitForJobCompletion();
      expect(mockWebhook).toHaveBeenCalledWith(
        expect.any(String),
        expect.objectContaining({ status: 'SUCCESS', txHash: '0xrent123' })
      );
    });
    
  • [ ] Test case 2 — Rent NFT (on-chain execution):
  • Mock blockchainUtil.rentNft → success
  • Verify webhook success callback
  • [ ] Test case 3 — Cancel Rent NFT:
  • Mock blockchainUtil.cancelRentNft → success
  • Verify cancellation webhook callback
  • [ ] Test case 4 — Return Rent NFT:
  • Mock blockchainUtil.returnRentNft → success
  • Verify return webhook callback
  • [ ] Test case 5 — IMX path cho createRentNft:
    it('POST /rental/create-rent-nft (IMX chainId) → ImmutableUtil called', async () => {
      mockImmutableUtil.createRentNft.mockResolvedValue({ orderId: 'imx-order-1' });
    
      await request(app.getHttpServer())
        .post('/rental/create-rent-nft')
        .send({ chainId: IMX_CHAIN_ID, nftId: '1', duration: 7, ... });
    
      await waitForJobCompletion();
      expect(mockImmutableUtil.createRentNft).toHaveBeenCalled();
      expect(mockBlockchainUtil.createRentNft).not.toHaveBeenCalled();
    });
    
  • [ ] Test case 6 — Unsupported chain error:
    it('returnRentNft with IMX chainId → RENTAL_UNSUPPORTED_CHAIN error', async () => {
      const res = await request(app.getHttpServer())
        .post('/rental/return-rent-nft')
        .send({ chainId: IMX_CHAIN_ID, ... });
    
      expect(res.body.code).toBe('RENTAL_UNSUPPORTED_CHAIN');
    });
    
  • [ ] Test case 7 — Error code: insufficient fundsRENTAL_INSUFFICIENT_BALANCE:
    it('blockchain throws insufficient funds → RENTAL_INSUFFICIENT_BALANCE webhook payload', async () => {
      mockBlockchainUtil.rentNft.mockRejectedValue(new Error('insufficient funds for gas'));
      // ...simulate max retries...
      expect(mockWebhook).toHaveBeenCalledWith(
        expect.any(String),
        expect.objectContaining({ errorCode: 'RENTAL_INSUFFICIENT_BALANCE' })
      );
    });
    

Verification / Acceptance Criteria

  • [ ] 4 operations (create, rent, cancel, return) đều trả HTTP 202 ngay lập tức
  • [ ] Webhook success được gọi sau khi job hoàn thành với txHash
  • [ ] IMX path: ImmutableUtil.createRentNft được gọi, không phải EVM path
  • [ ] IMX return → RENTAL_UNSUPPORTED_CHAIN error trong response
  • [ ] Granular error codes trong webhook failure payload khớp với loại blockchain error
  • [ ] Test pass khi chạy npm run test

Files to Modify

  • src/rental/tests/rental.integration.spec.ts (tạo mới)