/** * KHAELOR * File: tests/workspace/file-times.test.ts * Description: Unit tests for the FileTimeRegistry — read-before-write and external-modification detection. * * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import * as path from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { InMemoryFileTimeRegistry } from "../../src/workspace/index.js"; let dir: string; let registry: InMemoryFileTimeRegistry; beforeEach(() => { dir = mkdtempSync(path.join(tmpdir(), "khaelor-ft-")); registry = new InMemoryFileTimeRegistry(); }); afterEach(() => { rmSync(dir, { recursive: true, force: true }); }); describe("FileTimeRegistry", () => { it("reports unread for files never stamped", () => { expect(registry.check(path.join(dir, "unknown.ts"), "anything")).toBe("unread"); expect(registry.get(path.join(dir, "unknown.ts"))).toBeUndefined(); }); it("reports clean when disk content matches the stamped content", () => { const file = path.join(dir, "a.ts"); writeFileSync(file, "const a = 1;\n"); registry.stamp(file, "const a = 1;\n"); expect(registry.check(file, "const a = 1;\n")).toBe("clean"); }); it("detects external modification when content changed since the stamp", () => { const file = path.join(dir, "b.ts"); writeFileSync(file, "original\n"); registry.stamp(file, "original\n"); writeFileSync(file, "changed by the user\n"); expect(registry.check(file, "changed by the user\n")).toBe("externally-modified"); }); it("records path, size, sha256, mtime and timestamp", () => { const file = path.join(dir, "c.ts"); writeFileSync(file, "abc"); const before = Date.now(); registry.stamp(file, "abc"); const stamp = registry.get(file); expect(stamp).toBeDefined(); expect(stamp?.path).toBe(file); expect(stamp?.size).toBe(3); expect(stamp?.sha256).toMatch(/^[0-9a-f]{64}$/); expect(stamp?.mtimeMs).toBeGreaterThan(0); expect(stamp?.at).toBeGreaterThanOrEqual(before); }); it("re-stamping after an agent write returns the file to clean", () => { const file = path.join(dir, "d.ts"); writeFileSync(file, "v1"); registry.stamp(file, "v1"); writeFileSync(file, "v2"); expect(registry.check(file, "v2")).toBe("externally-modified"); registry.stamp(file, "v2"); expect(registry.check(file, "v2")).toBe("clean"); }); it("normalizes paths so relative and absolute forms agree", () => { const file = path.join(dir, "e.ts"); writeFileSync(file, "x"); registry.stamp(file, "x"); expect(registry.get(path.join(dir, ".", "e.ts"))?.path).toBe(file); }); });