import { describe, it, expect, afterEach } from "vitest";
import { mkdtempSync, writeFileSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { persistHfIdsIfNeeded, stampFileHfIds } from "./hfIdPersist.js";
describe("persistHfIdsIfNeeded", () => {
const tmpDirs: string[] = [];
afterEach(() => {
for (const d of tmpDirs) rmSync(d, { recursive: true, force: true });
tmpDirs.length = 0;
});
function tmpFile(content: string): string {
const dir = mkdtempSync(join(tmpdir(), "hfid-test-"));
tmpDirs.push(dir);
const file = join(dir, "index.html");
writeFileSync(file, content, "utf-8");
return file;
}
it("writes data-hf-id to disk when source is untagged", () => {
const raw = `
hello
`;
const file = tmpFile(raw);
const returned = persistHfIdsIfNeeded(file, raw);
expect(returned).toContain('data-hf-id="hf-');
const onDisk = readFileSync(file, "utf-8");
expect(onDisk).toContain('data-hf-id="hf-');
expect(onDisk).toBe(returned);
});
it("does not rewrite disk when source is already tagged", () => {
const raw = `hello
`;
const file = tmpFile(raw);
const tagged = persistHfIdsIfNeeded(file, raw);
const diskAfterFirst = readFileSync(file, "utf-8");
const returned2 = persistHfIdsIfNeeded(file, tagged);
expect(returned2).toBe(tagged);
expect(readFileSync(file, "utf-8")).toBe(diskAfterFirst);
});
it("does not rewrite when source is already tagged with non-standard HTML formatting", () => {
// Single-quoted attrs would cause a false-positive write under string-equality
// change detection; count-based detection handles this correctly.
const alreadyTagged = `hello
`;
const file = tmpFile(alreadyTagged);
persistHfIdsIfNeeded(file, alreadyTagged);
expect(readFileSync(file, "utf-8")).toBe(alreadyTagged);
});
it("returned id matches id written to disk (serve-time == persist-time invariant)", () => {
const raw = `text`;
const file = tmpFile(raw);
const result = persistHfIdsIfNeeded(file, raw);
const onDisk = readFileSync(file, "utf-8");
expect(result).toBe(onDisk);
});
it("skips write if file was modified concurrently (TOCTOU guard)", () => {
const old = `original
`;
const newer = `modified by user
`;
// Disk has newer content — simulates a concurrent save after the server read old.
const file = tmpFile(newer);
const returned = persistHfIdsIfNeeded(file, old);
// Serve-time HTML gets ids based on what we read.
expect(returned).toContain('data-hf-id="hf-');
// Disk must not be overwritten — user's concurrent save is preserved.
expect(readFileSync(file, "utf-8")).toBe(newer);
});
});
describe("stampFileHfIds", () => {
const tmpDirs: string[] = [];
afterEach(() => {
for (const d of tmpDirs) rmSync(d, { recursive: true, force: true });
tmpDirs.length = 0;
});
function tmpFile(content: string): string {
const dir = mkdtempSync(join(tmpdir(), "hfid-stamp-test-"));
tmpDirs.push(dir);
const file = join(dir, "scene.html");
writeFileSync(file, content, "utf-8");
return file;
}
it("stamps ids and writes back through the same fd", () => {
const file = tmpFile(`Hi
`);
const returned = stampFileHfIds(file);
expect(returned).toContain('data-hf-id="hf-');
expect(readFileSync(file, "utf-8")).toBe(returned);
});
it("does not rewrite an already-stamped file", () => {
const file = tmpFile(`Hi
`);
const before = readFileSync(file, "utf-8");
const returned = stampFileHfIds(file);
expect(returned).toContain('data-hf-id="hf-keep"');
expect(readFileSync(file, "utf-8")).toBe(before); // byte-identical, no write
});
it("returns null for a missing file", () => {
const dir = mkdtempSync(join(tmpdir(), "hfid-stamp-test-"));
tmpDirs.push(dir);
expect(stampFileHfIds(join(dir, "nope.html"))).toBeNull();
});
it("returns null for a directory", () => {
const dir = mkdtempSync(join(tmpdir(), "hfid-stamp-test-"));
tmpDirs.push(dir);
expect(stampFileHfIds(dir)).toBeNull();
});
});