From 0bdb04b0aa0c0500e68e2e985b1a2880730ad7e8 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Wed, 21 Oct 2026 14:46:06 +0900 Subject: fix(review-followup-fec): reconcile epoch rollback and retain reconnect backoff --- src/features/conversation-cache/cache.test.ts | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'src/features/conversation-cache/cache.test.ts') diff --git a/src/features/conversation-cache/cache.test.ts b/src/features/conversation-cache/cache.test.ts index 4306d51..72b4e20 100644 --- a/src/features/conversation-cache/cache.test.ts +++ b/src/features/conversation-cache/cache.test.ts @@ -214,3 +214,38 @@ it("serializes invalidation and rejects stale epoch commits", async () => { expect(await cache.commit("c", [chunk(103)], 1)).toEqual([]); expect(await store.historyEpoch("c")).toBe(2); }); + +it("replaces a confirmed full history at a lower epoch, without trusting stale commits", async () => { + const store = createFakeStore(); + const cache = createConversationCache(store); + await cache.commit("c", [chunk(9)], 5); + expect(await cache.replace("c", [chunk(1)], 1, 5)).toBe(true); + expect(await cache.historyEpoch("c")).toBe(1); + expect(await cache.load("c")).toEqual([chunk(1)]); + expect(await cache.commit("c", [chunk(9)], 5)).toEqual([chunk(1)]); + // A delayed full response started against epoch 5 must not restore it. + expect(await cache.replace("c", [chunk(9)], 5, 5)).toBe(false); + expect(await cache.load("c")).toEqual([chunk(1)]); +}); + +it("serializes a guarded replacement behind newer invalidation", async () => { + const store = createFakeStore(); + const cache = createConversationCache(store); + await cache.commit("c", [chunk(9)], 5); + const resetting = cache.reset("c", 6); + const replacing = cache.replace("c", [chunk(1)], 1, 5); + await resetting; + expect(await replacing).toBe(false); + expect(await cache.historyEpoch("c")).toBe(6); + expect(await cache.load("c")).toEqual([]); +}); + +it("allows an empty replacement and a legacy cache only if its observed epoch still matches", async () => { + const store = createFakeStore(); + const cache = createConversationCache(store); + await store.append("c", [chunk(9)]); + expect(await cache.replace("c", [], 0, undefined)).toBe(true); + expect(await cache.sinceSeq("c")).toBe(0); + expect(await cache.historyEpoch("c")).toBe(0); + expect(await cache.replace("c", [chunk(9)], 2, undefined)).toBe(false); +}); -- cgit v1.2.3