Files
app/tauri/src/runtime/conversation-store.test.ts
T

101 lines
4.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { ToolCallRecord } from "./tool-call-state";
import { ConversationStore } from "./conversation-store";
class MemoryStorage implements Storage {
private readonly values = new Map<string, string>();
public get length(): number { return this.values.size; }
public clear(): void { this.values.clear(); }
public getItem(key: string): string | null { return this.values.get(key) ?? null; }
public key(index: number): string | null { return [...this.values.keys()][index] ?? null; }
public removeItem(key: string): void { this.values.delete(key); }
public setItem(key: string, value: string): void { this.values.set(key, value); }
}
const conversationID = "user_test:2:agent_channel";
const createdAt = "2026-08-03T00:00:00.000Z";
describe("ConversationStore", () => {
it("keeps the inclusive sync cursor at zero after a send acknowledgement", () => {
const store = new ConversationStore(new MemoryStorage());
store.open(conversationID);
store.appendLocalText("local_001", "hello", createdAt);
store.markSubmitted("local_001", 41, createdAt);
expect(store.snapshot()).toMatchObject({ cursor: 0, outbox: [] });
expect(store.snapshot().items).toEqual([expect.objectContaining({
local_id: "local_001",
message_seq: 41,
item: expect.objectContaining({ kind: "user-message", delivery: expect.objectContaining({ status: "submitted" }) }),
})]);
});
it("deduplicates the inclusive sync echo of a locally submitted user message", () => {
const store = new ConversationStore(new MemoryStorage());
store.open(conversationID);
store.appendLocalText("local_001", "hello", createdAt);
store.markSubmitted("local_001", 41, createdAt);
const restored = store.recordSyncedUserText(41, "hello", createdAt);
expect(restored).toBeUndefined();
expect(store.snapshot()).toMatchObject({ cursor: 41 });
expect(store.snapshot().items).toHaveLength(1);
});
it("persists a local echo and restores it for the same conversation", () => {
const storage = new MemoryStorage();
const first = new ConversationStore(storage);
first.open(conversationID);
first.appendLocalText("local_002", "persist me", createdAt);
const reopened = new ConversationStore(storage);
const snapshot = reopened.open(conversationID);
expect(snapshot.cursor).toBe(0);
expect(snapshot.outbox).toEqual([expect.objectContaining({ local_id: "local_002", payload: "persist me" })]);
expect(snapshot.items).toEqual([expect.objectContaining({
local_id: "local_002",
item: expect.objectContaining({ kind: "user-message", text: "persist me" }),
})]);
});
it("accepts one remote message per IM sequence and advances the cursor only from sync data", () => {
const store = new ConversationStore(new MemoryStorage());
store.open(conversationID);
const item = { kind: "markdown" as const, id: "evt_remote_001", markdown: "remote" };
expect(store.recordIncoming(item, 42, createdAt)).toBe(true);
expect(store.recordIncoming(item, 42, createdAt)).toBe(false);
expect(store.snapshot()).toMatchObject({ cursor: 42 });
expect(store.snapshot().items).toHaveLength(1);
});
it("persists Tool Call state and an unsubmitted form draft per conversation", () => {
const storage = new MemoryStorage();
const state: ToolCallRecord = {
call_id: "call_form_001",
conversation_id: conversationID,
request: {
call_id: "call_form_001",
tool: "input",
title: "Deploy",
prompt: "Provide a version.",
data: { form: {} },
},
status: "pending",
created_at: createdAt,
updated_at: createdAt,
};
const first = new ConversationStore(storage);
first.open(conversationID);
first.replaceToolCalls([state]);
first.saveToolDraft("call_form_001", { version: "1.2.3" });
const reopened = new ConversationStore(storage).open(conversationID);
expect(reopened.tool_calls).toEqual([expect.objectContaining({ call_id: "call_form_001", status: "pending" })]);
expect(reopened.tool_drafts).toEqual({ call_form_001: { version: "1.2.3" } });
});
});