/** * wrap_dsse.test.ts — Vitest tests for W3C VC JSON-LD wrapping */ import { describe, it, expect } from "vitest"; import { wrapDSSEinVC, buildVerifiablePresentation, batchWrapReceipts, wrapForSCITT, serializeVC, prettySerializeVC, type SZLReceiptRecord, } from "./wrap_dsse.js"; import { generateKeyPairSync } from "node:crypto"; // --------------------------------------------------------------------------- // Fixtures // --------------------------------------------------------------------------- const SAMPLE_RECEIPT: SZLReceiptRecord = { receiptId: "szl-2026-001", timestamp: "2026-05-29T22:00:00Z", organId: "sentra", action: "deploy", outcome: "success", operatorDID: "did:key:z6MkHaXVHmX5w9RJqKYyDfAcgM6r8s1W", policyRef: "https://szl.io/policy/doctrine-v6", envelope: { payloadType: "application/vnd.szl.receipt.v1+json", payload: Buffer.from( JSON.stringify({ receiptId: "szl-2026-001", organ: "sentra", action: "deploy", }) ).toString("base64url"), signatures: [{ sig: "dGVzdHNpZw==", keyid: "szl-dev-key-v1" }], }, rekorAttestation: { uuid: "382898f8ae6ab0b4bf51dbf1b1df8f10b4ba2b5a8d8e47534c6e7e0ea7a2e4c", logIndex: 42_000_000, rekorEntryUrl: "https://rekor.sigstore.dev/api/v1/log/entries/382898f8ae6ab0b4", }, }; // --------------------------------------------------------------------------- // @context compliance (W3C VC 2.0) // --------------------------------------------------------------------------- describe("wrapDSSEinVC — W3C VC 2.0 compliance", () => { it("produces a valid @context array starting with W3C credentials URL", () => { const vc = wrapDSSEinVC(SAMPLE_RECEIPT); expect(Array.isArray(vc["@context"])).toBe(true); expect(vc["@context"][0]).toBe("https://www.w3.org/ns/credentials/v2"); }); it("type includes VerifiableCredential and SZLGovernanceReceipt", () => { const vc = wrapDSSEinVC(SAMPLE_RECEIPT); expect(vc.type).toContain("VerifiableCredential"); expect(vc.type).toContain("SZLGovernanceReceipt"); }); it("id is a urn:szl:receipt: URI", () => { const vc = wrapDSSEinVC(SAMPLE_RECEIPT); expect(vc.id).toBe("urn:szl:receipt:szl-2026-001"); }); it("issuer.id is a DID", () => { const vc = wrapDSSEinVC(SAMPLE_RECEIPT); expect(vc.issuer.id).toMatch(/^did:/); }); it("validFrom is an ISO-8601 string", () => { const vc = wrapDSSEinVC(SAMPLE_RECEIPT); expect(vc.validFrom).toBe("2026-05-29T22:00:00Z"); }); it("credentialSubject contains organId, action, payloadHash", () => { const vc = wrapDSSEinVC(SAMPLE_RECEIPT); expect(vc.credentialSubject.organId).toBe("sentra"); expect(vc.credentialSubject.action).toBe("deploy"); expect(vc.credentialSubject.payloadHash).toHaveLength(64); // hex SHA-256 }); it("credentialSubject.id is set to operatorDID", () => { const vc = wrapDSSEinVC(SAMPLE_RECEIPT); expect(vc.credentialSubject.id).toBe( "did:key:z6MkHaXVHmX5w9RJqKYyDfAcgM6r8s1W" ); }); it("embeds rekorAttestation when present", () => { const vc = wrapDSSEinVC(SAMPLE_RECEIPT); expect(vc.credentialSubject.rekorAttestation?.uuid).toBe( "382898f8ae6ab0b4bf51dbf1b1df8f10b4ba2b5a8d8e47534c6e7e0ea7a2e4c" ); }); it("omits rekorAttestation when not in receipt", () => { const r = { ...SAMPLE_RECEIPT, rekorAttestation: undefined }; const vc = wrapDSSEinVC(r); expect(vc.credentialSubject.rekorAttestation).toBeUndefined(); }); }); // --------------------------------------------------------------------------- // Proof tests // --------------------------------------------------------------------------- describe("wrapDSSEinVC — proof", () => { it("uses DSSESignature2024 when no signingKeyPem", () => { const vc = wrapDSSEinVC(SAMPLE_RECEIPT); expect(vc.proof.type).toBe("DataIntegrityProof"); expect(vc.proof.cryptosuite).toBe("DSSESignature2024"); expect(vc.proof.proofPurpose).toBe("assertionMethod"); }); it("uses ecdsa-rdfc-2022 when signingKeyPem provided", () => { const { privateKey } = generateKeyPairSync("ec", { namedCurve: "P-256", privateKeyEncoding: { type: "pkcs8", format: "pem" }, publicKeyEncoding: { type: "spki", format: "pem" }, }); const vc = wrapDSSEinVC(SAMPLE_RECEIPT, { signingKeyPem: privateKey as string, }); expect(vc.proof.cryptosuite).toBe("ecdsa-rdfc-2022"); expect(vc.proof.proofValue).toMatch(/^z/); // base58btc multibase prefix }); it("proof.created is a valid ISO date", () => { const vc = wrapDSSEinVC(SAMPLE_RECEIPT); expect(new Date(vc.proof.created).getTime()).not.toBeNaN(); }); it("proof.verificationMethod defaults to did:web:szl.io#key-1", () => { const vc = wrapDSSEinVC(SAMPLE_RECEIPT); expect(vc.proof.verificationMethod).toBe("did:web:szl.io#key-1"); }); it("accepts custom verificationMethod", () => { const vc = wrapDSSEinVC(SAMPLE_RECEIPT, { verificationMethod: "did:key:z6Mk123#key-1", }); expect(vc.proof.verificationMethod).toBe("did:key:z6Mk123#key-1"); }); }); // --------------------------------------------------------------------------- // Additional types // --------------------------------------------------------------------------- describe("wrapDSSEinVC — additionalTypes", () => { it("includes additional types in vc.type array", () => { const vc = wrapDSSEinVC(SAMPLE_RECEIPT, { additionalTypes: ["SCITTReceipt", "EUDIWCredential"], }); expect(vc.type).toContain("SCITTReceipt"); expect(vc.type).toContain("EUDIWCredential"); }); }); // --------------------------------------------------------------------------- // Verifiable Presentation // --------------------------------------------------------------------------- describe("buildVerifiablePresentation", () => { it("wraps multiple VCs in a VP", () => { const vc1 = wrapDSSEinVC(SAMPLE_RECEIPT); const vc2 = wrapDSSEinVC({ ...SAMPLE_RECEIPT, receiptId: "szl-2026-002" }); const vp = buildVerifiablePresentation([vc1, vc2], "did:web:szl.io"); expect(vp.type).toContain("VerifiablePresentation"); expect(vp.verifiableCredential).toHaveLength(2); expect(vp.holder).toBe("did:web:szl.io"); expect(vp["@context"][0]).toBe("https://www.w3.org/ns/credentials/v2"); }); }); // --------------------------------------------------------------------------- // Batch + SCITT // --------------------------------------------------------------------------- describe("batchWrapReceipts", () => { it("converts multiple receipts to VCs", () => { const receipts = [ SAMPLE_RECEIPT, { ...SAMPLE_RECEIPT, receiptId: "szl-2026-003" }, ]; const vcs = batchWrapReceipts(receipts); expect(vcs).toHaveLength(2); expect(vcs[0].id).toBe("urn:szl:receipt:szl-2026-001"); expect(vcs[1].id).toBe("urn:szl:receipt:szl-2026-003"); }); }); describe("wrapForSCITT", () => { it("returns a VP with SZLGovernanceAuditPresentation type", () => { const vp = wrapForSCITT([SAMPLE_RECEIPT], "did:web:szl.io"); expect(vp.type).toContain("SZLGovernanceAuditPresentation"); expect(vp.id).toMatch(/^urn:szl:scitt:/); expect(vp.holder).toBe("did:web:szl.io"); }); }); // --------------------------------------------------------------------------- // Serialization // --------------------------------------------------------------------------- describe("serialization helpers", () => { it("serializeVC produces valid JSON parseable to original shape", () => { const vc = wrapDSSEinVC(SAMPLE_RECEIPT); const json = serializeVC(vc); const parsed = JSON.parse(json); expect(parsed.id).toBe(vc.id); expect(parsed.proof).toBeDefined(); }); it("prettySerializeVC produces formatted JSON", () => { const vc = wrapDSSEinVC(SAMPLE_RECEIPT); const pretty = prettySerializeVC(vc); expect(pretty).toContain("\n"); expect(pretty).toContain(" "); }); });