/** * @file settings-import.test.js * @description Restoring a backup: `GET /api/settings/export` writes every table * the restore reads (with the format stamp), and `POST /api/settings/import` * takes that file as a multipart upload, restores it without overwriting * anything, refuses a file that is not an export without echoing its text, and * refuses a cross-origin caller. * @author Nguyễn Ngọc Trí Vĩ */ const { describe, it, before, after } = require("node:test"); const assert = require("node:assert/strict"); const path = require("path"); const fs = require("fs"); const os = require("os"); const TEST_DB = path.join(os.tmpdir(), `settings-import-${Date.now()}-${process.pid}.db`); process.env.DASHBOARD_DB_PATH = TEST_DB; process.env.DASHBOARD_LIVENESS_PROBE = "0"; const { createApp, startServer } = require("../index"); const { db } = require("../db"); let server; let BASE; function upload(text, headers = {}) { const form = new FormData(); form.append("file", new Blob([text], { type: "application/json" }), "backup.json"); return fetch(`${BASE}/api/settings/import`, { method: "POST", body: form, headers }); } before(async () => { server = await startServer(createApp(), 0); BASE = `http://127.0.0.1:${server.address().port}`; db.prepare("INSERT INTO sessions (id, name, status, started_at) VALUES (?, ?, ?, ?)").run( "imp-1", "Backed up", "completed", "2026-01-10T10:00:00.000Z" ); db.prepare("INSERT INTO events (session_id, event_type, data) VALUES (?, ?, ?)").run( "imp-1", "Stop", "{}" ); }); after(() => { if (server) server.close(); if (db) db.close(); for (const f of [TEST_DB, TEST_DB + "-wal", TEST_DB + "-shm"]) { try { fs.unlinkSync(f); } catch { // ignore } } }); describe("backup and restore", () => { let exported; it("the export carries the format stamp and every table the restore reads", async () => { const res = await fetch(`${BASE}/api/settings/export`); assert.equal(res.status, 200); exported = await res.text(); const bundle = JSON.parse(exported); assert.equal(bundle.format, "agenthub-export"); assert.equal(bundle.version, 1); for (const t of [ "sessions", "agents", "events", "token_usage", "workflows", "dashboard_runs", "alert_rules", "model_pricing", ]) { assert.ok(Array.isArray(bundle[t]), `missing ${t}`); } }); it("restores an uploaded export, skipping what is already there", async () => { let res = await upload(exported); let body = await res.json(); assert.equal(res.status, 200, JSON.stringify(body)); assert.equal(body.sessions_imported, 0); assert.equal(body.sessions_skipped, 1); db.prepare("DELETE FROM events WHERE session_id = 'imp-1'").run(); db.prepare("DELETE FROM sessions WHERE id = 'imp-1'").run(); res = await upload(exported); body = await res.json(); assert.equal(res.status, 200, JSON.stringify(body)); assert.equal(body.sessions_imported, 1); assert.equal(body.events, 1); assert.ok(db.prepare("SELECT 1 FROM sessions WHERE id = 'imp-1'").get()); }); it("refuses a file that is not an export, without echoing its text", async () => { const res = await upload("root:x:0:0:secret-line"); const body = await res.json(); assert.equal(res.status, 400); assert.equal(body.error.code, "INVALID_FORMAT"); assert.doesNotMatch(JSON.stringify(body), /secret-line/); const notBundle = await upload(JSON.stringify({ foo: 1 })); assert.equal(notBundle.status, 400); }); it("asks for a file", async () => { const res = await fetch(`${BASE}/api/settings/import`, { method: "POST" }); assert.equal(res.status, 400); assert.equal((await res.json()).error.code, "NO_FILE"); }); it("refuses a cross-origin caller", async () => { const res = await upload(exported, { Origin: "https://evil.example" }); assert.equal(res.status, 403); }); });