Files
EpicNext-Cms/src/features/housekeeping/foundation/contracts/contracts.test.ts
T

197 lines
4.6 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
allCapabilities,
anyCapability,
type CapabilityRequirement,
fail,
type HousekeepingCommand,
type HousekeepingDomainManifest,
type HousekeepingInboxSource,
type HousekeepingInboxSourceResult,
type HousekeepingQuery,
type HousekeepingSearchProvider,
type HousekeepingSearchResult,
type HousekeepingWidgetDefinition,
type HousekeepingWorkItem,
mapUnknownError,
ok,
} from ".";
const capability = anyCapability("admin.users.view");
const workItem = {
sourceId: "tickets",
itemId: "ticket-42",
deduplicationKey: "ticket:42",
domain: "people",
capability,
severity: "warning",
priority: "high",
ageMs: 60_000,
state: "open",
occurredAt: "2026-08-24T12:00:00.000Z",
titleKey: "pages.housekeeping.items.ticket",
context: { ticketId: "42" },
href: "/ase/people/tickets/42",
freshness: "fresh",
actions: [
{
id: "people.ticket.open",
labelKey: "pages.housekeeping.items.ticket.open",
commandId: "people.ticket.open",
capability,
},
],
} satisfies HousekeepingWorkItem;
const searchResult = {
id: "user-42",
domain: "people",
type: "entity",
title: "operator",
description: "A Housekeeping operator",
href: "/ase/people/users/42",
capability,
} satisfies HousekeepingSearchResult;
const searchProvider: HousekeepingSearchProvider = {
id: "people.users",
owner: "people",
capability,
search: async () => ok([searchResult], "search-1"),
};
const inboxSourceResult = {
items: [workItem],
availability: "available",
} satisfies HousekeepingInboxSourceResult;
const inboxSource: HousekeepingInboxSource = {
id: "people.tickets",
owner: "people",
capability,
getItems: async () => ok(inboxSourceResult, "inbox-1"),
};
const widget: HousekeepingWidgetDefinition = {
id: "people.queue",
owner: "people",
capability,
kind: "mandatory",
load: async () => ok({ count: 2 }, "widget-1"),
};
const query: HousekeepingQuery<{ id: number }, { id: number }> = {
id: "people.user",
owner: "people",
capability,
run: async (_context, input) => ok(input, "query-1"),
};
const command: HousekeepingCommand<{ id: number }, { id: number }> = {
id: "people.user.disable",
owner: "people",
risk: "sensitive",
capability,
requiresReason: true,
execute: async (_context, input) => ok(input, "command-1"),
};
const manifest: HousekeepingDomainManifest = {
id: "people",
labelKey: "pages.housekeeping.domains.people.title",
descriptionKey: "pages.housekeeping.domains.people.description",
iconId: "users",
canonicalHref: "/ase/people",
capability,
routes: [],
searchProviders: [searchProvider],
inboxSources: [inboxSource],
widgets: [widget],
};
describe("housekeeping foundation contracts", () => {
it("preserves correlation IDs for successful results", () => {
expect(ok({ count: 2 }, "corr-1")).toEqual({
ok: true,
data: { count: 2 },
correlationId: "corr-1",
});
});
it.each([
"UNAUTHENTICATED",
"FORBIDDEN",
"VALIDATION",
"NOT_FOUND",
"CONFLICT",
"RATE_LIMITED",
"DEPENDENCY_UNAVAILABLE",
"TIMEOUT",
"INTERNAL",
] as const)("returns the approved %s error contract", (code) => {
expect(
fail(code, `errors.housekeeping.${code.toLowerCase()}`, "corr-2", {
name: ["errors.validation.required"],
}),
).toEqual({
ok: false,
error: {
code,
messageKey: `errors.housekeeping.${code.toLowerCase()}`,
fieldErrors: { name: ["errors.validation.required"] },
},
correlationId: "corr-2",
});
});
it("sanitizes unknown errors with the stable internal taxonomy", () => {
expect(mapUnknownError(new Error("database password exposed"))).toEqual({
code: "INTERNAL",
messageKey: "errors.housekeeping.internal",
});
});
it("rejects empty capability requirements", () => {
expect(() => anyCapability()).toThrow("capability requirement is empty");
expect(() => allCapabilities()).toThrow("capability requirement is empty");
});
it("exports assignable type-only contracts", () => {
const requirements: readonly CapabilityRequirement[] = [
capability,
allCapabilities("admin.users.view"),
];
expect([
workItem,
searchProvider,
inboxSource,
widget,
query,
command,
manifest,
requirements,
]).toHaveLength(8);
});
it("carries stable ownership and capability metadata for registry entries", () => {
expect(searchProvider).toMatchObject({
id: "people.users",
owner: "people",
capability,
});
expect(inboxSource).toMatchObject({
id: "people.tickets",
owner: "people",
capability,
});
expect(widget).toMatchObject({
id: "people.queue",
owner: "people",
capability,
kind: "mandatory",
});
});
});