Files
healer-man/server/local-balance-lab-api.test.mjs
T
2026-09-19 21:01:51 -04:00

144 lines
6.1 KiB
JavaScript

import assert from "node:assert/strict";
import { EventEmitter } from "node:events";
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { createServer } from "node:http";
import os from "node:os";
import path from "node:path";
import { PassThrough } from "node:stream";
import test from "node:test";
import { createLocalBalanceLabApi } from "./local-balance-lab-api.mjs";
test("serves loopback-only local Balance Lab jobs without account authentication", async () => {
const directory = mkdtempSync(path.join(os.tmpdir(), "healer-man-local-balance-gui-"));
const spawnProcess = (_executable, args) => {
const child = new EventEmitter();
child.stdout = new PassThrough();
child.stderr = new PassThrough();
child.kill = () => child.emit("close", null);
const outputDir = args[args.indexOf("--output-dir") + 1];
setImmediate(() => {
mkdirSync(outputDir, { recursive: true });
writeFileSync(path.join(outputDir, "suite-report.json"), JSON.stringify({
summary: { partyConfigurations: 1, bosses: 1, runs: 1, classSpecs: 5, classAbilities: 5 },
}));
child.stdout.write("Party 1/1: wow-party-001\n");
child.stdout.end();
child.stderr.end();
child.emit("close", 0);
});
return child;
};
const api = createLocalBalanceLabApi({
dataDir: directory,
projectRoot: directory,
spawnProcess,
runtimeProbe: () => ({ available: true, executable: "python-test", version: "Python 3.test", error: null }),
});
const server = createServer((request, response) => {
void api.handle(request, response).then((handled) => {
if (!handled) response.writeHead(404).end();
});
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
const origin = `http://127.0.0.1:${server.address().port}`;
try {
const runtime = await (await fetch(`${origin}/api/local/balance-lab/runtime`)).json();
assert.equal(runtime.runtime.available, true);
const startedResponse = await fetch(`${origin}/api/local/balance-lab/jobs`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
source: "wow", level: 20, partyConfigurations: 1, simulationsPerParty: 1,
durationSeconds: 5, partySeed: 1, bossCategory: "all",
bossKeys: ["stormwind-stockade/entry-1717"],
}),
});
assert.equal(startedResponse.status, 202);
const started = (await startedResponse.json()).job;
assert.equal(started.requestedBy, "Local GUI");
await new Promise((resolve) => setImmediate(resolve));
const jobs = await (await fetch(`${origin}/api/local/balance-lab/jobs`)).json();
assert.equal(jobs.jobs[0].status, "complete");
const artifact = await fetch(`${origin}/api/local/balance-lab/jobs/${started.id}/artifacts/suite-report.json`);
assert.equal(artifact.status, 200);
assert.equal((await artifact.json()).summary.runs, 1);
assert.equal((await fetch(`${origin}/api/not-local-balance-lab`)).status, 404);
} finally {
await new Promise((resolve) => server.close(resolve));
api.close();
rmSync(directory, { recursive: true, force: true });
}
});
test("persists revisioned local companion readiness and recovers from a corrupt policy file", async () => {
const directory = mkdtempSync(path.join(os.tmpdir(), "healer-man-local-readiness-"));
const open = async () => {
const api = createLocalBalanceLabApi({
dataDir: directory,
projectRoot: directory,
runtimeProbe: () => ({ available: true, executable: "python-test", version: "Python 3.test", error: null }),
});
const server = createServer((request, response) => {
void api.handle(request, response).then((handled) => {
if (!handled) response.writeHead(404).end();
});
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
return {
api,
server,
origin: `http://127.0.0.1:${server.address().port}`,
close: async () => {
await new Promise((resolve) => server.close(resolve));
api.close();
},
};
};
try {
const first = await open();
const initial = await (await fetch(`${first.origin}/api/local/balance-lab/companion-spec-readiness`)).json();
const configured = {
...initial.config,
statuses: { ...initial.config.statuses, "paladin:retribution": "beta" },
};
const savedResponse = await fetch(`${first.origin}/api/local/balance-lab/companion-spec-readiness`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ expectedRevision: 0, config: configured }),
});
assert.equal(savedResponse.status, 200);
assert.equal((await savedResponse.json()).revision, 1);
const staleResponse = await fetch(`${first.origin}/api/local/balance-lab/companion-spec-readiness`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ expectedRevision: 0, config: configured }),
});
assert.equal(staleResponse.status, 409);
const invalidResponse = await fetch(`${first.origin}/api/local/balance-lab/companion-spec-readiness`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
expectedRevision: 1,
config: { ...configured, statuses: { ...configured.statuses, "warrior:protection": "needs-testing" } },
}),
});
assert.equal(invalidResponse.status, 400);
await first.close();
const second = await open();
const restored = await (await fetch(`${second.origin}/api/local/balance-lab/companion-spec-readiness`)).json();
assert.equal(restored.revision, 1);
assert.equal(restored.config.statuses["paladin:retribution"], "beta");
await second.close();
writeFileSync(path.join(directory, "companion-spec-readiness.json"), "{not-json");
const third = await open();
const fallback = await (await fetch(`${third.origin}/api/local/balance-lab/companion-spec-readiness`)).json();
assert.equal(fallback.revision, 0);
assert.equal(fallback.config.statuses["warrior:protection"], "ready");
await third.close();
} finally {
rmSync(directory, { recursive: true, force: true });
}
});