mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-03 20:13:15 +00:00
86 lines
3.4 KiB
TypeScript
86 lines
3.4 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { vendorPortalApi } from "@/domain/vendor-portal/api/vendor-portal-api";
|
|
import { createUpliftRequestKey } from "@/domain/vendor-portal/lib/request-key";
|
|
import { validateUpliftAmount } from "@/domain/vendor-portal/lib/uplift-validation";
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
function jsonResponse(data: unknown): Response {
|
|
return new Response(JSON.stringify({ status: "Success", data }), {
|
|
status: 200,
|
|
headers: { "Content-Type": "application/json" },
|
|
});
|
|
}
|
|
|
|
describe("vendor uplift workflow contract", () => {
|
|
it("requires reason and evidence only when the requested NTE exceeds the current NTE", () => {
|
|
expect(validateUpliftAmount("90", 100, "", {})).toBe("");
|
|
expect(validateUpliftAmount("110", 100, "", {})).toMatch(/reason/i);
|
|
expect(validateUpliftAmount("110", 100, "Emergency work", {})).toMatch(/evidence/i);
|
|
expect(validateUpliftAmount("110", 100, "Emergency work", { hasExisting: true })).toBe("");
|
|
});
|
|
|
|
it("keeps create-mode requests at or below current NTE as NoApprovalRequired", () => {
|
|
expect(validateUpliftAmount("90", 100, "", { mode: "create" })).toBe("");
|
|
expect(validateUpliftAmount("100", 100, "", { mode: "create" })).toBe("");
|
|
});
|
|
|
|
it("rejects revise-mode requests at or below current NTE with an actionable error", () => {
|
|
expect(
|
|
validateUpliftAmount("90", 100, "Reason", { hasExisting: true, mode: "revise" }),
|
|
).toMatch(/must be greater than the current NTE/i);
|
|
expect(
|
|
validateUpliftAmount("100", 100, "Reason", { hasExisting: true, mode: "revise" }),
|
|
).toMatch(/must be greater than the current NTE/i);
|
|
});
|
|
|
|
it("still requires approval details in revise mode when current NTE is unknown", () => {
|
|
expect(validateUpliftAmount("110", undefined, "", { mode: "revise" })).toMatch(/reason/i);
|
|
expect(validateUpliftAmount("110", undefined, "Emergency work", { mode: "revise" })).toMatch(
|
|
/evidence/i,
|
|
);
|
|
expect(
|
|
validateUpliftAmount("110", undefined, "Emergency work", {
|
|
hasExisting: true,
|
|
mode: "revise",
|
|
}),
|
|
).toBe("");
|
|
});
|
|
|
|
it("uses a secure UUID request key", () => {
|
|
expect(createUpliftRequestKey()).toMatch(
|
|
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
|
|
);
|
|
});
|
|
|
|
it("uses dispatch-scoped request, withdraw, revise, and status routes", async () => {
|
|
const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue(jsonResponse({ id: 31 }));
|
|
|
|
await vendorPortalApi.requestUplift("token", 7, {
|
|
requestedNTE: 125,
|
|
reason: "Parts",
|
|
requestKey: "key-1",
|
|
evidenceDocumentId: 22,
|
|
});
|
|
await vendorPortalApi.withdrawUplift("token", 7, 31);
|
|
await vendorPortalApi.reviseUplift("token", 7, 31, {
|
|
requestedNTE: 130,
|
|
reason: "Updated parts",
|
|
evidenceDocumentId: 22,
|
|
});
|
|
await vendorPortalApi.getDocumentStatus("token", 7, 22);
|
|
|
|
expect(fetchMock.mock.calls.map(([url]) => new URL(String(url)).pathname)).toEqual([
|
|
"/api/vendor-portal/dispatches/7/uplift-request",
|
|
"/api/vendor-portal/dispatches/7/uplift-request/31/withdraw",
|
|
"/api/vendor-portal/dispatches/7/uplift-request/31/revise",
|
|
"/api/vendor-portal/dispatches/7/documents/22/status",
|
|
]);
|
|
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toMatchObject({
|
|
requestedNTE: 125,
|
|
evidenceDocumentId: 22,
|
|
});
|
|
});
|
|
});
|