import { describe, it, expect, beforeEach, afterEach } from "vitest"; import { mkdtempSync, writeFileSync, mkdirSync, rmSync, readFileSync, } from "fs"; import { join } from "path"; import { tmpdir } from "os"; import { initProject } from "../src/init.js"; import { patchFile } from "../src/patch.js"; import { validateMaps } from "../src/validate.js"; describe("integration", () => { let dir: string; beforeEach(() => { dir = mkdtempSync(join(tmpdir(), "pi-map-int-")); }); afterEach(() => { rmSync(dir, { recursive: true }); }); it("init creates .pi-map.md files", async () => { mkdirSync(join(dir, "src")); writeFileSync(join(dir, "src", "index.ts"), `export function foo() {}\n`); await initProject(dir); const map = readFileSync(join(dir, "src", ".pi-map.md"), "utf8"); expect(map).toContain("# src"); expect(map).toContain("foo"); }); it("patch updates a file entry", async () => { mkdirSync(join(dir, "src")); writeFileSync(join(dir, "src", "index.ts"), `export function foo() {}\n`); writeFileSync(join(dir, "src", "utils.ts"), `export const bar = 1;\n`); await initProject(dir); // Modify a file writeFileSync( join(dir, "src", "index.ts"), `export function foo() {}\nexport function baz() {}\n`, ); await patchFile(join(dir, "src", "index.ts")); const map = readFileSync(join(dir, "src", ".pi-map.md"), "utf8"); expect(map).toContain("baz"); }); it("patch adds dirty marker for large packages", async () => { mkdirSync(join(dir, "src")); // Create 11 files so it's a "large" package for (let i = 0; i < 11; i++) { writeFileSync( join(dir, "src", `file${i}.ts`), `export const x${i} = ${i};\n`, ); } await initProject(dir); // Modify a file writeFileSync( join(dir, "src", "file0.ts"), `export const x0 = 0;\nexport const y = 99;\n`, ); await patchFile(join(dir, "src", "file0.ts")); const map = readFileSync(join(dir, "src", ".pi-map.md"), "utf8"); expect(map).toContain("y"); expect(map).toContain("dirty"); expect(map).toContain("patched"); }); it("validate detects new files", async () => { mkdirSync(join(dir, "src")); writeFileSync(join(dir, "src", "a.ts"), `export const a = 1;\n`); await initProject(dir); // Add new file writeFileSync(join(dir, "src", "b.ts"), `export const b = 2;\n`); const result = await validateMaps(dir); expect(result.clean).toBe(false); expect(result.discrepancies.some((d) => d.type === "missing")).toBe(true); }); it("validate detects deleted files", async () => { mkdirSync(join(dir, "src")); writeFileSync(join(dir, "src", "a.ts"), `export const a = 1;\n`); writeFileSync(join(dir, "src", "b.ts"), `export const b = 2;\n`); await initProject(dir); // Delete a file rmSync(join(dir, "src", "b.ts")); const result = await validateMaps(dir); expect(result.clean).toBe(false); expect(result.discrepancies.some((d) => d.type === "orphaned")).toBe(true); }); it("validate detects changed signatures", async () => { mkdirSync(join(dir, "src")); writeFileSync(join(dir, "src", "a.ts"), `export const a = 1;\n`); await initProject(dir); // Change exports writeFileSync( join(dir, "src", "a.ts"), `export const a = 1;\nexport const c = 3;\n`, ); const result = await validateMaps(dir); expect(result.clean).toBe(false); expect(result.discrepancies.some((d) => d.type === "stale-signature")).toBe( true, ); }); });