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; found by slug", async () => { insertCategory({ slug: "zeta", position: 0 }); insertCategory({ slug: "alpha", position: 1 }); insertCategory({ slug: "beta", position: 0 }); const list = await get("/api/categories"); assertEquals(list.data.map((c) => c.slug), [ "beta", "zeta", "alpha", ]); assertEquals( (await get("/api/categories/alpha")).data.slug, "alpha", ); assertEquals((await get("/api/categories/nope")).status, 404); }); 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("/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("/api/categories", { auth: admin.auth, json }); for ( const [json, message] of [ [{ slug: "Bad Slug", name: "x" }, "lowercase"], [{ slug: "users", 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(`/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/categories/:slug/dumps: 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>("/api/categories/music/dumps"); assertEquals(anon.data.items.map((d) => d.title), [ "in", ]); assertEquals(anon.data.items[0].categoryIds, [cat]); assertEquals( (await get>("/api/categories/music/dumps", { auth: owner.auth, })).data .items.length, 2, ); assertEquals((await get("/api/categories/nope/dumps")).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(`/api/dumps/${inCat.id}`)).data.categoryIds, [], ); });