feat(housekeeping): deliver content vertical

This commit is contained in:
Simo committed 2026-08-30 01:54:43 +02:00
1 parent bdab924bdf
commit fd68819d9b
67 files changed
+5742 -1916

No files matched your search

+19 -39
View File
@@ -1,59 +1,39 @@
// @ts-nocheck
import path from "node:path";
import { revalidatePath } from "next/cache";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { requirePermission } from "@/lib/admin/guard";
import { resolveMediaPath } from "@/lib/media-storage";
import { deleteMedia, uploadMedia, uploadMediaAndReturn } from "./admin-media";
const { execute } = vi.hoisted(() => ({ execute: vi.fn() }));
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/media-storage", () => {
const root = path.join("/tmp", "nexst-test-media");
return {
MEDIA_ROOT: root,
resolveMediaPath: vi.fn((name: string) => path.join(root, name)),
};
});
vi.mock("node:fs/promises", () => ({
mkdir: vi.fn(),
writeFile: vi.fn(),
unlink: vi.fn(),
}));
vi.mock("next/cache", () => ({ revalidatePath: vi.fn() }));
const staff = { id: 1, rank: 7, username: "admin" };
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(requirePermission).mockResolvedValue(staff as never);
vi.mocked(requirePermission).mockResolvedValue({ id: 1, rank: 7, username: "admin" });
execute.mockResolvedValue({ ok: true, data: { before: null, after: { name: "photo.png" }, output: { url: "/api/media/photo.png" } }, correlationId: "legacy" });
});
describe("uploadMedia", () => {
it("returns error when no file provided", async () => {
const result = await uploadMedia(new FormData());
expect(result.ok).toBe(false);
expect(result.error).toBe("No file provided");
});
});
describe("uploadMediaAndReturn", () => {
it("returns empty string when no file", async () => {
describe("Content media legacy wrappers", () => {
it("retains no-file validation", async () => {
expect(await uploadMedia(new FormData())).toEqual({ ok: false, error: "No file provided" });
expect(await uploadMediaAndReturn(new FormData())).toBe("");
expect(execute).not.toHaveBeenCalled();
});
});
describe("deleteMedia", () => {
it("deletes media file and revalidates", async () => {
it("delegates a valid upload and preserves both result shapes", async () => {
const file = new File(["bytes"], "photo.png", { type: "image/png" });
const form = new FormData();
form.set("file", file);
expect(await uploadMedia(form)).toEqual({ ok: true });
expect(await uploadMediaAndReturn(form)).toBe("/api/media/photo.png");
expect(execute).toHaveBeenCalledWith(expect.anything(), "media.upload", { file });
});
it("delegates deletion and preserves revalidation", async () => {
await deleteMedia("photo.png");
expect(execute).toHaveBeenCalledWith(expect.anything(), "media.delete", { filename: "photo.png" });
expect(revalidatePath).toHaveBeenCalledWith("/api/media");
});
it("skips deletion when path is outside media root", async () => {
vi.mocked(resolveMediaPath).mockReturnValue("/etc/passwd");
await deleteMedia("../../../etc/passwd");
const { unlink } = await import("node:fs/promises");
expect(unlink).not.toHaveBeenCalled();
expect(revalidatePath).toHaveBeenCalledWith("/admin/media");
});
});