import { describe, expect, it } from "vitest"; import { WEB_PUSH_PROTOCOLS, } from "../../src/contracts/web-push.ts"; import { createPushAssociationFenceStore, } from "../../src/adapters/web-push/push-association-fence-store.ts"; import { createFakePushControlStoreDependencies, } from "../helpers/fake-push-control-repository.ts"; const firstAuthority = Object.freeze({ fenceGeneration: "fence_01", sessionBindingEpoch: "session_01", releaseEpoch: "release_01", }); const nextAuthority = Object.freeze({ fenceGeneration: "fence_02", sessionBindingEpoch: "session_02", releaseEpoch: "release_01", }); function manualScheduler() { let sequence = 0; const callbacks = new Map void>(); return { scheduler: { setTimeout(callback: () => void) { sequence += 1; callbacks.set(sequence, callback); return sequence; }, clearTimeout(handle: unknown) { if (typeof handle === "number") callbacks.delete(handle); }, }, expireAll() { for (const callback of [...callbacks.values()]) callback(); }, }; } describe("Web Push durable control fence", () => { it.each([0, 2, 3, 9_999])( "rejects a CAS receipt that is not the exact next revision (%i)", async (revision) => { // WP-01. Only the exact next revision is evidence that this command // actually wrote the control it claims to have written. const dependencies = createFakePushControlStoreDependencies(); const repository = dependencies.repository; const compareAndSwap = repository.compareAndSwap.bind(repository); repository.compareAndSwap = async (input) => { const written = await compareAndSwap(input); return written.ok ? { ok: true as const, value: { ...written.value, revision }, } : written; }; const store = createPushAssociationFenceStore(dependencies); await expect( store.prepare({ authority: firstAuthority, updatedAt: "2026-07-28T00:00:00.000Z", }), ).resolves.toMatchObject({ ok: false, error: { code: "CONTROL_CORRUPT" }, }); }, ); it("CASes UNASSOCIATED to ACTIVE and prevents tombstone resurrection", async () => { const store = createPushAssociationFenceStore( createFakePushControlStoreDependencies(), ); const prepared = await store.prepare({ authority: firstAuthority, updatedAt: "2026-07-28T00:00:00.000Z", }); expect(prepared).toMatchObject({ ok: true, value: { revision: 1, control: { association: { state: "UNASSOCIATED" }, }, }, }); if (!prepared.ok) throw new Error("expected prepared control"); expect(prepared.value.control).not.toHaveProperty("revision"); const active = await store.activate({ expectedRevision: prepared.value.revision, authority: firstAuthority, associationEpoch: "association_01", updatedAt: "2026-07-28T00:00:01.000Z", }); expect(active).toMatchObject({ ok: true, value: { revision: 2, control: { association: { state: "ACTIVE", associationEpoch: "association_01", }, }, }, }); if (!active.ok) throw new Error("expected active control"); const revoked = await store.markRevoked({ expectedRevision: active.value.revision, authority: firstAuthority, updatedAt: "2026-07-28T00:00:02.000Z", }); expect(revoked).toMatchObject({ ok: true, value: { control: { association: { state: "REVOKED" } }, }, }); if (!revoked.ok) throw new Error("expected revoked control"); expect( await store.activate({ expectedRevision: revoked.value.revision, authority: firstAuthority, associationEpoch: "association_01", updatedAt: "2026-07-28T00:00:03.000Z", }), ).toMatchObject({ ok: false, error: { code: "TOMBSTONE_CONFLICT" }, }); expect( await store.activate({ expectedRevision: revoked.value.revision, authority: firstAuthority, associationEpoch: "association_02", updatedAt: "2026-07-28T00:00:04.000Z", }), ).toMatchObject({ ok: true, value: { control: { association: { state: "ACTIVE", associationEpoch: "association_02", }, }, }, }); }); it("lets logout generation rotation win an in-flight registration CAS", async () => { const dependencies = createFakePushControlStoreDependencies(); const store = createPushAssociationFenceStore(dependencies); const prepared = await store.prepare({ authority: firstAuthority, updatedAt: "2026-07-28T00:00:00.000Z", }); if (!prepared.ok) throw new Error("expected prepared control"); const paused = dependencies.repository.pauseNextWrite( (control) => control.association.state === "ACTIVE", ); const lateActivation = store.activate({ expectedRevision: prepared.value.revision, authority: firstAuthority, associationEpoch: "association_late", updatedAt: "2026-07-28T00:00:02.000Z", }); await paused.reached; const fenced = await store.rotateAndRevoke({ expectedRevision: prepared.value.revision, previousAuthority: firstAuthority, nextAuthority, updatedAt: "2026-07-28T00:00:01.000Z", }); expect(fenced).toMatchObject({ ok: true, value: { revision: 2, control: { fenceGeneration: "fence_02", association: { state: "UNASSOCIATED" }, }, }, }); paused.release(); expect(await lateActivation).toMatchObject({ ok: false, error: { code: "STALE_REVISION" }, }); expect(await store.read()).toMatchObject({ ok: true, value: { revision: 2, control: { fenceGeneration: "fence_02", association: { state: "UNASSOCIATED" }, }, }, }); }); it("purges only the captured revoked association and cannot delete a newer owner", async () => { const dependencies = createFakePushControlStoreDependencies(); const store = createPushAssociationFenceStore(dependencies); const prepared = await store.prepare({ authority: firstAuthority, updatedAt: "2026-07-28T00:00:00.000Z", }); if (!prepared.ok) throw new Error("expected prepared control"); const active = await store.activate({ expectedRevision: prepared.value.revision, authority: firstAuthority, associationEpoch: "association_old", updatedAt: "2026-07-28T00:00:01.000Z", }); if (!active.ok) throw new Error("expected active control"); const revoked = await store.markRevoked({ expectedRevision: active.value.revision, authority: firstAuthority, updatedAt: "2026-07-28T00:00:02.000Z", }); if (!revoked.ok) throw new Error("expected revoked control"); expect( await store.purge({ expectedRevision: revoked.value.revision, authority: firstAuthority, associationEpoch: "association_wrong", }), ).toMatchObject({ ok: false, error: { code: "ASSOCIATION_MISMATCH" }, }); const paused = dependencies.repository.pauseNextRemove(); const latePurge = store.purge({ expectedRevision: revoked.value.revision, authority: firstAuthority, associationEpoch: "association_old", }); await paused.reached; const newer = await store.activate({ expectedRevision: revoked.value.revision, authority: firstAuthority, associationEpoch: "association_new", updatedAt: "2026-07-28T00:00:03.000Z", }); expect(newer).toMatchObject({ ok: true, value: { control: { association: { state: "ACTIVE", associationEpoch: "association_new", }, }, }, }); paused.release(); await expect(latePurge).resolves.toMatchObject({ ok: false, error: { code: "STALE_REVISION" }, }); expect(await store.read()).toMatchObject({ ok: true, value: { control: { association: { state: "ACTIVE", associationEpoch: "association_new", }, }, }, }); }); it("removes the exact revoked tombstone with revision CAS", async () => { const store = createPushAssociationFenceStore( createFakePushControlStoreDependencies(), ); const prepared = await store.prepare({ authority: firstAuthority, updatedAt: "2026-07-28T00:00:00.000Z", }); if (!prepared.ok) throw new Error("expected prepared control"); const active = await store.activate({ expectedRevision: prepared.value.revision, authority: firstAuthority, associationEpoch: "association_01", updatedAt: "2026-07-28T00:00:01.000Z", }); if (!active.ok) throw new Error("expected active control"); const revoked = await store.markRevoked({ expectedRevision: active.value.revision, authority: firstAuthority, updatedAt: "2026-07-28T00:00:02.000Z", }); if (!revoked.ok) throw new Error("expected revoked control"); expect( await store.purge({ expectedRevision: revoked.value.revision, authority: firstAuthority, associationEpoch: "association_01", }), ).toEqual({ ok: true, value: undefined }); expect(await store.read()).toEqual({ ok: true, value: null }); }); it("fails closed when the injected repository returns a polluted record", async () => { const dependencies = createFakePushControlStoreDependencies(); dependencies.repository.seedRaw( { protocol: WEB_PUSH_PROTOCOLS.control, ...firstAuthority, updatedAt: "2026-07-28T00:00:00.000Z", association: { state: "UNASSOCIATED" }, endpoint: "https://must-not-persist.invalid", }, 7, ); const store = createPushAssociationFenceStore(dependencies); expect(await store.read()).toMatchObject({ ok: false, error: { code: "CONTROL_CORRUPT" }, }); }); it("aborts a late repository CAS at the hard operation deadline", async () => { const dependencies = createFakePushControlStoreDependencies(); const clock = manualScheduler(); const store = createPushAssociationFenceStore({ ...dependencies, operationDeadlineMs: 1, scheduler: clock.scheduler, }); const prepared = await store.prepare({ authority: firstAuthority, updatedAt: "2026-07-28T00:00:00.000Z", }); if (!prepared.ok) throw new Error("expected prepared control"); const paused = dependencies.repository.pauseNextWrite( (control) => control.association.state === "ACTIVE", ); const activation = store.activate({ expectedRevision: prepared.value.revision, authority: firstAuthority, associationEpoch: "association_late", updatedAt: "2026-07-28T00:00:01.000Z", }); await paused.reached; clock.expireAll(); expect(await activation).toMatchObject({ ok: false, error: { code: "DEADLINE_EXCEEDED" }, }); paused.release(); expect(await store.read()).toMatchObject({ ok: true, value: { revision: 1, control: { association: { state: "UNASSOCIATED" } }, }, }); }); });