Files
tyx_AI_xhs/tests/package/postv1-runtime.test.mjs
T
suyx 99fd3b1802
Dada P0-A isolated Windows CI / validate-and-package (push) Waiting to run
fix(POSTV1-02): 修复便携版网页入口门禁
2026-08-05 12:58:28 +08:00

156 lines
7.0 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import { mkdtemp, 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";
import { createServer } from "node:net";
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));
}
}
async function verifyWorkerStartup(configPath) {
const pipeName = `Dada.P0A.PostV1.${process.pid}.${Date.now()}`;
const pipePath = `\\\\.\\pipe\\${pipeName}`;
const server = createServer();
await new Promise((resolveListen, rejectListen) => {
server.once("error", rejectListen);
server.listen(pipePath, resolveListen);
});
const child = spawn(join(packageRoot, "runtime", "node.exe"), [
join(packageRoot, "server", "worker.mjs"),
"--dada-control-pipe", pipeName,
"--dada-credential-stdin",
], {
cwd: packageRoot,
env: {
...process.env,
DADA_INSTANCE_CONFIG_PATH: configPath,
DADA_SQLITE_NATIVE_BINDING: join(packageRoot, "server", "native", "better_sqlite3.node"),
},
stdio: ["pipe", "ignore", "ignore"],
windowsHide: true,
});
child.stdin.end(JSON.stringify({ "Dada/P0A/worker/ai-gateway": "synthetic-runtime-token" }));
try {
await new Promise((resolveReady, rejectReady) => {
let settled = false;
const finish = (error) => {
if (settled) return;
settled = true;
clearTimeout(deadline);
child.off("exit", onExit);
if (error) rejectReady(error); else resolveReady();
};
const deadline = setTimeout(() => finish(new Error("packaged worker ready timeout")), 15_000);
const onExit = (code) => finish(new Error(`packaged worker exited before ready: ${code}`));
child.once("exit", onExit);
server.once("connection", (connection) => {
connection.setEncoding("utf8");
let pending = "";
connection.on("data", (chunk) => {
pending += chunk;
while (pending.includes("\n")) {
const newline = pending.indexOf("\n");
const status = pending.slice(0, newline).trim();
pending = pending.slice(newline + 1);
if (status === "storage_unavailable") finish(new Error("packaged worker reported storage_unavailable"));
if (status === "ready") {
setTimeout(() => {
if (settled) return;
connection.write("shutdown\n");
finish();
}, 500);
}
}
});
});
});
const exitCode = await new Promise((resolveExit) => child.once("exit", resolveExit));
assert.equal(exitCode, 0);
} finally {
if (child.exitCode === null) child.kill();
await new Promise((resolveClose) => server.close(resolveClose));
}
}
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")));
const packagedWorker = await readFile(join(packageRoot, "server", "worker", "dist", "worker.js"), "utf8");
const packagedOneApiAdapter = await readFile(join(packageRoot, "server", "worker", "dist", "oneapi-generation-adapter.js"), "utf8");
assert.match(packagedWorker, /GenerationProcessor/);
assert.match(packagedWorker, /OneApiGenerationAdapter/);
assert.match(packagedOneApiAdapter, /oneapi\.intelligrow\.cn/);
assert.doesNotMatch(packagedWorker, /portable-mock-worker/);
const root = await mkdtemp(join(tmpdir(), "dada-postv1-"));
const dataRoot = join(root, "data");
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 initialPage = await fetch(`http://127.0.0.1:${port}/`, { headers: { host: `127.0.0.1:${port}` } });
assert.equal(initialPage.status, 200);
assert.match(await initialPage.text(), /当前浏览器无法使用 Dada/);
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.equal(support.status, 200);
const cookie = support.headers.get("set-cookie")?.split(";", 1)[0];
assert.ok(cookie);
const page = await fetch(`http://127.0.0.1:${port}/app`, { headers: { host: `127.0.0.1:${port}`, cookie, "sec-ch-ua": '"Google Chrome";v="150"' } });
assert.equal(page.status, 200);
assert.match(await page.text(), /<div id="root"><\/div>/);
assert.ok(existsSync(join(dataRoot, "db", "dada.sqlite3")));
await verifyWorkerStartup(configPath);
} finally {
await stop(api);
}
api = startApi(configPath, dataRoot);
try {
await waitForHealth(api);
assert.ok(existsSync(join(dataRoot, "db", "dada.sqlite3")));
} finally {
await stop(api);
await rm(root, { recursive: true, force: true });
}
});