Skip to content

test(realtime): executing test for the issue #23 body-cap fallback wiring (Closes #145)#146

Merged
dzhelezov merged 1 commit into
mainfrom
test/145-realtime-fallback-e2e
Jul 10, 2026
Merged

test(realtime): executing test for the issue #23 body-cap fallback wiring (Closes #145)#146
dzhelezov merged 1 commit into
mainfrom
test/145-realtime-fallback-e2e

Conversation

@dzhelezov

Copy link
Copy Markdown
Collaborator

Closes #145.

Problem

The issue #23 fix (PR #144) added a filtered-eth_getLogs fallback for the realtime block-data path when the unfiltered full-block eth_getLogs exceeds viem's ~10 MiB response-body cap. Its regression test (portal/realtime-getlogs-fallback.test.ts) covers only the extracted helpers (buildFilteredBlockLogRequests, eth_getFilteredBlockLogs, isResponseBodyTooLargeError, shouldRetry, validateLogsAndBlock). The wiring that assembles them inside createRealtimeSync's private fetchBlockEventData closure — the logsPromise.catch and the usedFilteredLogsFallback === false || logs.length > 0 guard — was proven only by inspection, never executed by a test.

Approach

portal/realtime-fallback-e2e.test.ts drives the exposed createRealtimeSync(...).sync(headBlock, blockCallback) async generator over one new head block, exercising fetchBlockEventData end-to-end. args.rpc.request is mocked to route by method and by whether an eth_getLogs call is the unfiltered full-block fetch (params carry no topics) or a filtered fallback fetch (params carry topics): the unfiltered fetch throws a real-shaped ResponseBodyTooLargeError (matched by name), and the filtered fetch returns controlled logs. The head block is passed pre-fetched (with transactions), so only the logs leg touches the RPC — keeping the mock surface to eth_getLogs alone. No anvil / no network.

Two assertions

  • (A) non-empty fallback — the .catch fires on the oversized error, recovers via the filtered fallback, and the block is ingested (a block event is yielded) carrying the assembled log. Also asserts the unfiltered fetch was attempted before the filtered one.
  • (B) empty fallback — a block with a non-empty logsBloom (built as the exact inverse of isInBloom so the filter's topic0 is present, forcing shouldRequestLogs) whose filtered fallback returns [] is still ingested, instead of throwing "logs array has length 0". This pins that validateLogsAndBlock is skipped only on the empty-fallback path.

Mutation verification

Applied to the patched src/sync-realtime/index.ts in the built tree:

  • M1 — make the .catch rethrow unconditionally (delete the fallback): both (A) and (B) FAIL — the oversized error propagates, no block is ingested.
  • M2 — drop the usedFilteredLogsFallback === false || guard so validateLogsAndBlock always runs: (B) FAILS (throws on 0 logs vs non-empty bloom) while (A) stays GREEN — confirming the guard is specifically load-bearing for the empty-fallback case, not incidentally covered.

Both restored → GREEN. Gates green on both pinned versions (0.16.8 and 0.16.6): 328 tests / 18 files each. Biome clean (error level).

Draft pending independent review.

🤖 Generated with Claude Code

…cap fallback wiring at runtime (#145)

The existing regression (realtime-getlogs-fallback.test.ts) covers only the
extracted helpers. This adds an executing test that drives the exposed
createRealtimeSync `sync(headBlock, blockCallback)` generator over one new head
block, exercising the private `fetchBlockEventData` closure end-to-end:

  (A) a ResponseBodyTooLargeError from the unfiltered full-block eth_getLogs is
      caught, recovered via the filtered per-filter fallback, and the block is
      ingested with the assembled logs.
  (B) the `usedFilteredLogsFallback === false || logs.length > 0` guard makes
      validateLogsAndBlock skip happen ONLY on the empty-fallback path (a
      non-empty logsBloom block whose filtered fallback returns zero logs is
      still ingested rather than throwing "logs array has length 0").

Mutation-verified: deleting the .catch fallback turns both RED; dropping the
empty-fallback guard turns (B) RED while (A) stays GREEN.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dzhelezov

Copy link
Copy Markdown
Collaborator Author

Independently reproduced the mutation-verification before merge (both against the 0.16.8 wiring):

  • M1 — disable the .catch fallback so the oversized-body error rethrows on the unfiltered full-block eth_getLogs: both new tests RED (expected undefined not to be undefined — no block event yielded), the other 326 green.
  • M2 — neuter the empty-fallback guard so validateLogsAndBlock always runs: only the empty-fallback test RED, the recovery test stays green — confirming the usedFilteredLogsFallback guard is load-bearing specifically on the empty path (and that the two tests are non-vacuous and independent).

Both-version sync-upstream.sh {0.16.8,0.16.6} --test green (328/328) and fork-seam CI green on all four pinned versions. Test-only, no production change. LGTM.

@dzhelezov dzhelezov marked this pull request as ready for review July 10, 2026 19:07
@dzhelezov dzhelezov merged commit fb8f045 into main Jul 10, 2026
12 checks passed
@dzhelezov dzhelezov deleted the test/145-realtime-fallback-e2e branch July 10, 2026 19:07
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.

test: realtime body-cap fallback catch path (createRealtimeSync) is not exercised end-to-end

1 participant