import { describe, it, before, after } from "node:test"; import assert from "node:assert/strict"; import { WrappedNodeSqliteDb } from "../../../src/db/sqlite/impl/node-sqlite.js"; import { sql, unsafeSql } from "../../../src/db/sqlite/sql.js"; import type { WrappedDb } from "../../../src/db/sqlite/impl/types.js"; function runWrappedDbSuite(name: string, makeDb: () => WrappedDb) { describe(`${name}: WrappedDb`, () => { let db: WrappedDb; before(() => { db = makeDb(); db.run(unsafeSql("CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")); }); after(() => db.destroy()); it("run INSERT returns changes count", () => { const { changes } = db.run(sql`INSERT INTO test(val) VALUES(${{ text: "hello" }})`); assert.equal(changes, 1); }); it("all SELECT returns all rows", () => { db.run(unsafeSql("DELETE FROM test")); db.run(sql`INSERT INTO test(val) VALUES(${{ text: "a" }})`); db.run(sql`INSERT INTO test(val) VALUES(${{ text: "b" }})`); const rows = db.all<{ val: string }>(unsafeSql("SELECT val FROM test ORDER BY val")); assert.equal(rows.length, 2); assert.equal(rows[0].val, "a"); assert.equal(rows[1].val, "b"); }); it("get returns single row or null", () => { db.run(unsafeSql("DELETE FROM test")); db.run(sql`INSERT INTO test(val) VALUES(${{ text: "one" }})`); const row = db.get<{ val: string }>(unsafeSql("SELECT val FROM test")); assert.ok(row !== null); assert.equal(row.val, "one"); const none = db.get<{ val: string }>( sql`SELECT val FROM test WHERE val = ${{ text: "none" }}` ); assert.equal(none, null); }); it("get throws when multiple rows match", () => { db.run(unsafeSql("DELETE FROM test")); db.run(sql`INSERT INTO test(val) VALUES(${{ text: "dup1" }})`); db.run(sql`INSERT INTO test(val) VALUES(${{ text: "dup2" }})`); assert.throws( () => db.get<{ val: string }>(unsafeSql("SELECT val FROM test")), /Multiple results/ ); }); it("multi commits on success", () => { db.run(unsafeSql("DELETE FROM test")); db.multi(() => { db.run(sql`INSERT INTO test(val) VALUES(${{ text: "in-tx" }})`); }); const rows = db.all<{ val: string }>(unsafeSql("SELECT val FROM test")); assert.equal(rows.length, 1); assert.equal(rows[0].val, "in-tx"); }); it("multi rolls back on error", () => { db.run(unsafeSql("DELETE FROM test")); assert.throws(() => db.multi(() => { db.run(sql`INSERT INTO test(val) VALUES(${{ text: "rollback" }})`); throw new Error("abort!"); }) ); const rows = db.all<{ val: string }>(unsafeSql("SELECT val FROM test")); assert.equal(rows.length, 0); }); it("rethrow passes through return value", () => { const result = db.rethrow(() => 42); assert.equal(result, 42); }); it("rethrow propagates non-SQLite errors unchanged", () => { const err = new Error("custom error"); assert.throws(() => db.rethrow(() => { throw err; }), (e) => e === err); }); }); } runWrappedDbSuite("node-sqlite", () => new WrappedNodeSqliteDb(":memory:")); describe("better-sqlite3 WrappedDb", async () => { let bs3Ctor: (new (path: string) => WrappedDb) | null = null; try { const mod = await import( "../../../src/db/sqlite/impl/better-sqlite3.js" ); bs3Ctor = mod.WrappedBetterSqlite3Db; } catch { // better-sqlite3 not available, skip } if (bs3Ctor) { runWrappedDbSuite("better-sqlite3", () => new bs3Ctor!(":memory:")); } else { it("better-sqlite3 is not available - skipped", (t) => { t.skip("better-sqlite3 optional dependency not found"); }); } });