test: add unit tests for readSessionMessages stable ID

Verify that readSessionMessages includes the JSONL entry ID in returned
messages. Tests cover:
- Messages with stable IDs are preserved
- Messages without IDs handle gracefully (id: undefined)
- Invalid JSON and empty lines are skipped
- Message content and metadata are preserved
- Nonexistent files return empty array
This commit is contained in:
Dominic 2026-01-27 20:13:27 -06:00
parent 3a48941c96
commit 56692db403

View File

@ -5,6 +5,7 @@ import { afterEach, beforeEach, describe, expect, test } from "vitest";
import {
readFirstUserMessageFromTranscript,
readLastMessagePreviewFromTranscript,
readSessionMessages,
readSessionPreviewItemsFromTranscript,
} from "./session-utils.fs.js";
@ -404,3 +405,119 @@ describe("readSessionPreviewItemsFromTranscript", () => {
expect(result[0]?.text.endsWith("...")).toBe(true);
});
});
describe("readSessionMessages", () => {
let tmpDir: string;
let sessionFile: string;
beforeEach(() => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "session-messages-test-"));
sessionFile = path.join(tmpDir, "test-session.jsonl");
});
afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});
test("includes stable entry ID in returned messages", () => {
// Create JSONL transcript with entries that have IDs
const entries = [
{
id: "entry-1",
message: { role: "user", content: "Hello" },
},
{
id: "entry-2",
message: { role: "assistant", content: "Hi there!" },
},
{
id: "entry-3",
message: { role: "user", content: "How are you?" },
},
];
const jsonl = entries.map((e) => JSON.stringify(e)).join("\n");
fs.writeFileSync(sessionFile, jsonl, "utf-8");
// Read messages
const messages = readSessionMessages("test-session", undefined, sessionFile);
// Verify each message includes the stable entry ID
expect(messages).toHaveLength(3);
expect(messages[0]).toMatchObject({ role: "user", content: "Hello", id: "entry-1" });
expect(messages[1]).toMatchObject({ role: "assistant", content: "Hi there!", id: "entry-2" });
expect(messages[2]).toMatchObject({ role: "user", content: "How are you?", id: "entry-3" });
});
test("handles entries without IDs gracefully", () => {
// Create JSONL with mixed entries (some with IDs, some without)
const entries = [
{
id: "entry-1",
message: { role: "user", content: "Hello" },
},
{
// No id field
message: { role: "assistant", content: "Hi!" },
},
];
const jsonl = entries.map((e) => JSON.stringify(e)).join("\n");
fs.writeFileSync(sessionFile, jsonl, "utf-8");
const messages = readSessionMessages("test-session", undefined, sessionFile);
expect(messages).toHaveLength(2);
expect(messages[0]).toMatchObject({ role: "user", content: "Hello", id: "entry-1" });
// Second message should have id: undefined (spread of undefined value)
expect(messages[1]).toMatchObject({ role: "assistant", content: "Hi!", id: undefined });
});
test("skips empty lines and invalid JSON", () => {
const jsonl = [
JSON.stringify({ id: "entry-1", message: { role: "user", content: "Valid" } }),
"", // Empty line
"invalid json {",
JSON.stringify({ id: "entry-2", message: { role: "user", content: "Also valid" } }),
].join("\n");
fs.writeFileSync(sessionFile, jsonl, "utf-8");
const messages = readSessionMessages("test-session", undefined, sessionFile);
expect(messages).toHaveLength(2);
expect(messages[0]).toMatchObject({ id: "entry-1" });
expect(messages[1]).toMatchObject({ id: "entry-2" });
});
test("returns empty array when session file does not exist", () => {
const messages = readSessionMessages("nonexistent", undefined, "/nonexistent/path.jsonl");
expect(messages).toEqual([]);
});
test("preserves message content and metadata", () => {
const entries = [
{
id: "msg-1",
message: {
role: "assistant",
content: [{ type: "text", text: "Complex content" }],
metadata: { foo: "bar" },
},
},
];
const jsonl = entries.map((e) => JSON.stringify(e)).join("\n");
fs.writeFileSync(sessionFile, jsonl, "utf-8");
const messages = readSessionMessages("test-session", undefined, sessionFile);
expect(messages).toHaveLength(1);
expect(messages[0]).toMatchObject({
role: "assistant",
content: [{ type: "text", text: "Complex content" }],
metadata: { foo: "bar" },
id: "msg-1",
});
});
});