plugin-inspector/test/ci-summary.test.js
2026-05-02 22:43:55 -07:00

179 lines
6.3 KiB
JavaScript

import assert from "node:assert/strict";
import { mkdtemp, mkdir, readFile, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { test } from "node:test";
import { buildCiSummary, deriveCiStatus, renderCiSummaryMarkdown, writeCiSummary } from "../src/advanced.js";
test("ci summary rolls up compatibility, policy, ref diff, and profile findings", async () => {
const summary = await buildCiSummary({
title: "Crabpot CI Summary",
mode: "full",
openclawLabel: "openclaw/openclaw@main",
reports: {
compatibility: {
summary: {
breakageCount: 0,
warningCount: 2,
suggestionCount: 3,
issueCount: 4,
p0IssueCount: 1,
p1IssueCount: 0,
liveIssueCount: 1,
compatGapCount: 1,
},
issues: [
{
severity: "P2",
issueClass: "inspector-gap",
fixture: "fixture",
code: "registration-capture-gap",
title: "runtime registrations need capture evidence",
decision: "inspector-follow-up",
},
],
},
refDiff: {
summary: {
hardRegressionCount: 0,
warningRegressionCount: 1,
},
regressions: [
{
action: "warn",
severity: "P3",
dimension: "sdkExports",
code: "sdkExports.removed-unused",
message: "unused export removed",
},
],
},
ciPolicy: {
summary: {
failCount: 0,
warnCount: 1,
},
checks: [
{
action: "warn",
id: "execution-results.blocked.fixture.registerChannel.0",
message: "allowed-blocked",
evidence: ["registerChannel"],
},
],
},
profileDiff: {
summary: {
warnCount: 1,
},
checks: [
{
action: "warn",
id: "profile.wall-p95",
metric: "p95WallMs",
baseline: 100,
current: 200,
message: "p95 regressed",
},
],
},
execution: {
summary: {
passCount: 6,
failCount: 0,
blockedCount: 2,
},
},
platform: {
summary: {
windowsRiskStepCount: 3,
containerRiskStepCount: 2,
jitiAlternativeCount: 1,
},
},
importLoop: {
summary: {
p50WallMs: 50,
p95WallMs: 75,
maxPeakRssMb: 40,
maxCpuMsEstimate: 30,
maxPluginPeakRssDeltaMb: 8,
maxPluginCpuDeltaMsEstimate: 6,
openClawLifecycleCount: 2,
p50OpenClawImportMs: 12,
p50OpenClawActivationMs: 3,
rssSampleCount: 2,
cpuSampleCount: 2,
},
},
},
});
assert.equal(summary.status, "pass");
assert.equal(summary.summary.p0Issues, 1);
assert.equal(summary.summary.policyWarnings, 1);
assert.equal(summary.summary.platformWindowsRisks, 3);
assert.equal(summary.summary.loaderJitiCandidates, 1);
assert.equal(summary.summary.importLoopP50Ms, 50);
assert.equal(summary.summary.importLoopMetricBasis, "baseline-adjusted");
assert.equal(summary.summary.importLoopMaxRssMb, 8);
assert.equal(summary.summary.importLoopOpenClawImportP50Ms, 12);
assert.match(renderCiSummaryMarkdown(summary), /Crabpot CI Summary/);
assert.match(renderCiSummaryMarkdown(summary), /Windows portability risks/);
assert.match(renderCiSummaryMarkdown(summary), /p50 50 ms \/ p95 75 ms \/ plugin delta RSS 8 MB \/ plugin delta CPU 6 ms \/ OpenClaw import 12 ms \/ activate 3 ms/);
assert.match(renderCiSummaryMarkdown(summary), /\| P0 issues\s+\| 1\s+\|/);
});
test("ci summary status fails on blocking report classes", () => {
assert.equal(deriveCiStatus({ compatibility: { summary: { breakageCount: 1 } } }), "fail");
assert.equal(deriveCiStatus({ refDiff: { summary: { hardRegressionCount: 1 } } }), "fail");
assert.equal(deriveCiStatus({ ciPolicy: { summary: { failCount: 1 } } }), "fail");
assert.equal(deriveCiStatus({ profileDiff: { summary: { failCount: 1 } } }), "fail");
assert.equal(deriveCiStatus({ execution: { summary: { failCount: 1 } } }), "fail");
assert.equal(deriveCiStatus({}), "pass");
});
test("ci summary discovers runtime capture artifacts from default paths", async () => {
const dir = await mkdtemp(path.join(os.tmpdir(), "plugin-inspector-ci-summary-runtime-capture-"));
const reportsDir = path.join(dir, "reports");
await mkdir(reportsDir, { recursive: true });
await writeFile(
path.join(reportsDir, "plugin-inspector-report.json"),
`${JSON.stringify({ summary: { breakageCount: 0, warningCount: 0, suggestionCount: 0, issueCount: 0, p0IssueCount: 0, p1IssueCount: 0, liveIssueCount: 0, liveP0IssueCount: 0, compatGapCount: 0, deprecationWarningCount: 0, inspectorGapCount: 0, upstreamIssueCount: 0 }, issues: [] }, null, 2)}\n`,
"utf8",
);
await writeFile(
path.join(reportsDir, "plugin-inspector-runtime-capture.json"),
`${JSON.stringify({ summary: { targetCount: 1, capturedCount: 1, skippedCount: 0, failedCount: 0, registrationCount: 2, hookCount: 0 } }, null, 2)}\n`,
"utf8",
);
const summary = await buildCiSummary({ reportsDir, artifactBaseDir: dir });
assert.equal(summary.artifacts.compatibility, "reports/plugin-inspector-report.json");
assert.equal(summary.artifacts.capture, "reports/plugin-inspector-runtime-capture.json");
});
test("ci summary writer emits JSON and Markdown artifacts", async () => {
const dir = await mkdtemp(path.join(os.tmpdir(), "plugin-inspector-ci-summary-"));
const jsonPath = path.join(dir, "summary.json");
const markdownPath = path.join(dir, "summary.md");
const summary = await buildCiSummary({
mode: "isolated",
openclawLabel: "openclaw/openclaw@fixture",
reports: {
execution: {
summary: {
passCount: 1,
failCount: 0,
blockedCount: 0,
},
},
},
});
assert.deepEqual(await writeCiSummary(summary, { jsonPath, markdownPath }), { jsonPath, markdownPath });
assert.equal(JSON.parse(await readFile(jsonPath, "utf8")).mode, "isolated");
assert.match(await readFile(markdownPath, "utf8"), /openclaw\/openclaw@fixture/);
});