/** * JSON -> DuckDB 自动迁移测试 * 独立进程运行:模块在 require 时读取 REGISTRY_DATA_DIR,需在 require 前注入种子数据目录 * 运行: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"); // 构造一个含 JSON 存量数据的种子目录,并设置为数据目录 const seedDir = path.join(os.tmpdir(), "registry-seed-" + Date.now()); fs.mkdirSync(seedDir, { recursive: true }); fs.writeFileSync(path.join(seedDir, "projects.json"), JSON.stringify([ { id: "proj-mig-1", name: "迁移项目A", enableIdc: false }, ])); fs.writeFileSync(path.join(seedDir, "users.json"), JSON.stringify([ { id: "u-mig", username: "migrator", displayName: "迁移用户", role: "user", enabled: true }, ])); fs.writeFileSync(path.join(seedDir, "api-tokens.json"), JSON.stringify([ { id: "t-mig", name: "迁移Token", value: "mig-secret", enabled: true }, ])); fs.writeFileSync(path.join(seedDir, "system.json"), JSON.stringify({ mode: "plan" })); fs.writeFileSync(path.join(seedDir, "mcp.json"), JSON.stringify({ enabled: true, token: "mcp-token" })); const configsDir = path.join(seedDir, "configs"); fs.mkdirSync(configsDir, { recursive: true }); fs.writeFileSync(path.join(configsDir, "proj-mig-1.json"), JSON.stringify([ { id: "cfg-mig", key: "db.host", value: "10.0.0.1", type: "string" }, ])); process.env.REGISTRY_DATA_DIR = seedDir; const duckdbAdapter = require("../src/persistence/duckdbAdapter"); test("自动迁移全部 JSON 存量到 DuckDB,数据无损且备份 JSON", async () => { await duckdbAdapter.init(); const projects = await duckdbAdapter.loadProjects(); assert.strictEqual(projects.length, 1); assert.strictEqual(projects[0].name, "迁移项目A"); assert.strictEqual(projects[0].id, "proj-mig-1"); const users = await duckdbAdapter.loadUsers(); assert.strictEqual(users[0].username, "migrator"); const tokens = await duckdbAdapter.loadApiTokens(); assert.strictEqual(tokens[0].value, "mig-secret"); const system = await duckdbAdapter.loadSystem(); assert.strictEqual(system.mode, "plan"); const mcp = await duckdbAdapter.loadMcpConfig(); assert.strictEqual(mcp.token, "mcp-token"); const configs = await duckdbAdapter.loadConfigs("proj-mig-1"); assert.strictEqual(configs.length, 1); assert.strictEqual(configs[0].value, "10.0.0.1"); // projectId 不应被注入进记录本体 assert.strictEqual("projectId" in configs[0], false); // 数据迁移后应生成 DB 文件 assert.ok(fs.existsSync(path.join(seedDir, "registry.duckdb")), "应生成 registry.duckdb"); // JSON 源文件应被备份移除(防止二次导入) assert.ok(!fs.existsSync(path.join(seedDir, "projects.json")), "迁移后 projects.json 应已移走"); const backupDirs = fs.readdirSync(seedDir).filter((n) => n.startsWith("json-backup-")); assert.strictEqual(backupDirs.length, 1, "应生成 json-backup- 目录"); }); test("再次初始化(DB 已存在)不重复迁移、不报错", async () => { const before = await duckdbAdapter.loadProjects(); await duckdbAdapter.init(); const after = await duckdbAdapter.loadProjects(); assert.deepStrictEqual(after, before); });