import { readFileSync } from "node:fs"; import { buildDocumentNumber, extractDocumentNumberFromLegacy, parseDocumentNumber, } from "../src/components/microservices/decision-center/src/document-contract"; import { runDecisionCenterCommandAsync } from "./src/decision-center"; type JsonRecord = Record; interface FetchCall { path: string; init?: { method?: string; body?: unknown }; } function assertCondition(condition: unknown, message: string, detail: JsonRecord = {}): void { if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`); } function source(path: string): string { return readFileSync(path, "utf8"); } function asRecord(value: unknown): JsonRecord { return typeof value === "object" && value !== null && !Array.isArray(value) ? value as JsonRecord : {}; } function makeFetcher(calls: FetchCall[]) { return async (path: string, init?: { method?: string; body?: unknown }): Promise => { calls.push({ path, init }); return { ok: true, status: init?.method === "POST" ? 201 : 200, body: { ok: true, record: { id: "dc_test", docNo: "DC-GOAL-P0-2026-001" } } }; }; } async function assertCliContract(): Promise { const checks: string[] = []; const config = {} as Parameters[0]; { const calls: FetchCall[] = []; await runDecisionCenterCommandAsync(config, [ "requirement", "create", "--title", "Doc Create", "--body", "body", "--type", "goal", "--priority", "P0", "--doc-type", "GOAL", "--doc-priority", "P0", "--doc-year", "2026", "--signer", "Decision Center", "--issued-at", "2026-05-21", "--effective-scope", "unidesk", ], makeFetcher(calls)); const call = calls[0]; const body = asRecord(call?.init?.body); assertCondition(call?.path === "/api/microservices/decision-center/proxy/api/requirements", "create must call requirements collection route", { call }); assertCondition(call?.init?.method === "POST", "create must use POST", { call }); assertCondition(body.docType === "GOAL" && body.docPriority === "P0" && body.docYear === 2026, "create must include document allocation fields", { body }); assertCondition(body.signer === "Decision Center" && body.issuedAt === "2026-05-21" && body.effectiveScope === "unidesk", "create must include document metadata fields", { body }); checks.push("cli-create-document-fields"); } { const calls: FetchCall[] = []; await runDecisionCenterCommandAsync(config, [ "requirement", "upsert", "--id", "dc_goal_big_paper_submission", "--title", "Doc Upsert", "--body", "body", "--doc-no", "dc-goal-p0-2026-001", "--supersedes", "DC-DCSN-P0-2026-001", ], makeFetcher(calls)); const call = calls[0]; const body = asRecord(call?.init?.body); assertCondition(call?.init?.method === "PUT", "upsert must use PUT", { call }); assertCondition(body.docNo === "DC-GOAL-P0-2026-001", "upsert must normalize explicit docNo", { body }); assertCondition(Array.isArray(body.supersedes) && body.supersedes[0] === "DC-DCSN-P0-2026-001", "upsert must include supersedes list", { body }); checks.push("cli-upsert-document-fields"); } { const calls: FetchCall[] = []; await runDecisionCenterCommandAsync(config, [ "requirement", "update", "DC-GOAL-P0-2026-001", "--signer", "Signer B", "--superseded-by", "DC-GOAL-P0-2026-002", ], makeFetcher(calls)); const call = calls[0]; const body = asRecord(call?.init?.body); assertCondition(call?.path === "/api/microservices/decision-center/proxy/api/requirements/DC-GOAL-P0-2026-001", "update must address records by docNo", { call }); assertCondition(call?.init?.method === "PUT", "update must use PUT", { call }); assertCondition(body.signer === "Signer B", "update must include signer", { body }); assertCondition(Array.isArray(body.supersededBy) && body.supersededBy[0] === "DC-GOAL-P0-2026-002", "update must include supersededBy list", { body }); checks.push("cli-update-document-fields"); } { const calls: FetchCall[] = []; await runDecisionCenterCommandAsync(config, [ "requirement", "list", "--doc-no", "DC-GOAL-P0-2026-001", "--doc-type", "GOAL", "--doc-priority", "P0", "--year", "2026", ], makeFetcher(calls)); const path = calls[0]?.path ?? ""; assertCondition(path.includes("/api/requirements?"), "list must call requirements query route", { path }); assertCondition(path.includes("docNo=DC-GOAL-P0-2026-001"), "list must filter by docNo", { path }); assertCondition(path.includes("docType=GOAL") && path.includes("docPriority=P0") && path.includes("docYear=2026"), "list must filter by document components", { path }); checks.push("cli-list-document-query"); } { const calls: FetchCall[] = []; await runDecisionCenterCommandAsync(config, ["requirement", "show", "DC-GOAL-P0-2026-001"], makeFetcher(calls)); assertCondition(calls[0]?.path === "/api/microservices/decision-center/proxy/api/requirements/DC-GOAL-P0-2026-001", "show must support docNo path keys", { call: calls[0] }); checks.push("cli-show-document-key"); } return checks; } export async function runDecisionCenterDocumentContract(): Promise { const service = source("src/components/microservices/decision-center/src/index.ts"); const cli = source("scripts/src/decision-center.ts"); const cliEntry = source("scripts/cli.ts"); const remote = source("scripts/src/remote.ts"); const doc = parseDocumentNumber("dc-goal-p0-2026-1"); assertCondition(doc.docNo === "DC-GOAL-P0-2026-001", "docNo parser must normalize sequence width", { doc }); assertCondition(buildDocumentNumber("DCSN", "P0", 2026, 1) === "DC-DCSN-P0-2026-001", "docNo builder must produce canonical sequence"); const legacyCases = [ { id: "dc_decision_thesis_unidesk_integration_rule", expected: "DC-DCSN-P0-2026-001" }, { id: "x", title: "DC-GOAL-P0-2026-001 big paper", expected: "DC-GOAL-P0-2026-001" }, { id: "x", body: "doc-no: DC-GOAL-P0-2026-002\n\nBody stays unchanged.", expected: "DC-GOAL-P0-2026-002" }, { id: "x", tags: ["doc-no:DC-DCSN-P0-2026-001"], expected: "DC-DCSN-P0-2026-001" }, { id: "dc_goal_big_paper_submission", expected: "DC-GOAL-P0-2026-001" }, { id: "dc_goal_small_paper_submission_gate", expected: "DC-GOAL-P0-2026-002" }, ]; for (const item of legacyCases) { const extracted = extractDocumentNumberFromLegacy(item); assertCondition(extracted?.docNo === item.expected, "legacy document number extraction failed", { item, extracted }); } assertCondition(service.includes("CREATE UNIQUE INDEX IF NOT EXISTS idx_decision_center_records_doc_no_unique"), "service must enforce docNo uniqueness"); assertCondition(service.includes("doc_no = 'DC-' || doc_type || '-' || doc_priority || '-' || doc_year::text || '-' || lpad(doc_seq::text, 3, '0')"), "schema must keep docNo and parsed components consistent"); assertCondition(service.includes("clearInvalidDocumentNumbers") && service.indexOf("await clearInvalidDocumentNumbers();") < service.indexOf("ADD CONSTRAINT decision_center_records_doc_shape_check"), "schema migration must clear invalid doc fields before adding the document shape check"); assertCondition(service.includes("document number already exists") && service.includes("code: \"doc_no_conflict\""), "service must expose structured duplicate docNo errors"); assertCondition(service.includes("nextDocumentSequence") && service.includes("LOCK TABLE decision_center_records IN SHARE ROW EXCLUSIVE MODE"), "service must allocate next doc sequence under table lock"); assertCondition(service.includes("getRecordByIdOrDocNo(id, docNo)") && service.includes("doc_no = ${docNo || null}"), "requirement upsert must resolve existing records by id or docNo"); assertCondition(service.includes("clearDuplicateDocumentNumbers") && service.indexOf("await backfillLegacyDocumentNumbers();") < service.indexOf("CREATE UNIQUE INDEX IF NOT EXISTS idx_decision_center_records_doc_no_unique"), "schema migration must de-duplicate/backfill before creating docNo unique index"); assertCondition(service.includes("backfillLegacyDocumentNumbers") && service.includes("updated_at = updated_at"), "service must run idempotent legacy backfill without touching body/title/tags"); assertCondition(service.includes("const documentFields = [\"docNo\"") && service.includes("delete inheritedBase[field]"), "meeting import must persist its own document fields without cloning them to child decisions"); assertCondition(service.includes("WHERE id = ${id}") && service.includes("doc_no = ${docNo || null}"), "service get route must support id or docNo lookup"); assertCondition(service.includes("ORDER BY") && service.includes("doc_seq ASC NULLS LAST"), "service list route must sort by document number components"); assertCondition(cli.includes("--doc-no") && cli.includes("--doc-type") && cli.includes("--doc-priority") && cli.includes("--issued-at"), "CLI must expose document options"); assertCondition(cliEntry.includes("const result = await runDecisionCenterCommand(config, args.slice(1))") && cliEntry.includes("if (!ok) process.exitCode = 1"), "local CLI must propagate decision ok:false as process failure"); assertCondition(remote.includes("const result = await runDecisionCenterCommandAsync(config, args.slice(1), fetcher)") && remote.includes("return ok ? 0 : 1"), "remote CLI must propagate decision ok:false as process failure"); const cliChecks = await assertCliContract(); return { ok: true, checks: [ "doc-number-parse-and-build", "doc-no-unique-structured-error-contract", "docNo-component-consistency-check", "invalid-docNo-migration-guard", "doc-sequence-auto-allocation-contract", "upsert-by-docNo-contract", "duplicate-docNo-migration-guard", "legacy-doc-no-title-tag-body-idempotent-backfill", "meeting-import-document-field-contract", "service-docNo-and-component-query-contract", "service-docNo-sort-contract", "cli-structured-error-exit-contract", ...cliChecks, ], }; } if (import.meta.main) { process.stdout.write(`${JSON.stringify(await runDecisionCenterDocumentContract(), null, 2)}\n`); }