feat(api): expose runtime profile helpers
This commit is contained in:
parent
441a44691b
commit
36eb577373
30
src/index.js
30
src/index.js
@ -65,7 +65,37 @@ export {
|
||||
renderExecutionResultsMarkdown,
|
||||
writeExecutionResultsReport,
|
||||
} from "./execution-results.js";
|
||||
export {
|
||||
buildImportLoopProfile,
|
||||
defaultImportLoopProfileOptions,
|
||||
renderImportLoopProfileMarkdown,
|
||||
validateImportLoopProfile,
|
||||
writeImportLoopProfile,
|
||||
} from "./import-loop-profile.js";
|
||||
export { classifyIssueFinding, issueId, knownIssueCodes } from "./issues.js";
|
||||
export { inspectFixtureSet, inspectPlugin, inspectSourceText } from "./inspector.js";
|
||||
export { openClawTargetPathCandidates } from "./openclaw-target.js";
|
||||
export {
|
||||
buildProfileDiff,
|
||||
defaultProfileDiffOptions,
|
||||
renderProfileDiffMarkdown,
|
||||
validateProfileDiff,
|
||||
writeProfileDiff,
|
||||
} from "./profile-diff.js";
|
||||
export {
|
||||
buildRefDiff,
|
||||
defaultRefDiffDimensions,
|
||||
defaultRefDiffOptions,
|
||||
renderRefDiffMarkdown,
|
||||
validateRefDiff,
|
||||
writeRefDiff,
|
||||
} from "./ref-diff.js";
|
||||
export { renderMarkdownReport, writeReport } from "./report.js";
|
||||
export {
|
||||
buildRuntimeProfile,
|
||||
defaultRuntimeProfileCommands,
|
||||
defaultRuntimeProfileOptions,
|
||||
renderRuntimeProfileMarkdown,
|
||||
validateRuntimeProfile,
|
||||
writeRuntimeProfile,
|
||||
} from "./runtime-profile.js";
|
||||
|
||||
150
test/api.test.js
150
test/api.test.js
@ -9,8 +9,12 @@ import {
|
||||
buildContractCapture,
|
||||
buildExecutionResultsReport,
|
||||
buildFixtureSetColdImportReadiness,
|
||||
buildImportLoopProfile,
|
||||
buildFixtureSetPlatformProbes,
|
||||
buildFixtureSetWorkspacePlan,
|
||||
buildProfileDiff,
|
||||
buildRefDiff,
|
||||
buildRuntimeProfile,
|
||||
capturePluginEntrypoint,
|
||||
classifyIssueFinding,
|
||||
inspectFixtureSet,
|
||||
@ -27,11 +31,15 @@ import {
|
||||
renderCiSummaryMarkdown,
|
||||
renderContractCaptureMarkdown,
|
||||
renderExecutionResultsMarkdown,
|
||||
renderImportLoopProfileMarkdown,
|
||||
renderMarkdownReport,
|
||||
renderFixtureSetColdImportReadinessMarkdown,
|
||||
renderFixtureSetIssuesReport,
|
||||
renderFixtureSetPlatformProbesMarkdown,
|
||||
renderFixtureSetWorkspacePlanMarkdown,
|
||||
renderProfileDiffMarkdown,
|
||||
renderRefDiffMarkdown,
|
||||
renderRuntimeProfileMarkdown,
|
||||
runFixtureSetColdImportReadiness,
|
||||
runFixtureSetPlatformProbes,
|
||||
runFixtureSetReport,
|
||||
@ -45,6 +53,10 @@ import {
|
||||
validateColdImportReadiness,
|
||||
validateFixtureSetPlatformProbes,
|
||||
validateFixtureSetWorkspacePlan,
|
||||
validateImportLoopProfile,
|
||||
validateProfileDiff,
|
||||
validateRefDiff,
|
||||
validateRuntimeProfile,
|
||||
writeFixtureSetColdImportReadiness,
|
||||
writeFixtureSetPlatformProbes,
|
||||
writeReport,
|
||||
@ -54,6 +66,10 @@ import {
|
||||
writeCiPolicyReport,
|
||||
writeCiSummary,
|
||||
writeExecutionResultsReport,
|
||||
writeImportLoopProfile,
|
||||
writeProfileDiff,
|
||||
writeRefDiff,
|
||||
writeRuntimeProfile,
|
||||
} from "../src/index.js";
|
||||
|
||||
test("public API runs the plugin-root check and writes reports", async () => {
|
||||
@ -401,6 +417,89 @@ test("public API exposes execution and CI rollup helpers", async () => {
|
||||
assert.equal(JSON.parse(await readFile(summaryPaths.jsonPath, "utf8")).status, "pass");
|
||||
});
|
||||
|
||||
test("public API exposes runtime profile and diff helpers", async () => {
|
||||
const outDir = await mkdtemp(path.join(os.tmpdir(), "plugin-inspector-profile-api-"));
|
||||
const runtimeProfile = await buildRuntimeProfile({ runs: 1 });
|
||||
const profileDiff = await buildProfileDiff({
|
||||
baseline: profileFixture({ p95WallMs: 100, maxPeakRssMb: 80, nodeBootMs: 25 }),
|
||||
current: profileFixture({ p95WallMs: 120, maxPeakRssMb: 90, nodeBootMs: 30 }),
|
||||
policy: {
|
||||
thresholds: {
|
||||
wallP95RegressionPercent: 50,
|
||||
peakRssRegressionMb: 50,
|
||||
bootRegressionMs: 500,
|
||||
strictMinimumSamples: 3,
|
||||
},
|
||||
},
|
||||
});
|
||||
const refDiff = await buildRefDiff({
|
||||
baseReport: diffReport({ hookNames: ["before_tool_call"], issues: [] }),
|
||||
headReport: diffReport({ hookNames: ["before_tool_call"], issues: [] }),
|
||||
});
|
||||
const importLoopProfile = {
|
||||
generatedAt: "deterministic",
|
||||
mode: "subprocess-cold-import-loop",
|
||||
entrypoint: "fixtures/plugin.mjs",
|
||||
summary: {
|
||||
runs: 1,
|
||||
p50WallMs: 5,
|
||||
p95WallMs: 5,
|
||||
maxPeakRssMb: 10,
|
||||
maxCpuMsEstimate: 2,
|
||||
capturedCount: 1,
|
||||
failCount: 0,
|
||||
},
|
||||
samples: [
|
||||
{
|
||||
index: 0,
|
||||
status: "captured",
|
||||
capturedCount: 1,
|
||||
wallMs: 5,
|
||||
peakRssMb: 10,
|
||||
cpuMsEstimate: 2,
|
||||
exitCode: 0,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
assert.equal(typeof buildImportLoopProfile, "function");
|
||||
const portableRuntimeProfile = {
|
||||
...runtimeProfile,
|
||||
platform: { ...runtimeProfile.platform, rssSampler: "unavailable" },
|
||||
};
|
||||
|
||||
assert.deepEqual(validateRuntimeProfile(portableRuntimeProfile), []);
|
||||
assert.deepEqual(validateProfileDiff(profileDiff), []);
|
||||
assert.deepEqual(validateRefDiff(refDiff), []);
|
||||
assert.deepEqual(validateImportLoopProfile(importLoopProfile), []);
|
||||
assert.match(renderRuntimeProfileMarkdown(portableRuntimeProfile), /Runtime Profile/);
|
||||
assert.match(renderProfileDiffMarkdown(profileDiff), /Runtime Profile Diff/);
|
||||
assert.match(renderRefDiffMarkdown(refDiff), /Ref Diff/);
|
||||
assert.match(renderImportLoopProfileMarkdown(importLoopProfile), /Import Loop Profile/);
|
||||
|
||||
const runtimePaths = await writeRuntimeProfile(portableRuntimeProfile, {
|
||||
jsonPath: path.join(outDir, "runtime.json"),
|
||||
markdownPath: path.join(outDir, "runtime.md"),
|
||||
});
|
||||
const profileDiffPaths = await writeProfileDiff(profileDiff, {
|
||||
jsonPath: path.join(outDir, "profile-diff.json"),
|
||||
markdownPath: path.join(outDir, "profile-diff.md"),
|
||||
});
|
||||
const refDiffPaths = await writeRefDiff(refDiff, {
|
||||
jsonPath: path.join(outDir, "ref-diff.json"),
|
||||
markdownPath: path.join(outDir, "ref-diff.md"),
|
||||
});
|
||||
const importLoopPaths = await writeImportLoopProfile(importLoopProfile, {
|
||||
jsonPath: path.join(outDir, "import-loop.json"),
|
||||
markdownPath: path.join(outDir, "import-loop.md"),
|
||||
});
|
||||
|
||||
assert.equal(JSON.parse(await readFile(runtimePaths.jsonPath, "utf8")).summary.commandCount, 1);
|
||||
assert.equal(JSON.parse(await readFile(profileDiffPaths.jsonPath, "utf8")).status, "pass");
|
||||
assert.equal(JSON.parse(await readFile(refDiffPaths.jsonPath, "utf8")).status, "pass");
|
||||
assert.equal(JSON.parse(await readFile(importLoopPaths.jsonPath, "utf8")).summary.runs, 1);
|
||||
});
|
||||
|
||||
test("public API honors config-driven runtime capture", async () => {
|
||||
const pluginRoot = await createPluginRoot();
|
||||
await writeFile(
|
||||
@ -413,6 +512,57 @@ test("public API honors config-driven runtime capture", async () => {
|
||||
assert.equal(result.runtimeCapture.summary.registrationCount, 1);
|
||||
});
|
||||
|
||||
function profileFixture({ p95WallMs, maxPeakRssMb, nodeBootMs }) {
|
||||
return {
|
||||
runs: 3,
|
||||
summary: { p95WallMs, maxPeakRssMb },
|
||||
targetOpenClaw: {
|
||||
compatRecords: 1,
|
||||
hookNames: 1,
|
||||
apiRegistrars: 1,
|
||||
capturedRegistrars: 1,
|
||||
sdkExports: 1,
|
||||
manifestFields: 1,
|
||||
manifestContractFields: 1,
|
||||
},
|
||||
fixtureInventory: {},
|
||||
commands: [{ id: "node-boot", wallMs: { median: nodeBootMs } }],
|
||||
};
|
||||
}
|
||||
|
||||
function diffReport({ hookNames, issues }) {
|
||||
return {
|
||||
summary: {
|
||||
fixtureCount: 1,
|
||||
breakageCount: 0,
|
||||
issueCount: issues.length,
|
||||
p0IssueCount: issues.filter((issue) => issue.severity === "P0").length,
|
||||
p1IssueCount: issues.filter((issue) => issue.severity === "P1").length,
|
||||
},
|
||||
targetOpenClaw: {
|
||||
status: "available",
|
||||
compatRecords: [],
|
||||
hookNames,
|
||||
apiRegistrars: ["registerTool"],
|
||||
capturedRegistrars: ["registerTool"],
|
||||
sdkExports: ["definePluginEntry"],
|
||||
manifestFields: ["name"],
|
||||
manifestContractFields: ["permissions"],
|
||||
},
|
||||
fixtures: [
|
||||
{
|
||||
id: "weather",
|
||||
hooks: hookNames,
|
||||
registrations: ["registerTool"],
|
||||
sdkImports: ["definePluginEntry"],
|
||||
pluginManifests: [{ name: "weather" }],
|
||||
manifestContracts: ["permissions"],
|
||||
},
|
||||
],
|
||||
issues,
|
||||
};
|
||||
}
|
||||
|
||||
async function createPluginRoot(options = {}) {
|
||||
const rootDir = await mkdtemp(path.join(os.tmpdir(), "plugin-inspector-api-root-"));
|
||||
await mkdir(path.join(rootDir, "src"), { recursive: true });
|
||||
|
||||
Loading…
Reference in New Issue
Block a user