Files
stack/extensions/goal/test/display.test.ts
T

66 lines
3.4 KiB
TypeScript

import assert from "node:assert/strict";
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { test } from "node:test";
import { goalDisplay, goalDetails } from "../lib/display.ts";
import { initialState, setGoal, pauseGoal, blockGoal, resumeGoal, completeGoal, clearGoal } from "../lib/state.ts";
import { saveState, loadState } from "../lib/store.ts";
const text = "Long goal\n" + "Full acceptance criteria, never truncate. ".repeat(100) + "\nEND-OF-GOAL";
test("footer maps each lifecycle state to text and a theme role", () => {
const active = setGoal(initialState(), text);
for (const [state, label, color] of [
[initialState(), "None", "muted"],
[active, "Active", "accent"],
[{ ...active, activeWait: { owner: "reviewer" } }, "Waiting", "warning"],
[{ ...active, activeWait: { owner: "reviewer", wakeSent: true } }, "Active", "accent"],
[pauseGoal(active, "operator stop"), "Paused", "warning"],
[pauseGoal(active, "blocked: legacy reason"), "Blocked", "error"],
[blockGoal(active, "missing permission"), "Blocked", "error"],
[completeGoal(active, "verified"), "Complete", "success"],
] as const) assert.deepEqual(goalDisplay(state), { label, color });
});
test("full multiline goal survives completion, persistence, and read-only recall", () => {
const dir = mkdtempSync(join(tmpdir(), "goal-display-"));
try {
const active = setGoal(initialState(), text);
const complete = completeGoal(active, "verified ".repeat(300));
assert.equal(complete.lastOutcome?.text, text);
assert.equal(complete.lastOutcome?.evidence.length, 1000);
assert.equal(complete.status, "none");
assert.deepEqual(resumeGoal(complete), complete);
const path = join(dir, "state.json");
saveState(complete, path);
assert.deepEqual(loadState(path), complete);
for (const state of [active, blockGoal(active, "denied"), pauseGoal(active, "stop"), loadState(path)]) {
const before = JSON.stringify(state);
assert.ok(goalDetails(state).includes(text));
assert.equal(JSON.stringify(state), before);
}
assert.equal(goalDisplay(clearGoal(complete)).label, "None");
assert.equal(setGoal(complete, "replacement").lastOutcome, undefined);
assert.equal(resumeGoal(blockGoal(active, "denied")).status, "active");
} finally { rmSync(dir, { recursive: true, force: true }); }
});
test("old records and malformed outcome fields are safe to recall", () => {
const dir = mkdtempSync(join(tmpdir(), "goal-display-"));
const path = join(dir, "state.json");
try {
const old = pauseGoal(setGoal(initialState(), text), "blocked: legacy");
saveState(old, path);
assert.equal(goalDisplay(loadState(path)).label, "Blocked");
for (const lastOutcome of [null, {}, { status: "complete", text: 123 }, { status: "complete", text, evidence: "yes", at: "invalid" }]) {
saveState({ ...initialState(), lastOutcome } as any, path);
assert.equal(goalDisplay(loadState(path)).label, "None");
assert.doesNotThrow(() => goalDetails(loadState(path)));
}
saveState({ ...old, pausedReason: {} } as any, path);
assert.doesNotThrow(() => goalDetails(loadState(path)));
assert.doesNotThrow(() => goalDetails({ ...setGoal(initialState(), text), activeWait: { owner: "test", deadlineAt: 1e100 } }));
} finally { rmSync(dir, { recursive: true, force: true }); }
});