summaryrefslogtreecommitdiffhomepage
path: root/src/features/chat/test-helpers.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/features/chat/test-helpers.ts')
-rw-r--r--src/features/chat/test-helpers.ts80
1 files changed, 80 insertions, 0 deletions
diff --git a/src/features/chat/test-helpers.ts b/src/features/chat/test-helpers.ts
new file mode 100644
index 0000000..e58818a
--- /dev/null
+++ b/src/features/chat/test-helpers.ts
@@ -0,0 +1,80 @@
+import type { StoredChunk } from "@dispatch/wire";
+import type { ConversationCache } from "../conversation-cache";
+import type { ChatTransport, HistorySync } from "./ports";
+
+export interface FakeTransport {
+ readonly sent: import("@dispatch/transport-contract").ChatSendMessage[];
+ readonly impl: ChatTransport;
+}
+
+export function createFakeTransport(): FakeTransport {
+ const sent: import("@dispatch/transport-contract").ChatSendMessage[] = [];
+ return {
+ sent,
+ impl: {
+ send(msg) {
+ sent.push(msg);
+ },
+ },
+ };
+}
+
+export interface FakeHistorySync {
+ readonly calls: Array<{ conversationId: string; sinceSeq: number }>;
+ /** 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 }> = [];
+ let returnChunks: readonly StoredChunk[] = [];
+ return {
+ calls,
+ get returnChunks() {
+ return returnChunks;
+ },
+ set returnChunks(v: readonly StoredChunk[]) {
+ returnChunks = v;
+ },
+ impl: async (conversationId, sinceSeq) => {
+ calls.push({ conversationId, sinceSeq });
+ const chunks = returnChunks;
+ const latestSeq = chunks.length > 0 ? Math.max(...chunks.map((c) => c.seq)) : sinceSeq;
+ return { chunks, latestSeq };
+ },
+ };
+}
+
+export interface FakeCache {
+ readonly store: Map<string, StoredChunk[]>;
+ readonly impl: ConversationCache;
+}
+
+export function createFakeCache(): FakeCache {
+ const store = new Map<string, StoredChunk[]>();
+ return {
+ store,
+ impl: {
+ async load(conversationId) {
+ return store.get(conversationId) ?? [];
+ },
+ async commit(conversationId, incoming) {
+ const existing = store.get(conversationId) ?? [];
+ const seen = new Set(existing.map((c) => c.seq));
+ const toAppend = incoming.filter((c) => !seen.has(c.seq));
+ const merged = [...existing, ...toAppend].sort((a, b) => a.seq - b.seq);
+ store.set(conversationId, merged);
+ return merged;
+ },
+ async sinceSeq(conversationId) {
+ const chunks = store.get(conversationId) ?? [];
+ if (chunks.length === 0) return 0;
+ return Math.max(...chunks.map((c) => c.seq));
+ },
+ async evictIfOverBudget() {
+ return [];
+ },
+ },
+ };
+}