/** * DuckDB 适配器单元测试 * 覆盖:写入/读取往返、JSON 异构值保留、JSON 存量自动迁移、重复初始化幂等 * 运行:npm test */ const test = require("node:test"); const assert = require("node:assert"); const fs = require("node:fs"); const os = require("node:os"); const path = require("node:path"); // 使用临时数据目录,避免污染真实 data/ const tmpDataDir = fs.mkdtempSync(path.join(os.tmpdir(), "registry-duckdb-test-")); process.env.REGISTRY_DATA_DIR = tmpDataDir; const { init, loadProjects, saveProjects, loadConfigs, saveConfigs, deleteConfigs, loadSystem, saveSystem, loadMcpConfig, saveMcpConfig, loadUsers, saveUsers, loadApiTokens, saveApiTokens, } = require("../src/persistence/duckdbAdapter"); test("projects 写入/读取往返,字段无损保留", async () => { await init(); const projects = [ { id: "p1", name: "项目A", enableIdc: true, nested: { arr: [1, 2, 3] } }, { id: "p2", name: "项目B", enableEnvironment: false }, ]; await saveProjects(projects); const loaded = await loadProjects(); assert.strictEqual(loaded.length, 2); assert.deepStrictEqual(loaded.find((p) => p.id === "p1").nested, { arr: [1, 2, 3] }); }); test("configs 写入/读取往返,值类型异构保留", async () => { await init(); const configs = [ { id: "c1", key: "count", value: 42, type: "number" }, { id: "c2", key: "flag", value: false, type: "boolean" }, { id: "c3", key: "raw", value: { deep: ["a", null, true] }, type: "json" }, ]; await saveConfigs("proj-x", configs); const loaded = await loadConfigs("proj-x"); assert.strictEqual(loaded.length, 3); assert.strictEqual(loaded.find((c) => c.id === "c1").value, 42); assert.strictEqual(loaded.find((c) => c.id === "c2").value, false); assert.deepStrictEqual(loaded.find((c) => c.id === "c3").value, { deep: ["a", null, true] }); // ensure projectId 未被注入进记录本体 assert.strictEqual("projectId" in loaded[0], false); // 其他项目不受影响 assert.deepStrictEqual(await loadConfigs("proj-other"), []); }); test("configs 删除仅作用于指定项目", async () => { await init(); await saveConfigs("proj-keep", [{ id: "k1", key: "a", value: 1 }]); await deleteConfigs("proj-keep"); await saveConfigs("proj-keep2", [{ id: "k2", key: "b", value: 2 }]); await deleteConfigs("proj-keep2"); await saveConfigs("proj-keep3", [{ id: "k3", key: "c", value: 3 }]); await deleteConfigs("proj-keep3"); await saveConfigs("proj-del", [{ id: "d1", key: "x", value: 1 }]); await deleteConfigs("proj-del"); await deleteConfigs("proj-del"); assert.deepStrictEqual(await loadConfigs("proj-del"), []); }); test("system / mcp 写入读取与默认值", async () => { await init(); const sys = await loadSystem(); assert.strictEqual(sys.mode, "edit"); await saveSystem({ mode: "readonly" }); assert.strictEqual((await loadSystem()).mode, "readonly"); const mcp = await loadMcpConfig(); assert.strictEqual(typeof mcp.token, "string"); await saveMcpConfig({ enabled: false, token: "abc" }); assert.deepStrictEqual(await loadMcpConfig(), { enabled: false, token: "abc" }); }); test("users / apiTokens 写入读取往返", async () => { await init(); await saveUsers([{ id: "u1", username: "admin", password: "hashed", roles: ["admin"] }]); const users = await loadUsers(); assert.strictEqual(users[0].username, "admin"); await saveApiTokens([{ id: "t1", name: "mcp", value: "secret" }]); assert.strictEqual((await loadApiTokens())[0].value, "secret"); });