Testing & Code Coverage
Test Execution
Run the complete local suite through the same entrypoints used by CI:
just test
Use just test-fast for the complete non-node gate, including non-node
integration targets, or just test-sandbox for the node-backed gate. The sandbox recipe prebuilds NEAR contracts before
starting its pooled neard instances; pass --stale to reuse the contracts already built into target/near.
Run the artifact drift check separately when validating the release catalog — pure in-memory invariant checks, no builds:
./script/check-artifact-drift.sh
Local Testing
Running Tests with Coverage
# Generate and open HTML coverage for the fast library-test cut
just coverage
# Generate coverage.lcov
just coverage-lcov
Test Categories
- Unit tests: Module-level functionality
- Integration tests: Cross-module interactions
- Contract tests: Smart contract behavior
- End-to-end tests: Full workflow validation
Performance Testing
Gas Usage Analysis
Gas usage analysis is available through existing tools:
./script/gas-report.sh
This generates a gas report for market operations, including average gas costs for individual operations and snapshot iteration limits.
Test-Gate Timing
The node gate is the slowest thing we run, so its cost is measured rather than guessed. Two tools:
# Time the harness primitives on a dedicated neard: block-latency floor,
# per-transaction and per-patch costs, fixture setup.
just bench-sandbox
# Compare two whole-suite runs. `just test-sandbox` writes per-test timings to
# target/nextest/sandbox/junit.xml (see [profile.sandbox.junit] in
# .config/nextest.toml); copy it aside before and after a change.
./script/bench/junit-diff.py before.xml after.xml
junit-diff.py totals are summed per-test durations. The gate runs several
tests concurrently, so those intervals overlap: the totals measure work done,
not gate wall clock. Its per-test breakdown is the point — a change that halves
the suite can still make one test much worse.
What the measurements established, so it need not be re-derived:
- Node round-trips dominate; WASM payload is ~free. 570KB of extra contract
code adds ~18ms to a deploy. Installing contract code via
sandbox_patch_stateworks but is not faster, and it forfeits batching deploy+init into one transaction. Same verdict rules out global contracts. Don't retry either. sandbox_patch_statecosts ~200ms per call, not per record. Minting N accounts in one patch therefore costs roughly what minting one does — hence the harness's batchedcreate_accounts.- Block production delay is the other lever, and it is local-only. See the sandbox cadence note below.
Sandbox Block Cadence
Locally the harness runs neard at a 40ms min_block_production_delay instead
of the stock 120ms, which roughly halves the node gate. CI pins the stock 120ms
(NEAR_SANDBOX_BLOCK_MS in .github/workflows/test.yml): a 4-vCPU runner cannot
sustain four nodes producing blocks 3× as often, and attempting it caused
widespread transaction-finality failures. Reducing parallelism to compensate was
measured and is slower than stock.
Override the cadence with NEAR_SANDBOX_BLOCK_MS=<ms>. max_block_production_delay
is compensated in the opposite direction so that avg(min, max) stays fixed at
310ms — nearcore credits a sandbox_fast_forward as
delta_height × avg(min, max), so holding that average keeps every
time-sensitive test's simulated time unchanged whatever the real cadence.
Because local blocks are faster than CI's, a test that leans on incidental
block cadence to cross a time boundary will pass in one place and fail in the
other. Advance chain time explicitly with fast_forward rather than relying on
how long some operations happen to take.