import test from "node:test"; import assert from "node:assert/strict"; import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises"; import { existsSync } from "node:fs"; import { join, resolve } from "node:path"; import { tmpdir } from "node:os"; import { spawn } from "node:child_process"; const packageRoot = resolve(process.env.DADA_POSTV1_PACKAGE_ROOT ?? ".build/portable-release/Dada-P0A-0.0.0-win-x64"); const port = 43121; async function waitForHealth(child) { const deadline = Date.now() + 15_000; while (Date.now() < deadline) { if (child.exitCode !== null) throw new Error(`packaged api exited: ${child.exitCode}: ${child.errorOutput ?? ""}`); try { const response = await fetch(`http://127.0.0.1:${port}/healthz`, { headers: { host: `127.0.0.1:${port}` } }); if (response.ok) return; } catch {} await new Promise((resolveDelay) => setTimeout(resolveDelay, 100)); } throw new Error("packaged api health timeout"); } function startApi(configPath, dataRoot) { const child = spawn(join(packageRoot, "runtime", "node.exe"), [join(packageRoot, "server", "api.mjs"), "--dada-credential-stdin"], { cwd: packageRoot, env: { ...process.env, DADA_INSTANCE_CONFIG_PATH: configPath, DADA_SUPPORT_GATE_ROOT: join(packageRoot, "web", "support-gate"), DADA_WEB_ROOT: join(packageRoot, "web") }, stdio: ["pipe", "ignore", "pipe"], windowsHide: true, }); child.errorOutput = ""; child.stderr.setEncoding("utf8"); child.stderr.on("data", (chunk) => { child.errorOutput += chunk; }); child.stdin.end(JSON.stringify({ "Dada/P0A/api/amap": "", "Dada/P0A/api/resend": "", "Dada/P0A/admin/pepper": "portable-test-pepper-00000000000000000000000000000000" })); return child; } async function stop(child) { if (child.exitCode === null) { child.kill(); await new Promise((resolveExit) => child.once("exit", resolveExit)); } } test("portable package serves the product and keeps SQLite data across API restart", async () => { assert.ok(existsSync(join(packageRoot, "Dada.exe"))); assert.ok(existsSync(join(packageRoot, "web", "index.html"))); assert.match(await readFile(join(packageRoot, "server", "worker", "dist", "worker.js"), "utf8"), /GenerationProcessor/); const root = await mkdtemp(join(tmpdir(), "dada-postv1-")); const dataRoot = join(root, "data"); await mkdir(join(dataRoot, "db"), { recursive: true }); const configPath = join(root, "instance.json"); await writeFile(configPath, JSON.stringify({ data_root: dataRoot, initialized: true, instance_id: "portable-test", schema_version: 1, secure_config_revision: 1, admin_allowlist_hashes: [], admin_recovery_hashes: [] })); let api = startApi(configPath, dataRoot); try { await waitForHealth(api); const support = await fetch(`http://127.0.0.1:${port}/api/v1/support/check`, { method: "POST", headers: { host: `127.0.0.1:${port}`, origin: `http://127.0.0.1:${port}`, "content-type": "application/json", "sec-ch-ua": '"Google Chrome";v="150"', "sec-ch-ua-full-version-list": '"Google Chrome";v="150.0.0.0"', "sec-ch-ua-platform": '"Windows"' }, body: JSON.stringify({ brands: [{ brand: "Google Chrome", version: "150" }], full_version_list: [{ brand: "Google Chrome", version: "150.0.0.0" }], platform: "Windows" }), }); assert.ok([200, 426].includes(support.status)); if (support.status === 200) { const cookie = support.headers.get("set-cookie")?.split(";", 1)[0]; const page = await fetch(`http://127.0.0.1:${port}/app`, { headers: { host: `127.0.0.1:${port}`, cookie } }); assert.equal(page.status, 200); assert.match(await page.text(), /