Skip to content

Add replay idempotency tests for duplicate and out-of-order contract events - #1392

Merged
Jagadeeshftw merged 1 commit into
Fluxora-Org:mainfrom
Fury03:issue-1257
Aug 31, 2026
Merged

Add replay idempotency tests for duplicate and out-of-order contract events#1392
Jagadeeshftw merged 1 commit into
Fluxora-Org:mainfrom
Fury03:issue-1257

Conversation

@Fury03

@Fury03 Fury03 commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Linked Issue

Closes #1257

Problem Statement (The Bug)

The backend has replay integrity, event-id, ingested-at, and stale-cursor tests, but lacked cross-path tests verifying the idempotency invariant for duplicate and out-of-order contract event delivery. Without these tests, there is no regression guard against:

  • Duplicate events creating multiple database rows
  • Out-of-order events being silently dropped
  • Cursor advancement breaking when duplicates are present
  • Health metrics becoming inaccurate after duplicate ingestion

This is not something that can be fixed with a local patch because the invariant spans multiple layers (store, ingestion service, metrics, cursor). Only dedicated regression tests can lock it down.

Solution Comparison and Decision (Why tests, not code changes)

  • Option A: Add dedup logic in the ingest layer -- Rejected because dedup is already correctly implemented in both InMemoryContractEventStore (Map-based eventId key) and PostgresContractEventStore (ON CONFLICT DO NOTHING via contract_event_dedup). The code works; it needs test coverage.
  • Option B: Add dedup at the streamEventService level -- Rejected because this is already covered by streamEventService.dedup.test.ts with property-based testing. The issue asks for store-level cross-path tests.
  • Option C: Add replay idempotency regression tests (chosen) -- This directly addresses the gap: tests that exercise the full path from insertMany through database state, cursor advancement, and metrics reporting.

The Change (Code modifications)

A single new test file tests/indexer-store.replay-idempotency.test.ts with 30 unit tests across 8 describe blocks:

Test Category Tests What it verifies
Duplicate delivery 4 Same event re-ingested is a no-op; batch replay; repeated delivery; corrected event rejected
Out-of-order events 4 Reverse ledger order; insertion order; ledger-order query; interleaved contracts
Mixed duplicate/new batches 4 1 dup + 2 new; all dup; all new; interleaved retries
Gap and retry 3 Partial retry; full retry 3x; out-of-order retry
Cursor advancement 3 Pagination stability; total count; fromLedger filter
Postgres store dedup 3 Mock ON CONFLICT; out-of-order; mixed batch via mock
No duplicate business events 5 eventId identity; distinct txs; distinct indices; first-writer-wins; empty batch
Concurrent delivery patterns 3 Parallel overlapping inserts; rapid re-delivery; multi-batch overlap

Compatibility Note (On INTERFACE_VERSION)

INTERFACE_VERSION is not modified. This PR adds only test files -- no production code changes, no API contract changes, no schema migrations. There is no risk to breaking live critical paths because no production code is touched.

Incidental Fixes

None. The scope is strictly limited to the new test file. No existing tests were modified or weakened.

Testing (Proving it works)

New test cases:

  • tests/indexer-store.replay-idempotency.test.ts -- 30 tests, all passing

Verification command from the issue:

pnpm vitest run tests/indexer-store.ingested-at.test.ts tests/indexer-store.ledger-hash.test.ts tests/streamEventService.eventId.test.ts

Results:

  • tests/streamEventService.eventId.test.ts -- 4 tests, all passing
  • tests/indexer-store.ledger-hash.test.ts -- 10 tests, all passing
  • tests/indexer-store.ingested-at.test.ts -- 1 pre-existing failure (unrelated to this change; InMemoryContractEventStore.getEvents() silently backfills missing ingestedAt with current time instead of throwing RowMappingError)

Additional Notes

  • Pre-existing failure: ingested-at.test.ts line 200 -- store.getEvents() resolves instead of rejecting when ingestedAt is deleted from a record. This is because InMemoryContractEventStore.getEvents() uses r.ingestedAt ?? new Date().toISOString() fallback. This is a known issue tracked separately and is NOT introduced by this PR.
  • Scope: Only tests/indexer-store.replay-idempotency.test.ts is added. No production modules, configuration files, or existing tests are touched.

…order contract events

Adds 30 unit tests covering the cross-path invariant that re-ingesting the
same event is a no-op, out-of-order events are accepted and stored, and no
duplicate business event is emitted.

Test coverage areas:
- Duplicate delivery: single event, batch, repeated delivery, corrected event
- Out-of-order events: reverse ledger, interleaved contracts, same-ledger
- Mixed batches: duplicates + new events in a single insertMany call
- Gap and retry: partial batch re-delivery, out-of-order retry
- Cursor advancement: pagination stability after duplicate ingestion
- Postgres store dedup: mock-based contract verification for ON CONFLICT
- No duplicate business events: eventId identity key, first-writer-wins
- Concurrent delivery: overlapping parallel inserts, rapid re-delivery

Closes Fluxora-Org#1257
@drips-wave

drips-wave Bot commented Aug 31, 2026

Copy link
Copy Markdown

@Fury03 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

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.

Add replay idempotency tests for duplicate and out-of-order contract events

2 participants