Files
EpicNext-Cms/src/features/housekeeping/domains/content/content-providers-production.test.ts
T

173 lines
4.5 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import { PERMS } from "@/lib/permission-slugs";
import type { HousekeepingCapabilityContext } from "../../foundation/contracts";
import { loadContentInboxItems } from "./inbox-production";
import { loadContentSearchCandidates } from "./search-production";
import { loadContentWidget } from "./widgets-production";
const { run } = vi.hoisted(() => ({ run: vi.fn() }));
vi.mock("./queries/content-queries", () => ({
contentQuery: { run },
}));
const context = {
actor: { id: 42, username: "operator", rank: 7 },
isSuperAdmin: false,
has: () => true,
hasAny: () => true,
hasAll: () => true,
} satisfies HousekeepingCapabilityContext;
function capability(granted: readonly string[]): HousekeepingCapabilityContext {
const permissions = new Set(granted);
return {
...context,
has: (slug) => permissions.has(slug),
hasAny: (...slugs) => slugs.some((slug) => permissions.has(slug)),
hasAll: (...slugs) => slugs.every((slug) => permissions.has(slug)),
};
}
function result(
routeId: string,
total: number,
items: readonly Record<string, unknown>[] = [],
) {
return {
ok: true as const,
data: {
kind: routeId.split(".")[1],
items,
total,
partialDependencies: [],
},
correlationId: "provider-production",
};
}
describe("Content production providers", () => {
beforeEach(() => {
vi.clearAllMocks();
run.mockImplementation(async (_context, input) =>
result(input.routeId, input.routeId.includes("articles") ? 7 : 3),
);
});
it("returns only truthful persisted counts from editorial and localization widgets", async () => {
const signal = new AbortController().signal;
await expect(
loadContentWidget("editorial", context, signal),
).resolves.toEqual({ articles: 7 });
await expect(
loadContentWidget("localization", context, signal),
).resolves.toEqual({ stores: 3 });
});
it("loads real search candidates from bounded query routes", async () => {
run.mockImplementation(async (_context, input) =>
result(input.routeId, 1, [
{
id: "9",
title: "Launch",
description: "Published",
href: "/ase/content/editorial/articles/9",
},
]),
);
const candidates = await loadContentSearchCandidates(
"articles",
context,
"launch",
25,
);
expect(candidates).toEqual([
expect.objectContaining({
id: "content.editorial.articles:9",
href: "/ase/content/editorial/articles/9",
}),
]);
expect(run).toHaveBeenCalledWith(
context,
expect.objectContaining({
list: { search: "launch", pageSize: 25, offset: 0 },
}),
);
});
it("builds publication inbox items only from real query rows", async () => {
run.mockImplementation(async (_context, input) =>
result(input.routeId, 1, [
{
id: "5",
title: "Release",
status: "published",
updatedAt: new Date().toISOString(),
href: "/ase/content/editorial/articles/5",
},
]),
);
const items = await loadContentInboxItems(
"publication",
context,
new AbortController().signal,
);
expect(items).toEqual([]);
});
it("loads only capability-matched media counts", async () => {
const result = await loadContentWidget(
"media",
capability([PERMS.BANNERS_VIEW]),
new AbortController().signal,
);
expect(result).toEqual({ banners: 3 });
expect(run).toHaveBeenCalledTimes(1);
expect(run).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({ routeId: "content.media.banners" }),
);
});
it("does not invent a zero when a widget dependency is unavailable", async () => {
run.mockResolvedValueOnce({
ok: false,
error: { code: "DEPENDENCY_UNAVAILABLE", messageKey: "dependency" },
correlationId: "unavailable",
});
await expect(
loadContentWidget("editorial", context, new AbortController().signal),
).rejects.toThrow("Content widget query unavailable");
});
it("emits only actionable publication statuses", async () => {
run.mockResolvedValueOnce(
result("content.editorial.articles", 2, [
{
id: "draft",
title: "Draft",
status: "draft",
updatedAt: new Date().toISOString(),
href: "/ase/content/editorial/articles/draft",
},
{
id: "published",
title: "Published",
status: "published",
updatedAt: new Date().toISOString(),
href: "/ase/content/editorial/articles/published",
},
]),
);
const items = await loadContentInboxItems(
"publication",
context,
new AbortController().signal,
);
expect(items.map((item) => item.itemId)).toEqual(["draft"]);
expect(items[0]?.state).toBe("draft");
});
});