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('\nvalue\n'), ), ).toMatchObject({ kind: "xml", equivalent: true }); expect( compareSemanticEntry( "content.xml", bytes(""), bytes(""), )?.summary, ).toMatch(/could not/u); }); });