import { describe, expect, it } from "vitest";
import { strToU8, zipSync } from "fflate";
import { analyzePackage } from "../../src/package/analyze";
import { compareSemanticEntry } from "../../src/package/content-compare";
import { verifyPackageContents } from "../../src/package/content-compare";
const bytes = (value: string) => new TextEncoder().encode(value);
describe("bounded semantic package comparison", () => {
it("hashes verified decompressed bytes instead of ZIP representation", async () => {
const contents = { "same.txt": strToU8("same decompressed value") };
const left = await analyzePackage(
new File([zipSync(contents, { level: 0 })], "left.zip"),
);
const right = await analyzePackage(
new File([zipSync(contents, { level: 9 })], "right.zip"),
);
const evidence = await verifyPackageContents(left, right);
expect(evidence["same.txt"]?.leftSha256).toMatch(/^[0-9a-f]{64}$/u);
expect(evidence["same.txt"]?.leftSha256).toBe(
evidence["same.txt"]?.rightSha256,
);
});
it("recognizes equivalent JSON serialization and reports value paths", () => {
expect(
compareSemanticEntry(
"manifest.json",
bytes('{"name":"demo","items":[1,2]}'),
bytes('{\n "items": [1, 2], "name": "demo"\n}'),
),
).toMatchObject({ kind: "json", equivalent: true });
expect(
compareSemanticEntry(
"manifest.json",
bytes('{"name":"before"}'),
bytes('{"name":"after"}'),
)?.differences,
).toContain("$.name: value changed");
});
it("normalizes safe XML attributes but rejects active declarations", () => {
expect(
compareSemanticEntry(
"content.xml",
bytes('- value
'),
bytes('\n- value
\n'),
),
).toMatchObject({ kind: "xml", equivalent: true });
expect(
compareSemanticEntry(
"content.xml",
bytes(""),
bytes(""),
)?.summary,
).toMatch(/could not/u);
});
});