/*
Unit tests for fingerprints covering its documented public contract and regression-sensitive edge cases.
- Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only.
- RFC-0364: initial fixture suite covering TypeScript, JSON, Markdown, and binary.
*/
import { test, expect } from "vitest";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { byteHash, stableJsonHash } from "../index.ts";
import { fingerprintFile } from "../semantic.ts";
const fixturesDir = path.join(path.dirname(fileURLToPath(import.meta.url)), "fixtures");
async function semanticHash(relPath: string): Promise {
const result = await fingerprintFile(path.join(fixturesDir, relPath), { mode: "semantic" });
return result.hash;
}
async function byteHashFile(relPath: string): Promise {
const result = await fingerprintFile(path.join(fixturesDir, relPath), { mode: "byte" });
return result.hash;
}
test("TypeScript: comment-only changes keep the same semantic hash", async () => {
const before = await semanticHash("ts-comment.before.ts");
const after = await semanticHash("ts-comment.after.ts");
expect(before).toBe(after);
});
test("TypeScript: meaningful AST changes alter semantic hash", async () => {
const before = await semanticHash("ts-meaningful.before.ts");
const after = await semanticHash("ts-meaningful.after.ts");
expect(before).not.toBe(after);
});
test("JSON: key order changes keep the same semantic hash", async () => {
const before = await semanticHash("json-keyorder.before.json");
const after = await semanticHash("json-keyorder.after.json");
expect(before).toBe(after);
});
test("JSON: value changes alter semantic hash", async () => {
const before = await semanticHash("json-value.before.json");
const after = await semanticHash("json-value.after.json");
expect(before).not.toBe(after);
});
test("Markdown: trailing whitespace and HTML comments do not alter semantic hash", async () => {
const before = await semanticHash("md-comment.before.md");
const after = await semanticHash("md-comment.after.md");
expect(before).toBe(after);
});
test("Markdown: fenced code block changes alter semantic hash", async () => {
const before = await semanticHash("md-codefence.before.md");
const after = await semanticHash("md-codefence.after.md");
expect(before).not.toBe(after);
});
test("Binary: byte changes alter byte hash", async () => {
const before = await byteHashFile("binary.before.bin");
const after = await byteHashFile("binary.after.bin");
expect(before).not.toBe(after);
});
test("byteHash produces sha256-prefixed hex", () => {
const hash = byteHash("test");
expect(hash).toMatch(/^sha256:[0-9a-f]{64}$/);
});
test("stableJsonHash is deterministic regardless of key order", () => {
const a = stableJsonHash({ b: 2, a: 1 });
const b = stableJsonHash({ a: 1, b: 2 });
expect(a).toBe(b);
});