Integration test: idempotency cache survives replica restart (op-infra Step 12 follow-up)
Context
Op-infra Step 12 (MR pending) ships a PostgreSQL-backed IdempotencyCache::with_pool that persists cached responses across process restart and shares state across replicas. The unit tests in crates/canopy-api/src/idempotency.rs cover the in-memory backend only — the Backend::Postgres paths in check_cache and execute_and_cache (and the hourly TTL cleanup task) are not exercised by automated tests.
The original plan (Step 12.5) called for "tests for idempotency across simulated pod restarts", which was deferred from the implementation MR because the project does not have a multi-replica test harness yet.
Acceptance criteria
- New devstack-gated integration test under
crates/canopy-api/tests/idempotency_persistence_test.rs:- Construct
IdempotencyCache::with_poolagainst a livePgPool. - POST a request with an
Idempotency-Key→ cache it. - Drop the cache (simulate process restart) and reconstruct from the same pool.
- Replay the same key → assert the cached response is returned with the
x-idempotency-replay: trueheader.
- Construct
- Cross-replica scenario: two
IdempotencyCache::with_poolinstances on the same pool — store on one, replay on the other. - TTL test: insert a row with
created_at = now() - interval '25 hours'directly via SQL → assertcheck_cachereturnsNone(thecreated_at >= now() - interval '24 hours'filter is the safety net while cleanup is async). - Test gated by
infrastructure_available()and skipped on host runs without devstack — same pattern ascanopy-mqreconnect tests (per ADR-015).
Out of scope
- Redis backend coverage (still speculative; Postgres-only for phase 1 per plan §Step 12).
- Performance benchmarks (separate concern).
Source
Op-infra plan Step 12, deferred sub-item (5).