test: share matrix harness fixtures

This commit is contained in:
Peter Steinberger
2026-04-20 20:21:47 +01:00
parent fe30b31a97
commit 50b9526951

View File

@@ -4,6 +4,47 @@ import path from "node:path";
import { describe, expect, it, vi } from "vitest";
import { __testing, startMatrixQaHarness, writeMatrixQaHarnessFiles } from "./harness.runtime.js";
type MatrixQaHarnessDeps = Parameters<typeof startMatrixQaHarness>[1];
type MatrixQaHarnessResult = Awaited<ReturnType<typeof startMatrixQaHarness>>;
async function withStartedMatrixHarness(
deps: MatrixQaHarnessDeps,
verify: (params: { outputDir: string; result: MatrixQaHarnessResult }) => Promise<void> | void,
) {
const outputDir = await mkdtemp(path.join(os.tmpdir(), "matrix-qa-harness-"));
try {
const result = await startMatrixQaHarness(
{
outputDir,
repoRoot: "/repo/openclaw",
homeserverPort: 28008,
},
deps,
);
await verify({ outputDir, result });
} finally {
await rm(outputDir, { recursive: true, force: true });
}
}
function createContainerNetworkRunCommand(calls?: string[]) {
return async function runCommand(command: string, args: string[], cwd?: string) {
calls?.push([command, ...args, `@${cwd}`].join(" "));
const rendered = args.join(" ");
if (rendered.includes("ps --format json")) {
return { stdout: '{"State":"running"}\n', stderr: "" };
}
if (rendered.includes("ps -q")) {
return { stdout: "container-123\n", stderr: "" };
}
if (rendered.includes("inspect --format")) {
return { stdout: "172.18.0.10\n", stderr: "" };
}
return { stdout: "", stderr: "" };
};
}
describe("matrix harness runtime", () => {
it("writes a pinned Tuwunel compose file and redacted manifest", async () => {
const outputDir = await mkdtemp(path.join(os.tmpdir(), "matrix-qa-harness-"));
@@ -46,227 +87,144 @@ describe("matrix harness runtime", () => {
it("starts the harness, waits for versions, and exposes a stop command", async () => {
const calls: string[] = [];
const fetchCalls: string[] = [];
const outputDir = await mkdtemp(path.join(os.tmpdir(), "matrix-qa-harness-"));
try {
const result = await startMatrixQaHarness(
{
outputDir,
repoRoot: "/repo/openclaw",
homeserverPort: 28008,
await withStartedMatrixHarness(
{
async runCommand(command, args, cwd) {
calls.push([command, ...args, `@${cwd}`].join(" "));
if (args.join(" ").includes("ps --format json")) {
return { stdout: '[{"State":"running"}]\n', stderr: "" };
}
return { stdout: "", stderr: "" };
},
{
async runCommand(command, args, cwd) {
calls.push([command, ...args, `@${cwd}`].join(" "));
if (args.join(" ").includes("ps --format json")) {
return { stdout: '[{"State":"running"}]\n', stderr: "" };
}
return { stdout: "", stderr: "" };
},
fetchImpl: vi.fn(async (input: string) => {
fetchCalls.push(input);
return { ok: true };
}),
sleepImpl: vi.fn(async () => {}),
resolveHostPortImpl: vi.fn(async (port: number) => port),
},
);
expect(calls).toEqual([
`docker compose -f ${outputDir}/docker-compose.matrix-qa.yml down --remove-orphans @/repo/openclaw`,
`docker compose -f ${outputDir}/docker-compose.matrix-qa.yml up -d @/repo/openclaw`,
`docker compose -f ${outputDir}/docker-compose.matrix-qa.yml ps --format json matrix-qa-homeserver @/repo/openclaw`,
]);
expect(fetchCalls).toEqual([
"http://127.0.0.1:28008/_matrix/client/versions",
"http://127.0.0.1:28008/_matrix/client/versions",
]);
expect(result.baseUrl).toBe("http://127.0.0.1:28008/");
expect(result.stopCommand).toBe(
`docker compose -f ${outputDir}/docker-compose.matrix-qa.yml down --remove-orphans`,
);
await result.restartService();
expect(calls).toContain(
`docker compose -f ${outputDir}/docker-compose.matrix-qa.yml restart matrix-qa-homeserver @/repo/openclaw`,
);
} finally {
await rm(outputDir, { recursive: true, force: true });
}
fetchImpl: vi.fn(async (input: string) => {
fetchCalls.push(input);
return { ok: true };
}),
sleepImpl: vi.fn(async () => {}),
resolveHostPortImpl: vi.fn(async (port: number) => port),
},
async ({ outputDir, result }) => {
expect(calls).toEqual([
`docker compose -f ${outputDir}/docker-compose.matrix-qa.yml down --remove-orphans @/repo/openclaw`,
`docker compose -f ${outputDir}/docker-compose.matrix-qa.yml up -d @/repo/openclaw`,
`docker compose -f ${outputDir}/docker-compose.matrix-qa.yml ps --format json matrix-qa-homeserver @/repo/openclaw`,
]);
expect(fetchCalls).toEqual([
"http://127.0.0.1:28008/_matrix/client/versions",
"http://127.0.0.1:28008/_matrix/client/versions",
]);
expect(result.baseUrl).toBe("http://127.0.0.1:28008/");
expect(result.stopCommand).toBe(
`docker compose -f ${outputDir}/docker-compose.matrix-qa.yml down --remove-orphans`,
);
await result.restartService();
expect(calls).toContain(
`docker compose -f ${outputDir}/docker-compose.matrix-qa.yml restart matrix-qa-homeserver @/repo/openclaw`,
);
},
);
});
it("treats empty Docker health fields as a fallback to running state", async () => {
const outputDir = await mkdtemp(path.join(os.tmpdir(), "matrix-qa-harness-"));
try {
const result = await startMatrixQaHarness(
{
outputDir,
repoRoot: "/repo/openclaw",
homeserverPort: 28008,
await withStartedMatrixHarness(
{
async runCommand(_command, args) {
if (args.join(" ").includes("ps --format json")) {
return { stdout: '{"Health":"","State":"running"}\n', stderr: "" };
}
return { stdout: "", stderr: "" };
},
{
async runCommand(_command, args) {
if (args.join(" ").includes("ps --format json")) {
return { stdout: '{"Health":"","State":"running"}\n', stderr: "" };
}
return { stdout: "", stderr: "" };
},
fetchImpl: vi.fn(async () => ({ ok: true })),
sleepImpl: vi.fn(async () => {}),
resolveHostPortImpl: vi.fn(async (port: number) => port),
},
);
expect(result.baseUrl).toBe("http://127.0.0.1:28008/");
} finally {
await rm(outputDir, { recursive: true, force: true });
}
fetchImpl: vi.fn(async () => ({ ok: true })),
sleepImpl: vi.fn(async () => {}),
resolveHostPortImpl: vi.fn(async (port: number) => port),
},
({ result }) => {
expect(result.baseUrl).toBe("http://127.0.0.1:28008/");
},
);
});
it("falls back to the container IP when the host port is unreachable", async () => {
const calls: string[] = [];
const outputDir = await mkdtemp(path.join(os.tmpdir(), "matrix-qa-harness-"));
try {
const result = await startMatrixQaHarness(
{
outputDir,
repoRoot: "/repo/openclaw",
homeserverPort: 28008,
},
{
async runCommand(command, args, cwd) {
calls.push([command, ...args, `@${cwd}`].join(" "));
const rendered = args.join(" ");
if (rendered.includes("ps --format json")) {
return { stdout: '{"State":"running"}\n', stderr: "" };
}
if (rendered.includes("ps -q")) {
return { stdout: "container-123\n", stderr: "" };
}
if (rendered.includes("inspect --format")) {
return { stdout: "172.18.0.10\n", stderr: "" };
}
return { stdout: "", stderr: "" };
},
fetchImpl: vi.fn(async (input: string) => ({
ok: input.startsWith("http://172.18.0.10:8008/"),
})),
sleepImpl: vi.fn(async () => {}),
resolveHostPortImpl: vi.fn(async (port: number) => port),
},
);
expect(result.baseUrl).toBe("http://172.18.0.10:8008/");
expect(calls).toContain(
`docker compose -f ${outputDir}/docker-compose.matrix-qa.yml ps -q matrix-qa-homeserver @/repo/openclaw`,
);
expect(calls).toContain(
"docker inspect --format {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}} container-123 @/repo/openclaw",
);
} finally {
await rm(outputDir, { recursive: true, force: true });
}
await withStartedMatrixHarness(
{
runCommand: createContainerNetworkRunCommand(calls),
fetchImpl: vi.fn(async (input: string) => ({
ok: input.startsWith("http://172.18.0.10:8008/"),
})),
sleepImpl: vi.fn(async () => {}),
resolveHostPortImpl: vi.fn(async (port: number) => port),
},
({ outputDir, result }) => {
expect(result.baseUrl).toBe("http://172.18.0.10:8008/");
expect(calls).toContain(
`docker compose -f ${outputDir}/docker-compose.matrix-qa.yml ps -q matrix-qa-homeserver @/repo/openclaw`,
);
expect(calls).toContain(
"docker inspect --format {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}} container-123 @/repo/openclaw",
);
},
);
});
it("keeps the host URL when the container IP is also unreachable", async () => {
const fetchCalls: string[] = [];
const outputDir = await mkdtemp(path.join(os.tmpdir(), "matrix-qa-harness-"));
try {
const result = await startMatrixQaHarness(
{
outputDir,
repoRoot: "/repo/openclaw",
homeserverPort: 28008,
},
{
async runCommand(_command, args) {
const rendered = args.join(" ");
if (rendered.includes("ps --format json")) {
return { stdout: '{"State":"running"}\n', stderr: "" };
}
if (rendered.includes("ps -q")) {
return { stdout: "container-123\n", stderr: "" };
}
if (rendered.includes("inspect --format")) {
return { stdout: "172.18.0.10\n", stderr: "" };
}
return { stdout: "", stderr: "" };
},
fetchImpl: vi.fn(async (input: string) => {
fetchCalls.push(input);
return {
ok:
input === "http://127.0.0.1:28008/_matrix/client/versions" &&
fetchCalls.filter((url) => url === input).length > 1,
};
}),
sleepImpl: vi.fn(async () => {}),
resolveHostPortImpl: vi.fn(async (port: number) => port),
},
);
expect(result.baseUrl).toBe("http://127.0.0.1:28008/");
expect(fetchCalls).toEqual([
"http://127.0.0.1:28008/_matrix/client/versions",
"http://127.0.0.1:28008/_matrix/client/versions",
"http://127.0.0.1:28008/_matrix/client/versions",
]);
} finally {
await rm(outputDir, { recursive: true, force: true });
}
await withStartedMatrixHarness(
{
runCommand: createContainerNetworkRunCommand(),
fetchImpl: vi.fn(async (input: string) => {
fetchCalls.push(input);
return {
ok:
input === "http://127.0.0.1:28008/_matrix/client/versions" &&
fetchCalls.filter((url) => url === input).length > 1,
};
}),
sleepImpl: vi.fn(async () => {}),
resolveHostPortImpl: vi.fn(async (port: number) => port),
},
({ result }) => {
expect(result.baseUrl).toBe("http://127.0.0.1:28008/");
expect(fetchCalls).toEqual([
"http://127.0.0.1:28008/_matrix/client/versions",
"http://127.0.0.1:28008/_matrix/client/versions",
"http://127.0.0.1:28008/_matrix/client/versions",
]);
},
);
});
it("keeps probing the container URL until it becomes reachable", async () => {
const fetchCalls: string[] = [];
const outputDir = await mkdtemp(path.join(os.tmpdir(), "matrix-qa-harness-"));
try {
const result = await startMatrixQaHarness(
{
outputDir,
repoRoot: "/repo/openclaw",
homeserverPort: 28008,
},
{
async runCommand(_command, args) {
const rendered = args.join(" ");
if (rendered.includes("ps --format json")) {
return { stdout: '{"State":"running"}\n', stderr: "" };
}
if (rendered.includes("ps -q")) {
return { stdout: "container-123\n", stderr: "" };
}
if (rendered.includes("inspect --format")) {
return { stdout: "172.18.0.10\n", stderr: "" };
}
return { stdout: "", stderr: "" };
},
fetchImpl: vi.fn(async (input: string) => {
fetchCalls.push(input);
return {
ok:
input === "http://172.18.0.10:8008/_matrix/client/versions" &&
fetchCalls.filter((url) => url === input).length > 1,
};
}),
sleepImpl: vi.fn(async () => {}),
resolveHostPortImpl: vi.fn(async (port: number) => port),
},
);
expect(result.baseUrl).toBe("http://172.18.0.10:8008/");
expect(fetchCalls).toEqual([
"http://127.0.0.1:28008/_matrix/client/versions",
"http://127.0.0.1:28008/_matrix/client/versions",
"http://172.18.0.10:8008/_matrix/client/versions",
"http://127.0.0.1:28008/_matrix/client/versions",
"http://172.18.0.10:8008/_matrix/client/versions",
"http://172.18.0.10:8008/_matrix/client/versions",
]);
} finally {
await rm(outputDir, { recursive: true, force: true });
}
await withStartedMatrixHarness(
{
runCommand: createContainerNetworkRunCommand(),
fetchImpl: vi.fn(async (input: string) => {
fetchCalls.push(input);
return {
ok:
input === "http://172.18.0.10:8008/_matrix/client/versions" &&
fetchCalls.filter((url) => url === input).length > 1,
};
}),
sleepImpl: vi.fn(async () => {}),
resolveHostPortImpl: vi.fn(async (port: number) => port),
},
({ result }) => {
expect(result.baseUrl).toBe("http://172.18.0.10:8008/");
expect(fetchCalls).toEqual([
"http://127.0.0.1:28008/_matrix/client/versions",
"http://127.0.0.1:28008/_matrix/client/versions",
"http://172.18.0.10:8008/_matrix/client/versions",
"http://127.0.0.1:28008/_matrix/client/versions",
"http://172.18.0.10:8008/_matrix/client/versions",
"http://172.18.0.10:8008/_matrix/client/versions",
]);
},
);
});
});