123 lines
3.2 KiB
TypeScript
123 lines
3.2 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const doubles = vi.hoisted(() => ({
|
|
canAccess: vi.fn(),
|
|
getApiAdminContext: vi.fn(),
|
|
mutationExecute: vi.fn(),
|
|
reportError: vi.fn(),
|
|
revalidateTag: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@/features/housekeeping/domains/system/services/mutations", () => ({
|
|
systemMutationService: { execute: doubles.mutationExecute },
|
|
}));
|
|
|
|
vi.mock("@/lib/permissions", () => ({
|
|
canAccess: doubles.canAccess,
|
|
getApiAdminContext: doubles.getApiAdminContext,
|
|
}));
|
|
|
|
vi.mock("@/lib/admin/authorization-events", () => ({
|
|
logAuthorizationEvent: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@/lib/auth", () => ({ auth: vi.fn() }));
|
|
vi.mock("@/lib/rate-limit", () => ({ rateLimit: vi.fn() }));
|
|
vi.mock("@/lib/report-error", () => ({ reportError: doubles.reportError }));
|
|
vi.mock("@/lib/foundation/security", () => ({
|
|
extractClientIpAsync: vi.fn(async () => "198.51.100.8"),
|
|
}));
|
|
vi.mock("@/lib/foundation/request-context", () => ({
|
|
createStore: vi.fn(() => ({})),
|
|
getRequestId: vi.fn(() => "legacy-permissions-request"),
|
|
runWithStore: vi.fn((_store: unknown, callback: () => Promise<unknown>) =>
|
|
callback(),
|
|
),
|
|
setContextUserId: vi.fn(),
|
|
}));
|
|
vi.mock("next/cache", () => ({ revalidateTag: doubles.revalidateTag }));
|
|
|
|
import { deleteRank, setCmsPermissions } from "./permissions";
|
|
|
|
const permissions = {
|
|
has: () => true,
|
|
hasAny: () => true,
|
|
hasAll: () => true,
|
|
isSuperAdmin: false,
|
|
};
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
doubles.canAccess.mockReturnValue(true);
|
|
doubles.getApiAdminContext.mockResolvedValue({
|
|
session: {
|
|
expires: "2099-01-01T00:00:00.000Z",
|
|
user: {
|
|
id: 42,
|
|
name: "operator",
|
|
username: "operator",
|
|
rank: 7,
|
|
look: "hd-180-1",
|
|
mail: "[email protected]",
|
|
},
|
|
},
|
|
permissions,
|
|
});
|
|
});
|
|
|
|
describe("legacy permission action error parity", () => {
|
|
it("sanitizes typed infrastructure failure through the real adminAction boundary", async () => {
|
|
doubles.mutationExecute.mockResolvedValue({
|
|
ok: false,
|
|
error: {
|
|
code: "DEPENDENCY_UNAVAILABLE",
|
|
messageKey: "errors.housekeeping.dependencyUnavailable",
|
|
},
|
|
correlationId: "dependency-correlation",
|
|
});
|
|
|
|
await expect(deleteRank({ id: 7 })).resolves.toEqual({
|
|
ok: false,
|
|
error: "Internal server error",
|
|
fieldErrors: undefined,
|
|
});
|
|
});
|
|
|
|
it("preserves the established rank-in-use ActionError text", async () => {
|
|
doubles.mutationExecute.mockResolvedValue({
|
|
ok: false,
|
|
error: {
|
|
code: "CONFLICT",
|
|
messageKey: "errors.housekeeping.system.rankInUse",
|
|
fieldErrors: { rank: ["3"] },
|
|
},
|
|
correlationId: "rank-in-use-correlation",
|
|
});
|
|
|
|
await expect(deleteRank({ id: 7 })).resolves.toEqual({
|
|
ok: false,
|
|
error: "Cannot delete: 3 users have this rank",
|
|
fieldErrors: undefined,
|
|
});
|
|
});
|
|
|
|
it("preserves the established role-not-found ActionError text", async () => {
|
|
doubles.mutationExecute.mockResolvedValue({
|
|
ok: false,
|
|
error: {
|
|
code: "NOT_FOUND",
|
|
messageKey: "errors.housekeeping.system.roleNotFound",
|
|
},
|
|
correlationId: "role-not-found-correlation",
|
|
});
|
|
|
|
await expect(
|
|
setCmsPermissions({ roleId: 7, permissionSlugs: [] }),
|
|
).resolves.toEqual({
|
|
ok: false,
|
|
error: "Role not found",
|
|
fieldErrors: undefined,
|
|
});
|
|
});
|
|
});
|