feat(housekeeping): deliver content vertical
This commit is contained in:
1 parent
bdab924bdf
commit
fd68819d9b
67 files changed
+5742
-1916
No files matched your search
@@ -0,0 +1,109 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { PERMS } from "@/lib/permission-slugs";
|
||||
import type { HousekeepingCapabilityContext } from "../../../foundation/contracts";
|
||||
import {
|
||||
CONTENT_MUTATION_OPERATIONS,
|
||||
createContentMutationInvocation,
|
||||
createContentMutationService,
|
||||
} from "./mutations";
|
||||
|
||||
function context(
|
||||
granted: readonly string[],
|
||||
actorId = 42,
|
||||
): HousekeepingCapabilityContext {
|
||||
const permissions = new Set(granted);
|
||||
return {
|
||||
actor: { id: actorId, 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)),
|
||||
};
|
||||
}
|
||||
|
||||
describe("Content mutation service authority", () => {
|
||||
it("rehydrates server authority and rejects forged actor identity", async () => {
|
||||
const adapter = { execute: vi.fn() };
|
||||
const service = createContentMutationService(
|
||||
adapter,
|
||||
async () => context([PERMS.NEWS_EDIT], 42),
|
||||
);
|
||||
|
||||
const result = await service.execute(
|
||||
{ correlationId: "forged", expectedActorId: 7 },
|
||||
"article.change",
|
||||
{ action: "create", title: "Forged" },
|
||||
);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
error: { code: "FORBIDDEN" },
|
||||
});
|
||||
expect(adapter.execute).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("requires the exact operation ACL even after dispatcher authorization", async () => {
|
||||
const adapter = { execute: vi.fn() };
|
||||
const service = createContentMutationService(
|
||||
adapter,
|
||||
async () => context([PERMS.NEWS_EDIT]),
|
||||
);
|
||||
|
||||
const result = await service.execute(
|
||||
{ correlationId: "brand", expectedActorId: 42 },
|
||||
"theme.update",
|
||||
{},
|
||||
);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
error: { code: "FORBIDDEN" },
|
||||
});
|
||||
expect(adapter.execute).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("executes every declared operation through the real service boundary", async () => {
|
||||
const execute = vi.fn(async (_operation, _input, mutationContext) => ({
|
||||
before: null,
|
||||
after: { actorId: mutationContext.capability.actor.id },
|
||||
}));
|
||||
const service = createContentMutationService(
|
||||
{ execute },
|
||||
async () => context(Object.values(PERMS)),
|
||||
);
|
||||
const invocation = createContentMutationInvocation(
|
||||
{ id: 42 },
|
||||
"operation-matrix",
|
||||
);
|
||||
|
||||
for (const operation of CONTENT_MUTATION_OPERATIONS) {
|
||||
const result = await service.execute(invocation, operation, {});
|
||||
expect(result.ok, operation).toBe(true);
|
||||
}
|
||||
expect(execute).toHaveBeenCalledTimes(CONTENT_MUTATION_OPERATIONS.length);
|
||||
});
|
||||
|
||||
it("maps adapter failures without exposing storage or template payloads", async () => {
|
||||
const service = createContentMutationService(
|
||||
{
|
||||
execute: async () => {
|
||||
throw new Error("C:\\private\\template.json: secret body");
|
||||
},
|
||||
},
|
||||
async () => context([PERMS.SETTINGS_EDIT]),
|
||||
);
|
||||
const result = await service.execute(
|
||||
{ correlationId: "redacted", expectedActorId: 42 },
|
||||
"translation.cms.save",
|
||||
{ data: { secret: "body" } },
|
||||
);
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
error: {
|
||||
code: "DEPENDENCY_UNAVAILABLE",
|
||||
messageKey: "errors.housekeeping.dependencyUnavailable",
|
||||
},
|
||||
});
|
||||
expect(JSON.stringify(result)).not.toContain("secret body");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user