Files
CurrenciCombo/orchestrator/tests/services/completeCredential.test.ts
nsatoshi c1aef82ede
Some checks failed
CI / Frontend Lint (push) Failing after 6s
CI / Frontend Type Check (push) Failing after 7s
CI / Frontend Build (push) Failing after 7s
CI / Frontend E2E Tests (push) Failing after 7s
CI / Orchestrator Build (push) Failing after 7s
CI / Orchestrator Unit Tests (push) Failing after 5s
CI / Orchestrator E2E (Testcontainers) (push) Failing after 6s
CI / Contracts Compile (push) Failing after 7s
CI / Contracts Test (push) Failing after 5s
Security Scan / Dependency Vulnerability Scan (push) Failing after 5s
Security Scan / OWASP ZAP Scan (push) Failing after 3s
PR #27 (squash-merged via Gitea API)
2026-04-22 21:12:21 +00:00

233 lines
7.1 KiB
TypeScript

/**
* Unit tests for the Complete Credential (DBIS cc-*) adapters.
*
* All tests are headless — they either exercise the embedded mock /
* matrix or stub `fetch` directly. No network, no UI.
*/
import {
createCcIdentityClient,
loadControlsMatrix,
findControl,
type CcIdentityClient,
} from "../../src/services/completeCredential";
describe("completeCredential.createCcIdentityClient() — mock mode", () => {
let client: CcIdentityClient;
beforeAll(() => {
delete process.env.CC_IDENTITY_URL;
delete process.env.CC_IDENTITY_API_KEY;
client = createCcIdentityClient();
});
it("reports mock mode", () => {
expect(client.mode).toBe("mock");
});
it("returns ok for health()", async () => {
const h = await client.health();
expect(h.status).toBe("ok");
expect(h.service).toBe("cc-identity-core");
});
it("returns a ready response with persistence=false in mock", async () => {
const r = await client.ready();
expect(r.status).toBe("ok");
expect(r.persistence).toBe(false);
});
it("creates a subject with a uuid and defaulted tenant/entity", async () => {
const s = await client.createSubject({});
expect(s.subjectId).toMatch(/^[0-9a-f-]{36}$/);
expect(s.tenantId).toBe("tenant-demo");
expect(s.entityId).toBe("entity-demo");
});
it("passes tenant/entity through when provided", async () => {
const s = await client.createSubject({
tenantId: "t-acme",
entityId: "e-bank-1",
});
expect(s.tenantId).toBe("t-acme");
expect(s.entityId).toBe("e-bank-1");
});
});
describe("completeCredential.createCcIdentityClient() — live mode (stubbed fetch)", () => {
function makeFetch(
record: (url: string, init: RequestInit) => void,
responseBody: unknown,
status = 200,
): typeof fetch {
return (async (input: string | URL | Request, init?: RequestInit) => {
record(String(input), init ?? {});
return new Response(JSON.stringify(responseBody), {
status,
headers: { "Content-Type": "application/json" },
});
}) as typeof fetch;
}
it("reports live mode when baseUrl is set", () => {
const client = createCcIdentityClient({
baseUrl: "http://cc.example.test",
fetchImpl: makeFetch(() => undefined, { status: "ok", service: "x" }),
});
expect(client.mode).toBe("live");
});
it("hits GET /health", async () => {
const calls: string[] = [];
const client = createCcIdentityClient({
baseUrl: "http://cc.example.test",
fetchImpl: makeFetch(
(url) => {
calls.push(url);
},
{ status: "ok", service: "cc-identity-core" },
),
});
const h = await client.health();
expect(h.status).toBe("ok");
expect(calls[0]).toBe("http://cc.example.test/health");
});
it("posts to /v1/subjects with X-Correlation-Id + api key header", async () => {
const calls: { url: string; headers: Record<string, string>; body?: string }[] = [];
const client = createCcIdentityClient({
baseUrl: "http://cc.example.test",
apiKey: "k-1",
fetchImpl: makeFetch(
(url, init) => {
calls.push({
url,
headers: (init.headers ?? {}) as Record<string, string>,
body: init.body as string,
});
},
{
subjectId: "11111111-2222-3333-4444-555555555555",
tenantId: "t-1",
entityId: "e-1",
createdAt: "2026-01-01T00:00:00Z",
},
),
});
const s = await client.createSubject({ tenantId: "t-1", entityId: "e-1" }, "corr-42");
expect(s.subjectId).toContain("-");
expect(calls[0].url).toBe("http://cc.example.test/v1/subjects");
expect(calls[0].headers["X-API-Key"]).toBe("k-1");
expect(calls[0].headers["X-Correlation-Id"]).toBe("corr-42");
expect(JSON.parse(calls[0].body ?? "{}")).toEqual({
tenantId: "t-1",
entityId: "e-1",
});
});
it("auto-generates a correlation id when not provided", async () => {
const calls: Record<string, string>[] = [];
const client = createCcIdentityClient({
baseUrl: "http://cc.example.test",
fetchImpl: makeFetch(
(_url, init) => {
calls.push((init.headers ?? {}) as Record<string, string>);
},
{
subjectId: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
tenantId: "t",
entityId: "e",
createdAt: "2026-01-01T00:00:00Z",
},
),
});
await client.createSubject({});
expect(calls[0]["X-Correlation-Id"]).toMatch(/^[0-9a-f-]{36}$/);
});
it("throws a descriptive error on non-2xx", async () => {
const client = createCcIdentityClient({
baseUrl: "http://cc.example.test",
fetchImpl: makeFetch(() => undefined, { error: "boom" }, 500),
});
await expect(client.health()).rejects.toThrow(/HTTP 500/);
});
});
describe("completeCredential.loadControlsMatrix() — embedded mode", () => {
beforeAll(() => {
delete process.env.CC_CONTROLS_MATRIX_URL;
});
it("returns the embedded v0 matrix when no URL is set", async () => {
const m = await loadControlsMatrix();
expect(m.source).toBe("embedded");
expect(m.version).toBe(0);
expect(m.domains.length).toBeGreaterThan(0);
});
it("exposes expected control ids", async () => {
const m = await loadControlsMatrix();
const ids = m.domains.flatMap((d) => d.controls.map((c) => c.id));
expect(ids).toEqual(expect.arrayContaining(["IDP-001", "PAY-001", "AUD-001", "REG-001"]));
});
it("findControl() resolves by id", async () => {
const m = await loadControlsMatrix();
const c = findControl(m, "PAY-001");
expect(c?.title).toContain("PAN");
});
it("findControl() returns undefined for unknown ids", async () => {
const m = await loadControlsMatrix();
expect(findControl(m, "NOPE-999")).toBeUndefined();
});
});
describe("completeCredential.loadControlsMatrix() — remote mode", () => {
function makeFetch(responseBody: unknown, status = 200): typeof fetch {
return (async () =>
new Response(JSON.stringify(responseBody), {
status,
headers: { "Content-Type": "application/json" },
})) as typeof fetch;
}
it("fetches and normalises a JSON matrix", async () => {
const matrix = await loadControlsMatrix({
url: "http://cc.example.test/controls/matrix/v0.json",
fetchImpl: makeFetch({
version: 1,
domains: [
{
id: "extra",
controls: [
{
id: "X-001",
title: "Extra",
evidence_type: "doc_review",
owner_team: "ops",
frequency: "monthly",
},
],
},
],
}),
});
expect(matrix.source).toBe("remote");
expect(matrix.version).toBe(1);
expect(matrix.domains[0].controls[0].evidenceType).toBe("doc_review");
expect(matrix.domains[0].controls[0].ownerTeam).toBe("ops");
});
it("throws on non-2xx", async () => {
await expect(
loadControlsMatrix({
url: "http://cc.example.test/nope",
fetchImpl: makeFetch({}, 404),
}),
).rejects.toThrow(/HTTP 404/);
});
});