import { assertEquals, assertFalse } from "@std/assert"; import { addWebhookSubscription, deleteWebhookById, getNotes, getNotesByDids, getWebhooksByDidAndVerb, listWebhooksByDid, upsertNote, } from "../src/data/db.ts"; import { seedNote, seedWebhook, withTestDb } from "./_helpers/db.ts"; Deno.test( "upsertNote inserts then updates on (did, rkey) conflict", withTestDb(() => { upsertNote({ did: "did:plc:user", rkey: "rkey1", title: "First", publishedAt: "2026-01-01T00:00:00.000Z", createdAt: "2026-01-01T00:00:00.000Z", }); upsertNote({ did: "did:plc:user", rkey: "rkey1", title: "Second", publishedAt: "2026-02-01T00:00:00.000Z", createdAt: "2026-01-01T00:00:00.000Z", }); const { notes } = getNotes(undefined, 10); assertEquals(notes.length, 1); assertEquals(notes[0].title, "Second"); assertEquals(notes[0].publishedAt, "2026-02-01T00:00:00.000Z"); }), ); Deno.test( "upsertNote treats undefined discoverable as 1 and false as 0", withTestDb(() => { upsertNote({ did: "did:plc:user", rkey: "default", title: "Default", publishedAt: "2026-01-01T00:00:00.000Z", createdAt: "2026-01-01T00:00:00.000Z", }); upsertNote({ did: "did:plc:user", rkey: "hidden", title: "Hidden", publishedAt: "2026-01-01T00:00:00.000Z", createdAt: "2026-01-01T00:00:00.000Z", discoverable: false, }); const { notes } = getNotes(undefined, 10); const rkeys = notes.map((n) => n.rkey); assertEquals(rkeys, ["default"]); }), ); Deno.test( "getNotes paginates rkey desc and returns cursor only when page is full", withTestDb(() => { for (const rkey of ["a", "b", "c", "d", "e"]) { seedNote({ rkey }); } const firstPage = getNotes(undefined, 3); assertEquals(firstPage.notes.map((n) => n.rkey), ["e", "d", "c"]); assertEquals(firstPage.cursor, "c"); const secondPage = getNotes(firstPage.cursor, 3); assertEquals(secondPage.notes.map((n) => n.rkey), ["b", "a"]); assertEquals(secondPage.cursor, undefined); }), ); Deno.test( "getNotesByDids returns empty without running SQL on empty did list", withTestDb(() => { seedNote({ did: "did:plc:alice", rkey: "a" }); const result = getNotesByDids([], undefined, 10); assertEquals(result, { notes: [] }); }), ); Deno.test( "webhook CRUD: add omits token, list orders desc, delete by id reports hit", withTestDb(() => { const created = addWebhookSubscription({ did: "did:plc:user", method: "POST", url: "https://example.com/a", token: "secret", verb: "create", }); assertEquals(created.token, undefined); assertEquals(created.url, "https://example.com/a"); seedWebhook({ did: "did:plc:user", url: "https://example.com/b", verb: "create", }); seedWebhook({ did: "did:plc:user", url: "https://example.com/c", verb: "delete", }); const list = listWebhooksByDid("did:plc:user"); assertEquals(list.length, 3); const ids = list.map((w) => w.id); assertEquals(ids, [...ids].sort((a, b) => b - a)); assertFalse(deleteWebhookById({ did: "did:plc:user", id: 9999 })); const deleted = deleteWebhookById({ did: "did:plc:user", id: created.id }); assertEquals(deleted, true); assertEquals(listWebhooksByDid("did:plc:user").length, 2); }), ); Deno.test( "getWebhooksByDidAndVerb filters by verb and caps at 10", withTestDb(() => { for (let i = 0; i < 12; i++) { seedWebhook({ url: `https://example.com/c${i}`, verb: "create" }); } seedWebhook({ url: "https://example.com/d", verb: "delete" }); const creates = getWebhooksByDidAndVerb("did:plc:test", "create"); assertEquals(creates.length, 10); for (const row of creates) assertEquals(row.verb, "create"); const deletes = getWebhooksByDidAndVerb("did:plc:test", "delete"); assertEquals(deletes.length, 1); assertEquals(deletes[0].verb, "delete"); }), );