1import { ThreadId } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3import { collectComposerContextReferences } from "@t3tools/shared/composerContextReferences";
4import { terminalContextRecord } from "./composerContextRecords";
5
6import {
7 filterTerminalContextsWithText,
8 formatTerminalContextLabel,
9 formatTerminalContextReference,
10 hasTerminalContextText,
11 INLINE_TERMINAL_CONTEXT_PLACEHOLDER,
12 isTerminalContextExpired,
13 migrateLegacyTerminalContextPlaceholders,
14 type TerminalContextDraft,
15} from "./terminalContext";
16
17function makeContext(overrides?: Partial<TerminalContextDraft>): TerminalContextDraft {
18 return {
19 id: "context-1",
20 threadId: ThreadId.make("thread-1"),
21 terminalId: "default",
22 terminalLabel: "Terminal 1",
23 lineStart: 12,
24 lineEnd: 13,
25 text: "git status\nOn branch main",
26 createdAt: "2026-03-13T12:00:00.000Z",
27 ...overrides,
28 };
29}
30
31describe("terminalContext", () => {
32 it("folds legacy producer ids consistently in records and references", () => {
33 const context = makeContext({ id: "old terminal:one" });
34 const reference = collectComposerContextReferences(formatTerminalContextReference(context))[0];
35 expect(reference).toBeDefined();
36 expect(reference?.contextId).toBe(terminalContextRecord(context).contextId);
37 });
38 it("formats terminal labels with line ranges", () => {
39 expect(formatTerminalContextLabel(makeContext())).toBe("Terminal 1 lines 12-13");
40 expect(
41 formatTerminalContextLabel(
42 makeContext({
43 lineStart: 9,
44 lineEnd: 9,
45 }),
46 ),
47 ).toBe("Terminal 1 line 9");
48 });
49
50 it("formats a terminal context as a canonical reference link", () => {
51 expect(formatTerminalContextReference(makeContext())).toBe(
52 "[Terminal 1 lines 12-13](t3-context://v1/terminal/terminal_context-1)",
53 );
54 });
55
56 it("migrates legacy placeholders to references in order and drops extras", () => {
57 const placeholder = INLINE_TERMINAL_CONTEXT_PLACEHOLDER;
58 const first = formatTerminalContextReference(makeContext());
59 const contexts = [makeContext()];
60 expect(
61 migrateLegacyTerminalContextPlaceholders(`a ${placeholder} b ${placeholder}`, contexts),
62 ).toBe(`a ${first} b `);
63 expect(migrateLegacyTerminalContextPlaceholders("plain", contexts)).toBe("plain");
64 });
65
66 it("marks contexts without snapshot text as expired and filters them from sendable contexts", () => {
67 const liveContext = makeContext();
68 const expiredContext = makeContext({
69 id: "context-2",
70 text: "",
71 });
72
73 expect(hasTerminalContextText(liveContext)).toBe(true);
74 expect(isTerminalContextExpired(liveContext)).toBe(false);
75 expect(hasTerminalContextText(expiredContext)).toBe(false);
76 expect(isTerminalContextExpired(expiredContext)).toBe(true);
77 expect(filterTerminalContextsWithText([expiredContext, liveContext])).toEqual([liveContext]);
78 });
79});