mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 18:40:43 +00:00
* refactor(qa): add shared transport contract and suite migration * refactor(qa): harden worker gateway startup * fix(qa): scope waits and sanitize shutdown artifacts * fix(qa): confine artifacts and redact preserved logs * fix(qa): block symlink escapes in artifact paths * fix(gateway): clear shutdown race timers * fix(qa): harden shutdown cleanup paths * fix(qa): sanitize gateway logs in thrown errors * fix(qa): harden suite startup and artifact paths * fix(qa): stage bundled plugins from mutated config * fix(qa): broaden gateway log bearer redaction * fix(qa-channel): restore runtime export * fix(qa): stop failed gateway startups as a process tree * fix(qa-channel): load runtime hook from api surface
102 lines
3.3 KiB
TypeScript
102 lines
3.3 KiB
TypeScript
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
|
import type { QaBusState } from "./bus-state.js";
|
|
import { createQaTransportAdapter, type QaTransportId } from "./qa-transport-registry.js";
|
|
import { renderQaMarkdownReport } from "./report.js";
|
|
import { runQaScenario, type QaScenarioResult } from "./scenario.js";
|
|
import { createQaSelfCheckScenario } from "./self-check-scenario.js";
|
|
|
|
export type QaSelfCheckResult = {
|
|
outputPath: string;
|
|
report: string;
|
|
checks: Array<{ name: string; status: "pass" | "fail"; details?: string }>;
|
|
scenarioResult: QaScenarioResult;
|
|
};
|
|
|
|
export function resolveQaSelfCheckOutputPath(params?: { outputPath?: string; repoRoot?: string }) {
|
|
if (params?.outputPath) {
|
|
return params.outputPath;
|
|
}
|
|
const repoRoot = path.resolve(params?.repoRoot ?? process.cwd());
|
|
return path.join(repoRoot, ".artifacts", "qa-e2e", "self-check.md");
|
|
}
|
|
|
|
export async function runQaSelfCheckAgainstState(params: {
|
|
state: QaBusState;
|
|
cfg: OpenClawConfig;
|
|
transportId?: QaTransportId;
|
|
outputPath?: string;
|
|
repoRoot?: string;
|
|
notes?: string[];
|
|
}): Promise<QaSelfCheckResult> {
|
|
const startedAt = new Date();
|
|
const transport = createQaTransportAdapter({
|
|
id: params.transportId ?? "qa-channel",
|
|
state: params.state,
|
|
});
|
|
params.state.reset();
|
|
const scenarioResult = await runQaScenario(createQaSelfCheckScenario(), {
|
|
state: params.state,
|
|
performAction: async (action, args) =>
|
|
await transport.handleAction({
|
|
action,
|
|
args,
|
|
cfg: params.cfg,
|
|
accountId: transport.accountId,
|
|
}),
|
|
});
|
|
const checks = [
|
|
{
|
|
name: "QA self-check scenario",
|
|
status: scenarioResult.status,
|
|
details: `${scenarioResult.steps.filter((step) => step.status === "pass").length}/${scenarioResult.steps.length} steps passed`,
|
|
},
|
|
] satisfies Array<{ name: string; status: "pass" | "fail"; details?: string }>;
|
|
const finishedAt = new Date();
|
|
const snapshot = params.state.getSnapshot();
|
|
const timeline = snapshot.events.map((event) => {
|
|
switch (event.kind) {
|
|
case "thread-created":
|
|
return `${event.cursor}. ${event.kind} ${event.thread.conversationId}/${event.thread.id}`;
|
|
case "reaction-added":
|
|
return `${event.cursor}. ${event.kind} ${event.message.id} ${event.emoji}`;
|
|
default:
|
|
return `${event.cursor}. ${event.kind} ${"message" in event ? event.message.id : ""}`.trim();
|
|
}
|
|
});
|
|
const report = renderQaMarkdownReport({
|
|
title: "OpenClaw QA E2E Self-Check",
|
|
startedAt,
|
|
finishedAt,
|
|
checks,
|
|
scenarios: [
|
|
{
|
|
name: scenarioResult.name,
|
|
status: scenarioResult.status,
|
|
details: scenarioResult.details,
|
|
steps: scenarioResult.steps,
|
|
},
|
|
],
|
|
timeline,
|
|
notes: params.notes ?? [
|
|
"Vertical slice: qa-channel + qa-lab bus + private debugger surface.",
|
|
"Docker orchestration, matrix runs, and auto-fix loops remain follow-up work.",
|
|
],
|
|
});
|
|
|
|
const outputPath = resolveQaSelfCheckOutputPath({
|
|
outputPath: params.outputPath,
|
|
repoRoot: params.repoRoot,
|
|
});
|
|
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
await fs.writeFile(outputPath, report, "utf8");
|
|
|
|
return {
|
|
outputPath,
|
|
report,
|
|
checks,
|
|
scenarioResult,
|
|
};
|
|
}
|