mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 05:31:47 +00:00
105 lines
4.1 KiB
JavaScript
105 lines
4.1 KiB
JavaScript
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
|
|
const originalDataDir = process.env.DATA_DIR;
|
|
let tempDir;
|
|
let db;
|
|
let ownerOne;
|
|
let ownerTwo;
|
|
|
|
beforeAll(async () => {
|
|
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "9router-cli-tool-config-"));
|
|
process.env.DATA_DIR = tempDir;
|
|
try { global._dbAdapter?.instance?.close?.(); } catch {}
|
|
delete global._dbAdapter;
|
|
vi.resetModules();
|
|
db = await import("@/lib/db/index.js");
|
|
await db.initDb();
|
|
ownerOne = await db.createUser({ username: "cli-owner-one", password: "password", role: "admin" });
|
|
ownerTwo = await db.createUser({ username: "cli-owner-two", password: "password", role: "user" });
|
|
});
|
|
|
|
afterAll(() => {
|
|
try { global._dbAdapter?.instance?.close?.(); } catch {}
|
|
delete global._dbAdapter;
|
|
if (tempDir) fs.rmSync(tempDir, { recursive: true, force: true });
|
|
if (originalDataDir === undefined) delete process.env.DATA_DIR;
|
|
else process.env.DATA_DIR = originalDataDir;
|
|
});
|
|
|
|
const claudeConfig = (model) => ({
|
|
baseUrl: "https://router.example/v1",
|
|
apiKeyMode: "managed",
|
|
apiKeyId: null,
|
|
claudeModels: { sonnet: model, opus: "", haiku: "" },
|
|
claudeThinking: { sonnet: "high" },
|
|
});
|
|
|
|
describe("CLI tool configuration persistence", () => {
|
|
it("round-trips and atomically overwrites one owner/tool row", async () => {
|
|
const created = await db.upsertCliToolConfig(ownerOne.id, "claude", claudeConfig("cc/sonnet-a"));
|
|
expect(created).toMatchObject({ ownerId: ownerOne.id, toolId: "claude", config: claudeConfig("cc/sonnet-a") });
|
|
|
|
const updated = await db.upsertCliToolConfig(ownerOne.id, "claude", claudeConfig("cc/sonnet-b"));
|
|
expect(updated.config.claudeModels.sonnet).toBe("cc/sonnet-b");
|
|
expect((await db.getCliToolConfigsByOwnerId(ownerOne.id)).filter((row) => row.toolId === "claude")).toHaveLength(1);
|
|
});
|
|
|
|
it("isolates users and permits concurrent saves to different tools", async () => {
|
|
await Promise.all([
|
|
db.upsertCliToolConfig(ownerOne.id, "cursor", {
|
|
apiKeyMode: "custom",
|
|
selectedModels: ["cc/a", "cx/b"],
|
|
}),
|
|
db.upsertCliToolConfig(ownerTwo.id, "cursor", {
|
|
apiKeyMode: "managed",
|
|
apiKeyId: null,
|
|
selectedModels: ["gg/c"],
|
|
}),
|
|
db.upsertCliToolConfig(ownerOne.id, "codex", {
|
|
baseUrl: "https://router.example/v1",
|
|
apiKeyMode: "managed",
|
|
apiKeyId: null,
|
|
codexModel: "cx/gpt",
|
|
codexThinking: "high",
|
|
}),
|
|
]);
|
|
|
|
expect((await db.getCliToolConfig(ownerOne.id, "cursor")).config.selectedModels).toEqual(["cc/a", "cx/b"]);
|
|
expect((await db.getCliToolConfig(ownerTwo.id, "cursor")).config.selectedModels).toEqual(["gg/c"]);
|
|
expect((await db.getCliToolConfig(ownerOne.id, "codex")).config.codexModel).toBe("cx/gpt");
|
|
});
|
|
|
|
it("includes valid configurations in export/import and skips malformed rows", async () => {
|
|
const exported = await db.exportDb();
|
|
expect(exported.cliToolConfigs).toEqual(expect.arrayContaining([
|
|
expect.objectContaining({ ownerId: ownerOne.id, toolId: "claude" }),
|
|
expect.objectContaining({ ownerId: ownerTwo.id, toolId: "cursor" }),
|
|
]));
|
|
|
|
exported.cliToolConfigs.push({
|
|
ownerId: ownerOne.id,
|
|
toolId: "kiro",
|
|
config: { apiKey: "must-not-import" },
|
|
});
|
|
await db.importDb(exported);
|
|
|
|
expect(await db.getCliToolConfig(ownerOne.id, "claude")).not.toBeNull();
|
|
expect(await db.getCliToolConfig(ownerOne.id, "kiro")).toBeNull();
|
|
});
|
|
|
|
it("removes configurations when their owner is deleted", async () => {
|
|
const disposable = await db.createUser({ username: "cli-disposable", password: "password", role: "user" });
|
|
await db.upsertCliToolConfig(disposable.id, "cursor", {
|
|
apiKeyMode: "managed",
|
|
apiKeyId: null,
|
|
selectedModels: ["cc/a"],
|
|
});
|
|
|
|
expect(await db.deleteUser(disposable.id)).toBe(true);
|
|
expect(await db.getCliToolConfig(disposable.id, "cursor")).toBeNull();
|
|
});
|
|
});
|