import { expect, test } from "bun:test"; import { forEachDialect } from "@sttracker/db/testing"; import { NotFoundError, ValidationError } from "../src/errors.ts"; import { getHistory, restoreVersion } from "../src/history.ts"; import { addListItem, createList, getList, listLists, moveListItem, removeListItem, softDeleteList, updateList, } from "../src/lists.ts"; import { makeUser, makeWork } from "./user-layer-fixtures.ts"; forEachDialect("lists", (getDb) => { test("grouping list can be rated", async () => { const db = getDb(); const userId = await makeUser(db); const g = await createList(db, userId, { name: "Silent Hill, everything", kind: "grouping", rating: 90, review: "Dread." }); expect(g.kind).toBe("grouping"); expect(g.rating).toBe(90); expect(g.review).toBe("Dread."); expect(g.visibility).toBe("private"); const up = await updateList(db, userId, g.id, { rating: 95 }); expect(up.rating).toBe(95); await expect(createList(db, userId, { name: "Plain", kind: "list", rating: 50 })).rejects.toThrow(ValidationError); await expect(createList(db, userId, { name: "Bad", kind: "grouping", rating: 101 })).rejects.toThrow( ValidationError, ); await expect(updateList(db, userId, g.id, { kind: "thread" })).rejects.toThrow(ValidationError); await expect(createList(db, userId, { name: " " })).rejects.toThrow(ValidationError); await expect(createList(db, userId, { name: "x", kind: "box" as never })).rejects.toThrow(ValidationError); }); test("items keep order through add, move and remove", async () => { const db = getDb(); const userId = await makeUser(db); const l = await createList(db, userId, { name: "Cosmic dread", kind: "thread" }); const [a, b, c] = [await makeWork(db, { title: "A" }), await makeWork(db, { title: "B" }), await makeWork(db, { title: "C" })]; const ia = await addListItem(db, userId, l.id, { workId: a, note: "start here" }); const ib = await addListItem(db, userId, l.id, { workId: b }); const ic = await addListItem(db, userId, l.id, { workId: c }); const titles = async () => (await getList(db, userId, l.id)).items.map((i) => i.work.title); expect(await titles()).toEqual(["A", "B", "C"]); expect((await getList(db, userId, l.id)).items[0]!.note).toBe("start here"); await moveListItem(db, userId, ic.id, 0); expect(await titles()).toEqual(["C", "A", "B"]); await moveListItem(db, userId, ic.id, 99); expect(await titles()).toEqual(["A", "B", "C"]); await removeListItem(db, userId, ib.id); expect(await titles()).toEqual(["A", "C"]); expect((await getList(db, userId, l.id)).items.map((i) => i.position)).toEqual([0, 1]); const inserted = await addListItem(db, userId, l.id, { workId: b, position: 1 }); expect(await titles()).toEqual(["A", "B", "C"]); expect(inserted.position).toBe(1); expect(ia.position).toBe(0); }); test("removing an item is recorded and restorable", async () => { const db = getDb(); const userId = await makeUser(db); const l = await createList(db, userId, { name: "Reading order" }); await addListItem(db, userId, l.id, { workId: await makeWork(db, { title: "A" }) }); const ib = await addListItem(db, userId, l.id, { workId: await makeWork(db, { title: "B" }) }); await removeListItem(db, userId, ib.id); const [h] = await getHistory(db, userId, "list", l.id); await restoreVersion(db, userId, h!.id); expect((await getList(db, userId, l.id)).items.map((i) => i.work.title)).toEqual(["A", "B"]); }); test("listLists and soft delete", async () => { const db = getDb(); const userId = await makeUser(db); const l1 = await createList(db, userId, { name: "One" }); const l2 = await createList(db, userId, { name: "Two", kind: "grouping" }); expect((await listLists(db, userId)).map((l) => l.id).sort()).toEqual([l1.id, l2.id].sort()); expect((await listLists(db, userId, { kind: "grouping" })).map((l) => l.id)).toEqual([l2.id]); await softDeleteList(db, userId, l1.id); expect((await listLists(db, userId)).map((l) => l.id)).toEqual([l2.id]); await expect(getList(db, userId, l1.id)).rejects.toThrow(NotFoundError); }); test("another user's lists and private works are not found", async () => { const db = getDb(); const ada = await makeUser(db); const bob = await makeUser(db, "bob"); const l = await createList(db, ada, { name: "Mine" }); const item = await addListItem(db, ada, l.id, { workId: await makeWork(db) }); await expect(getList(db, bob, l.id)).rejects.toThrow(NotFoundError); await expect(updateList(db, bob, l.id, { name: "x" })).rejects.toThrow(NotFoundError); await expect(addListItem(db, bob, l.id, { workId: await makeWork(db) })).rejects.toThrow(NotFoundError); await expect(moveListItem(db, bob, item.id, 0)).rejects.toThrow(NotFoundError); await expect(removeListItem(db, bob, item.id)).rejects.toThrow(NotFoundError); expect(await listLists(db, bob)).toEqual([]); const adaPrivate = await makeWork(db, { ownerUserId: ada }); const bobList = await createList(db, bob, { name: "Bob's" }); await expect(addListItem(db, bob, bobList.id, { workId: adaPrivate })).rejects.toThrow(NotFoundError); }); });