447a1170ce
Task 2 of the native-surfaces migration plan. GitBaselineAdapter resolves a
document's HEAD blob via the built-in vscode.git extension (hardened with a
.git/logs/HEAD watch that nudges repo.status(), since the git extension's own
watcher doesn't reliably notice out-of-band commits on non-workspace-folder
repos in the E2E host). DiffViewController is reworked into a baseline router:
head mode (git-tracked, never persisted, re-read on commit) vs snapshot mode
(captured on CoeditingRegistry entry, re-pinned by "Mark Changes as Reviewed"
— renamed from cowriting.pinDiffBaseline, D14). The shipped machine-landing
baseline advance (#48/INV-18) is retired: a landed Claude edit now stays a
visible change-since-baseline until commit or review (INV-7/D21). Legacy
on-disk reasons ("opened"/"machine-landing") migrate to "entered" via
BaselineStore.normalizeReason on load.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
98 lines
3.8 KiB
TypeScript
98 lines
3.8 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
import * as fs from "node:fs";
|
|
import * as os from "node:os";
|
|
import * as path from "node:path";
|
|
import { BaselineStore, normalizeReason, type Baseline } from "../src/baselineStore";
|
|
|
|
let dir: string;
|
|
|
|
beforeEach(() => {
|
|
dir = fs.mkdtempSync(path.join(os.tmpdir(), "baseline-store-"));
|
|
});
|
|
afterEach(() => {
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
});
|
|
|
|
const KEY = "a1b2c3"; // a stand-in for the controller's sha256(uri) key
|
|
|
|
function sample(uri = "file:///ws/notes/chapter-1.md"): Baseline {
|
|
return { uri, text: "hello\nworld\n", capturedAt: "2026-06-11T00:00:00.000Z", reason: "entered" };
|
|
}
|
|
|
|
describe("normalizeReason (INV-7 legacy migration)", () => {
|
|
it("migrates legacy 'opened' → 'entered'", () => {
|
|
expect(normalizeReason("opened")).toBe("entered");
|
|
});
|
|
it("migrates legacy 'machine-landing' → 'entered'", () => {
|
|
expect(normalizeReason("machine-landing")).toBe("entered");
|
|
});
|
|
it("passes current reasons through unchanged", () => {
|
|
expect(normalizeReason("entered")).toBe("entered");
|
|
expect(normalizeReason("pinned")).toBe("pinned");
|
|
expect(normalizeReason("head")).toBe("head");
|
|
});
|
|
});
|
|
|
|
describe("BaselineStore", () => {
|
|
it("returns null for a key with no baseline", () => {
|
|
expect(new BaselineStore(dir).load(KEY)).toBeNull();
|
|
});
|
|
|
|
it("computes the baseline path as baselines/<key>.json", () => {
|
|
const store = new BaselineStore(dir);
|
|
expect(store.baselinePath(KEY)).toBe(path.join(dir, "baselines", "a1b2c3.json"));
|
|
});
|
|
|
|
it("save then load round-trips the baseline (including the uri identity)", () => {
|
|
const store = new BaselineStore(dir);
|
|
const b = sample();
|
|
store.save(KEY, b);
|
|
expect(store.load(KEY)).toEqual(b);
|
|
});
|
|
|
|
it("migrates a legacy on-disk reason ('opened') to 'entered' on load", () => {
|
|
const store = new BaselineStore(dir);
|
|
fs.mkdirSync(path.dirname(store.baselinePath(KEY)), { recursive: true });
|
|
fs.writeFileSync(
|
|
store.baselinePath(KEY),
|
|
JSON.stringify({ uri: "file:///a.md", text: "legacy\n", capturedAt: "2026-06-11T00:00:00.000Z", reason: "opened" }),
|
|
"utf8",
|
|
);
|
|
expect(store.load(KEY)?.reason).toBe("entered");
|
|
});
|
|
|
|
it("migrates a legacy on-disk reason ('machine-landing') to 'entered' on load", () => {
|
|
const store = new BaselineStore(dir);
|
|
fs.mkdirSync(path.dirname(store.baselinePath(KEY)), { recursive: true });
|
|
fs.writeFileSync(
|
|
store.baselinePath(KEY),
|
|
JSON.stringify({ uri: "file:///a.md", text: "legacy\n", capturedAt: "2026-06-11T00:00:00.000Z", reason: "machine-landing" }),
|
|
"utf8",
|
|
);
|
|
expect(store.load(KEY)?.reason).toBe("entered");
|
|
});
|
|
|
|
it("overwrites in place: the newest epoch wins, no history kept", () => {
|
|
const store = new BaselineStore(dir);
|
|
store.save(KEY, { uri: "untitled:Untitled-1", text: "v1", capturedAt: "2026-06-11T00:00:00.000Z", reason: "entered" });
|
|
store.save(KEY, { uri: "untitled:Untitled-1", text: "v2", capturedAt: "2026-06-11T00:01:00.000Z", reason: "pinned" });
|
|
expect(store.load(KEY)).toEqual({ uri: "untitled:Untitled-1", text: "v2", capturedAt: "2026-06-11T00:01:00.000Z", reason: "pinned" });
|
|
});
|
|
|
|
it("writes pretty JSON with a trailing newline", () => {
|
|
const store = new BaselineStore(dir);
|
|
const b = sample();
|
|
store.save(KEY, b);
|
|
const raw = fs.readFileSync(store.baselinePath(KEY), "utf8");
|
|
expect(raw).toBe(JSON.stringify(b, null, 2) + "\n");
|
|
});
|
|
|
|
it("distinct keys are independent files (no collision)", () => {
|
|
const store = new BaselineStore(dir);
|
|
store.save("k1", sample("file:///a.md"));
|
|
store.save("k2", sample("file:///b.md"));
|
|
expect(store.load("k1")!.uri).toBe("file:///a.md");
|
|
expect(store.load("k2")!.uri).toBe("file:///b.md");
|
|
});
|
|
});
|