36 lines
1.8 KiB
JavaScript
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"]);
|
|
});
|
|
});
|