import { describe, expect, it } from "vitest"; import { findRoutes, properties } from "../../src/lab/catalog"; import { parseData, runRoundTrip, serializeData } from "../../src/lab/data"; import { parseEvidence, serializeEvidence } from "../../src/lab/evidence"; const all = properties.data.map((property) => property.id); const sample = JSON.stringify([ { id: 1, active: true, nullable: null, nested: { score: 1.25 }, text: "Café 🚲", }, ]); describe("structured data lab", () => { it("measures typed XML round trips without claiming metadata", () => { const route = findRoutes("data", "json", "xml", all)[0]!; const result = runRoundTrip(sample, route); expect(result.targetText).toContain( 'true', ); expect( result.measurements.filter( (measurement) => measurement.status === "measured-changed", ), ).toEqual([]); expect(result.measurements.at(-1)).toMatchObject({ property: "metadata", status: "not-tested", }); expect(parseEvidence(serializeEvidence(result.evidence))).toEqual( result.evidence, ); expect(result.evidence).toMatchObject({ contractVersion: 1, provenance: { execution: "local-browser", networkRequired: false }, }); }); it("exposes CSV type, null and nested-value changes", () => { const route = findRoutes("data", "json", "csv", all)[0]!; const result = runRoundTrip(sample, route); const changed = result.measurements .filter((measurement) => measurement.status === "measured-changed") .map((measurement) => measurement.property); expect(changed).toEqual( expect.arrayContaining(["scalar-types", "nulls", "nested", "numbers"]), ); expect(result.notes.join(" ")).toContain("CSV cells"); }); it("neutralizes spreadsheet formulas and keeps the change measurable", () => { const document = parseData('[{"value":" =2+2"}]', "json"); const csv = serializeData(document, "csv"); expect(csv).toContain("' =2+2"); const route = findRoutes("data", "json", "csv", all)[0]!; expect( runRoundTrip('[{"value":" =2+2"}]', route).measurements.find( (measurement) => measurement.property === "unicode", ), ).toMatchObject({ status: "measured-changed" }); }); it("imports generic XML honestly and rejects active or dangerous input", () => { const generic = parseData( "1Ada", "xml", ); expect(generic.rows).toEqual([{ id: "1", name: "Ada" }]); expect(generic.notes.join(" ")).toMatch(/string fields/u); expect(() => parseData(']>', "xml"), ).toThrow(/DOCTYPE/u); expect(() => parseData("__proto__,name\nvalue,Ada", "csv")).toThrow( /Dangerous/u, ); }); it("rejects duplicate headings and non-record JSON", () => { expect(() => parseData("id,id\n1,2", "csv")).toThrow(/unique/u); expect(() => parseData("[1,2]", "json")).toThrow(/Record 1/u); }); });