import { test } from "node:test"; import assert from "node:assert/strict"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { openDatabase, migrate } from "../src/database.js"; const steps = [{ version: 1, name: "projects", sql: "CREATE TABLE projects (id INTEGER PRIMARY KEY, name TEXT NOT NULL) STRICT;" }]; test("migrations persist, reopen idempotently, and allow unrelated tables", () => { const directory = mkdtempSync(join(tmpdir(), "token-db-")); try { const path = join(directory, "nested/state.sqlite"); const first = openDatabase(path, steps); first.prepare("INSERT INTO projects (name) VALUES (?)").run("garden"); first.close(); const second = openDatabase(path, steps); try { assert.equal(second.prepare("SELECT name FROM projects").get()?.name, "garden"); assert.equal(second.prepare("PRAGMA foreign_keys").get()?.foreign_keys, 1); assert.equal(second.prepare("PRAGMA journal_mode").get()?.journal_mode, "wal"); assert.throws(() => migrate(second, []), /history differs/); assert.throws(() => migrate(second, [{ version: 1, name: "projects", sql: "SELECT 1" }]), /history differs/); } finally { second.close(); } } finally { rmSync(directory, { recursive: true, force: true }); } }); test("failed migrations roll back schema changes and history", () => { const database = openDatabase(":memory:", steps); try { assert.throws(() => migrate(database, [...steps, { version: 2, name: "broken", sql: "CREATE TABLE temporary_table (id INTEGER); INVALID SQL;", }])); assert.equal(database.prepare("SELECT count(*) AS count FROM schema_migrations").get()?.count, 1); assert.equal(database.prepare("SELECT name FROM sqlite_master WHERE name = 'temporary_table'").get(), undefined); } finally { database.close(); } });