CHAPTER 6 of 11

Testing Infrastructure

39 automated tests. Every adapter, every attack scenario, every edge case. 1.3 seconds.

Why This Chapter Exists

Early changes to a live site shipped without automated tests, and three of them broke the build. A TypeScript type error, a BigInt syntax incompatibility, and a DNS failure in a price API — any one of which would have been caught by a 5-second test run. This chapter is the fix: a test suite that proves the framework works before code ships.

Run the Tests

$ cd x402-demo
$ npx vitest run

 ✓ __tests__/x402-client.test.js        (10 tests)
 ✓ __tests__/adapters/solana.test.js    (13 tests)
 ✓ __tests__/adapters/evm.test.js       (7 tests)
 ✓ __tests__/adapters/walletconnect.test.js (9 tests)

 Test Files  4 passed (4)
      Tests  39 passed (39)
   Duration  1.32s
Zero external dependencies. No real blockchain, no real wallets, no API keys, no browser binaries. Tests run in Node with jsdom via Vitest. Wallet providers are mocked via reusable factory functions. Transaction verification uses real Solana/Ethereum RPC JSON captured as fixture files.

What's Covered

Client Core (10 tests)

Creation with valid/invalid config, adapter registry (register, list chains, reject incomplete adapters, chaining), event emitter (subscribe, emit, unsubscribe), wallet detection aggregation across multiple chains, pay-path error handling for unsupported chains and missing amounts.

Solana Adapter (13 tests)

Wallet detection (Phantom installed, multiple wallets, no wallet). USDC verification with 8 attack scenarios from our security specialist's threat model:

TestAttack Blocked
Non-existent txHashFake payment submission
Failed transactionReverted tx used as proof
No transfer to recipientPayment sent to wrong wallet
Partial paymentUnderpayment fraud ($0.50 for $12 item)
Correct amount (positive)Proves legitimate payments work
Net-delta refund attackJupiter multi-hop: credit $10, claw back $9.50
Wrong tokenScam token impersonation
Stale transactionReusing an ancient unrelated payment
The net-delta refund attack test is the highest-value security test. It verifies the framework correctly computes NET received when a transaction both credits and debits the merchant in the same atomic operation — blocking a sophisticated attack where a DEX aggregator sends USDC then claws it back.

Ethereum Adapter (7 tests)

Same attack coverage for Base/Polygon/Arbitrum USDC: valid transfer, not-found, failed, wrong recipient, partial payment, wrong token contract, and the Ethereum net-delta refund attack (two Transfer events — credit then debit in one receipt).

WalletConnect Adapter (9 tests)

Deep link URL generation (Phantom, Solflare, query params, localhost). Mobile vs desktop detection. Project ID handling. Deep-link redirect throw behavior. WalletConnect error messaging.

How It Works

Wallet Mocking

Reusable factory functions simulate wallet providers without real browser extensions:

// From __tests__/helpers/mock-providers.js
function mockPhantomProvider(opts = {}) {
  return {
    isPhantom: true,
    connect: async () => ({
      publicKey: { toString: () => opts.pubkey || 'FakePubkey...' }
    }),
    signTransaction: async (tx) => {
      if (opts.rejectSign) throw new Error('User rejected')
      return { serialize: () => new Uint8Array(256) }
    },
  }
}

Transaction Fixtures

Verification tests use real RPC JSON responses captured from the Solana network; a real $0.02 USDC payment is one of the fixture files. Attack scenarios are crafted variants. Because fixtures ARE the RPC protocol spec, they only break if the blockchain protocol itself changes.

Why Vitest, Not Playwright

The adapters are pure ES modules with injected dependencies. Mocking window.phantom.solana in jsdom is identical to mocking it in a real browser — the API surface is the same JavaScript object. Playwright adds CI complexity (browser binaries, flake) for zero additional coverage. Reserved for one future smoke test: "page loads, Buffer polyfill present, no console errors."

What This Prevents

If we'd had this suite before our three broken PRs:

Rule going forward: npx vitest run passes before any PR ships. If it doesn't pass, it doesn't ship.