feat(cms): improve catalog, editorial recovery and operations
CI / check (push) Successful in 52s
CI / deploy (push) Successful in 2m10s
CI / publish-container (push) Failing after 1m18s

This commit is contained in:
Simo committed 2026-09-09 19:36:15 +02:00
1 parent 2fead134e6
commit c389c3893d
122 files changed
+8137 -703

No files matched your search

+51 -8
View File
@@ -27,12 +27,16 @@ vi.mock("next/navigation", () => ({
throw Error(`redirect ${url}`);
},
}));
vi.mock("@/lib/db", async () => ({
...(await import("@/db/schema")),
db: {
vi.mock("@/lib/db", async () => {
const database = {
select: () => ({
from: () => ({
where: () => ({ limit: async () => state.rows.shift() ?? [] }),
where: () => ({
limit: () => {
const result = Promise.resolve(state.rows.shift() ?? []);
return Object.assign(result, { for: () => result });
},
}),
}),
}),
insert: () => ({ values: state.insert }),
@@ -42,9 +46,13 @@ vi.mock("@/lib/db", async () => ({
return { where: async () => [{ affectedRows: 1 }] };
},
}),
},
}));
transaction: async (fn: (tx: unknown) => unknown): Promise<unknown> =>
fn(database),
};
return { ...(await import("@/db/schema")), db: database };
});
import { articleEditToken } from "@/lib/article-edit-token";
import { createArticle, updateArticle } from "./admin-articles";
function form(extra: Record<string, string> = {}) {
@@ -89,11 +97,25 @@ describe("news saves", () => {
expect(state.invalidate).toHaveBeenCalledOnce();
});
it("preserves the slug and clears old scheduling for immediate publication", async () => {
state.rows = [[{ slug: "test-news", status: "draft", publishedAt: null }]];
const existing = {
title: "Old",
slug: "test-news",
image: "",
shortStory: "",
fullStory: "",
status: "draft",
publishAt: null,
publishedAt: null,
};
state.rows = [[existing]];
expect(
(
await updateArticle(
form({ id: "1", publishAt: "2099-01-01T00:00:00Z" }),
form({
id: "1",
baseToken: articleEditToken(existing),
publishAt: "2099-01-01T00:00:00Z",
}),
)
).ok,
).toBe(true);
@@ -106,6 +128,27 @@ describe("news saves", () => {
);
expect(state.invalidate).toHaveBeenCalledOnce();
});
it("rejects an outdated editor without inserting a revision or overwriting the article", async () => {
state.rows = [
[
{
title: "Changed by another editor",
slug: "test-news",
image: "",
shortStory: "",
fullStory: "",
status: "published",
publishAt: null,
},
],
];
expect(
await updateArticle(form({ id: "1", baseToken: "outdated" })),
).toEqual({ ok: false, error: "editConflict" });
expect(state.insert).not.toHaveBeenCalled();
expect(state.update).not.toHaveBeenCalled();
expect(state.invalidate).not.toHaveBeenCalled();
});
it("rejects invalid scheduled dates before writing", async () => {
expect(
(await createArticle(form({ status: "scheduled", publishAt: "invalid" })))