import { expect, test } from "bun:test"; import type { Db } from "@sttracker/db/db"; import { newId, nowIso } from "@sttracker/db/ids"; import { forEachDialect } from "@sttracker/db/testing"; import { acceptSuggestion, getEntry, quickAdd, rejectSuggestion } from "../src/entries.ts"; import { createCopy, listCopies } from "../src/copies.ts"; import { NotFoundError, ValidationError } from "../src/errors.ts"; import { getHistory } from "../src/history.ts"; import { addListItem, createList, getList } from "../src/lists.ts"; import { makeUser, makeWork } from "./user-layer-fixtures.ts"; async function suggest(db: Db, userId: string, workId: string, candidateWorkId: string): Promise { const id = newId(); await db.k .insertInto("match_suggestions") .values({ id, user_id: userId, work_id: workId, candidate_work_id: candidateWorkId, score: 0.9, status: "pending", created_at: nowIso() }) .execute(); return id; } async function statusOf(db: Db, id: string): Promise { return (await db.k.selectFrom("match_suggestions").select("status").where("id", "=", id).executeTakeFirstOrThrow()).status; } forEachDialect("suggestions", (getDb) => { test("acceptSuggestion moves the entry and rejects the other pending suggestions", async () => { const db = getDb(); const userId = await makeUser(db); const draft = await quickAdd(db, userId, { title: "Dune??", mediaType: "book" }); const good = await makeWork(db, { title: "Dune", externalIds: { isbn13: "1" } }); const other = await makeWork(db, { title: "Dune Messiah" }); const s1 = await suggest(db, userId, draft.workId, good); const s2 = await suggest(db, userId, draft.workId, other); await acceptSuggestion(db, userId, s1); expect(await statusOf(db, s1)).toBe("accepted"); expect(await statusOf(db, s2)).toBe("rejected"); const view = await getEntry(db, userId, draft.id); expect(view.workId).toBe(good); expect(view.draft).toBe(false); }); test("rejectSuggestion; other users get NotFoundError", async () => { const db = getDb(); const ada = await makeUser(db); const bob = await makeUser(db, "bob"); const s = await suggest(db, ada, await makeWork(db), await makeWork(db)); await expect(acceptSuggestion(db, bob, s)).rejects.toThrow(NotFoundError); await expect(rejectSuggestion(db, bob, s)).rejects.toThrow(NotFoundError); expect(await statusOf(db, s)).toBe("pending"); await rejectSuggestion(db, ada, s); expect(await statusOf(db, s)).toBe("rejected"); await expect(rejectSuggestion(db, ada, s)).rejects.toThrow(ValidationError); await expect(acceptSuggestion(db, ada, s)).rejects.toThrow(ValidationError); }); test("accepting moves this user's copies and list items to the target work", async () => { const db = getDb(); const userId = await makeUser(db); const draft = await quickAdd(db, userId, { title: "Dune??", mediaType: "book" }); const target = await makeWork(db, { title: "Dune", externalIds: { isbn13: "1" } }); const copy = await createCopy(db, userId, { workId: draft.workId, format: "paperback" }); const list = await createList(db, userId, { name: "Desert" }); await addListItem(db, userId, list.id, { workId: draft.workId }); await acceptSuggestion(db, userId, await suggest(db, userId, draft.workId, target)); expect((await listCopies(db, userId, { workId: target })).map((c) => c.id)).toEqual([copy.id]); const [ch] = await getHistory(db, userId, "copy", copy.id); expect(ch!.after).toEqual({ workId: target }); expect((await getList(db, userId, list.id)).items.map((i) => i.work.title)).toEqual(["Dune"]); }); });