Skip to content

Test coverage: gate + nightly live tests for devnet/mainnet/mesa (#181) - #199

Open
dkijania wants to merge 2 commits into
mainfrom
test/coverage-and-live-networks
Open

Test coverage: gate + nightly live tests for devnet/mainnet/mesa (#181)#199
dkijania wants to merge 2 commits into
mainfrom
test/coverage-and-live-networks

Conversation

@dkijania

Copy link
Copy Markdown
Contributor

What & why

Part of the production-readiness epic (#163). Closes #181.

Two gaps from the test-coverage audit: no coverage threshold in CI, and no live integration tests against real per-network data (devnet existed only as a static dump; mainnet was a single skipped endpoint; mesa had none).

Changes

  • Coverage gatetest:coverage now runs c8 with --check-coverage --lines 50 --functions 50 --statements 50 --branches 80. Current coverage (~53% lines, 95% branches) clears it; the floor blocks regressions and can be raised as the suite grows (this epic adds ~10 test files).
  • test:live-api script — runs the live-api suite against STAGING_GRAPHQL_ENDPOINT.
  • Live Integration workflow — nightly + manual; runs live-api against devnet/mainnet/mesa using per-network repo variables (DEVNET_/MAINNET_/MESA_ARCHIVE_API_URL). A network with no configured endpoint is skipped, so the job is green until the maintainer sets the URLs; fail-fast: false keeps networks independent.
  • Docs — live-api README documents the setup and the remaining fixture work.

Honest remaining work (documented, not silently skipped)

  • The maintainer must set the *_ARCHIVE_API_URL repo variables for the nightly runs to actually exercise each network.
  • actions.test.ts is still a placeholder and a successful-zkappCommands fixture is still needed — both require known mainnet/mesa zkApps to snapshot against. Noted in the README.

Testing

  • npm run test:coverage → passes the gate locally (exit 0)
  • npm run lint / npx prettier --debug-check . — clean
  • Unit suite green

🤖 Generated with Claude Code

…mesa

Two gaps from the test-coverage audit (#181): no coverage threshold in CI, and
no live integration tests against real per-network data (devnet existed only as
a static dump; mainnet was one skipped endpoint; mesa had none).

- Add a coverage gate to `test:coverage` via c8 `--check-coverage` (lines 50,
  functions 50, statements 50, branches 80). Current coverage (~53% lines, 95%
  branches) clears it; the floor blocks regressions and rises as the suite grows.
- Add a `test:live-api` script that runs the live-api suite against
  `STAGING_GRAPHQL_ENDPOINT`.
- Add the `Live Integration` workflow: nightly + manual, runs the live-api suite
  against devnet/mainnet/mesa using per-network repo variables
  (`*_ARCHIVE_API_URL`), skipping any network without a configured endpoint so it
  stays green until they're set. `fail-fast: false` keeps networks independent.
- Document the setup and the remaining `actions`/successful-`zkappCommands`
  fixture work in the live-api README.

Closes #181.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QSuak9smCHbp4N17xjjLF6
@dkijania dkijania added enhancement New feature or request production-readiness Work toward making the API production-ready / publicly available labels Jun 29, 2026
@SanabriaRusso

SanabriaRusso commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Thanks for pushing coverage forward here — the design is solid: scheduled-only (never on PRs), fail-fast: false, endpoints via repo vars with skip-until-set, and read-only. I ran the new gate locally and it clears comfortably (stmts 53.3 / funcs 53.2 / lines 53.3 / branch 95.5 vs the 50/50/50/80 floors, exit 0), so it won't block the production-readiness train.

One heads-up before the *_ARCHIVE_API_URL variables get set: test:live-api runs the entire tests/live-api/* suite against all three networks, and the two events.test.ts "Canonical Chain" cases assert deepStrictEqual against network-specific snapshots — fixtures/…_65_66.json (address B62qp…J1fu, blocks 433465–433466) and …_84_83.json (429984–439983). Those fixtures capture one network's stateHash / parentHash / eventData, so on the other two networks the same block heights return different data and the assertions go deterministically red — which would look like drift when it isn't.

Since the goal is catching real per-network drift, it'd help to gate the fixture-pinned events cases to their origin network while running network-agnostic shape/relationship checks (the blocks.test.ts style) everywhere, e.g. pass the matrix network into the step:

env:
  STAGING_GRAPHQL_ENDPOINT: ${{ steps.endpoint.outputs.url }}
  LIVE_API_NETWORK: ${{ matrix.network }}
// events.test.ts
const FIXTURE_NETWORK = 'mainnet'; // wherever the J1fu fixtures were captured
const canonical = process.env.LIVE_API_NETWORK === FIXTURE_NETWORK ? describe : describe.skip;
canonical('Canonical Chain', () => { /* the deepStrictEqual cases */ });

To be clear, networkState.test.ts's pendingMax − canonicalMax === 290 is not network-specific and is fine as-is — that's the protocol-wide finality depth k = 290 (the test documents it exactly that way), so it holds on every Mina network and is precisely the kind of cross-network invariant worth keeping. The only thing to gate is the fixture-pinned events data.

All nightly-only, so nothing blocks merges today — just flagging it so turning on the endpoints doesn't immediately light up part of the matrix. Thanks again for closing the coverage gap!

…nnet

Two problems that would have surfaced the moment the *_ARCHIVE_API_URL
variables were set.

The fixtures never reached the test. events.test.ts read them relative to
__dirname, but the suite runs compiled out of build/ and tsc emits only
JavaScript — the .json files stay in the source tree. So the read threw
ENOENT at module load and took the whole file down before any test ran,
on every network, not just two. Fixtures now resolve from the source tree
via process.cwd(), matching tests/integration/setup.ts.

The fixture-pinned cases only hold on one network. They deepStrictEqual
against a captured stateHash / parentHash / eventData at fixed heights,
so elsewhere they fail deterministically — drift-looking failures that
aren't drift. Confirmed against the live networks: at blocks 433465–433466
this address returns exactly the fixture data on mainnet and nothing at
all on devnet and mesa, so the fixtures are mainnet. They now run only
there, with the matrix network passed in as LIVE_API_NETWORK; when it is
unset (local runs) they still run, preserving current behaviour.

Verified end-to-end: the cases skip cleanly under LIVE_API_NETWORK=devnet
and both pass against a live mainnet archive endpoint.

Only the fixture-pinned data is gated. networkState.test.ts's
pendingMax − canonicalMax === 290 is the protocol-wide finality depth and
holds on every network, so it keeps running everywhere — that
cross-network invariant is the point of the matrix.

Addresses review feedback on #199.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dkijania

Copy link
Copy Markdown
Contributor Author

Thanks @SanabriaRusso — fixed in 1d6607b, and you were right on both counts. Investigating it turned up a second, larger problem sitting underneath.

Your finding, confirmed empirically. Rather than assume where the J1fu fixtures came from, I queried all three live networks for the fixture's exact block range:

network events(address: B62qp…J1fu, from: 433465, to: 433466)
mainnet returns the fixture's exact stateHash 3NLrHxJXQbR3AHRjUMfLz1iy96xt1CDouJLFarkQfUKvodybLhJ5, parentHash, tx hash, and eventData
devnet []
mesa []

So the fixtures are mainnet, and on the other two the deepStrictEqual compares live-empty against mainnet data — deterministically red, exactly as you said. They're now gated to FIXTURE_NETWORK = 'mainnet' with the matrix network passed in as LIVE_API_NETWORK, per your suggestion. When it's unset (local runs) they still run, so nobody pointing the suite at mainnet by hand loses coverage.

The bigger one: the fixtures never loaded at all. events.test.ts read them relative to __dirname, but test:live-api runs compiled out of build/ and tsc emits only JavaScript — the .json files stay in the source tree. So the read threw ENOENT at module load, taking the whole file down before a single test ran:

Error: ENOENT: no such file or directory, open
  '…/build/tests/live-api/fixtures/B62qpHtWX41NstxzzUe8xooKogqomDwgJ4CN8J3V2274v5B9dnfJ1fu_65_66.json'

That's worse than the 2-of-3 you predicted — the events suite would have failed on all three networks, mainnet included, and as a module-load crash rather than an assertion, so the reason wouldn't have been obvious. Fixtures now resolve from the source tree via process.cwd(), matching the existing pattern in tests/integration/setup.ts.

Verified end-to-end, since a fix here is only worth as much as its proof:

  • LIVE_API_NETWORK=devnet → module loads, both fixture suites report # SKIP, nothing red.
  • LIVE_API_NETWORK=mainnet against a live mainnet archive endpoint → both cases run and pass.

And +1 on your k = 290 point — agreed, networkState.test.ts is protocol-wide finality depth, not network-specific. Left running everywhere; that cross-network invariant is the whole point of the matrix. Only the fixture-pinned data is gated.

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

Labels

enhancement New feature or request production-readiness Work toward making the API production-ready / publicly available

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test coverage: audit + add live integration tests for devnet / mainnet / mesa

2 participants