import { assertEquals } from "@std/assert"; import { Database } from "@db/sqlite"; import { applyMigrations } from "../src/migrations/apply.ts"; type TableInfoRow = { name: string }; type ColumnInfoRow = { name: string }; const tableNames = (db: Database): string[] => db.prepare( "SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name", ).all().map((r) => r.name); const columnNames = (db: Database, table: string): string[] => db.prepare(`PRAGMA table_info(${table})`) .all() .map((r) => r.name) .sort(); const indexNames = (db: Database): string[] => db.prepare( "SELECT name FROM sqlite_master WHERE type = 'index' AND name NOT LIKE 'sqlite_%' ORDER BY name", ).all().map((r) => r.name); Deno.test("applyMigrations creates expected tables, columns, and indices", () => { const db = new Database(":memory:"); try { applyMigrations(db); const tables = tableNames(db); assertEquals( tables.includes("note") && tables.includes("state") && tables.includes("webhook_subscription"), true, ); const noteCols = columnNames(db, "note"); for ( const col of [ "title", "publishedAt", "createdAt", "did", "rkey", "discoverable", "language", ] ) { assertEquals(noteCols.includes(col), true, `note.${col} missing`); } const webhookCols = columnNames(db, "webhook_subscription"); for (const col of ["id", "did", "method", "url", "token", "verb"]) { assertEquals( webhookCols.includes(col), true, `webhook_subscription.${col} missing`, ); } const indices = indexNames(db); assertEquals(indices.includes("idx_webhook_subscription_did"), true); assertEquals(indices.includes("idx_webhook_subscription_did_verb"), true); } finally { db.close(); } }); Deno.test("applyMigrations is idempotent", () => { const db = new Database(":memory:"); try { applyMigrations(db); db.exec( "INSERT INTO webhook_subscription (did, method, url, verb) VALUES ('did:plc:x', 'POST', 'https://e.com', 'create')", ); applyMigrations(db); applyMigrations(db); const [{ count }] = db.prepare( "SELECT COUNT(*) AS count FROM webhook_subscription WHERE did = 'did:plc:x' AND verb = 'create'", ).all<{ count: number }>(); assertEquals(count, 1); } finally { db.close(); } }); Deno.test( "applyMigrations upgrades a legacy schema missing language and discoverable", () => { const db = new Database(":memory:"); try { db.exec(` CREATE TABLE note ( title TEXT NOT NULL, publishedAt DATETIME, createdAt DATETIME NOT NULL, did TEXT NOT NULL, rkey TEXT NOT NULL, listed INTEGER NOT NULL DEFAULT 1, PRIMARY KEY (did, rkey) ); `); applyMigrations(db); const cols = columnNames(db, "note"); assertEquals(cols.includes("discoverable"), true); assertEquals(cols.includes("language"), true); assertEquals(cols.includes("listed"), false); } finally { db.close(); } }, );