// Nightly: requires a non-core registry command. import { existsSync } from "node:fs"; import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import { NodeFileSystem, createKernel, createWasmVmRuntime, describeIf, } from "@rivet-dev/agentos-test-harness"; import type { Kernel } from "@rivet-dev/agentos-test-harness"; import { afterEach, expect, it } from "vitest"; const DIFF_COMMAND_DIR = fileURLToPath(new URL("../bin", import.meta.url)); const hasDiffPackageBinary = existsSync(join(DIFF_COMMAND_DIR, "diff")); let tempRoot: string | undefined; async function writeFixture(path: string, contents: string): Promise { if (!tempRoot) throw new Error("fixture root not initialized"); const hostPath = join(tempRoot, path.replace(/^\/+/, "")); await mkdir(dirname(hostPath), { recursive: true }); await writeFile(hostPath, contents); } async function createTestVFS(): Promise { tempRoot = await mkdtemp(join(tmpdir(), "agentos-diff-")); await writeFixture("/project/a.txt", "alpha\nbeta\ngamma\n"); await writeFixture("/project/b.txt", "alpha\ndelta\ngamma\n"); await writeFixture("/project/same.txt", "alpha\nbeta\ngamma\n"); await writeFixture("/project/ws-a.txt", "Alpha\n\nbeta value\n"); await writeFixture("/project/ws-b.txt", "alpha\n BETA VALUE\n"); await writeFixture("/project/left/common.txt", "one\ntwo\nthree\n"); await writeFixture("/project/right/common.txt", "one\n2\nthree\n"); await writeFixture("/project/right/extra.txt", "only on right\n"); return new NodeFileSystem({ root: tempRoot }); } describeIf(hasDiffPackageBinary, "diff command", { timeout: 10_000 }, () => { let kernel: Kernel | undefined; afterEach(async () => { await kernel?.dispose(); kernel = undefined; if (tempRoot) { await rm(tempRoot, { recursive: true, force: true }); tempRoot = undefined; } }); async function mountFixture(): Promise { const vfs = await createTestVFS(); kernel = createKernel({ filesystem: vfs }); await kernel.mount(createWasmVmRuntime({ commandDirs: [DIFF_COMMAND_DIR] })); } async function runDiff(args: string[]) { if (!kernel) throw new Error("kernel not mounted"); let stdout = ""; let stderr = ""; const proc = kernel.spawn("diff", args, { onStdout: (chunk) => { stdout += Buffer.from(chunk).toString("utf8"); }, onStderr: (chunk) => { stderr += Buffer.from(chunk).toString("utf8"); }, }); const exitCode = await proc.wait(); await new Promise((resolve) => setTimeout(resolve, 0)); return { stdout, stderr, exitCode }; } it("returns zero for identical files", async () => { await mountFixture(); const result = await runDiff(["/project/a.txt", "/project/same.txt"]); expect(result.exitCode, result.stderr || result.stdout).toBe(0); expect(result.stdout).toBe(""); }); it("prints normal diffs for changed files", async () => { await mountFixture(); const result = await runDiff(["/project/a.txt", "/project/b.txt"]); expect(result.exitCode).toBe(1); expect(result.stdout).toContain("2c2"); expect(result.stdout).toContain("< beta"); expect(result.stdout).toContain("> delta"); }); it("prints unified diffs", async () => { await mountFixture(); const result = await runDiff(["-u", "/project/a.txt", "/project/b.txt"]); expect(result.exitCode).toBe(1); expect(result.stdout).toContain("--- /project/a.txt"); expect(result.stdout).toContain("+++ /project/b.txt"); expect(result.stdout).toContain("-beta"); expect(result.stdout).toContain("+delta"); }); it("prints brief output with -q", async () => { await mountFixture(); const result = await runDiff(["-q", "/project/a.txt", "/project/b.txt"]); expect(result.exitCode).toBe(1); expect(result.stdout.trim()).toBe( "Files /project/a.txt and /project/b.txt differ", ); }); it("honors ignore-case, whitespace, and blank-line flags", async () => { await mountFixture(); const result = await runDiff([ "-i", "-w", "-B", "/project/ws-a.txt", "/project/ws-b.txt", ]); expect(result.exitCode, result.stderr || result.stdout).toBe(0); expect(result.stdout).toBe(""); }); it("compares directories recursively", async () => { await mountFixture(); const result = await runDiff(["-r", "/project/left", "/project/right"]); expect(result.exitCode).toBe(1); expect(result.stdout).toContain("Only in /project/right: extra.txt"); expect(result.stdout).toContain("/project/left/common.txt"); expect(result.stdout).toContain("/project/right/common.txt"); }); it("returns an error for missing inputs", async () => { await mountFixture(); const result = await runDiff(["/project/a.txt", "/project/missing.txt"]); expect(result.exitCode).toBe(2); expect(result.stderr).toContain("/project/missing.txt"); }); });