Skip to content

Add unit tests for deploy.ts and compiler utilities with Vitest #79

Description

@Pvsaint

Summary

The test suite currently has no coverage of the critical paths that matter most: the multi-step deployContract() pipeline, the Soroban simulation flow, the full compile-to-WASM codegen pipeline, and the graph persistence/import/export layer. This issue defines and implements a comprehensive test coverage plan across all four layers with mock strategies, edge case coverage, and a coverage gate enforced in CI.

Why This Is Hard

  • Mocking the Stellar SDK: deployContract() calls SorobanRpc.Server, Horizon.Server, TransactionBuilder, and the Freighter API across three async stages. Setting up correct mocks that simulate both the happy path and the five failure modes (wallet rejection, simulation error, upload collision, tx poll timeout, chain failure) requires careful dependency injection design.
  • Codegen correctness is combinatorial: generateContractSource has 6 block types × 3 param combinations × 2 execution order variants. Testing all meaningful combinations without writing 50 copy-paste test cases requires parameterised test factories.
  • The compile/validate pipeline has subtle invariants: validateContractGraph has 5 validation passes. Each pass can fail independently or in combination. Tests must cover single-pass failures, multi-pass failures, and valid graphs that must not regress.
  • Coverage gate: the CI pipeline must fail if coverage drops below 80% on the src/lib/compile/ and src/lib/stellar/ directories. Setting this up correctly in Vitest requires per-directory thresholds in vitest.config.ts, which is non-trivial configuration.
  • Test isolation: localStorage is shared across tests. Graph persistence tests must reset state in beforeEach to avoid flaky cross-test contamination.

Test Plan by Module

src/lib/stellar/deploy.ts

Test Scenario
connectWallet returns public key on success Happy path
connectWallet throws on extension rejection User denies
connectWallet throws on missing extension Not installed
fetchWalletBalance returns native XLM balance Happy path
fetchWalletBalance throws on missing native balance No XLM account
compileContract sends correct payload and returns wasm Happy path
compileContract throws CompileContractError on API error Compile fail
deployContract calls wallet → compile → upload → instantiate in order Full pipeline
deployContract skips upload when "ExistingValue" error WASM already uploaded
deployContract returns contract ID from createSim retval ID from simulation
deployContract falls back to hash-derived ID when retval absent Fallback ID

src/lib/stellar/simulate.ts

Test Scenario
inferArgsFromGraph returns correct args for Transfer graph Transfer nodes
inferArgsFromGraph returns correct args for Storage+Condition graph Storage/Condition
inferArgsFromGraph returns input fallback for empty graph No typed nodes
simulateContract serialises graph and returns result Happy path

src/lib/compile/codegen.ts

Test Scenario
generateContractSource emits require_auth for Auth node Auth block
generateContractSource emits token::Client::new for Transfer node Transfer block
generateContractSource emits env.storage().instance().set for Storage node Storage write
generateContractSource emits env.events().publish for Event node Event block
generateContractSource emits structured if !() for Condition with expression Condition block
generateContractSource emits legacy if !release for Condition without expression Condition legacy
generateContractSource includes Address in imports when Auth present Import derivation
generateContractSource includes token in imports when Transfer present Import derivation
getExecutionOrder returns nodes in BFS order from Start Ordering
getExecutionOrder skips unreachable nodes Reachability

src/lib/compile/validate.ts

Test Scenario
validateContractGraph rejects payload over MAX_GRAPH_BYTES Size limit
validateContractGraph rejects missing nodes array Invalid payload
validateContractGraph rejects duplicate node IDs Duplicate IDs
validateContractGraph rejects edge referencing unknown node Dangling edge
validateContractGraph rejects graph with no Start node Missing start
validateContractGraph rejects graph with two Start nodes Multiple starts
validateGraphStructure rejects graph with no reachable executable blocks No logic
validateConditionExpression rejects invalid operator Bad operator
validateConditionExpression rejects empty left operand Incomplete expr

src/lib/editor/graphPersistence.ts

Test Scenario
saveGraphToStorage + loadGraphFromStorage round-trip Persist & restore
loadGraphFromStorage returns null when storage empty Cold start
parseImportedGraphJson accepts valid graph JSON Import success
parseImportedGraphJson rejects malformed JSON Import fail
downloadGraphJson triggers browser download with correct filename Export

Coverage Gate

vitest.config.ts must set:

coverage: {
  thresholds: {
    "src/lib/compile/": { lines: 80, functions: 80 },
    "src/lib/stellar/": { lines: 80, functions: 80 },
    "src/lib/editor/": { lines: 75, functions: 75 },
  }
}

CI fails if any threshold is not met.

Acceptance Criteria

  • All test cases in the table above are implemented and passing.
  • vi.mock("@stellar/freighter-api", ...) is used for all wallet-dependent tests — no real extension calls.
  • SorobanRpc.Server and Horizon.Server are mocked via vi.mock("@stellar/stellar-sdk", ...).
  • localStorage is cleared in beforeEach for all graph persistence tests.
  • Coverage thresholds are configured in vitest.config.ts and enforced in CI.
  • npm run test:coverage runs and produces a coverage report.
  • No test uses setTimeout with real delays — async resolution is mocked.
  • All 30+ test cases are parameterised where possible (no copy-paste test bodies).
  • All existing tests continue to pass and the CI pipeline remains green.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions