Files
OmniRoute/tests/unit/cli-sqlite-no-fallback-7586.test.ts
Diego Rodrigues de Sa e Souza 13cc128dae fix(cli): fall back to node:sqlite when better-sqlite3 is unavailable in omniroute doctor (#7586) (#8501)
bin/cli/sqlite.mjs::loadSqlite() had no fallback beyond better-sqlite3, unlike
the real server's driver cascade (src/lib/db/adapters/driverFactory.ts::tryOpenSync,
which tries bun:sqlite -> better-sqlite3 -> node:sqlite). On machines without a
working better-sqlite3 native binary, every `omniroute doctor` DB check reported
a false FAIL even when the actual server was healthy via its own driver cascade.

openSqliteDatabase() now falls back to tryOpenSync() when better-sqlite3 fails
to import, reusing the same already-tested cascade the real server uses instead
of re-deriving a second one.

Co-authored-by: ikelvingo <im.kelvinwong@gmail.com>
2026-07-25 04:57:00 -03:00

82 lines
3.4 KiB
TypeScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { register } from "node:module";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { DatabaseSync } from "node:sqlite";
// #7586: `omniroute doctor`'s "Database" and "Storage/encryption" checks call
// readDatabaseHealth()/readEncryptedCredentialSamples() from bin/cli/sqlite.mjs,
// which — unlike the real server's driver cascade
// (src/lib/db/adapters/driverFactory.ts::tryOpenSync, which tries
// bun:sqlite -> better-sqlite3 -> node:sqlite) — used to have NO fallback beyond
// better-sqlite3. On any machine where better-sqlite3's native binary is
// unavailable (Windows without a prebuilt addon, per @jmaxdev's report), doctor
// would ALWAYS report "FAIL Database" / "FAIL Storage/encryption" even when the
// real server was perfectly healthy via its own resilient driver selection.
//
// This test simulates that exact machine by intercepting the ESM specifier
// "better-sqlite3" (the one `bin/cli/sqlite.mjs::loadSqlite()` imports) so it
// throws the same "Could not locate the bindings file" error jmax hit, then
// proves readDatabaseHealth() still succeeds by falling back to node:sqlite.
register(
"data:text/javascript," +
encodeURIComponent(`
export async function resolve(specifier, context, nextResolve) {
if (specifier === "better-sqlite3") {
return {
url:
"data:text/javascript," +
encodeURIComponent(
"throw new Error(" +
JSON.stringify(
"Could not locate the bindings file. Tried:\\n \\u2192 /fake/path/better_sqlite3.node"
) +
");"
),
shortCircuit: true,
};
}
return nextResolve(specifier, context);
}
`),
import.meta.url
);
const { readDatabaseHealth } = await import("../../bin/cli/sqlite.mjs");
test("#7586: readDatabaseHealth() falls back to node:sqlite when better-sqlite3 is unavailable", async (t) => {
const dbPath = path.join(os.tmpdir(), `cli-sqlite-no-fallback-7586-${Date.now()}.sqlite`);
t.after(() => {
try {
fs.unlinkSync(dbPath);
} catch {}
});
// Seed a normal, healthy DB using node:sqlite directly — simulating what the
// ACTUAL server (which DOES have a node:sqlite fallback) would have created
// on a machine without a better-sqlite3 native binary.
const seed = new DatabaseSync(dbPath);
seed.exec(
"CREATE TABLE _omniroute_migrations (version TEXT PRIMARY KEY, name TEXT NOT NULL, applied_at TEXT);" +
"INSERT INTO _omniroute_migrations (version, name, applied_at) VALUES ('001', 'initial_schema', datetime('now'));"
);
seed.close();
// Exercise the CLI helper's own health check — the exact function
// `omniroute doctor` calls (bin/cli/commands/doctor.mjs:checkDatabase ->
// readDatabaseHealth). Before the fix this threw "better-sqlite3 is not
// installed..." even though the DB is perfectly healthy.
const result = await readDatabaseHealth(dbPath);
assert.equal(
result.quickCheckValue,
"ok",
"readDatabaseHealth() should report a healthy DB via the node:sqlite fallback, " +
"not throw, when better-sqlite3 is unavailable (#7586)"
);
assert.equal(result.hasMigrationTable, true);
assert.deepEqual(result.appliedMigrationVersions, ["001"]);
});