import { describe, expect, it, vi } from "vitest"; import { PERMS } from "@/lib/permission-slugs"; import { anyCapability, fail, type HousekeepingCapabilityContext, type HousekeepingDomainManifest, type HousekeepingInboxSource, type HousekeepingWorkItem, ok, } from "../contracts"; import type { HousekeepingRegistry } from "../registry"; import { createHousekeepingInboxService, INBOX_POLICY } from "./inbox-service"; function context(granted: readonly string[]): HousekeepingCapabilityContext { const permissions = new Set(granted); return { actor: { id: 42, username: "operator", rank: 7 }, isSuperAdmin: false, has: (slug) => permissions.has(slug), hasAny: (...slugs) => slugs.some((slug) => permissions.has(slug)), hasAll: (...slugs) => slugs.every((slug) => permissions.has(slug)), }; } function item( itemId: string, options: Partial = {}, ): HousekeepingWorkItem { return { sourceId: "people.queue", itemId, deduplicationKey: `people.queue:${itemId}`, domain: "people", capability: anyCapability(PERMS.USERS_VIEW), severity: "info", priority: "normal", ageMs: 1_000, state: "open", occurredAt: "2026-08-30T10:00:00.000Z", titleKey: "pages.housekeeping.items.test", href: "/ase/people/users", freshness: "fresh", actions: [], ...options, }; } function source( id: string, permission: string, getItems: HousekeepingInboxSource["getItems"], owner: "people" | "content" = "people", ): HousekeepingInboxSource { return { id, owner, capability: anyCapability(permission), getItems, }; } function manifest( id: "people" | "content", permission: string, inboxSources: readonly HousekeepingInboxSource[], ): HousekeepingDomainManifest { return { id, labelKey: `pages.housekeeping.domains.${id}.title`, descriptionKey: `pages.housekeeping.domains.${id}.description`, iconId: id === "people" ? "users" : "file-text", canonicalHref: `/ase/${id}`, capability: anyCapability(permission), routes: [], searchProviders: [], inboxSources, widgets: [], }; } function registry( ...domains: readonly HousekeepingDomainManifest[] ): HousekeepingRegistry { return { domains }; } describe("Housekeeping inbox service", () => { it("locks timeout, cap, and deduplication policy", () => { expect(INBOX_POLICY).toEqual({ timeoutMs: 2_000, combinedLimit: 200, dedupe: "sourceId:itemId", }); }); it("filters domain and source capabilities before invoking sources", async () => { const permitted = vi.fn(async () => ok({ availability: "available" as const, items: [] }, "permitted"), ); const forbiddenSource = vi.fn(async () => ok({ availability: "available" as const, items: [] }, "source"), ); const forbiddenDomain = vi.fn(async () => ok({ availability: "available" as const, items: [] }, "domain"), ); const service = createHousekeepingInboxService({ registry: registry( manifest("people", PERMS.USERS_VIEW, [ source("people.ready", PERMS.USERS_VIEW, permitted), source("people.bans", PERMS.BANS_VIEW, forbiddenSource), ]), manifest("content", PERMS.NEWS_VIEW, [ source("content.users", PERMS.USERS_VIEW, forbiddenDomain, "content"), ]), ), createCorrelationId: () => "inbox-capabilities", }); await service(context([PERMS.USERS_VIEW])); expect(permitted).toHaveBeenCalledOnce(); expect(forbiddenSource).not.toHaveBeenCalled(); expect(forbiddenDomain).not.toHaveBeenCalled(); }); it("deduplicates by source/item and orders by priority then oldest age", async () => { const items = [ item("normal", { priority: "normal", ageMs: 900 }), item("high-new", { priority: "high", ageMs: 100 }), item("critical", { priority: "critical", ageMs: 10 }), item("high-old", { priority: "high", ageMs: 1_000 }), item("low", { priority: "low", ageMs: 5_000 }), item("high-old", { priority: "high", ageMs: 999 }), ]; const service = createHousekeepingInboxService({ registry: registry( manifest("people", PERMS.USERS_VIEW, [ source("people.queue", PERMS.USERS_VIEW, async () => ok({ availability: "available", items }, "ordered"), ), ]), ), createCorrelationId: () => "inbox-order", }); const response = await service(context([PERMS.USERS_VIEW])); expect(response.items.map((entry) => entry.itemId)).toEqual([ "critical", "high-old", "high-new", "normal", "low", ]); }); it("filters item capabilities before enforcing the 200-item cap", async () => { const items = Array.from({ length: 230 }, (_, index) => item(String(index), { capability: anyCapability( index < 20 ? PERMS.BANS_VIEW : PERMS.USERS_VIEW, ), }), ); const service = createHousekeepingInboxService({ registry: registry( manifest("people", PERMS.USERS_VIEW, [ source("people.queue", PERMS.USERS_VIEW, async () => ok({ availability: "available", items }, "capped"), ), ]), ), createCorrelationId: () => "inbox-cap", }); const response = await service(context([PERMS.USERS_VIEW])); expect(response.items).toHaveLength(200); expect(response.items[0]?.itemId).toBe("20"); expect(response.items.at(-1)?.itemId).toBe("219"); }); it("returns successful items with stable timeout and source errors", async () => { let timeoutSignal: AbortSignal | undefined; const service = createHousekeepingInboxService({ registry: registry( manifest("people", PERMS.USERS_VIEW, [ source("people.timeout", PERMS.USERS_VIEW, (_context, signal) => { timeoutSignal = signal; return new Promise(() => undefined); }), source("people.failed", PERMS.USERS_VIEW, async () => fail( "DEPENDENCY_UNAVAILABLE", "errors.housekeeping.dependencyUnavailable", "failed", ), ), source("people.ready", PERMS.USERS_VIEW, async () => ok( { availability: "available", items: [item("ready", { sourceId: "people.ready" })], }, "ready", ), ), ]), ), createCorrelationId: () => "inbox-partial", timeoutMs: 5, }); const response = await service(context([PERMS.USERS_VIEW])); expect(response.items.map((entry) => entry.itemId)).toEqual(["ready"]); expect(response.errors).toEqual([ { sourceId: "people.timeout", code: "TIMEOUT" }, { sourceId: "people.failed", code: "DEPENDENCY_UNAVAILABLE" }, ]); expect(timeoutSignal?.aborted).toBe(true); expect(response.correlationId).toBe("inbox-partial"); }); });