Skip to content

Task 3-1: Integration Test - Gasless NFT Transfer

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

Background

Chưa có integration test cho luồng gasless NFT transfer end-to-end. Luồng này bao gồm nhiều bước: nhận API request, kiểm tra network support, đẩy job vào queue, Processor xử lý job, gọi Biconomy SDK, và callback webhook về Backend. Cần test đủ các nhánh bao gồm gasless, non-gasless, và idempotency khi txHash đã tồn tại.

Tasks

Note: Sử dụng @nestjs/testing để tạo test module. Mock BullModule queue bằng bull-mock hoặc in-memory queue. Mock BiconomyService hoặc Biconomy SDK calls để tránh gọi thật lên chain. Dùng supertest cho API-level test. Đảm bảo task-1-1 đã hoàn thành (Logger thay thế console.log) để log output có thể kiểm tra trong test.

  • [ ] Tạo test file biconomy.integration.spec.ts hoặc biconomy.e2e-spec.ts
  • [ ] Test case 1 — Happy path gasless NFT transfer:
    it('POST /biconomy/send-transaction → job queued → processor completes → webhook called', async () => {
      const mockWebhook = jest.fn().mockResolvedValue({ status: 200 });
      // Mock postWithRetry
      jest.spyOn(httpService, 'post').mockImplementation(mockWebhook);
    
      const res = await request(app.getHttpServer())
        .post('/biconomy/send-transaction')
        .send({ chainId: GASLESS_SUPPORTED_CHAIN, ...validPayload });
    
      expect(res.status).toBe(201);
      // Wait for job processing
      await waitForJobCompletion();
      expect(mockWebhook).toHaveBeenCalledWith(
        expect.stringContaining('/webhook'),
        expect.objectContaining({ txHash: expect.any(String) })
      );
    });
    
  • [ ] Test case 2 — Idempotency: job đã có data.txHash → kiểm tra receipt thay vì gửi lại:
    it('job with existing txHash → checks receipt, does not re-send', async () => {
      // Pre-populate job with txHash
      const job = await queue.add({ ...payload, txHash: '0xabc123' });
      await processor.handleBiconomySendGaslessNft(job);
      // Verify: createSmartAccountClient NOT called
      expect(mockCreateSmartAccount).not.toHaveBeenCalled();
    });
    
  • [ ] Test case 3 — Non-gasless path: chain không trong GASLESS_SUPPORTED_NETWORK → job vào GASSLESS_NFT queue với path thông thường
  • [ ] Test case 4 — Queue job retry khi Biconomy SDK throw error
  • [ ] Test case 5 — Webhook failure: backend webhook trả 500 → retry theo QUEUE_MAX_RETRY

Verification / Acceptance Criteria

  • [ ] Tất cả test cases pass khi chạy npm run test:e2e hoặc npm run test:integration
  • [ ] Coverage cho biconomy.processor.ts đạt ≥ 80% branch coverage
  • [ ] Test không gọi thật lên Biconomy API hoặc blockchain (fully mocked)
  • [ ] Test idempotency: createSmartAccountClient không được gọi khi txHash đã tồn tại
  • [ ] Test thất bại khi backend webhook không phản hồi sau max retry

Files to Modify

  • src/biconomy/tests/biconomy.integration.spec.ts (tạo mới)
  • src/biconomy/tests/biconomy.e2e-spec.ts (tạo mới nếu dùng e2e)