PR A: 12-state transaction machine + issueInstrument step + SoD matrix
Some checks failed
Code Quality / SonarQube Analysis (pull_request) Failing after 23s
Code Quality / Code Quality Checks (pull_request) Failing after 11s
Security Scan / Dependency Vulnerability Scan (pull_request) Failing after 4s
Security Scan / OWASP ZAP Scan (pull_request) Failing after 5s
Some checks failed
Code Quality / SonarQube Analysis (pull_request) Failing after 23s
Code Quality / Code Quality Checks (pull_request) Failing after 11s
Security Scan / Dependency Vulnerability Scan (pull_request) Failing after 4s
Security Scan / OWASP ZAP Scan (pull_request) Failing after 5s
Architecture note steps 1, 2, 10 (data model). - types/transactionState.ts: 12 states, allowed-transition table, SoD matrix - types/plan.ts: add InstrumentTerms + 'issueInstrument' PlanStep type - services/planValidation.ts: validate SBLC step (BIC, ISO-4217, sha256, YYYY-MM-DD expiry, >0 amount) - services/stateMachine.ts: transition() enforces legality + SoD + appends to transaction_state_transitions - db/migrations/002: plans.transaction_state (CHECK) + transaction_state_transitions append-only table - tests/unit: 13 + 8 unit tests (31 total, all pass) No behaviour change yet: coordinator still uses legacy status field. PRs B-G will migrate execution paths onto the new machine.
This commit is contained in:
82
orchestrator/tests/unit/planValidation.instrument.test.ts
Normal file
82
orchestrator/tests/unit/planValidation.instrument.test.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { describe, it, expect } from "@jest/globals";
|
||||
import { validatePlan } from "../../src/services/planValidation";
|
||||
import type { InstrumentTerms, Plan } from "../../src/types/plan";
|
||||
|
||||
const goodTerms: InstrumentTerms = {
|
||||
applicant: "Solace Bank Group PLC",
|
||||
issuingBankBIC: "SOLBAE22",
|
||||
beneficiaryBankBIC: "MEBLAEAD", // Emirates Islamic BIC prefix example
|
||||
beneficiaryName: "Acme Trading LLC",
|
||||
beneficiaryAccount: "AE070331234567890123456",
|
||||
amount: 1_000_000,
|
||||
currency: "USD",
|
||||
tenor: "90D",
|
||||
expiryDate: "2026-06-30",
|
||||
placeOfPresentation: "Dubai, UAE",
|
||||
governingLaw: "URDG 758",
|
||||
templateRef: "EIB-SBLC-v3.2",
|
||||
templateHash:
|
||||
"a".repeat(64), // dummy sha256
|
||||
};
|
||||
|
||||
function planWith(terms: Partial<InstrumentTerms> | null): Plan {
|
||||
return {
|
||||
creator: "solace-ops-01",
|
||||
steps: [
|
||||
{
|
||||
type: "issueInstrument",
|
||||
amount: terms?.amount ?? 1_000_000,
|
||||
instrument: terms === null ? undefined : ({ ...goodTerms, ...terms } as InstrumentTerms),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
describe("validatePlan — issueInstrument step", () => {
|
||||
it("accepts a well-formed SBLC step", () => {
|
||||
const result = validatePlan(planWith({}));
|
||||
expect(result.valid).toBe(true);
|
||||
expect(result.errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("rejects a step missing the instrument object", () => {
|
||||
const result = validatePlan(planWith(null));
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.errors[0]).toMatch(/missing instrument terms/);
|
||||
});
|
||||
|
||||
it("rejects an invalid BIC", () => {
|
||||
const result = validatePlan(planWith({ issuingBankBIC: "NOTABIC" }));
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.errors.join("\n")).toMatch(/issuingBankBIC is not a valid BIC/);
|
||||
});
|
||||
|
||||
it("rejects a non-ISO-4217 currency", () => {
|
||||
const result = validatePlan(planWith({ currency: "usd" }));
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.errors.join("\n")).toMatch(/currency must be ISO 4217/);
|
||||
});
|
||||
|
||||
it("rejects a non-ISO-8601 expiry date", () => {
|
||||
const result = validatePlan(planWith({ expiryDate: "30-06-2026" }));
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.errors.join("\n")).toMatch(/expiryDate must be YYYY-MM-DD/);
|
||||
});
|
||||
|
||||
it("rejects a non-sha256 template hash", () => {
|
||||
const result = validatePlan(planWith({ templateHash: "deadbeef" }));
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.errors.join("\n")).toMatch(/templateHash must be 64 hex chars/);
|
||||
});
|
||||
|
||||
it("rejects an instrument with non-positive amount", () => {
|
||||
const result = validatePlan(planWith({ amount: 0 }));
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.errors.join("\n")).toMatch(/instrument.amount must be > 0/);
|
||||
});
|
||||
|
||||
it("accepts 11-char branched BIC", () => {
|
||||
const result = validatePlan(planWith({ issuingBankBIC: "SOLBAE22XXX" }));
|
||||
expect(result.valid).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user