Files
homelab/chat/lib/message-graph.test.mjs
T

36 lines
1.8 KiB
JavaScript

import {describe, expect, test} from "bun:test";
import {mergeMessageGraph, messageChildrenInGraph, messagePathInGraph, newestBranchTipInGraph, rootEditAlternativesInGraph} from "./message-graph.ts";
const node = (id, parentMessageId, createdAt) => ({
id, parentMessageId, role: id.startsWith("a") ? "assistant" : "user", parts: [],
origin: {type: "legacy"}, completion: {status: "complete"}, createdAt, completedAt: createdAt
});
describe("message graph", () => {
const root = node("u1", null, "2026-01-01T00:00:00Z");
const answerA = node("a1", "u1", "2026-01-01T00:00:01Z");
const answerB = node("a2", "u1", "2026-01-01T00:00:02Z");
const followupB = node("u2", "a2", "2026-01-01T00:00:03Z");
const graph = mergeMessageGraph([root, answerA], [answerB, followupB]);
test("sorts sibling branches deterministically", () => {
expect(messageChildrenInGraph(graph, "u1").map((message) => message.id)).toEqual(["a1", "a2"]);
});
test("builds the selected path without changing a ref", () => {
expect(messagePathInGraph(graph, "u2").map((message) => message.id)).toEqual(["u1", "a2", "u2"]);
});
test("uses the current ref for its branch and newest descendants for alternatives", () => {
expect(newestBranchTipInGraph(graph, "a1", new Set(["u1", "a1"]), "a1")).toBe("a1");
expect(newestBranchTipInGraph(graph, "a2", new Set(["u1", "a1"]), "a1")).toBe("u2");
});
test("does not mix unrelated root messages into first-message edit branches", () => {
const editedRoot = {...node("u3", null, "2026-01-01T00:00:04Z"), origin: {type: "user", sourceMessageId: "u1"}};
const unrelated = node("u4", null, "2026-01-01T00:00:05Z");
const roots = mergeMessageGraph([...graph.values()], [editedRoot, unrelated]);
expect(rootEditAlternativesInGraph(roots, "u3").map((message) => message.id)).toEqual(["u1", "u3"]);
});
});