mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-14 10:41:23 +00:00
125 lines
5.0 KiB
TypeScript
125 lines
5.0 KiB
TypeScript
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import { buildQaDockerHarnessImage, writeQaDockerHarnessFiles } from "./docker-harness.js";
|
|
|
|
const cleanups: Array<() => Promise<void>> = [];
|
|
|
|
afterEach(async () => {
|
|
while (cleanups.length > 0) {
|
|
await cleanups.pop()?.();
|
|
}
|
|
});
|
|
|
|
describe("qa docker harness", () => {
|
|
it("writes compose, env, config, and workspace scaffold files", async () => {
|
|
const outputDir = await mkdtemp(path.join(os.tmpdir(), "qa-docker-test-"));
|
|
cleanups.push(async () => {
|
|
await rm(outputDir, { recursive: true, force: true });
|
|
});
|
|
|
|
const result = await writeQaDockerHarnessFiles({
|
|
outputDir,
|
|
gatewayPort: 18889,
|
|
qaLabPort: 43124,
|
|
gatewayToken: "qa-token",
|
|
providerBaseUrl: "http://host.docker.internal:45123/v1",
|
|
repoRoot: "/repo/openclaw",
|
|
usePrebuiltImage: true,
|
|
bindUiDist: true,
|
|
});
|
|
|
|
expect(result.files).toEqual(
|
|
expect.arrayContaining([
|
|
path.join(outputDir, ".env.example"),
|
|
path.join(outputDir, "README.md"),
|
|
path.join(outputDir, "docker-compose.qa.yml"),
|
|
path.join(outputDir, "state", "openclaw.json"),
|
|
path.join(outputDir, "state", "seed-workspace", "QA_KICKOFF_TASK.md"),
|
|
path.join(outputDir, "state", "seed-workspace", "QA_SCENARIO_PLAN.md"),
|
|
path.join(outputDir, "state", "seed-workspace", "QA_SCENARIOS.md"),
|
|
path.join(outputDir, "state", "seed-workspace", "IDENTITY.md"),
|
|
]),
|
|
);
|
|
|
|
const compose = await readFile(path.join(outputDir, "docker-compose.qa.yml"), "utf8");
|
|
expect(compose).toContain("image: openclaw:qa-local-prebaked");
|
|
expect(compose).toContain("qa-mock-openai:");
|
|
expect(compose).toContain("18889:18789");
|
|
expect(compose).toContain(' - "43124:43123"');
|
|
expect(compose).toContain(":/opt/openclaw-qa-lab-ui:ro");
|
|
expect(compose).toContain(" - sh");
|
|
expect(compose).toContain(" - -lc");
|
|
expect(compose).toContain(
|
|
' - fetch("http://127.0.0.1:18789/healthz").then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))',
|
|
);
|
|
expect(compose).toContain(" - --control-ui-proxy-target");
|
|
expect(compose).toContain(' - "http://openclaw-qa-gateway:18789/"');
|
|
expect(compose).toContain(" - --send-kickoff-on-start");
|
|
expect(compose).toContain(" - --ui-dist-dir");
|
|
expect(compose).toContain(' - "/opt/openclaw-qa-lab-ui"');
|
|
expect(compose).toContain(":/opt/openclaw-repo:ro");
|
|
expect(compose).toContain("./state:/opt/openclaw-scaffold:ro");
|
|
expect(compose).toContain(
|
|
"cp -R /opt/openclaw-scaffold/seed-workspace/. /tmp/openclaw/workspace/",
|
|
);
|
|
expect(compose).toContain("OPENCLAW_CONFIG_PATH: /tmp/openclaw/openclaw.json");
|
|
expect(compose).toContain("OPENCLAW_STATE_DIR: /tmp/openclaw/state");
|
|
expect(compose).toContain('OPENCLAW_NO_RESPAWN: "1"');
|
|
|
|
const envExample = await readFile(path.join(outputDir, ".env.example"), "utf8");
|
|
expect(envExample).toContain("OPENCLAW_GATEWAY_TOKEN=qa-token");
|
|
expect(envExample).toContain("QA_BUS_BASE_URL=http://qa-lab:43123");
|
|
expect(envExample).toContain("QA_PROVIDER_BASE_URL=http://host.docker.internal:45123/v1");
|
|
expect(envExample).toContain("QA_LAB_URL=http://127.0.0.1:43124");
|
|
|
|
const config = await readFile(path.join(outputDir, "state", "openclaw.json"), "utf8");
|
|
expect(config).toContain('"allowInsecureAuth": true');
|
|
expect(config).toContain('"enabled": false');
|
|
expect(config).toContain("/app/dist/control-ui");
|
|
expect(config).toContain("C-3PO QA");
|
|
expect(config).toContain('"/tmp/openclaw/workspace"');
|
|
|
|
const kickoff = await readFile(
|
|
path.join(outputDir, "state", "seed-workspace", "QA_KICKOFF_TASK.md"),
|
|
"utf8",
|
|
);
|
|
expect(kickoff).toContain("Lobster Invaders");
|
|
|
|
const scenarios = await readFile(
|
|
path.join(outputDir, "state", "seed-workspace", "QA_SCENARIOS.md"),
|
|
"utf8",
|
|
);
|
|
expect(scenarios).toContain("```yaml qa-pack");
|
|
expect(scenarios).toContain("subagent-fanout-synthesis");
|
|
|
|
const readme = await readFile(path.join(outputDir, "README.md"), "utf8");
|
|
expect(readme).toContain("in-process restarts inside Docker");
|
|
expect(readme).toContain("pnpm qa:lab:watch");
|
|
});
|
|
|
|
it("builds the reusable QA image with bundled QA extensions", async () => {
|
|
const calls: string[] = [];
|
|
const result = await buildQaDockerHarnessImage(
|
|
{
|
|
repoRoot: "/repo/openclaw",
|
|
imageName: "openclaw:qa-local-prebaked",
|
|
},
|
|
{
|
|
async runCommand(command, args, cwd) {
|
|
calls.push([command, ...args, `@${cwd}`].join(" "));
|
|
return { stdout: "", stderr: "" };
|
|
},
|
|
},
|
|
);
|
|
|
|
expect(result.imageName).toBe("openclaw:qa-local-prebaked");
|
|
expect(calls).toEqual([
|
|
expect.stringContaining(
|
|
"docker build -t openclaw:qa-local-prebaked --build-arg OPENCLAW_EXTENSIONS=qa-channel qa-lab -f Dockerfile . @/repo/openclaw",
|
|
),
|
|
]);
|
|
});
|
|
});
|