CI (SIAX Cloud) / integration (push) Skipped
CI (SIAX Cloud) / sonar (push) Skipped
masterplan-lock / check (push) Successful in 1m3s
CI (SIAX Cloud) / security (push) Successful in 1m4s
Security Scan (SIAX Cloud) / security (push) Successful in 1m7s
CI (SIAX Cloud) / quality (push) Successful in 1m52s
CI (SIAX Cloud) / sonar (pull_request) Skipped
masterplan-lock / check (pull_request) Successful in 6s
CI (SIAX Cloud) / security (pull_request) Successful in 18s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 19s
CI (SIAX Cloud) / quality (pull_request) Successful in 1m20s
CI (SIAX Cloud) / integration (pull_request) Successful in 1m58s
106 lines
4.2 KiB
TypeScript
106 lines
4.2 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import {
|
|
ORDER_STATUSES,
|
|
NEXT_STATUS,
|
|
isKnownOrderStatus,
|
|
nextOrderStatus,
|
|
resolveAdvanceAction,
|
|
buildOrderInboxItem,
|
|
} from "@/lib/order-flow";
|
|
|
|
describe("order-flow — server-renderad advance-resolution (ADR-0001)", () => {
|
|
it("flödets fem steg har nästa status", () => {
|
|
expect(NEXT_STATUS.draft).toBe("quoted");
|
|
expect(NEXT_STATUS.accepted).toBe("delivered");
|
|
});
|
|
|
|
it("aktivt steg → available", () => {
|
|
expect(resolveAdvanceAction("draft")).toMatchObject({ state: "available" });
|
|
expect(resolveAdvanceAction("review")).toMatchObject({ state: "available" });
|
|
});
|
|
|
|
it("terminalt steg → read_only/read_only_resource (inte fake available)", () => {
|
|
expect(resolveAdvanceAction("delivered")).toMatchObject({ state: "read_only", reason: "read_only_resource" });
|
|
expect(resolveAdvanceAction("cancelled")).toMatchObject({ state: "read_only", reason: "read_only_resource" });
|
|
});
|
|
});
|
|
|
|
describe("order-flow — kanonisk statusmaskin (ADR-0002, C3)", () => {
|
|
const TERMINAL: ReadonlySet<string> = new Set(["delivered", "cancelled"]);
|
|
const EXPECTED_NEXT: Record<string, string | null> = {
|
|
draft: "quoted",
|
|
quoted: "in_production",
|
|
in_production: "review",
|
|
review: "accepted",
|
|
accepted: "delivered",
|
|
delivered: null,
|
|
cancelled: null,
|
|
};
|
|
|
|
it("ORDER_STATUSES är utömmande: varje känd status → korrekt nästa/read_only", () => {
|
|
expect(ORDER_STATUSES).toHaveLength(7);
|
|
for (const status of ORDER_STATUSES) {
|
|
expect(isKnownOrderStatus(status), `känd status: ${status}`).toBe(true);
|
|
if (TERMINAL.has(status)) {
|
|
expect(nextOrderStatus(status), `${status} är terminal`).toBeNull();
|
|
expect(resolveAdvanceAction(status)).toMatchObject({ state: "read_only", reason: "read_only_resource" });
|
|
expect(buildOrderInboxItem({ order_id: "abcd1234-5678", status, flow: "custom" })).toBeNull();
|
|
} else {
|
|
expect(nextOrderStatus(status)).toBe(EXPECTED_NEXT[status]);
|
|
expect(resolveAdvanceAction(status)).toMatchObject({ state: "available" });
|
|
}
|
|
}
|
|
});
|
|
|
|
it("flödet är en linjedraft→…→delivered-kedja utan genvägar", () => {
|
|
let cursor: string | null = "draft";
|
|
const walked: string[] = [];
|
|
while (cursor) {
|
|
walked.push(cursor);
|
|
cursor = nextOrderStatus(cursor);
|
|
}
|
|
expect(walked).toEqual(["draft", "quoted", "in_production", "review", "accepted", "delivered"]);
|
|
});
|
|
|
|
it("okänd status (fri text ur DB) → read_only, ingen nästa, ingen inbox-post (guardad)", () => {
|
|
for (const status of ["klar", "Klar", "done", "", "in prod", "cancelled "]) {
|
|
expect(isKnownOrderStatus(status)).toBe(false);
|
|
expect(nextOrderStatus(status)).toBeNull();
|
|
expect(resolveAdvanceAction(status)).toMatchObject({ state: "read_only", reason: "read_only_resource" });
|
|
expect(buildOrderInboxItem({ order_id: "abcd1234-5678", status, flow: "custom" })).toBeNull();
|
|
}
|
|
});
|
|
|
|
it("cancelled ingår i unionen (används som terminal status i tests/produkten)", () => {
|
|
expect(ORDER_STATUSES).toContain("cancelled");
|
|
});
|
|
});
|
|
|
|
describe("order-flow — inbox-projektion (våg 3.3)", () => {
|
|
const order = (over: Partial<{ order_id: string; status: string; flow: string; total_ore: number }> = {}) => ({
|
|
order_id: "abcd1234-5678",
|
|
status: "review",
|
|
flow: "custom",
|
|
total_ore: 500000,
|
|
...over,
|
|
});
|
|
|
|
it("review-order → approval-post med hög prioritet och advance-action", () => {
|
|
const item = buildOrderInboxItem(order());
|
|
expect(item).toMatchObject({ kind: "approval", priority: "high", state: "open" });
|
|
expect(item!.actions?.[0].label).toContain("accepted");
|
|
expect(item!.entity).toMatchObject({ type: "order", id: "abcd1234-5678" });
|
|
});
|
|
|
|
it("levererad order → ingen post (inkorgen är öppna arbetsobjekt)", () => {
|
|
expect(buildOrderInboxItem(order({ status: "delivered" }))).toBeNull();
|
|
expect(buildOrderInboxItem(order({ status: "cancelled" }))).toBeNull();
|
|
});
|
|
|
|
it("draft-order → task med normal prioritet", () => {
|
|
const item = buildOrderInboxItem(order({ status: "draft" }));
|
|
expect(item).toMatchObject({ kind: "task", priority: "normal" });
|
|
expect(item!.subject).toContain("draft");
|
|
});
|
|
});
|