147 lines
4.6 KiB
TypeScript
147 lines
4.6 KiB
TypeScript
import {
|
|
createUser,
|
|
db,
|
|
del,
|
|
get,
|
|
insertCategory,
|
|
insertDump,
|
|
post,
|
|
put,
|
|
test,
|
|
} from "../helpers.ts";
|
|
import type { Category } from "../../../shared/category.ts";
|
|
import type { Dump } from "../../../shared/dump.ts";
|
|
import type { PaginatedData } from "../../../shared/api.ts";
|
|
import { assertEquals, assertStringIncludes } from "@std/assert";
|
|
|
|
test("categories: listed by position, then slug", async () => {
|
|
insertCategory({ slug: "zeta", position: 0 });
|
|
insertCategory({ slug: "alpha", position: 1 });
|
|
insertCategory({ slug: "beta", position: 0 });
|
|
const list = await get<Category[]>("/api/categories");
|
|
assertEquals(list.data.map((c) => c.slug), [
|
|
"beta",
|
|
"zeta",
|
|
"alpha",
|
|
]);
|
|
});
|
|
|
|
test("categories: only category:manage may create, update or delete", async () => {
|
|
const admin = await createUser({ role: "admin" });
|
|
const mod = await createUser({ role: "moderator" });
|
|
const json = { slug: "music", name: "Music" };
|
|
assertEquals((await post("/api/categories", { json })).status, 401);
|
|
assertEquals(
|
|
(await post("/api/categories", { auth: mod.auth, json })).status,
|
|
403,
|
|
);
|
|
const created = await post<Category>("/api/categories", {
|
|
auth: admin.auth,
|
|
json,
|
|
});
|
|
assertEquals(created.status, 201);
|
|
const id = created.data.id;
|
|
assertEquals(
|
|
(await put(`/api/categories/${id}`, {
|
|
auth: mod.auth,
|
|
json: { name: "X" },
|
|
})).status,
|
|
403,
|
|
);
|
|
assertEquals(
|
|
(await del(`/api/categories/${id}`, { auth: mod.auth })).status,
|
|
403,
|
|
);
|
|
assertEquals(
|
|
(await del(`/api/categories/${id}`, { auth: admin.auth })).status,
|
|
200,
|
|
);
|
|
assertEquals(
|
|
(await del(`/api/categories/${id}`, { auth: admin.auth })).status,
|
|
404,
|
|
);
|
|
});
|
|
|
|
test("categories: slug and name rules, and friendly conflicts", async () => {
|
|
const admin = await createUser({ role: "admin" });
|
|
const create = (json: unknown) =>
|
|
post<Category>("/api/categories", { auth: admin.auth, json });
|
|
for (
|
|
const [json, message] of [
|
|
[{ slug: "Bad Slug", name: "x" }, "lowercase"],
|
|
[{ slug: "hot", name: "x" }, "reserved"],
|
|
[{ slug: "settings", name: "x" }, "reserved"],
|
|
[{ slug: "a".repeat(51), name: "x" }, "at most 50"],
|
|
[{ slug: "ok", name: " " }, "Name is required"],
|
|
[{ slug: "ok", name: "x".repeat(51) }, "at most 50"],
|
|
] as const
|
|
) {
|
|
const res = await create(json);
|
|
assertEquals(res.status, 400, JSON.stringify(json));
|
|
assertStringIncludes(res.error.message, message);
|
|
}
|
|
assertEquals((await create({ slug: "ok" })).status, 400);
|
|
|
|
const first = await create({ slug: "music", name: " Music " });
|
|
assertEquals(first.data.name, "Music");
|
|
const dupSlug = await create({ slug: "music", name: "Other" });
|
|
assertEquals(dupSlug.status, 409);
|
|
assertStringIncludes(dupSlug.error.message, "Slug");
|
|
const dupName = await create({ slug: "other", name: "Music" });
|
|
assertEquals(dupName.status, 409);
|
|
assertStringIncludes(dupName.error.message, "Name");
|
|
|
|
const second = await create({ slug: "films", name: "Films" });
|
|
const rename = await put(`/api/categories/${second.data.id}`, {
|
|
auth: admin.auth,
|
|
json: { slug: "music" },
|
|
});
|
|
assertEquals(rename.status, 409);
|
|
const ok = await put<Category>(`/api/categories/${second.data.id}`, {
|
|
auth: admin.auth,
|
|
json: { name: "Movies", position: 3 },
|
|
});
|
|
assertEquals(ok.data.slug, "films");
|
|
assertEquals(ok.data.name, "Movies");
|
|
assertEquals(ok.data.position, 3);
|
|
});
|
|
|
|
test("GET /api/dumps?category=: members only, private for the owner", async () => {
|
|
const owner = await createUser();
|
|
const cat = insertCategory({ slug: "music" });
|
|
const inCat = insertDump({ userId: owner.id, title: "in" });
|
|
const priv = insertDump({
|
|
userId: owner.id,
|
|
title: "private",
|
|
isPrivate: true,
|
|
});
|
|
insertDump({ userId: owner.id, title: "out" });
|
|
for (const d of [inCat, priv]) {
|
|
db.prepare(
|
|
`INSERT INTO dump_categories (dump_id, category_id) VALUES (?, ?);`,
|
|
)
|
|
.run(d.id, cat);
|
|
}
|
|
const anon = await get<PaginatedData<Dump>>("/api/dumps?category=music");
|
|
assertEquals(anon.data.items.map((d) => d.title), [
|
|
"in",
|
|
]);
|
|
assertEquals(anon.data.items[0].categoryIds, [cat]);
|
|
assertEquals(
|
|
(await get<PaginatedData<Dump>>("/api/dumps?category=music", {
|
|
auth: owner.auth,
|
|
})).data
|
|
.items.length,
|
|
2,
|
|
);
|
|
assertEquals((await get("/api/dumps?category=nope")).status, 404);
|
|
|
|
// Deleting the category unlinks its dumps without deleting them.
|
|
const admin = await createUser({ role: "admin" });
|
|
await del(`/api/categories/${cat}`, { auth: admin.auth });
|
|
assertEquals(
|
|
(await get<Dump>(`/api/dumps/${inCat.id}`)).data.categoryIds,
|
|
[],
|
|
);
|
|
});
|