From 4f6a2804c4db86803be736e1f1be4b3db719fc70 Mon Sep 17 00:00:00 2001 From: ak68a Date: Wed, 25 Mar 2026 16:52:44 -0500 Subject: [PATCH 1/2] test(vc): add tests for isCredential and isStatusListCredential type guards Cover both type guard functions that previously had zero tests: isCredential: valid credentials, string issuer, optional fields, rejection of null/undefined/string/empty objects, and each required field missing individually. isStatusListCredential: valid status list credential, wrong subject type, missing encodedList, missing statusPurpose, regular credential without status list subject, and non-credential inputs. --- packages/vc/src/is-credential.test.ts | 83 +++++++++++++++++++ .../is-status-list-credential.test.ts | 81 ++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 packages/vc/src/is-credential.test.ts create mode 100644 packages/vc/src/revocation/is-status-list-credential.test.ts diff --git a/packages/vc/src/is-credential.test.ts b/packages/vc/src/is-credential.test.ts new file mode 100644 index 0000000..e40843e --- /dev/null +++ b/packages/vc/src/is-credential.test.ts @@ -0,0 +1,83 @@ +import { describe, expect, it } from "vitest" + +import { isCredential } from "./is-credential" + +// A minimal valid W3C credential +const validCredential = { + "@context": ["https://www.w3.org/2018/credentials/v1"], + type: ["VerifiableCredential"], + issuer: { id: "did:web:issuer.example.com" }, + issuanceDate: "2025-01-01T00:00:00.000Z", + credentialSubject: { id: "did:web:subject.example.com" }, +} + +describe("isCredential", () => { + it("returns true for a valid credential", () => { + expect(isCredential(validCredential)).toBe(true) + }) + + it("accepts issuer as a plain string", () => { + expect( + isCredential({ + ...validCredential, + issuer: "did:web:issuer.example.com", + }), + ).toBe(true) + }) + + it("accepts credentials with optional fields", () => { + expect( + isCredential({ + ...validCredential, + id: "urn:uuid:123", + expirationDate: "2030-01-01T00:00:00.000Z", + credentialStatus: { + id: "https://status.example.com/1", + type: "BitstringStatusListEntry", + }, + proof: { type: "JwtProof2020" }, + }), + ).toBe(true) + }) + + it("rejects null", () => { + expect(isCredential(null)).toBe(false) + }) + + it("rejects undefined", () => { + expect(isCredential(undefined)).toBe(false) + }) + + it("rejects a plain string", () => { + expect(isCredential("not a credential")).toBe(false) + }) + + it("rejects an empty object", () => { + expect(isCredential({})).toBe(false) + }) + + it("rejects when @context is missing", () => { + const { "@context": _, ...noContext } = validCredential + expect(isCredential(noContext)).toBe(false) + }) + + it("rejects when type is missing", () => { + const { type: _, ...noType } = validCredential + expect(isCredential(noType)).toBe(false) + }) + + it("rejects when issuer is missing", () => { + const { issuer: _, ...noIssuer } = validCredential + expect(isCredential(noIssuer)).toBe(false) + }) + + it("rejects when issuanceDate is missing", () => { + const { issuanceDate: _, ...noDate } = validCredential + expect(isCredential(noDate)).toBe(false) + }) + + it("rejects when credentialSubject is missing", () => { + const { credentialSubject: _, ...noSubject } = validCredential + expect(isCredential(noSubject)).toBe(false) + }) +}) diff --git a/packages/vc/src/revocation/is-status-list-credential.test.ts b/packages/vc/src/revocation/is-status-list-credential.test.ts new file mode 100644 index 0000000..354d22f --- /dev/null +++ b/packages/vc/src/revocation/is-status-list-credential.test.ts @@ -0,0 +1,81 @@ +import { describe, expect, it } from "vitest" + +import { isStatusListCredential } from "./is-status-list-credential" + +const baseCredential = { + "@context": ["https://www.w3.org/2018/credentials/v1"], + type: ["VerifiableCredential", "BitstringStatusListCredential"], + issuer: { id: "did:web:issuer.example.com" }, + issuanceDate: "2025-01-01T00:00:00.000Z", +} + +const validStatusListCredential = { + ...baseCredential, + credentialSubject: { + id: "https://status.example.com/list/1", + type: "BitstringStatusList", + statusPurpose: "revocation", + encodedList: "H4sIAAAAAAAA...", + }, +} + +describe("isStatusListCredential", () => { + it("returns true for a valid status list credential", () => { + expect(isStatusListCredential(validStatusListCredential)).toBe(true) + }) + + it("returns false when credentialSubject has wrong type", () => { + expect( + isStatusListCredential({ + ...baseCredential, + credentialSubject: { + id: "https://status.example.com/list/1", + type: "SomethingElse", + statusPurpose: "revocation", + encodedList: "H4sIAAAAAAAA...", + }, + }), + ).toBe(false) + }) + + it("returns false when credentialSubject is missing encodedList", () => { + expect( + isStatusListCredential({ + ...baseCredential, + credentialSubject: { + id: "https://status.example.com/list/1", + type: "BitstringStatusList", + statusPurpose: "revocation", + }, + }), + ).toBe(false) + }) + + it("returns false when credentialSubject is missing statusPurpose", () => { + expect( + isStatusListCredential({ + ...baseCredential, + credentialSubject: { + id: "https://status.example.com/list/1", + type: "BitstringStatusList", + encodedList: "H4sIAAAAAAAA...", + }, + }), + ).toBe(false) + }) + + it("returns false for a regular credential without status list subject", () => { + expect( + isStatusListCredential({ + ...baseCredential, + credentialSubject: { id: "did:web:subject.example.com" }, + }), + ).toBe(false) + }) + + it("returns false when the outer shape is not a valid credential", () => { + expect(isStatusListCredential(null)).toBe(false) + expect(isStatusListCredential({})).toBe(false) + expect(isStatusListCredential("not a credential")).toBe(false) + }) +}) From 03af4141ea8ab879ff61466841fb5149dc84ccc2 Mon Sep 17 00:00:00 2001 From: ak68a Date: Wed, 25 Mar 2026 16:55:22 -0500 Subject: [PATCH 2/2] test(vc): split bundled assertions into individual test cases --- .../vc/src/revocation/is-status-list-credential.test.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/vc/src/revocation/is-status-list-credential.test.ts b/packages/vc/src/revocation/is-status-list-credential.test.ts index 354d22f..db598d3 100644 --- a/packages/vc/src/revocation/is-status-list-credential.test.ts +++ b/packages/vc/src/revocation/is-status-list-credential.test.ts @@ -73,9 +73,15 @@ describe("isStatusListCredential", () => { ).toBe(false) }) - it("returns false when the outer shape is not a valid credential", () => { + it("rejects null", () => { expect(isStatusListCredential(null)).toBe(false) + }) + + it("rejects an empty object", () => { expect(isStatusListCredential({})).toBe(false) + }) + + it("rejects a plain string", () => { expect(isStatusListCredential("not a credential")).toBe(false) }) })