mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-22 15:12:23 +03:00
test(db): assert resetDbInstance swaps the singleton, WAL mode, and schema_version seed (#10906)
⭐5 — Preenche os 3 test.skip com asserções reais (resetDbInstance troca o singleton preservando a linha no disco, journal_mode WAL, schema_version=1). Além do valor pretendido, o autor redesenhou o setup()/cleanup() do arquivo corrigindo um bug de isolamento pré-existente que eu apontei em review: DATA_DIR/SQLITE_FILE são const de topo de módulo; o cleanup() usava require() CJS que nunca resetava a instância ESM-importada, então os testes 1-4 passavam "por acidente" contra a conexão nunca fechada. Agora: tempDir compartilhado definido antes do primeiro import, resetDbInstance importado via ESM uma vez, handle fechado antes de cada reopen, e o catch{} silencioso removido. 7/7 verdes no arquivo inteiro.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- **test(db):** replace three empty `test.skip` placeholders in the critical DB-state suite with real assertions — `resetDbInstance` must swap the singleton while the on-disk row survives, the on-disk DB must open in WAL journal mode, and `db_meta` must hold the seeded `schema_version` — so a regression in any of those invariants can no longer pass as silently green ([#10906](https://github.com/diegosouzapw/OmniRoute/pull/10906))
|
||||
@@ -1,24 +1,40 @@
|
||||
import test from "node:test";
|
||||
import { before, after, 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";
|
||||
|
||||
// Shared across all tests — the module caches DATA_DIR / SQLITE_FILE at load time,
|
||||
// so we must create the temp dir and import exactly once.
|
||||
let tempDir: string;
|
||||
let originalDataDir: string | undefined;
|
||||
let getDbInstance: any;
|
||||
let resetDbInstance: any;
|
||||
let ensureDbInitialized: any;
|
||||
let closeDbInstance: any;
|
||||
|
||||
function setup() {
|
||||
before(async () => {
|
||||
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-db-test-"));
|
||||
originalDataDir = process.env.DATA_DIR;
|
||||
process.env.DATA_DIR = tempDir;
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
getDbInstance = core.getDbInstance;
|
||||
resetDbInstance = core.resetDbInstance;
|
||||
ensureDbInitialized = core.ensureDbInitialized;
|
||||
closeDbInstance = core.closeDbInstance;
|
||||
|
||||
// Clear any singleton left by a previous test file in the same shard
|
||||
closeDbInstance();
|
||||
// Create a fresh DB in the temp dir (handles async driver initialization)
|
||||
await ensureDbInitialized();
|
||||
});
|
||||
|
||||
after(() => {
|
||||
try {
|
||||
const { resetDbInstance } = require("../../src/lib/db/core.ts");
|
||||
resetDbInstance();
|
||||
} catch {
|
||||
// ignore if import fails
|
||||
// ignore
|
||||
}
|
||||
if (originalDataDir !== undefined) {
|
||||
process.env.DATA_DIR = originalDataDir;
|
||||
@@ -30,120 +46,139 @@ function cleanup() {
|
||||
} catch {
|
||||
// ignore cleanup errors
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("getDbInstance returns a valid database handle", async () => {
|
||||
setup();
|
||||
try {
|
||||
const { getDbInstance } = await import("../../src/lib/db/core.ts");
|
||||
const db = getDbInstance();
|
||||
const db = getDbInstance();
|
||||
|
||||
assert.ok(db, "db should be defined");
|
||||
assert.equal(typeof db.prepare, "function", "db.prepare should be a function");
|
||||
assert.equal(typeof db.exec, "function", "db.exec should be a function");
|
||||
assert.equal(typeof db.pragma, "function", "db.pragma should be a function");
|
||||
assert.equal(db.open !== false, true, "db should be open");
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
assert.ok(db, "db should be defined");
|
||||
assert.equal(typeof db.prepare, "function", "db.prepare should be a function");
|
||||
assert.equal(typeof db.exec, "function", "db.exec should be a function");
|
||||
assert.equal(typeof db.pragma, "function", "db.pragma should be a function");
|
||||
assert.equal(db.open !== false, true, "db should be open");
|
||||
});
|
||||
|
||||
test("getDbInstance creates tables from SCHEMA_SQL (proves initialization succeeded with captureSucceeded sentinel)", async () => {
|
||||
setup();
|
||||
try {
|
||||
const { getDbInstance } = await import("../../src/lib/db/core.ts");
|
||||
const db = getDbInstance();
|
||||
const db = getDbInstance();
|
||||
|
||||
const tables = db
|
||||
.prepare("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
|
||||
.all() as Array<{ name: string }>;
|
||||
const tableNames = new Set(tables.map((t) => t.name));
|
||||
const tables = db
|
||||
.prepare("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
|
||||
.all() as Array<{ name: string }>;
|
||||
const tableNames = new Set(tables.map((t) => t.name));
|
||||
|
||||
const expectedTables = [
|
||||
"provider_connections",
|
||||
"provider_nodes",
|
||||
"key_value",
|
||||
"combos",
|
||||
"api_keys",
|
||||
"db_meta",
|
||||
"usage_history",
|
||||
"call_logs",
|
||||
"domain_circuit_breakers",
|
||||
"semantic_cache",
|
||||
"_omniroute_migrations",
|
||||
];
|
||||
const expectedTables = [
|
||||
"provider_connections",
|
||||
"provider_nodes",
|
||||
"key_value",
|
||||
"combos",
|
||||
"api_keys",
|
||||
"db_meta",
|
||||
"usage_history",
|
||||
"call_logs",
|
||||
"domain_circuit_breakers",
|
||||
"semantic_cache",
|
||||
"_omniroute_migrations",
|
||||
];
|
||||
|
||||
for (const name of expectedTables) {
|
||||
assert.ok(tableNames.has(name), `table "${name}" should exist`);
|
||||
}
|
||||
|
||||
// The preservedCriticalState sentinel is captureSucceeded: true on fresh DB
|
||||
// (no existing file = no corruption path = initialized with default sentinel).
|
||||
// Verify this indirectly: the DB is fully functional and migrations ran.
|
||||
const migrationCount = db
|
||||
.prepare("SELECT COUNT(*) as c FROM _omniroute_migrations")
|
||||
.get() as { c: number };
|
||||
assert.ok(migrationCount.c >= 1, "at least one migration should be recorded");
|
||||
} finally {
|
||||
cleanup();
|
||||
for (const name of expectedTables) {
|
||||
assert.ok(tableNames.has(name), `table "${name}" should exist`);
|
||||
}
|
||||
|
||||
// The preservedCriticalState sentinel is captureSucceeded: true on fresh DB
|
||||
// (no existing file = no corruption path = initialized with default sentinel).
|
||||
// Verify this indirectly: the DB is fully functional and migrations ran.
|
||||
const migrationCount = db
|
||||
.prepare("SELECT COUNT(*) as c FROM _omniroute_migrations")
|
||||
.get() as { c: number };
|
||||
assert.ok(migrationCount.c >= 1, "at least one migration should be recorded");
|
||||
});
|
||||
|
||||
test("getDbInstance supports basic CRUD operations after startup", async () => {
|
||||
setup();
|
||||
try {
|
||||
const { getDbInstance } = await import("../../src/lib/db/core.ts");
|
||||
const db = getDbInstance();
|
||||
const db = getDbInstance();
|
||||
|
||||
// Insert into key_value
|
||||
db.prepare("INSERT INTO key_value (namespace, key, value) VALUES (?, ?, ?)").run(
|
||||
"test_ns",
|
||||
"test_key",
|
||||
JSON.stringify({ hello: "world" })
|
||||
);
|
||||
// Insert into key_value
|
||||
db.prepare("INSERT INTO key_value (namespace, key, value) VALUES (?, ?, ?)").run(
|
||||
"test_ns",
|
||||
"test_key",
|
||||
JSON.stringify({ hello: "world" })
|
||||
);
|
||||
|
||||
const row = db
|
||||
.prepare("SELECT value FROM key_value WHERE namespace = ? AND key = ?")
|
||||
.get("test_ns", "test_key") as { value: string };
|
||||
assert.ok(row, "row should exist");
|
||||
assert.deepEqual(JSON.parse(row.value), { hello: "world" });
|
||||
const row = db
|
||||
.prepare("SELECT value FROM key_value WHERE namespace = ? AND key = ?")
|
||||
.get("test_ns", "test_key") as { value: string };
|
||||
assert.ok(row, "row should exist");
|
||||
assert.deepEqual(JSON.parse(row.value), { hello: "world" });
|
||||
|
||||
// Update
|
||||
db.prepare("UPDATE key_value SET value = ? WHERE namespace = ? AND key = ?").run(
|
||||
JSON.stringify({ hello: "updated" }),
|
||||
"test_ns",
|
||||
"test_key"
|
||||
);
|
||||
const updated = db
|
||||
.prepare("SELECT value FROM key_value WHERE namespace = ? AND key = ?")
|
||||
.get("test_ns", "test_key") as { value: string };
|
||||
assert.deepEqual(JSON.parse(updated.value), { hello: "updated" });
|
||||
// Update
|
||||
db.prepare("UPDATE key_value SET value = ? WHERE namespace = ? AND key = ?").run(
|
||||
JSON.stringify({ hello: "updated" }),
|
||||
"test_ns",
|
||||
"test_key"
|
||||
);
|
||||
const updated = db
|
||||
.prepare("SELECT value FROM key_value WHERE namespace = ? AND key = ?")
|
||||
.get("test_ns", "test_key") as { value: string };
|
||||
assert.deepEqual(JSON.parse(updated.value), { hello: "updated" });
|
||||
|
||||
// Delete
|
||||
db.prepare("DELETE FROM key_value WHERE namespace = ? AND key = ?").run("test_ns", "test_key");
|
||||
const deleted = db
|
||||
.prepare("SELECT value FROM key_value WHERE namespace = ? AND key = ?")
|
||||
.get("test_ns", "test_key");
|
||||
assert.equal(deleted, undefined, "row should be deleted");
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
// Delete
|
||||
db.prepare("DELETE FROM key_value WHERE namespace = ? AND key = ?").run("test_ns", "test_key");
|
||||
const deleted = db
|
||||
.prepare("SELECT value FROM key_value WHERE namespace = ? AND key = ?")
|
||||
.get("test_ns", "test_key");
|
||||
assert.equal(deleted, undefined, "row should be deleted");
|
||||
});
|
||||
|
||||
test("getDbInstance returns same singleton on repeated calls", async () => {
|
||||
setup();
|
||||
try {
|
||||
const { getDbInstance } = await import("../../src/lib/db/core.ts");
|
||||
const db1 = getDbInstance();
|
||||
const db2 = getDbInstance();
|
||||
assert.equal(db1, db2, "should return the same singleton instance");
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
const db1 = getDbInstance();
|
||||
const db2 = getDbInstance();
|
||||
assert.equal(db1, db2, "should return the same singleton instance");
|
||||
});
|
||||
|
||||
test.skip("resetDbInstance clears the singleton so next call creates a new DB", () => {});
|
||||
test("resetDbInstance clears the singleton so next call creates a new DB", async () => {
|
||||
const db1 = getDbInstance();
|
||||
|
||||
test.skip("getDbInstance sets WAL journal mode", () => {});
|
||||
// Write a marker row so we can prove the post-reset handle reopens the same
|
||||
// on-disk file through a freshly opened connection (not the cached one).
|
||||
db1.prepare("INSERT INTO key_value (namespace, key, value) VALUES (?, ?, ?)").run(
|
||||
"reset_ns",
|
||||
"marker",
|
||||
JSON.stringify({ v: 1 })
|
||||
);
|
||||
|
||||
test.skip("getDbInstance stores schema_version in db_meta", () => {});
|
||||
resetDbInstance();
|
||||
|
||||
// Re-initialize after reset — drivers may need async pre-init (sql.js WASM)
|
||||
await ensureDbInitialized();
|
||||
|
||||
const db2 = getDbInstance();
|
||||
assert.notEqual(db1, db2, "reset must swap the cached singleton for a new handle");
|
||||
|
||||
// The new handle reopens the same DATA_DIR file, so the persisted marker
|
||||
// survives while the object identity does not.
|
||||
const row = db2
|
||||
.prepare("SELECT value FROM key_value WHERE namespace = ? AND key = ?")
|
||||
.get("reset_ns", "marker") as { value: string } | undefined;
|
||||
assert.ok(row, "persisted row should survive a singleton reset");
|
||||
assert.deepEqual(JSON.parse(row.value), { v: 1 });
|
||||
});
|
||||
|
||||
test("getDbInstance sets WAL journal mode", async () => {
|
||||
const db = getDbInstance();
|
||||
|
||||
const mode = db.pragma("journal_mode", { simple: true }) as string;
|
||||
assert.equal(
|
||||
String(mode).toLowerCase(),
|
||||
"wal",
|
||||
"on-disk DB should open in WAL journal mode"
|
||||
);
|
||||
});
|
||||
|
||||
test("getDbInstance stores schema_version in db_meta", async () => {
|
||||
const db = getDbInstance();
|
||||
|
||||
const row = db
|
||||
.prepare("SELECT value FROM db_meta WHERE key = 'schema_version'")
|
||||
.get() as { value: string } | undefined;
|
||||
assert.ok(row, "db_meta should hold a schema_version row after init");
|
||||
assert.equal(row.value, "1", "schema_version should be seeded to '1'");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user