Files
OmniRoute/tests/unit/db-adapters/bunSqliteAdapter.test.ts
Nguyễn Viết Tuấn 9ad90fe7a3 fix(bun): use native bun:sqlite during startup to avoid N-API crash (#11468)
Validated in a combined 4-PR batch worktree off release/v3.8.51 tip (Bun-native SQLite infrastructure cluster from the same contributor).
- Focused test: bunSqliteAdapter.test.ts — part of batch's 5/5 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff

Thanks for tracing this to the pre-boot better-sqlite3 require — a native NAPI abort that JS try/catch cannot recover from is exactly the kind of failure mode that needs the guard moved earlier, and aligning bootstrap/sync-env with the already-preferred bun:sqlite driver is the right fix.
2026-08-25 20:31:34 -03:00

96 lines
3.2 KiB
TypeScript

import { test } from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import {
createBunSqliteAdapter,
type BunSqliteDatabaseLike,
} from "../../../src/lib/db/adapters/bunSqliteAdapter.ts";
test("bun:sqlite adapter supports CRUD, pragmas, transactions, and close", async (t) => {
if (!process.versions.bun) {
t.skip("bun:sqlite is only available under Bun");
return;
}
const { Database } = await import("bun:sqlite");
const adapter = createBunSqliteAdapter(new Database(":memory:"), ":memory:");
t.after(() => adapter.close());
adapter.exec("CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT)");
const result = adapter.prepare("INSERT INTO items (name) VALUES (?)").run("bun");
assert.equal(result.changes, 1);
assert.equal((adapter.prepare("SELECT name FROM items").get() as { name: string }).name, "bun");
assert.equal(adapter.driver, "bun:sqlite");
assert.equal(adapter.pragma("user_version", { simple: true }), 0);
adapter.prepare("INSERT INTO items (name) VALUES (@name)").run({ name: "named" });
assert.equal(
(
adapter.prepare("SELECT name FROM items WHERE name = :name").get({ name: "named" }) as {
name: string;
}
).name,
"named"
);
adapter.transaction(() => {
adapter.prepare("INSERT INTO items (name) VALUES (?)").run("transaction");
})();
assert.equal(adapter.prepare("SELECT COUNT(*) AS count FROM items").get().count, 3);
assert.equal(adapter.open, true);
adapter.close();
assert.equal(adapter.open, false);
});
test("bun:sqlite adapter backs up on-disk databases without serializing them", async (t) => {
const sourcePath = path.join(os.tmpdir(), `bun-sqlite-source-${Date.now()}.sqlite`);
const destinationPath = path.join(os.tmpdir(), `bun-sqlite-destination-${Date.now()}.sqlite`);
const sourceContents = "sqlite fixture";
const execCalls: string[] = [];
fs.writeFileSync(sourcePath, sourceContents);
t.after(() => {
for (const filePath of [sourcePath, destinationPath]) {
try {
fs.unlinkSync(filePath);
} catch {}
}
});
const db = {
query() {
throw new Error("query should not be called during backup");
},
exec(sql: string) {
execCalls.push(sql);
},
transaction() {
throw new Error("transaction should not be called during backup");
},
close() {},
serialize() {
throw new Error("serialize must not be called for an on-disk backup");
},
} as unknown as BunSqliteDatabaseLike;
const adapter = createBunSqliteAdapter(db, sourcePath);
await adapter.backup(destinationPath);
assert.deepEqual(execCalls, ["PRAGMA wal_checkpoint(TRUNCATE)"]);
assert.equal(fs.readFileSync(destinationPath, "utf8"), sourceContents);
});
test("loadSqliteRuntime prioritizes bun:sqlite under Bun without loading better-sqlite3", async (t) => {
if (!process.versions.bun) {
t.skip("bun:sqlite is only available under Bun");
return;
}
const { loadSqliteRuntime } = await import("../../../bin/cli/runtime/sqliteRuntime.mjs");
const runtime = await loadSqliteRuntime();
assert.equal(runtime.driver.kind, "bun-sqlite");
assert.equal(runtime.source, "bun-sqlite");
});