summaryrefslogtreecommitdiffhomepage
path: root/src/features/chat/test-helpers.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-12 19:00:29 +0900
committerAdam Malczewski <[email protected]>2026-06-12 19:00:29 +0900
commit1776912f39830060f47aeca2cf452674786da36d (patch)
treeb1e6f01704296260ae06157fa941a2d31dcf9b47 /src/features/chat/test-helpers.ts
parent4d2e6d817f65a13631f00786c45b403d7d7d58ec (diff)
downloaddispatch-web-1776912f39830060f47aeca2cf452674786da36d.tar.gz
dispatch-web-1776912f39830060f47aeca2cf452674786da36d.zip
feat(chat): consume CR-5 history windowing — server-windowed cold loads + show-earlier backfill
Re-pinned [email protected]>0.10.0 + [email protected]>0.6.1 (reply frontend-history-windowing-handoff.md); re-mirrored both .dispatch references. - HistorySync port gains optional { limit?, beforeSeq? } (CR-5 params); the app's createHistorySync appends them to GET /conversations/:id. - COLD-cache fresh load now fetches ?sinceSeq=0&limit=<floor(0.75xL)> — a huge conversation no longer ships whole to show 192 chunks. A warm-cache tail sync stays unwindowed (windowing a tail that outgrew the limit would leave a silent seq gap behind the cache). - hasEarlier now derives from the [email protected] CONTRACT (1-based gap-free seqs): loaded window starting above seq 1 => older history exists — covering both locally-trimmed AND server-windowed transcripts (the watermark stays as the merge floor only). - showEarlier(): local cache first; when the cache doesn't reach far enough back, backfills the missing older run via ?beforeSeq=<oldestKnown>&limit= and persists it (next page-in is local). latestSeq windowed-read caveat is satisfied structurally (tail cursor derives from the cache's max seq). - live-probe: +6 CR-5 checks (seq origin, newest-k ascending, short-chat exactness, beforeSeq paging, 400 validation x2). NOT yet run live — backend was down at commit time; run pending. - backend-handoff.md: CR-5 RESOLVED, pins/mirrors current. 602 tests green x2.
Diffstat (limited to 'src/features/chat/test-helpers.ts')
-rw-r--r--src/features/chat/test-helpers.ts23
1 files changed, 17 insertions, 6 deletions
diff --git a/src/features/chat/test-helpers.ts b/src/features/chat/test-helpers.ts
index 07dad26..6bb98a1 100644
--- a/src/features/chat/test-helpers.ts
+++ b/src/features/chat/test-helpers.ts
@@ -1,6 +1,6 @@
import type { StoredChunk } from "@dispatch/wire";
import type { ConversationCache } from "../conversation-cache";
-import type { ChatTransport, HistorySync, MetricsSync } from "./ports";
+import type { ChatTransport, HistorySync, HistoryWindow, MetricsSync } from "./ports";
export interface FakeTransport {
readonly sent: import("@dispatch/transport-contract").ChatSendMessage[];
@@ -20,14 +20,14 @@ export function createFakeTransport(): FakeTransport {
}
export interface FakeHistorySync {
- readonly calls: Array<{ conversationId: string; sinceSeq: number }>;
+ readonly calls: Array<{ conversationId: string; sinceSeq: number; window?: HistoryWindow }>;
/** Set the chunks to return on the next call. */
returnChunks: readonly StoredChunk[];
readonly impl: HistorySync;
}
export function createFakeHistorySync(): FakeHistorySync {
- const calls: Array<{ conversationId: string; sinceSeq: number }> = [];
+ const calls: Array<{ conversationId: string; sinceSeq: number; window?: HistoryWindow }> = [];
let returnChunks: readonly StoredChunk[] = [];
return {
calls,
@@ -37,9 +37,20 @@ export function createFakeHistorySync(): FakeHistorySync {
set returnChunks(v: readonly StoredChunk[]) {
returnChunks = v;
},
- impl: async (conversationId, sinceSeq) => {
- calls.push({ conversationId, sinceSeq });
- const chunks = returnChunks;
+ impl: async (conversationId, sinceSeq, window) => {
+ calls.push({ conversationId, sinceSeq, ...(window !== undefined ? { window } : {}) });
+ // Apply the CR-5 WINDOW semantics (`beforeSeq` bound, then newest-`limit`)
+ // so store tests exercise the real windowed flows. `sinceSeq` filtering is
+ // deliberately NOT applied — tests set `returnChunks` to the slice they
+ // mean the server to hold past the cursor.
+ let chunks = returnChunks;
+ const before = window?.beforeSeq;
+ if (before !== undefined) {
+ chunks = chunks.filter((c) => c.seq < before);
+ }
+ if (window?.limit !== undefined && chunks.length > window.limit) {
+ chunks = chunks.slice(-window.limit);
+ }
const latestSeq = chunks.length > 0 ? Math.max(...chunks.map((c) => c.seq)) : sinceSeq;
return { chunks, latestSeq };
},