116 lines
3.5 KiB
TypeScript
116 lines
3.5 KiB
TypeScript
// @ts-nocheck
|
|
import { redirect } from "next/navigation";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { requirePermission } from "@/lib/admin/guard";
|
|
import { logger } from "@/lib/logger";
|
|
import { ActionError } from "@/lib/safe-action-shared";
|
|
import { createAd, deleteAd } from "./admin-ads";
|
|
|
|
const { execute } = vi.hoisted(() => ({
|
|
execute: vi.fn(async () => ({
|
|
ok: true,
|
|
data: { before: null, after: { id: "1" }, output: { id: "1" } },
|
|
correlationId: "legacy",
|
|
})),
|
|
}));
|
|
vi.mock("@/features/housekeeping/domains/content/services/mutations", () => ({
|
|
contentMutationService: { execute },
|
|
createContentMutationInvocation: (actor, correlationId) => ({
|
|
expectedActorId: actor.id,
|
|
correlationId,
|
|
legacy: true,
|
|
}),
|
|
}));
|
|
vi.mock("@/lib/admin/guard", () => ({ requirePermission: vi.fn() }));
|
|
vi.mock("@/lib/permissions", () => ({ PERMS: { PAGES_EDIT: "pages.edit" } }));
|
|
vi.mock("@/lib/logger", () => ({ logger: { error: vi.fn() } }));
|
|
vi.mock("@/lib/safe-action", () => ({
|
|
adminAction: (_options, handler) => handler,
|
|
}));
|
|
vi.mock("@/lib/safe-action-shared", () => ({
|
|
ActionError: class ActionError extends Error {},
|
|
actionOk: () => "ok",
|
|
}));
|
|
vi.mock("next/cache", () => ({ revalidatePath: vi.fn() }));
|
|
vi.mock("next/navigation", () => ({ redirect: vi.fn() }));
|
|
|
|
const staff = { id: 1, rank: 7, username: "admin" };
|
|
const fakeForm = (data) => ({ get: (key) => data[key] ?? null });
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
vi.mocked(requirePermission).mockResolvedValue(staff);
|
|
execute.mockResolvedValue({
|
|
ok: true,
|
|
data: { before: null, after: { id: "1" }, output: { id: "1" } },
|
|
correlationId: "legacy",
|
|
});
|
|
});
|
|
|
|
describe("Content advertisement legacy wrappers", () => {
|
|
it("delegates creation and preserves redirect", async () => {
|
|
await createAd(fakeForm({ image: "https://example.com/ad.png" }));
|
|
expect(execute).toHaveBeenCalledWith(
|
|
expect.objectContaining({ expectedActorId: 1, legacy: true }),
|
|
"ad.change",
|
|
{ action: "create", image: "https://example.com/ad.png" },
|
|
);
|
|
expect(redirect).toHaveBeenCalledWith("/admin/ads");
|
|
});
|
|
|
|
it("returns early when image is empty", async () => {
|
|
await createAd(fakeForm({ image: "" }));
|
|
expect(execute).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("logs a redacted service failure", async () => {
|
|
execute.mockResolvedValue({
|
|
ok: false,
|
|
error: {
|
|
code: "DEPENDENCY_UNAVAILABLE",
|
|
messageKey: "errors.housekeeping.dependencyUnavailable",
|
|
},
|
|
correlationId: "legacy",
|
|
});
|
|
await createAd(fakeForm({ image: "x" }));
|
|
expect(logger.error).toHaveBeenCalledWith(
|
|
"Action failed: createAd",
|
|
expect.objectContaining({
|
|
error: "errors.housekeeping.dependencyUnavailable",
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("delegates deletion and preserves action result", async () => {
|
|
const handler = deleteAd as unknown as (ctx: unknown) => Promise<string>;
|
|
await expect(
|
|
handler({
|
|
data: { id: 99n },
|
|
session: { user: { id: "1" } },
|
|
requestId: "delete",
|
|
}),
|
|
).resolves.toBe("ok");
|
|
expect(execute).toHaveBeenCalledWith(
|
|
expect.objectContaining({ correlationId: "delete" }),
|
|
"ad.change",
|
|
{ action: "delete", id: "99" },
|
|
);
|
|
});
|
|
|
|
it("preserves not-found ActionError", async () => {
|
|
execute.mockResolvedValue({
|
|
ok: false,
|
|
error: { code: "NOT_FOUND", messageKey: "errors.housekeeping.notFound" },
|
|
correlationId: "legacy",
|
|
});
|
|
const handler = deleteAd as unknown as (ctx: unknown) => Promise<string>;
|
|
await expect(
|
|
handler({
|
|
data: { id: 999n },
|
|
session: { user: { id: "1" } },
|
|
requestId: "missing",
|
|
}),
|
|
).rejects.toThrow(ActionError);
|
|
});
|
|
});
|