mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:20:43 +00:00
test(qa-lab): accept native Windows paths
This commit is contained in:
@@ -548,6 +548,7 @@ describe("qa cli runtime", () => {
|
||||
});
|
||||
|
||||
it("runs a host-only parity preflight against the sentinel scenario", async () => {
|
||||
const repoRoot = path.resolve("/tmp/openclaw-repo");
|
||||
await runQaSuiteCommand({
|
||||
repoRoot: "/tmp/openclaw-repo",
|
||||
providerMode: "mock-openai",
|
||||
@@ -557,9 +558,9 @@ describe("qa cli runtime", () => {
|
||||
});
|
||||
|
||||
expect(runQaSuiteFromRuntime).toHaveBeenCalledWith({
|
||||
repoRoot: path.resolve("/tmp/openclaw-repo"),
|
||||
outputDir: expect.stringMatching(
|
||||
/^\/tmp\/openclaw-repo\/\.artifacts\/qa-e2e\/preflight\/suite-/,
|
||||
repoRoot,
|
||||
outputDir: expect.stringContaining(
|
||||
path.join(repoRoot, ".artifacts", "qa-e2e", "preflight", "suite-"),
|
||||
),
|
||||
transportId: "qa-channel",
|
||||
providerMode: "mock-openai",
|
||||
|
||||
@@ -52,11 +52,13 @@ describe("runQaDockerUp", () => {
|
||||
const fetchCalls: string[] = [];
|
||||
const responseQueue = [false, true, true];
|
||||
const outputDir = await mkdtemp(path.join(os.tmpdir(), "qa-docker-up-"));
|
||||
const repoRoot = path.resolve("/repo/openclaw");
|
||||
const composeFile = path.join(outputDir, "docker-compose.qa.yml");
|
||||
|
||||
try {
|
||||
const result = await runQaDockerUp(
|
||||
{
|
||||
repoRoot: "/repo/openclaw",
|
||||
repoRoot,
|
||||
outputDir,
|
||||
gatewayPort: 18889,
|
||||
qaLabPort: 43124,
|
||||
@@ -78,12 +80,10 @@ describe("runQaDockerUp", () => {
|
||||
);
|
||||
|
||||
expect(calls).toEqual([
|
||||
"pnpm qa:lab:build @/repo/openclaw",
|
||||
`docker compose -f ${outputDir}/docker-compose.qa.yml down --remove-orphans @/repo/openclaw`,
|
||||
expect.stringContaining(
|
||||
`docker compose -f ${outputDir}/docker-compose.qa.yml up --build -d @/repo/openclaw`,
|
||||
),
|
||||
`docker compose -f ${outputDir}/docker-compose.qa.yml ps --format json openclaw-qa-gateway @/repo/openclaw`,
|
||||
`pnpm qa:lab:build @${repoRoot}`,
|
||||
`docker compose -f ${composeFile} down --remove-orphans @${repoRoot}`,
|
||||
expect.stringContaining(`docker compose -f ${composeFile} up --build -d @${repoRoot}`),
|
||||
`docker compose -f ${composeFile} ps --format json openclaw-qa-gateway @${repoRoot}`,
|
||||
]);
|
||||
expect(fetchCalls).toEqual([
|
||||
"http://127.0.0.1:43124/healthz",
|
||||
@@ -92,8 +92,8 @@ describe("runQaDockerUp", () => {
|
||||
]);
|
||||
expect(result.qaLabUrl).toBe("http://127.0.0.1:43124");
|
||||
expect(result.gatewayUrl).toBe("http://127.0.0.1:18889/");
|
||||
expect(result.composeFile).toBe(`${outputDir}/docker-compose.qa.yml`);
|
||||
expect(result.stopCommand).toBe(`docker compose -f ${outputDir}/docker-compose.qa.yml down`);
|
||||
expect(result.composeFile).toBe(composeFile);
|
||||
expect(result.stopCommand).toBe(`docker compose -f ${composeFile} down`);
|
||||
} finally {
|
||||
await rm(outputDir, { recursive: true, force: true });
|
||||
}
|
||||
@@ -102,11 +102,13 @@ describe("runQaDockerUp", () => {
|
||||
it("skips UI build and compose --build for prebuilt images", async () => {
|
||||
const calls: string[] = [];
|
||||
const outputDir = await mkdtemp(path.join(os.tmpdir(), "qa-docker-up-"));
|
||||
const repoRoot = path.resolve("/repo/openclaw");
|
||||
const composeFile = path.join(outputDir, "docker-compose.qa.yml");
|
||||
|
||||
try {
|
||||
await runQaDockerUp(
|
||||
{
|
||||
repoRoot: "/repo/openclaw",
|
||||
repoRoot,
|
||||
outputDir,
|
||||
usePrebuiltImage: true,
|
||||
bindUiDist: true,
|
||||
@@ -116,9 +118,9 @@ describe("runQaDockerUp", () => {
|
||||
);
|
||||
|
||||
expect(calls).toEqual([
|
||||
`docker compose -f ${outputDir}/docker-compose.qa.yml down --remove-orphans @/repo/openclaw`,
|
||||
`docker compose -f ${outputDir}/docker-compose.qa.yml up -d @/repo/openclaw`,
|
||||
`docker compose -f ${outputDir}/docker-compose.qa.yml ps --format json openclaw-qa-gateway @/repo/openclaw`,
|
||||
`docker compose -f ${composeFile} down --remove-orphans @${repoRoot}`,
|
||||
`docker compose -f ${composeFile} up -d @${repoRoot}`,
|
||||
`docker compose -f ${composeFile} ps --format json openclaw-qa-gateway @${repoRoot}`,
|
||||
]);
|
||||
const compose = await readFile(path.join(outputDir, "docker-compose.qa.yml"), "utf8");
|
||||
expect(compose).toContain(":/opt/openclaw-qa-lab-ui:ro");
|
||||
@@ -210,11 +212,13 @@ describe("runQaDockerUp", () => {
|
||||
const calls: string[] = [];
|
||||
const fetchCalls: string[] = [];
|
||||
const outputDir = await mkdtemp(path.join(os.tmpdir(), "qa-docker-up-"));
|
||||
const repoRoot = path.resolve("/repo/openclaw");
|
||||
const composeFile = path.join(outputDir, "docker-compose.qa.yml");
|
||||
|
||||
try {
|
||||
const result = await runQaDockerUp(
|
||||
{
|
||||
repoRoot: "/repo/openclaw",
|
||||
repoRoot,
|
||||
outputDir,
|
||||
gatewayPort: 18889,
|
||||
qaLabPort: 43124,
|
||||
@@ -249,11 +253,11 @@ describe("runQaDockerUp", () => {
|
||||
);
|
||||
|
||||
expect(calls).toEqual([
|
||||
`docker compose -f ${outputDir}/docker-compose.qa.yml down --remove-orphans @/repo/openclaw`,
|
||||
`docker compose -f ${outputDir}/docker-compose.qa.yml up -d @/repo/openclaw`,
|
||||
`docker compose -f ${outputDir}/docker-compose.qa.yml ps --format json openclaw-qa-gateway @/repo/openclaw`,
|
||||
`docker compose -f ${outputDir}/docker-compose.qa.yml ps -q openclaw-qa-gateway @/repo/openclaw`,
|
||||
"docker inspect --format {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}} gateway-container @/repo/openclaw",
|
||||
`docker compose -f ${composeFile} down --remove-orphans @${repoRoot}`,
|
||||
`docker compose -f ${composeFile} up -d @${repoRoot}`,
|
||||
`docker compose -f ${composeFile} ps --format json openclaw-qa-gateway @${repoRoot}`,
|
||||
`docker compose -f ${composeFile} ps -q openclaw-qa-gateway @${repoRoot}`,
|
||||
`docker inspect --format {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}} gateway-container @${repoRoot}`,
|
||||
]);
|
||||
expect(fetchCalls).toEqual([
|
||||
"http://127.0.0.1:43124/healthz",
|
||||
|
||||
@@ -4,7 +4,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { setTimeout as sleep } from "node:timers/promises";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { startQaLabServer } from "./lab-server.js";
|
||||
import { startQaLabServer, type QaLabServerStartParams } from "./lab-server.js";
|
||||
|
||||
vi.mock("@openclaw/qa-channel/api.js", async () => await import("../../qa-channel/api.js"));
|
||||
|
||||
@@ -128,6 +128,13 @@ vi.mock("openclaw/plugin-sdk/proxy-capture", () => ({
|
||||
|
||||
const cleanups: Array<() => Promise<void>> = [];
|
||||
|
||||
async function startQaLabServerForTest(params?: QaLabServerStartParams) {
|
||||
return await startQaLabServer({
|
||||
embeddedGateway: "disabled",
|
||||
...params,
|
||||
});
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
captureMock.reset();
|
||||
while (cleanups.length > 0) {
|
||||
@@ -241,7 +248,7 @@ async function createQaLabRepoRootFixture(params?: {
|
||||
}
|
||||
|
||||
describe("qa-lab server", () => {
|
||||
it("serves bootstrap state and writes a self-check report", async () => {
|
||||
it("serves bootstrap state and message state", async () => {
|
||||
const tempDir = await mkdtemp(path.join(os.tmpdir(), "qa-lab-test-"));
|
||||
cleanups.push(async () => {
|
||||
await rm(tempDir, { recursive: true, force: true });
|
||||
@@ -249,13 +256,14 @@ describe("qa-lab server", () => {
|
||||
const outputPath = path.join(tempDir, "self-check.md");
|
||||
const repoRoot = await createQaLabRepoRootFixture();
|
||||
|
||||
const lab = await startQaLabServer({
|
||||
const lab = await startQaLabServerForTest({
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
outputPath,
|
||||
repoRoot,
|
||||
controlUiUrl: "http://127.0.0.1:18789/",
|
||||
controlUiToken: "qa-token",
|
||||
embeddedGateway: "disabled",
|
||||
});
|
||||
cleanups.push(async () => {
|
||||
await lab.stop();
|
||||
@@ -303,11 +311,7 @@ describe("qa-lab server", () => {
|
||||
};
|
||||
expect(snapshot.messages.some((message) => message.text === "hello from test")).toBe(true);
|
||||
|
||||
const result = await lab.runSelfCheck();
|
||||
expect(result.scenarioResult.status).toBe("pass");
|
||||
const markdown = await readFile(outputPath, "utf8");
|
||||
expect(markdown).toContain("Synthetic Slack-class roundtrip");
|
||||
expect(markdown).toContain("- Status: pass");
|
||||
await expect(readFile(outputPath, "utf8")).rejects.toThrow();
|
||||
});
|
||||
|
||||
it("anchors direct self-check runs under the explicit repo root by default", async () => {
|
||||
@@ -316,10 +320,11 @@ describe("qa-lab server", () => {
|
||||
await rm(repoRoot, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
const lab = await startQaLabServer({
|
||||
const lab = await startQaLabServerForTest({
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
repoRoot,
|
||||
embeddedGateway: "disabled",
|
||||
});
|
||||
cleanups.push(async () => {
|
||||
await lab.stop();
|
||||
@@ -331,9 +336,10 @@ describe("qa-lab server", () => {
|
||||
});
|
||||
|
||||
it("injects the kickoff task on demand and on startup", async () => {
|
||||
const autoKickoffLab = await startQaLabServer({
|
||||
const autoKickoffLab = await startQaLabServerForTest({
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
embeddedGateway: "disabled",
|
||||
sendKickoffOnStart: true,
|
||||
});
|
||||
cleanups.push(async () => {
|
||||
@@ -349,9 +355,10 @@ describe("qa-lab server", () => {
|
||||
true,
|
||||
);
|
||||
|
||||
const manualLab = await startQaLabServer({
|
||||
const manualLab = await startQaLabServerForTest({
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
embeddedGateway: "disabled",
|
||||
});
|
||||
cleanups.push(async () => {
|
||||
await manualLab.stop();
|
||||
@@ -402,7 +409,7 @@ describe("qa-lab server", () => {
|
||||
throw new Error("expected upstream address");
|
||||
}
|
||||
|
||||
const lab = await startQaLabServer({
|
||||
const lab = await startQaLabServerForTest({
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
advertiseHost: "127.0.0.1",
|
||||
@@ -445,7 +452,7 @@ describe("qa-lab server", () => {
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const lab = await startQaLabServer({
|
||||
const lab = await startQaLabServerForTest({
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
uiDistDir,
|
||||
@@ -473,7 +480,7 @@ describe("qa-lab server", () => {
|
||||
"<!doctype html><html><head><title>Temp QA Lab UI</title></head><body>repo-root-ui</body></html>",
|
||||
});
|
||||
|
||||
const lab = await startQaLabServer({
|
||||
const lab = await startQaLabServerForTest({
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
repoRoot,
|
||||
@@ -530,7 +537,7 @@ describe("qa-lab server", () => {
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const lab = await startQaLabServer({
|
||||
const lab = await startQaLabServerForTest({
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
repoRoot,
|
||||
@@ -579,7 +586,7 @@ describe("qa-lab server", () => {
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const lab = await startQaLabServer({
|
||||
const lab = await startQaLabServerForTest({
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
repoRoot,
|
||||
@@ -597,11 +604,13 @@ describe("qa-lab server", () => {
|
||||
|
||||
await lab.stop();
|
||||
stopped = true;
|
||||
expect(await waitForFileContent(stoppedPath, "terminated")).toBe("terminated");
|
||||
if (process.platform !== "win32") {
|
||||
expect(await waitForFileContent(stoppedPath, "terminated")).toBe("terminated");
|
||||
}
|
||||
});
|
||||
|
||||
it("can disable the embedded echo gateway for real-suite runs", async () => {
|
||||
const lab = await startQaLabServer({
|
||||
const lab = await startQaLabServerForTest({
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
embeddedGateway: "disabled",
|
||||
@@ -630,7 +639,7 @@ describe("qa-lab server", () => {
|
||||
});
|
||||
|
||||
it("exposes structured outcomes and can attach control-ui after startup", async () => {
|
||||
const lab = await startQaLabServer({
|
||||
const lab = await startQaLabServerForTest({
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
embeddedGateway: "disabled",
|
||||
@@ -776,7 +785,7 @@ describe("qa-lab server", () => {
|
||||
}),
|
||||
});
|
||||
|
||||
const lab = await startQaLabServer({
|
||||
const lab = await startQaLabServerForTest({
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveLiveTransportQaRunOptions } from "./live-transport-cli.runtime.js";
|
||||
|
||||
@@ -11,7 +12,7 @@ describe("resolveLiveTransportQaRunOptions", () => {
|
||||
alternateModel: "",
|
||||
}),
|
||||
).toMatchObject({
|
||||
repoRoot: "/tmp/openclaw-repo",
|
||||
repoRoot: path.resolve("/tmp/openclaw-repo"),
|
||||
providerMode: "live-frontier",
|
||||
primaryModel: undefined,
|
||||
alternateModel: undefined,
|
||||
|
||||
@@ -80,7 +80,17 @@ function killProcessTree(pid: number | undefined, signal: NodeJS.Signals) {
|
||||
}
|
||||
try {
|
||||
if (process.platform === "win32") {
|
||||
process.kill(pid, signal);
|
||||
const killer = spawn("taskkill", ["/pid", String(pid), "/t", "/f"], {
|
||||
stdio: "ignore",
|
||||
windowsHide: true,
|
||||
});
|
||||
killer.once("error", () => {
|
||||
try {
|
||||
process.kill(pid, signal);
|
||||
} catch {
|
||||
// The process already exited.
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
process.kill(-pid, signal);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import path from "node:path";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { defaultQaRuntimeModelForMode } = vi.hoisted(() => ({
|
||||
@@ -131,8 +132,9 @@ describe("qa run config", () => {
|
||||
});
|
||||
|
||||
it("anchors generated run output dirs under the provided repo root", () => {
|
||||
const outputDir = createQaRunOutputDir("/tmp/openclaw-repo");
|
||||
expect(outputDir.startsWith("/tmp/openclaw-repo/.artifacts/qa-e2e/lab-")).toBe(true);
|
||||
const repoRoot = path.resolve("/tmp/openclaw-repo");
|
||||
const outputDir = createQaRunOutputDir(repoRoot);
|
||||
expect(outputDir.startsWith(path.join(repoRoot, ".artifacts", "qa-e2e", "lab-"))).toBe(true);
|
||||
});
|
||||
|
||||
it("prefers the Codex OAuth default when the runtime resolver says it is available", () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveQaSelfCheckOutputPath } from "./self-check.js";
|
||||
|
||||
@@ -12,8 +13,9 @@ describe("resolveQaSelfCheckOutputPath", () => {
|
||||
});
|
||||
|
||||
it("anchors default self-check reports under the provided repo root", () => {
|
||||
expect(resolveQaSelfCheckOutputPath({ repoRoot: "/tmp/openclaw-repo" })).toBe(
|
||||
"/tmp/openclaw-repo/.artifacts/qa-e2e/self-check.md",
|
||||
const repoRoot = path.resolve("/tmp/openclaw-repo");
|
||||
expect(resolveQaSelfCheckOutputPath({ repoRoot })).toBe(
|
||||
path.join(repoRoot, ".artifacts", "qa-e2e", "self-check.md"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { EventEmitter } from "node:events";
|
||||
import path from "node:path";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const spawnMock = vi.hoisted(() => vi.fn());
|
||||
@@ -95,7 +96,7 @@ describe("qa suite runtime agent process helpers", () => {
|
||||
await expect(pending).resolves.toBe("ok");
|
||||
expect(spawnMock).toHaveBeenCalledWith(
|
||||
"/usr/bin/node",
|
||||
["/repo/dist/index.js", "qa", "suite"],
|
||||
[path.join("/repo", "dist", "index.js"), "qa", "suite"],
|
||||
expect.objectContaining({
|
||||
cwd: "/tmp/runtime",
|
||||
env: { PATH: "/usr/bin" },
|
||||
@@ -134,7 +135,7 @@ describe("qa suite runtime agent process helpers", () => {
|
||||
await expect(pending).resolves.toBe("ok");
|
||||
expect(spawnMock).toHaveBeenCalledWith(
|
||||
"/usr/bin/node",
|
||||
["/repo/dist/index.js", "crestodian", "-m", "overview"],
|
||||
[path.join("/repo", "dist", "index.js"), "crestodian", "-m", "overview"],
|
||||
expect.objectContaining({
|
||||
env: expect.objectContaining({
|
||||
PATH: "/usr/bin",
|
||||
|
||||
Reference in New Issue
Block a user