Documentation
¶
Overview ¶
Package projectiontest provides conformance test helpers for projection.CheckpointStore, projection.ReplaySource, and projection.Cursor implementations.
RunCheckpointConformance exercises the offset roundtrip contract that every CheckpointStore must satisfy:
- Cold start returns (0, nil)
- Save followed by Load returns the saved value
- Monotonically advancing saves are reflected immediately
- Keys are isolated (different (cellID, projectionID) pairs do not interfere)
- Re-saving the same offset is idempotent
- A backward write (lower offset) is accepted, not rejected (caller responsibility #1)
RunReplaySourceConformance exercises the ReplaySource read contract (Head accounting, Replay delivery order, the from-offset skip, replay stability).
RunCursorConformance exercises the four Cursor position invariants documented on projection.Cursor (1-based, monotonic, gap-allowed, permanent-error).
The Checkpoint helper does NOT exercise transaction semantics (ambient-tx binding is the responsibility of each adapter's own integration test). The PG adapter (PR-02) invokes this funnel directly on a real *postgres.ProjectionCheckpointStore; bare-ctx calls route through the pool (each statement auto-commits), and tx atomicity (commit/rollback) is exercised separately in the adapter's own integration tests.
stdlib-only: no external test frameworks are imported.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RunCheckpointConformance ¶
func RunCheckpointConformance(t *testing.T, store projection.CheckpointStore)
RunCheckpointConformance runs the six canonical conformance sub-tests against store. Each sub-test uses a distinct (cellID, projectionID) key to prevent ordering-dependent interference on a shared store instance.
Caller responsibilities ¶
Monotonicity is the Coordinator's responsibility, not the store's. The store must accept any int64 offset, including backward writes. MemCheckpointStore overwrites unconditionally; a PG implementation must do the same (no "reject-if-lower" guard) — deduplication is handled by the Coordinator's pos <= checkpoint check.
Isolation before call: a durable store (e.g. PR-02 Postgres adapter) must start from an empty or isolated namespace before calling this helper. Use a separate schema, a per-test table prefix, or a TRUNCATE to prevent interference from other tests or prior runs.
Transaction semantics are NOT asserted here: this helper only verifies the offset roundtrip contract (SaveOffset → LoadOffset). Atomic commit behavior (Apply + SaveOffset in a single transaction) is exercised in each adapter's own integration test suite (PR-02 for Postgres).
Usage (from a package that creates its own store):
func TestMyStore_Conformance(t *testing.T) {
projectiontest.RunCheckpointConformance(t, mystore.New())
}
func RunCursorConformance ¶
func RunCursorConformance( t *testing.T, cursor projection.Cursor, seed func(n int) []projection.ProjectionEvent, newUnseeded func() projection.ProjectionEvent, )
RunCursorConformance verifies the four Cursor position invariants documented on projection.Cursor (cursor.go):
- 1-based: every resolved position is >= 1.
- Monotonic: positions advance across distinct entries delivered in order.
- Gap-allowed: positions need not be contiguous — only strictly increasing across distinct entries (this sub-test does NOT require pos[i+1]==pos[i]+1).
- Permanent-error: an entry the backing store does not know yields a permanent error (outbox.PermanentError), never a transient one.
seed persists n fresh entries into the backing store the cursor resolves against (same contract as RunReplaySourceConformance.seed) and returns them in stream order. newUnseeded constructs a fresh entry that is NOT persisted — used to exercise the permanent-error path. A read-only production Cursor needs no test-only method: the caller wires both closures.
func RunReplaySourceConformance ¶
func RunReplaySourceConformance(t *testing.T, src projection.ReplaySource, seed func(n int) []projection.ProjectionEvent)
RunReplaySourceConformance verifies the canonical ReplaySource contract for src: Head accounting, Replay delivery ordering, the from-offset skip, and replay stability across calls.
seed persists n fresh entries into src's backing store (the caller wires the concrete persistence: a mem source appends; a PG source inserts via the real outbox writer) and returns them in append order. The suite never writes to src itself, so a production read-only ReplaySource needs NO test-only seed method on its public surface.
Numeric stream positions are deliberately NOT asserted here — that is the Cursor's contract, verified by RunCursorConformance. Ordering is checked by entry identity (Entry.ID()), which is robust to a shared, concurrently-seeded backing store: other callers' entries may interleave, so this suite asserts only that its own seeded entries appear in append order (an ordered subsequence of what Replay delivers).
This conformance helper is intended for external packages (e.g. adapters) that provide their own ReplaySource implementations. Calling it from within the projection package itself would create an import cycle.
Types ¶
This section is empty.