Skip to content

refactor(chain): tidy ChainService and rework its test suite#825

Merged
tcoratger merged 1 commit into
leanEthereum:mainfrom
tcoratger:refactor/chain-service-and-tests
Jun 2, 2026
Merged

refactor(chain): tidy ChainService and rework its test suite#825
tcoratger merged 1 commit into
leanEthereum:mainfrom
tcoratger:refactor/chain-service-and-tests

Conversation

@tcoratger
Copy link
Copy Markdown
Collaborator

Summary

Cleans up the consensus clock driver (ChainService) and replaces its mock-heavy test suite with a clearer, real-object suite that covers the full scenario spread.

Service (src/lean_spec/node/chain/service.py)

  • Reuse SlotClock's own pre-genesis wait (sleep_until_next_interval) instead of recomputing the wait by hand — one home for that timing, and it no longer truncates the fractional remainder.
  • Remove the redundant no-op store reassignment in the skip-ahead path (the real Store mutates in place), and drop the needless int() cast around INTERVALS_PER_SLOT.
  • Add a forward-only precondition (assert target_interval >= store.time) documenting the invariant the catch-up loop relies on, plus a note that attestation acceptance for jumped-over slots defers safely to the final slot's tick.
  • Rewrite docstrings/comments to the repo documentation rules (one sentence per line, no identifier names in prose, WHY over what).

Tests (tests/lean_spec/node/chain/test_service.py)

  • Drop the MockStore / MockCheckpoint / StoreInterceptingSpec scaffolding in favor of a real Store plus a thin recording spec (ProbeSpec) and a 3-field SyncServiceStub naming exactly what the driver touches.
  • Remove three tests that exercised SlotClock.sleep_until_next_interval rather than ChainService — already covered in test_clock.py (test-mirrors-source rule).
  • Call the catch-up helpers (_tick_to, _initial_tick) directly where they return; only drive the infinite run() loop in the handful of tests where the loop itself is the subject.
  • Cover the scenario spread with parametrized cases: no-op (target == now), within-slot, exact slot boundary, just-over, deep skip (4 and 20 slots); plus edge cases — the backward-target precondition, and a store swapped in during the yield (verifies the post-yield re-read).

Verification

  • 22 tests, 100% line and branch coverage of service.py.
  • The two new guard tests are mutation-checked: removing the post-yield re-read or the precondition assert makes the targeted test fail.
  • just check passes (lint, format, type check, spell, mdformat).

🤖 Generated with Claude Code

Service:
- Reuse the clock's own pre-genesis wait instead of recomputing it by hand.
- Drop the redundant no-op store reassignment in the skip-ahead path, and
  the needless int() cast around INTERVALS_PER_SLOT.
- Add a forward-only precondition (target >= store.time) and a note that
  attestation acceptance for jumped slots defers safely to the final tick.
- Rewrite docstrings and comments to the repo documentation rules.

Tests:
- Replace the mock-heavy suite (MockStore/MockCheckpoint/StoreInterceptingSpec)
  with a real Store plus a thin recording spec; the clock-timing tests that
  belonged to SlotClock are dropped (already covered in test_clock.py).
- Test the catch-up helpers directly where they return, and drive the run loop
  only where the loop itself is under test.
- Cover the full scenario spread with parametrized cases: no-op, within-slot,
  slot boundary, just-over, deep skip; plus the backward-target precondition
  and a store swapped in during the yield (the post-yield re-read).
- 22 tests, 100% line and branch coverage of service.py.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tcoratger tcoratger merged commit 5eef395 into leanEthereum:main Jun 2, 2026
12 of 13 checks passed
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