Summary
src/lean_spec/subspecs/networking/service/service.py has 64% test coverage — no test file exists. This module is the bridge between the network layer and the sync service, routing gossip events (blocks, attestations, peer status) and publishing locally-produced messages to peers.
Coverage stats:
| File |
Statements |
Covered |
Coverage |
networking/service/service.py |
80 |
28 uncovered |
64% |
Uncovered lines: 109, 113–118, 160, 169–185, 201, 213–218, 233–238, 249–254
What needs testing
NetworkService — Event routing
run() (async main loop)
- Normal event loop: Feed events through a mock
EventSource, verify each is dispatched
- Stop flag: Call
stop() mid-loop, verify the loop exits cleanly
- Source exhaustion:
EventSource raises StopAsyncIteration, verify graceful termination
- Running state:
is_running transitions correctly during run/stop lifecycle
_handle_event() — Pattern matching dispatch
GossipBlockEvent: Verify sync_service.on_gossip_block() is called with correct args
GossipAttestationEvent: Verify sync_service.on_gossip_attestation() is called
GossipAggregatedAttestationEvent: Verify sync_service.on_gossip_aggregated_attestation() is called
PeerStatusEvent: Verify sync_service.on_peer_status() is called
PeerConnectedEvent: Verify peer is added to peer_manager and count logged
PeerDisconnectedEvent: Verify peer is removed from peer_manager
publish_block()
- Happy path: Verify block is SSZ-encoded, snappy-compressed, and published to correct gossip topic
- Topic format: Verify
GossipTopic.block(fork_digest) produces correct topic ID
publish_attestation()
- Happy path: Verify attestation published to correct subnet topic
- Subnet routing: Different
SubnetId values produce different topic IDs
publish_aggregated_attestation()
- Happy path: Verify aggregated attestation published to committee aggregation topic
stop() / is_running
- Lifecycle: Verify running flag transitions
- Thread safety:
stop() can be called before run() without error
Why this matters
- Event routing correctness: If events are misrouted, blocks might be dropped or attestations lost
- Publishing reliability: Incorrectly encoded or compressed messages will be rejected by peers
- Topic correctness: Publishing to wrong gossip topics means messages never reach subscribers
How to test
Running tests with coverage
uv run pytest tests/lean_spec/subspecs/networking/service/test_service.py -v \
--cov=src/lean_spec/subspecs/networking/service/service --cov-report=term-missing
Target: ≥90% line coverage.
Verifying with the coverage gate
Test file location
tests/lean_spec/subspecs/networking/service/test_service.py
Testing tips
- Use a mock
EventSource that yields predefined events (async iterator)
- Mock
SyncService to verify dispatch calls without needing real forkchoice logic
- Mock
snappy.compress and encode_bytes() to test publish methods without real SSZ
- Use
pytest-asyncio for async tests
Using Claude Code subagents
1. code-tester agent — Generate the tests
Generate unit tests for src/lean_spec/subspecs/networking/service/service.py. Cover the event routing dispatch for all 6 event types, the run() loop lifecycle (stop, source exhaustion), and all three publish methods (block, attestation, aggregated attestation). Use mock EventSource and SyncService.
2. doc-writer agent — Document the test module
Add documentation to the network service test file explaining the testing strategy for event dispatch and publish encoding.
Workflow
- Create mock
EventSource and SyncService
- Test each event type dispatch independently
- Test publish methods with mocked encoding
- Test run() lifecycle (start, stop, source exhaustion)
- Run
uvx tox -e all-checks to pass all quality checks
Summary
src/lean_spec/subspecs/networking/service/service.pyhas 64% test coverage — no test file exists. This module is the bridge between the network layer and the sync service, routing gossip events (blocks, attestations, peer status) and publishing locally-produced messages to peers.Coverage stats:
networking/service/service.pyUncovered lines: 109, 113–118, 160, 169–185, 201, 213–218, 233–238, 249–254
What needs testing
NetworkService— Event routingrun()(async main loop)EventSource, verify each is dispatchedstop()mid-loop, verify the loop exits cleanlyEventSourceraisesStopAsyncIteration, verify graceful terminationis_runningtransitions correctly during run/stop lifecycle_handle_event()— Pattern matching dispatchGossipBlockEvent: Verifysync_service.on_gossip_block()is called with correct argsGossipAttestationEvent: Verifysync_service.on_gossip_attestation()is calledGossipAggregatedAttestationEvent: Verifysync_service.on_gossip_aggregated_attestation()is calledPeerStatusEvent: Verifysync_service.on_peer_status()is calledPeerConnectedEvent: Verify peer is added topeer_managerand count loggedPeerDisconnectedEvent: Verify peer is removed frompeer_managerpublish_block()GossipTopic.block(fork_digest)produces correct topic IDpublish_attestation()SubnetIdvalues produce different topic IDspublish_aggregated_attestation()stop()/is_runningstop()can be called beforerun()without errorWhy this matters
How to test
Running tests with coverage
Target: ≥90% line coverage.
Verifying with the coverage gate
Test file location
Testing tips
EventSourcethat yields predefined events (async iterator)SyncServiceto verify dispatch calls without needing real forkchoice logicsnappy.compressandencode_bytes()to test publish methods without real SSZpytest-asynciofor async testsUsing Claude Code subagents
1.
code-testeragent — Generate the tests2.
doc-writeragent — Document the test moduleWorkflow
EventSourceandSyncServiceuvx tox -e all-checksto pass all quality checks