test(e2e): webhook coverage for reorgs, mempool, and heartbeats - #511
Open
bkeroack wants to merge 1 commit into
Open
test(e2e): webhook coverage for reorgs, mempool, and heartbeats#511bkeroack wants to merge 1 commit into
bkeroack wants to merge 1 commit into
Conversation
The webhook E2E suite drove only `status` and `chain`, and within `chain` only forward block connection. Three categories of the dispatcher's behavior had never been exercised over a socket: - **Reorgs.** Reorg notification is why the alert dispatcher exists (it absorbed `reorgwebhook=`), and it is the one alert an operator cannot reconstruct after the fact: by the time anything polls, the abandoned blocks are off the active chain. The new test invalidates a block two deep and asserts the fork-point marker, one disconnect per rolled-back block, and the one-shot `deep_reorg` status edge with the true depth -- all signed, all to one hook subscribing both categories. - **Mempool.** No test had ever delivered a `mempool` event. The new one covers admission and confirmation of a real spend, and asserts the negative that matters more: a mempool-only hook received none of the 102 blocks connected during the test. The category mask is what stands between an operator's pager and per-transaction volume, and it was only ever asserted against an in-process publisher. - **Heartbeats.** A dead-man's switch that silently never fires is indistinguishable from a healthy node until it is needed. Downsampling is asserted on the spacing of consecutive signing timestamps rather than on a delivery count: the filter compares whole seconds, so at a 1s setting the 1 Hz bus loses roughly every other beat to truncation and any count-based bound admits both 1s and 2s. Every assertion was negative-verified by disabling the behavior it covers and observing the specific failure: `alertreorgdepth=0` leaves the marker and disconnects arriving but no `deep_reorg`; adding `chain` to the mempool hook reports 102 off-category deliveries; a 1s heartbeat interval reports consecutive deliveries 1s apart. `broadcast_spend` becomes `pub(crate)` so the mempool test reuses the canonical block-1-coinbase spend rather than open-coding one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GJqZbJtTUo7K7G9wJcrvHv
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The webhook E2E suite drove only
statusandchain, and withinchainonly forward block connection. Three categories of dispatcher behavior had never been exercised over a socket. This adds one test each.Not stacked — branches off
master, no dependents.What was missing
Reorgs. Reorg notification is why the alert dispatcher exists (it absorbed
reorgwebhook=), and it is the one alert an operator cannot reconstruct after the fact: by the time anything polls, the abandoned blocks are off the active chain.a_reorg_delivers_the_disconnects_and_a_deep_reorg_edgeinvalidates a block two deep and asserts the fork-point marker (from_height3 →to_height1), oneblock_disconnectedper rolled-back block, and the one-shotdeep_reorgstatus edge carrying the true depth — all signed, all to a single hook subscribing both categories.Mempool. No test had ever delivered a
mempoolevent.a_mempool_hook_sees_admission_and_confirmation_and_nothing_elsecoversenterandleave_confirmedfor a real spend, and asserts the negative that matters more: a mempool-only hook received none of the 102 blocks connected during the test. The category mask is what stands between an operator's pager and per-transaction volume, and it had only ever been asserted against an in-process publisher.Heartbeats. A dead-man's switch that silently never fires is indistinguishable from a healthy node until it is needed.
heartbeats_reach_a_hook_downsampled_to_its_intervalasserts pings keep arriving and that they are downsampled — on the spacing of consecutive signing timestamps, not on a delivery count. The filter compares whole seconds (as_secs() >= interval), so at a 1s setting the 1 Hz bus loses roughly every other beat to truncation; a count-based bound admits both 1s and 2s and would have passed regardless of configuration. My first draft had exactly that bug.Negative verification
Each assertion was shown failing before being trusted:
--alertreorgdepth=0deep_reorgnever doeschaina mempool-only hook received 102 off-category deliveriesheartbeat_interval_secs = 1consecutive deliveries 1s apartAlso
broadcast_spendinstreaming.rsbecomespub(crate)so the mempool test reuses the canonical block-1-coinbase spend.write_alertfile_keys/node_with_hook_keyslet a test set per-hook TOML keys (heartbeat_interval_secs); the existingwrite_alertfiledelegates and its output is byte-identical.quiet_detectors()silences the self-scheduled detectors, so a test asserting on which events arrived isn't interrupted bydisk_lowraising at startup on a host under 10 GiB free.Tests only — no production code, no user-facing change, so no changelog entry.
Verification
cargo clippy --all-targets --all-features --locked -- -D warnings— cleancargo test --all --locked— passcargo test --test e2e --locked --features e2e—118 passed; 0 failed(115 before)🤖 Generated with Claude Code
https://claude.ai/code/session_01GJqZbJtTUo7K7G9wJcrvHv