Commit 2e9971d
committed
test(cached-adapter-store): rewrite test surface around prime() and middleware-driven trigger
Public-API surface tests (new):
- Returned module has exactly {getAll, getById, getOrFailById, generateNew, prime} — five enumerable keys.
- `expect(store).not.toHaveProperty('retrieveAll')` — verifies the narrowing.
- `expect(store).not.toHaveProperty('retrieveById')` — verifies the narrowing.
prime() behavior (new group):
- Cold start (localHash null, no header) → exactly one inner fetch.
- localHash set but no header → exactly one inner fetch.
- Already in sync → no inner fetch.
- Mismatch on cold-start with header already seen → fires inner once, persists new hash.
- Idempotency: two rapid prime() calls → exactly one inner fetch (in-flight dedup).
- Post-success no-op: after successful prime, second prime returns immediately.
- Post-success no-op requires localHash !== null — if persist did not happen (no header), second prime CAN fire again (pins the localHash !== null clause of the guard).
Middleware-driven trigger (new group):
- Response with hash differing from localHash (cold + warm) → middleware fires inner exactly once and updates persisted hash on success.
- Response with hash equal to localHash → middleware does NOT fire inner.
- Response with header missing/malformed (5a/5b/5c) → middleware does NOT fire inner.
prime + middleware race coordination (new group):
- Mid-flight response arriving during a prime() call → ONE inner fetch via shared in-flight ref (v1 once-per-burst contract; later mismatches handled by the NEXT header).
Persist-after-success timing (rewritten):
- ONLY after inner success does storageService.put fire — verified with a pending Promise that holds the inner fetch open so we can assert the negative before resolving.
- Inner rejection (prime path) → no persist; rejection surfaces to caller.
- Inner rejection (middleware path) → no persist; rejection swallowed (fire-and-forget; no unhandled-rejection escape).
Idempotent middleware registration (rewritten):
- Existing assertions preserved; entry-point swapped from retrieveAll() to prime().
Parser branches (rewritten):
- All malformed-header tests now drive through prime() instead of retrieveAll. Setup-rig comment updated to describe the new entry point.
Exception-safe response middleware (Architecture Lock #10, rewritten):
- Existing 5a/5b/5c contract-pin tests preserved (no longer chain to a removed retrieveAll on the rig, just verify no-throw on delivery).
- 5-success: rewritten to await store.prime() so the assertion rendezvous with the middleware-triggered inner fetch.
- NEW: inner.retrieveAll rejection on the middleware path does NOT propagate back through the middleware — verified via `process.on('unhandledRejection', ...)` capturing during microtask drain.
types.spec.ts (rewritten):
- Asserts the factory's return type is `CachedStoreModuleForAdapter<DemoItem, DemoAdapted, DemoNewAdapted>` (was `StoreModuleForAdapter<...>`).
- Adds a `@ts-expect-error` assertion that the wrapper's return is NOT assignable to `StoreModuleForAdapter<...>`. If a future refactor re-adds retrieveAll/retrieveById, the directive becomes unused and tsc errors out.
- Type-test body guarded by `if (false as boolean)` so the type-level assignment is checked but the runtime call into the real factory (against an empty config) never executes.
Test count: 69 → 80 (+11, well above the order's ≥69 baseline).
Coverage: 100% statements/branches/functions/lines on src/cached-adapter-store.ts and src/types.ts.
Mutation: 94.81% on cached-adapter-store.ts (≥90% threshold; 4 surviving mutants are documented equivalent — parser try/catch fall-throughs and the post-success hasCompletedAtLeastOnce/early-return optimization, observationally indistinguishable from the unmutated form because skip-when-equal in triggerInnerRetrieveAll catches the same case).1 parent e130993 commit 2e9971d
2 files changed
Lines changed: 422 additions & 150 deletions
0 commit comments