Skip to content

test: round-2 coverage hardening (edge, runtime, generative consensus) + edge reconnect fix#9

Merged
naveed949 merged 15 commits into
mainfrom
claude/test-coverage-followups
Jun 23, 2026
Merged

test: round-2 coverage hardening (edge, runtime, generative consensus) + edge reconnect fix#9
naveed949 merged 15 commits into
mainfrom
claude/test-coverage-followups

Conversation

@naveed949

@naveed949 naveed949 commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up to #8, working through the "what's next" list. Built with parallel implementer + reviewer agents in isolated git worktrees (3 implementers, 3 reviewers), then integrated here. Suite grew 273 → 347 tests; coverage 90.4% stmt / 76.7% branch / 90.7% func / 92.3% line (up from 88.5/73.4/89.7/90.5).

What's included

  1. Edge stream-source internals (tests/edgeStreamSource.test.ts, 19 tests) — HttpStreamSource against a stub SSE server + EventSourceStreamSource via an injected double: frame parsing, split frames, malformed payloads, auth/non-200, all termination paths. httpStreamSource 75→100% stmt, eventSourceStreamSource 80→100% stmt.
    • Real bug found & fixed (src/edge/httpStreamSource.ts): an abrupt socket reset emits aborted/close (not end), which the source ignored — so EdgeReplica silently stalled instead of reconnecting. Now surfaced as a single onError via a streamDone/closed guard (all termination paths routed through one fail()).
  2. Runtime unit tests (tests/runtime/{canonical,shardRouter,keyedModule,projection}Unit.test.ts) — drove those four files to 100% statements and branches (from 76/74/78/64%).
  3. Generative consensus safety suite (tests/consensusProperties.test.ts) — seeded mulberry32 (no new deps) randomized command streams + leader-crash churn over LocalTransport clusters, asserting the Raft safety triple: state convergence, committed-log agreement up to the shared commit index, and replay determinism. Reproducible via SEED=<n>; verified to exercise real re-elections and that assertions are sensitive (not vacuous).
  4. CI: coverage emitted as lcov + uploaded as an artifact per Node leg; global threshold floor ratcheted to 88/74/88/90. Plus jest ignores .claude/ worktrees and .claude/ is git-ignored.
  5. Opt-in mutation testing (yarn test:mutation, Stryker) — dev-only, not in the CI gate, scoped to a small module by default (ADR-0024 documents the dev-only exception to ADR-0013). Initial canonical.ts run scored ~94%, surfacing a few survivable mutants — test-strength signal coverage alone can't give.

Reviewer notes addressed

  • Generative suite: tightened the submit-retry to only swallow NotLeaderError/'node stopped' (real regressions now surface), +timeout headroom.
  • Edge: routed auth/non-200 errors through the same single-fire guard.

Known gap (intentional)

The 2s READ_BARRIER_TIMEOUT branch remains unit-untested — lastApplied advances before apply(), so the only trigger is a racy partition of a lagging follower mid-ReadIndex. Forcing it cleanly needs a test-only src seam; adjacent fail-closed paths are already covered. Documented in docs/TEST_COVERAGE.md.

Test plan

tsc --noEmit clean; yarn test 347/347 green; yarn test:coverage passes thresholds on the new floor; yarn test:mutation runs Stryker (~94% on the scoped module). Generative suite verified stable across repeated runs.

🤖 Generated with Claude Code

claude added 15 commits June 22, 2026 19:13
Adds tests/consensusProperties.test.ts: a property-based suite that runs many
randomized iterations against a fresh LocalTransport book cluster (3- and
5-node), driving valid randomized book commands through the current leader and
periodically crashing/restarting nodes to force re-elections. After the cluster
heals it asserts the Raft safety invariants: state convergence across live
nodes, committed-log agreement up to the shared commit index, and determinism
(replaying the committed command sequence into a fresh state machine reproduces
the converged state).

Randomness is a hand-rolled seeded mulberry32 PRNG (no new deps); each iteration
prints its seed and SEED=<n> replays a single case for reproducibility.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_0159Rma834V7wPgft7fzoFtg
…ches

Add focused unit tests for under-tested runtime files, hitting their
uncovered error/edge branches without duplicating the existing suites:

- canonicalUnit: 'throw' vs 'drop' undefined modes, nested structures,
  key-order independence.
- shardRouterUnit: empty-shard guard, out-of-range RangeError,
  NoShardLeaderError rejection, collectMetrics with a leaderless shard
  (driven by fake ShardNode stubs, no clusters/timers).
- keyedModuleUnit: name/command validation, reserved __ names, strict
  vs { strict: false } lint paths.
- projectionUnit: defineProjection name/init/on/queries validation.

All four target files reach 100% stmt/branch/func/line coverage.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_0159Rma834V7wPgft7fzoFtg
Add explicit coverageReporters (text-summary + text + lcov) and a CI step that
uploads coverage/ per Node matrix leg, so reviewers can inspect per-PR coverage
(lcov.info + HTML report) without rerunning locally.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_0159Rma834V7wPgft7fzoFtg
Tooling parks git worktrees under .claude/, each a full copy of this suite;
without this, `jest` globs into them and multiplies test discovery.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_0159Rma834V7wPgft7fzoFtg
Claude Code parks agent worktrees and local state under .claude/; it should
never be committed.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_0159Rma834V7wPgft7fzoFtg
Add tests/edgeStreamSource.test.ts driving HttpStreamSource against a stub
Node http SSE server (snapshot->entry->caughtup ordering, multi-chunk frames,
malformed/comment/unknown frames, clean-end and abrupt-reset disconnects,
non-200/401-403 statuses, https routing, and the close() teardown contract),
plus EventSourceStreamSource via an injected in-memory EventSource double
(URL/token building, open/snapshot/entry/caughtup dispatch, bad-payload onError,
error self-close + suppression, close() idempotency, missing-ctor throw, and
global-EventSource fallback).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_0159Rma834V7wPgft7fzoFtg
HttpStreamSource only listened for res 'end' and req 'error'. An abrupt
mid-stream socket reset (RST / socket.destroy) makes Node emit 'aborted'/'close'
on the response WITHOUT 'end', so the drop was swallowed and the replica
silently stalled instead of reconnecting. Listen for 'aborted'/'close' too,
collapsing all termination paths to a single onError via a streamDone guard
(and a no-op after an intentional close()). Surfaced by the new
tests/edgeStreamSource.test.ts coverage; the abrupt-reset test now asserts the
single onError fires.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_0159Rma834V7wPgft7fzoFtg
Only retry NotLeaderError / 'node stopped' (the genuine churn transients) in
the submit loop; rethrow any other error so a real regression surfaces instead
of degrading into a low-commit run. Bump the per-test timeout to 45s for
slow-CI headroom.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_0159Rma834V7wPgft7fzoFtg
After the follow-up tests (edge stream sources, runtime units, generative
consensus suite), coverage is ~90.4/76.7/90.7/92.3. Raise the global floor to
88/74/88/90 to lock the gains in. Documents why a single global bucket is used
rather than per-path floors (jest subtracts glob-matched files from global).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_0159Rma834V7wPgft7fzoFtg
Per review: send the 401/403 and non-200 onError calls through fail() too, so
"exactly one terminal onError ever" is a single uniform invariant (and a late
close on the drained response can't double-fire). Note the aborted/close
redundancy is intentional belt-and-suspenders across Node versions.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_0159Rma834V7wPgft7fzoFtg
Dev-only, not in the default test/CI gate: `yarn test:mutation` runs Stryker
scoped (via coverageAnalysis perTest) to a small pure module by default, reusing
jest.config.js. Measures test effectiveness, not just reach — the initial
canonical.ts run scored ~94% and surfaced a few survivable mutants in the
'drop'-mode branch. ADR-0024 documents the dev-only exception to ADR-0013;
Stryker output is git-ignored.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_0159Rma834V7wPgft7fzoFtg
@naveed949
naveed949 marked this pull request as ready for review June 23, 2026 04:51
@naveed949
naveed949 merged commit 20ba1ee into main Jun 23, 2026
2 checks passed
@naveed949
naveed949 deleted the claude/test-coverage-followups branch June 23, 2026 05:54
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.

2 participants