Skip to main content

Two suites, deliberately separated

Unit -- bun test test/unit

40 tests, 0 network calls, ~200ms total. Runs on every change.

E2e -- bun test test/e2e

9 tests, real testnet transactions, ~2 minutes. Run on demand.
Bare bun test (no path) discovers both suites — that’s test:all’s intentional job, not an accident. The default bun run test script is correctly scoped to test/unit only.

What the unit suite actually guards against

Every hand-rolled Soroban XDR encoder in src/scval-encoders.ts was wrong on at least one real attempt during development — each bug produced a silent on-chain wasm trap, not a TypeScript error, because the SDK happily accepts a structurally-valid-but-semantically-wrong ScVal tree. The unit tests decode the actual produced structure and assert its exact shape:
test/unit/scval-encoders.test.ts
Other unit tests cover the host’s canonical Map key ordering (Symbol("Ed25519") sorts before Symbol("Policy"), lexicographically — get this wrong and the signatures map is silently malformed), the MCP tool schema generator’s type mapping, and the address auto-fill heuristic.

What the e2e suite proves live

Deploys one fresh environment, then drives the real allow/reject sequence directly through invokeAsWallet:
  • An allowed, uncapped deposit succeeds
  • An allowed withdrawal within the spend cap succeeds
  • A withdrawal exceeding the cumulative cap is rejected on-chain (Auth, InvalidAction)
  • A call to a method off the allow-list is rejected on-chain
  • The wallet owner (unrestricted) can still call the same disallowed method — proving the restriction is agent-signer-specific, not contract-wide
Every e2e test generates and funds its own fresh testnet keys — none of them read .env, and none of them depend on the state of any previous run. Two runs of the same test file never interfere with each other.

Running everything