Skip to content

test(cli): raise defi-cli coverage 65.78% → 75.72% (lp autopilot, MCP tools, lending collateral, bridge LI.FI, swap/agent)#33

Open
Hiksang wants to merge 6 commits into
mainfrom
qa/2026-05-17-cli-coverage
Open

test(cli): raise defi-cli coverage 65.78% → 75.72% (lp autopilot, MCP tools, lending collateral, bridge LI.FI, swap/agent)#33
Hiksang wants to merge 6 commits into
mainfrom
qa/2026-05-17-cli-coverage

Conversation

@Hiksang
Copy link
Copy Markdown
Collaborator

@Hiksang Hiksang commented May 23, 2026

Summary

Test-only PR closing the five highest-leverage coverage gaps in defi-cli
identified in the 2026-05-17 sweep. No source changes — adds 68 unit tests
across 6 new files.

defi-cli coverage:

  • Statements/Lines: 65.78% → 75.72% (+9.94pp)
  • Branches: 64.18% → 68.85% (+4.67pp)

What's covered

  • lp autopilot (lp.ts:1595-1891, was 31.82%) — budget/whitelist guards, per-entry yield scan, dry-run allocation plan (20% reserve, max_allocation_pct cap), broadcast execution
  • mcp-server tool handlers (was 21.9%) — intercepts server.tool() registrations via vi.mock and invokes handler bodies directly (defi_status / lending_rates / lending_supply / lending_withdraw / dex_quote / lp_positions)
  • lending supply/withdraw-collateral (lending.ts:289-350, was 53.28%) — Morpho-Blue happy paths + feature-detect error envelope for non-Morpho adapters
  • bridge LI.FI provider (bridge.ts:582-622, was 61.19% line / 9.09% branch) — native vs ERC20 approvals branch, approvalAddress fallback, quote/fetch errors
  • swap openocean ERC20 + agent schema — swap.ts native/non-native branch matrix + agent.ts handleSchema switch (price/bridge cases)

Test plan

  • pnpm -r build passes
  • pnpm -r lint (tsc --noEmit) passes across all 3 packages
  • pnpm -r test — 499 tests pass (defi-cli 303/303)
  • CI green on Node 20/22/24 matrix (this PR)

🤖 Generated with Claude Code

Hiksang and others added 6 commits May 17, 2026 07:04
Adds 14 unit tests for lp.ts:1595-1891 (autopilot handler), which was
the largest single uncovered handler in defi-cli at 31.82% line
coverage.

Coverage:
- budget / whitelist validation guards (process.exit intercepted)
- per-entry yield scan via mocked adapter constructors
  (createLending, createMerchantMoeLB, createKittenSwapFarming,
  createGauge)
- dry-run allocation plan: 20% reserve, max_allocation_pct cap,
  --chain filter, scan_error envelopes
- broadcast execution path: buildSupply + executor.execute for
  lending entries; warning + skip for lb/farming/gauge entries;
  unknown-chain rows skipped without aborting the loop

The new test program omits the root --chain option because the
autopilot subcommand defines its own --chain (filters the whitelist,
not the executor's target chain) and commander would otherwise let
the root flag consume the value before the subcommand sees it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 24 unit tests for mcp-server.ts tool handler bodies. The
pre-existing mcp-server.test.ts only covers the ok/err envelope
helpers + the server constructor; the 22 server.tool() handler
bodies sat at 21.9% line coverage.

Strategy: vi.mock @modelcontextprotocol/sdk/server/mcp.js to
intercept server.tool(name, desc, schema, handler) registrations
into a module-scoped Map, then invoke each handler directly with
the same params shape an MCP transport would deliver.

Covered handlers:
- defi_status (happy path + default chain + unknown-chain err)
- defi_lending_rates (rates round-trip + unknown-protocol err)
- defi_lending_supply (executor preview + on_behalf_of forwarding)
- defi_lending_withdraw (preview + unknown-chain err)
- defi_dex_quote (quote round-trip + unknown-chain err)
- defi_lp_positions (empty result + DEFI_WALLET_ADDRESS default +
  --protocol filter; the largest single uncovered handler at
  lines 1545-1663)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 7 unit tests for the Morpho-Blue-only supply-collateral and
withdraw-collateral subcommands at lending.ts:289-350. Both sat
uncovered at 53.28% line coverage because the existing
lending.test.ts stub didn't expose buildSupplyCollateral /
buildWithdrawCollateral.

Coverage:
- supply-collateral happy path: amount echoed through builder,
  --amount max maps to type(uint256).max, --on-behalf-of forwarding
- withdraw-collateral happy path: same shape, --amount max coverage
- feature-detection error envelope when the adapter doesn't expose
  the collateral builders (Aave V3 / Compound V2 etc) — uses
  vi.mocked.mockReturnValueOnce to swap in an Aave-shaped stub
  while keeping the default Morpho-shaped stub for happy-path tests

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 5 unit tests for the LI.FI provider path in bridge.ts:582-622
(the default --provider, which sat at 61.19% line / 9.09% branch
coverage because it requires a live HTTPS call to li.quest).

Strategy: mock globalThis.fetch with canned quote payloads (per the
swap.test.ts pattern) + wrap the Executor in a capture shim so the
DeFiTx + approvals[] the handler builds can be asserted. Executor's
dry-run result strips approvals[] from its return shape, so the
shim records the inbound tx for verification.

Coverage:
- native input (token = 0x0...0): empty approvals[], value forwarded
- ERC20 input: approvals[0] populated with quote.estimate.approvalAddress
- approvalAddress missing: spender falls through to transactionRequest.to
  (pins the ?? expression at bridge.ts:598)
- quote missing transactionRequest: "No LI.FI route found" envelope
- fetch throws: caught and surfaced as "LI.FI API error" envelope

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two related branch-coverage gaps from the 2026-05-17 sweep, bundled
because they're both single-purpose follow-ups:

1. swap-openocean.test.ts (3 tests) — covers swap.ts:331-365 branch
   matrix that the existing swap.test.ts left half-open:
   - ERC20 → ERC20 input populates approvals[0] with router as
     spender (covers swap.ts:364 — the non-native arm)
   - 0x-prefixed --from triggers the address-based registry lookup
     at swap.ts:331-332 (existing test only used symbol form)
   - 0xeeee... sentinel as --from omits approvals[] (covers the
     second arm of isNativeInput at swap.ts:356)

   Uses the same capturing-executor shim as bridge-lifi.test.ts.

2. agent.test.ts (15 tests) — walks every handleSchema switch case
   so a future schema change can't drop a route silently. The
   "price" (lines 214-222) and "bridge" (lines 224-234) cases were
   uncovered; agent.ts sat at 63.81% line / 14.28% branch coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous callTool helper returned { ok, payload, isError } where
`ok` was a plain boolean stripped from the discriminated union. That
made test code like `if (!ok) throw …` followed by `payload.data.x`
fail TypeScript narrowing — every `payload.data` access tripped
TS2339 ("Property 'data' does not exist on ErrEnvelope"). Local
vitest still passed (the runtime cast worked) but `tsc --noEmit`
(the lint step in CI) caught 14 errors.

Drop the redundant `ok` field; callers now use `payload.ok` directly,
which TypeScript recognises as the discriminator. Mechanical
replacements applied:
- const { ok, payload, ... } → const { payload, ... }
- expect(ok).toBe(true|false) → expect(payload.ok).toBe(…)
- if (!ok) throw → if (!payload.ok) throw
- if (ok) throw → if (payload.ok) throw

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Hiksang Hiksang marked this pull request as ready for review May 23, 2026 15:07
@Hiksang Hiksang requested review from USEONGEE and removed request for USEONGEE May 23, 2026 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant