Files
openswarm/scripts/ci/verify-9router-functional.js
T

68 lines
2.9 KiB
JavaScript

#!/usr/bin/env node
// Drives a real HTTP request against the bundled 9router, not just a TCP probe. /api/providers is the same endpoint the backend itself polls at startup (see backend/apps/agents/providers/registry.py); if it 200s with parseable JSON, the router is actually routing. A misconfigured router can listen on :20128 and 500 every real request, which the TCP-only check in verify-network would silently green.
'use strict';
const http = require('http');
const h = require('./lib/app-harness');
function parseArgs(argv) {
const out = { app: null, timeoutMs: 120000 };
for (let i = 0; i < argv.length; i++) {
if (argv[i] === '--app') out.app = argv[++i];
else if (argv[i] === '--timeout-ms') out.timeoutMs = Number(argv[++i]);
}
return out;
}
function getJson(port, path, timeoutMs = 8000) {
return new Promise((resolve) => {
const req = http.get({ host: '127.0.0.1', port, path }, (res) => {
let buf = '';
res.on('data', (c) => { buf += c; });
res.on('end', () => { let json = null; try { json = JSON.parse(buf); } catch {} resolve({ status: res.statusCode, json, text: buf }); });
});
req.on('error', () => resolve({ status: 0, json: null, text: '' }));
req.setTimeout(timeoutMs, () => { req.destroy(); resolve({ status: 0, json: null, text: '' }); });
});
}
async function pollUntil(fn, ok, timeoutMs) {
const deadline = Date.now() + timeoutMs;
let last = null;
while (Date.now() < deadline) {
last = await fn();
if (ok(last)) return last;
await h.sleep(1000);
}
return last;
}
async function main() {
const args = parseArgs(process.argv.slice(2));
const appPath = h.packagedAppPath(args.app);
process.stdout.write(`Launching: ${appPath}\n`);
const res = await h.launchAndWait({ appPath, timeoutMs: args.timeoutMs });
const child = res.child;
try {
// Wait for the router to answer /api/providers with 200 + JSON. Until then
// either the port isn't open or the framework hasn't bound the routes yet.
const final = await pollUntil(
() => getJson(20128, '/api/providers'),
(r) => r && r.status === 200 && r.json && typeof r.json === 'object',
30_000,
);
if (!final || final.status !== 200) throw new Error(`/api/providers returned ${final && final.status}, expected 200`);
if (!final.json) throw new Error(`/api/providers returned non-JSON body: ${(final.text || '').slice(0, 200)}`);
process.stdout.write(` ok 9router GET /api/providers -> 200 (${JSON.stringify(final.json).slice(0, 100)}...)\n`);
process.stdout.write('\n9ROUTER PASS: bundled router is actually routing, not just listening.\n');
} catch (e) {
process.stderr.write(`\n9ROUTER FAIL: ${e && e.message || e}\n`);
h.killApp(child);
process.exit(1);
}
h.killApp(child);
process.exit(0);
}
main().catch((e) => { process.stderr.write(`\n9ROUTER FAIL: ${e && e.message || e}\n`); process.exit(1); });