Files
2026-07-13 13:39:12 +08:00

471 lines
16 KiB
TypeScript

import { describe, it, beforeEach, afterEach, after } from "node:test";
import assert from "node:assert/strict";
import path from "node:path";
import fs from "node:fs";
import os from "node:os";
import Database from "better-sqlite3";
const fileTmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "omni-dbvm-test-"));
const moduleDataDir = fs.mkdtempSync(path.join(os.tmpdir(), "omni-dbvm-module-"));
process.env.DATA_DIR = moduleDataDir;
const coreDb = await import("../../src/lib/db/core.ts");
const versionManagerDb = await import("../../src/lib/db/versionManager.ts");
const SCHEMA = `
CREATE TABLE IF NOT EXISTS version_manager (
id INTEGER PRIMARY KEY AUTOINCREMENT,
tool TEXT NOT NULL UNIQUE,
current_version TEXT,
installed_version TEXT,
pinned_version TEXT,
binary_path TEXT,
status TEXT NOT NULL DEFAULT 'not_installed',
pid INTEGER,
port INTEGER DEFAULT 8317,
api_key TEXT,
management_key TEXT,
auto_update INTEGER NOT NULL DEFAULT 1,
auto_start INTEGER NOT NULL DEFAULT 0,
last_health_check TEXT,
last_update_check TEXT,
health_status TEXT DEFAULT 'unknown',
config_overrides TEXT,
error_message TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
`;
let testDb;
let testDbPath;
beforeEach(() => {
testDbPath = path.join(fileTmpDir, `test-${Date.now()}.db`);
testDb = new Database(testDbPath);
testDb.exec(SCHEMA);
});
afterEach(() => {
testDb.close();
try {
fs.unlinkSync(testDbPath);
} catch {}
});
after(() => {
if (fs.existsSync(fileTmpDir)) fs.rmSync(fileTmpDir, { recursive: true, force: true });
});
async function resetModuleStorage() {
coreDb.resetDbInstance();
fs.rmSync(moduleDataDir, { recursive: true, force: true });
fs.mkdirSync(moduleDataDir, { recursive: true });
}
function upsertTool(db, data) {
db.prepare(
`
INSERT INTO version_manager (
tool, current_version, installed_version, pinned_version, binary_path,
status, pid, port, api_key, management_key, auto_update, auto_start,
health_status, config_overrides, error_message, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
ON CONFLICT(tool) DO UPDATE SET
current_version = excluded.current_version,
installed_version = excluded.installed_version,
pinned_version = excluded.pinned_version,
binary_path = excluded.binary_path,
status = excluded.status,
pid = excluded.pid,
port = excluded.port,
api_key = excluded.api_key,
management_key = excluded.management_key,
auto_update = excluded.auto_update,
auto_start = excluded.auto_start,
health_status = excluded.health_status,
config_overrides = excluded.config_overrides,
error_message = excluded.error_message,
updated_at = datetime('now')
`
).run(
data.tool,
data.currentVersion ?? null,
data.installedVersion ?? null,
data.pinnedVersion ?? null,
data.binaryPath ?? null,
data.status ?? "not_installed",
data.pid ?? null,
data.port ?? 8317,
data.apiKey ?? null,
data.managementKey ?? null,
data.autoUpdate !== undefined ? (data.autoUpdate ? 1 : 0) : 1,
data.autoStart !== undefined ? (data.autoStart ? 1 : 0) : 0,
data.healthStatus ?? "unknown",
data.configOverrides !== undefined
? data.configOverrides
? JSON.stringify(data.configOverrides)
: null
: null,
data.errorMessage ?? null
);
return db.prepare("SELECT * FROM version_manager WHERE tool = ?").get(data.tool);
}
function parseConfigOverrides(value) {
if (!value || typeof value !== "string" || value.trim() === "") return null;
try {
const parsed = JSON.parse(value);
return typeof parsed === "object" && parsed !== null ? parsed : null;
} catch {
return null;
}
}
function toTool(row) {
if (!row) return null;
return {
id: row.id,
tool: row.tool,
currentVersion: row.current_version,
installedVersion: row.installed_version,
pinnedVersion: row.pinned_version,
binaryPath: row.binary_path,
status: row.status,
pid: row.pid,
port: row.port,
apiKey: row.api_key,
managementKey: row.management_key,
autoUpdate: row.auto_update === 1,
autoStart: row.auto_start === 1,
lastHealthCheck: row.last_health_check,
lastUpdateCheck: row.last_update_check,
healthStatus: row.health_status,
configOverrides: parseConfigOverrides(row.config_overrides),
errorMessage: row.error_message,
createdAt: row.created_at,
updatedAt: row.updated_at,
};
}
describe("db/versionManager (logic)", () => {
describe("upsertVersionManagerTool", () => {
it("should insert a new tool and read it back", () => {
const tool = toTool(
upsertTool(testDb, {
tool: "cliproxyapi",
installedVersion: "6.9.7",
binaryPath: "/tmp/bin/cliproxyapi",
status: "installed",
})
);
assert.equal(tool.tool, "cliproxyapi");
assert.equal(tool.installedVersion, "6.9.7");
assert.equal(tool.binaryPath, "/tmp/bin/cliproxyapi");
assert.equal(tool.status, "installed");
assert.equal(tool.port, 8317);
assert.equal(tool.autoUpdate, true);
assert.equal(tool.autoStart, false);
assert.equal(tool.healthStatus, "unknown");
assert.ok(tool.id > 0);
});
it("should update on conflict (upsert)", () => {
upsertTool(testDb, { tool: "test-tool", status: "installed" });
const tool = toTool(
upsertTool(testDb, { tool: "test-tool", installedVersion: "7.0.0", status: "running" })
);
assert.equal(tool.installedVersion, "7.0.0");
assert.equal(tool.status, "running");
});
it("should store boolean fields as 0/1", () => {
const tool = toTool(
upsertTool(testDb, { tool: "bool-test", autoUpdate: false, autoStart: true })
);
assert.equal(tool.autoUpdate, false);
assert.equal(tool.autoStart, true);
});
it("should store config overrides as JSON", () => {
const tool = toTool(upsertTool(testDb, { tool: "cfg", configOverrides: { port: 9999 } }));
assert.deepEqual(tool.configOverrides, { port: 9999 });
});
it("should handle null config overrides", () => {
const tool = toTool(upsertTool(testDb, { tool: "null-cfg" }));
assert.equal(tool.configOverrides, null);
});
it("should handle pid and error message", () => {
const tool = toTool(
upsertTool(testDb, { tool: "pid-test", pid: 12345, errorMessage: "err" })
);
assert.equal(tool.pid, 12345);
assert.equal(tool.errorMessage, "err");
});
});
describe("getVersionManagerTool", () => {
it("should return null for non-existent tool", () => {
assert.equal(
toTool(testDb.prepare("SELECT * FROM version_manager WHERE tool = ?").get("ghost")),
null
);
});
it("should return tool by name", () => {
upsertTool(testDb, { tool: "findme", installedVersion: "1.0.0" });
const tool = toTool(
testDb.prepare("SELECT * FROM version_manager WHERE tool = ?").get("findme")
);
assert.equal(tool.installedVersion, "1.0.0");
});
});
describe("getVersionManagerStatus", () => {
it("should return all tools", () => {
upsertTool(testDb, { tool: "a" });
upsertTool(testDb, { tool: "b" });
assert.equal(testDb.prepare("SELECT * FROM version_manager").all().length, 2);
});
it("should return empty when no tools", () => {
assert.equal(testDb.prepare("SELECT * FROM version_manager").all().length, 0);
});
});
describe("updateVersionManagerTool (partial)", () => {
it("should update pinnedVersion and autoUpdate", () => {
upsertTool(testDb, { tool: "upd", status: "installed" });
testDb
.prepare(
"UPDATE version_manager SET pinned_version = ?, auto_update = ?, updated_at = datetime('now') WHERE tool = ?"
)
.run("6.8.0", 0, "upd");
const tool = toTool(
testDb.prepare("SELECT * FROM version_manager WHERE tool = ?").get("upd")
);
assert.equal(tool.pinnedVersion, "6.8.0");
assert.equal(tool.autoUpdate, false);
assert.equal(tool.status, "installed");
});
it("should set fields to null", () => {
upsertTool(testDb, { tool: "nulls", installedVersion: "1.0.0", pid: 42 });
testDb
.prepare("UPDATE version_manager SET installed_version = NULL, pid = NULL WHERE tool = ?")
.run("nulls");
const tool = toTool(
testDb.prepare("SELECT * FROM version_manager WHERE tool = ?").get("nulls")
);
assert.equal(tool.installedVersion, null);
assert.equal(tool.pid, null);
});
});
describe("deleteVersionManagerTool", () => {
it("should delete existing tool", () => {
upsertTool(testDb, { tool: "del" });
assert.equal(
testDb.prepare("DELETE FROM version_manager WHERE tool = ?").run("del").changes,
1
);
assert.equal(
testDb.prepare("SELECT * FROM version_manager WHERE tool = ?").get("del"),
undefined
);
});
it("should return 0 changes for non-existent", () => {
assert.equal(
testDb.prepare("DELETE FROM version_manager WHERE tool = ?").run("ghost").changes,
0
);
});
});
describe("updateToolHealth", () => {
it("should update health_status and last_health_check", () => {
upsertTool(testDb, { tool: "h" });
const r = testDb
.prepare(
"UPDATE version_manager SET health_status = ?, last_health_check = datetime('now') WHERE tool = ?"
)
.run("healthy", "h");
assert.equal(r.changes, 1);
const tool = toTool(testDb.prepare("SELECT * FROM version_manager WHERE tool = ?").get("h"));
assert.equal(tool.healthStatus, "healthy");
assert.ok(tool.lastHealthCheck);
});
it("should return 0 changes for non-existent", () => {
assert.equal(
testDb
.prepare("UPDATE version_manager SET health_status = ? WHERE tool = ?")
.run("healthy", "ghost").changes,
0
);
});
});
describe("updateToolVersion", () => {
it("should update current_version", () => {
upsertTool(testDb, { tool: "v1" });
testDb
.prepare("UPDATE version_manager SET current_version = ? WHERE tool = ?")
.run("7.0.0", "v1");
assert.equal(
testDb.prepare("SELECT * FROM version_manager WHERE tool = ?").get("v1").current_version,
"7.0.0"
);
});
it("should update installed_version", () => {
upsertTool(testDb, { tool: "v2" });
testDb
.prepare("UPDATE version_manager SET installed_version = ? WHERE tool = ?")
.run("7.0.0", "v2");
assert.equal(
testDb.prepare("SELECT * FROM version_manager WHERE tool = ?").get("v2").installed_version,
"7.0.0"
);
});
});
describe("setToolStatus", () => {
it("should update status with pid", () => {
upsertTool(testDb, { tool: "s1" });
testDb
.prepare("UPDATE version_manager SET status = ?, pid = ?, error_message = ? WHERE tool = ?")
.run("running", 9999, "ok", "s1");
const row = testDb.prepare("SELECT * FROM version_manager WHERE tool = ?").get("s1");
assert.equal(row.status, "running");
assert.equal(row.pid, 9999);
assert.equal(row.error_message, "ok");
});
it("should update status without pid", () => {
upsertTool(testDb, { tool: "s2" });
testDb
.prepare("UPDATE version_manager SET status = ?, error_message = ? WHERE tool = ?")
.run("error", "crashed", "s2");
const row = testDb.prepare("SELECT * FROM version_manager WHERE tool = ?").get("s2");
assert.equal(row.status, "error");
});
it("should return 0 for non-existent", () => {
assert.equal(
testDb
.prepare("UPDATE version_manager SET status = ? WHERE tool = ?")
.run("running", "ghost").changes,
0
);
});
});
describe("parseConfigOverrides", () => {
it("should parse valid JSON", () => {
assert.deepEqual(parseConfigOverrides('{"key":"val"}'), { key: "val" });
});
it("should return null for invalid JSON", () => {
assert.equal(parseConfigOverrides("not-json"), null);
assert.equal(parseConfigOverrides(""), null);
assert.equal(parseConfigOverrides(null), null);
assert.equal(parseConfigOverrides("123"), null);
});
});
});
describe("db/versionManager (module coverage)", () => {
beforeEach(async () => {
await resetModuleStorage();
});
after(async () => {
coreDb.resetDbInstance();
fs.rmSync(moduleDataDir, { recursive: true, force: true });
});
it("round-trips inserts, updates and status listings through the production module", async () => {
const inserted = await versionManagerDb.upsertVersionManagerTool({
tool: "cliproxyapi",
installedVersion: "6.9.7",
binaryPath: "/tmp/cliproxyapi",
status: "installed",
autoUpdate: false,
autoStart: true,
configOverrides: { port: 9999 },
});
assert.equal(inserted.tool, "cliproxyapi");
assert.equal(inserted.autoUpdate, false);
assert.equal(inserted.autoStart, true);
assert.deepEqual(inserted.configOverrides, { port: 9999 });
const updated = await versionManagerDb.updateVersionManagerTool("cliproxyapi", {
installedVersion: "7.0.0",
configOverrides: { port: 8317, host: "127.0.0.1" },
apiKey: null,
ignoredField: "noop",
});
assert.equal(updated.installedVersion, "7.0.0");
assert.deepEqual(updated.configOverrides, { port: 8317, host: "127.0.0.1" });
const status = await versionManagerDb.getVersionManagerStatus();
const cliproxy = status.find((row) => row.tool === "cliproxyapi");
assert.ok(cliproxy, "expected cliproxyapi entry in status listing");
assert.equal(cliproxy.tool, "cliproxyapi");
});
it("parses invalid config overrides defensively and returns null for missing updates", async () => {
const db = coreDb.getDbInstance();
db.prepare(
`
INSERT INTO version_manager
(tool, status, config_overrides, created_at, updated_at)
VALUES (?, ?, ?, datetime('now'), datetime('now'))
`
).run("broken-tool", "installed", "{not-json");
const loaded = await versionManagerDb.getVersionManagerTool("broken-tool");
assert.equal(loaded.configOverrides, null);
assert.equal(
await versionManagerDb.updateVersionManagerTool("ghost", { status: "running" }),
null
);
});
it("updates health/version/status fields and deletes tools", async () => {
await versionManagerDb.upsertVersionManagerTool({
tool: "managed-tool",
status: "installed",
});
assert.equal(await versionManagerDb.updateToolHealth("managed-tool", "healthy"), true);
assert.equal(
await versionManagerDb.updateToolVersion("managed-tool", "current_version", "1.2.3"),
true
);
assert.equal(
await versionManagerDb.updateToolVersion("managed-tool", "installed_version", "1.2.0"),
true
);
assert.equal(await versionManagerDb.setToolStatus("managed-tool", "running", 4321, "ok"), true);
assert.equal(await versionManagerDb.setToolStatus("managed-tool", "error"), true);
const stored = await versionManagerDb.getVersionManagerTool("managed-tool");
assert.equal(stored.currentVersion, "1.2.3");
assert.equal(stored.installedVersion, "1.2.0");
assert.equal(stored.status, "error");
assert.equal(await versionManagerDb.updateToolHealth("ghost", "healthy"), false);
assert.equal(await versionManagerDb.setToolStatus("ghost", "running"), false);
assert.equal(await versionManagerDb.deleteVersionManagerTool("managed-tool"), true);
assert.equal(await versionManagerDb.deleteVersionManagerTool("managed-tool"), false);
});
});