Files
pikasTech-unidesk/scripts/src/hwlab-node-web-observe-runner-trace-readability-source.test.ts
T
2026-07-10 19:23:36 +02:00

214 lines
8.3 KiB
TypeScript

import assert from "node:assert/strict";
import { test } from "bun:test";
import { nodeWebObserveRunnerSource } from "./hwlab-node-web-observe-runner-source";
import { nodeWebObserveRunnerTraceReadabilitySource } from "./hwlab-node-web-observe-runner-trace-readability-source";
test("Workbench 产品 Trace 可读性源码解析独立 YAML 时序并识别终态", () => {
const source = nodeWebObserveRunnerTraceReadabilitySource();
const build = new Function(`${source}\nreturn { parseWorkbenchTraceReadabilityConfig, workbenchTraceReadabilityTerminalStatus, workbenchTraceReadabilityFailures, workbenchTraceReadabilityRetention };`) as () => {
parseWorkbenchTraceReadabilityConfig: (raw: string) => Record<string, unknown>;
workbenchTraceReadabilityTerminalStatus: (value: string) => boolean;
workbenchTraceReadabilityFailures: (value: Record<string, unknown>) => Array<Record<string, unknown>>;
workbenchTraceReadabilityRetention: (running: Record<string, unknown>, terminal: Record<string, unknown>) => Record<string, unknown>;
};
const helpers = build();
assert.deepEqual(helpers.parseWorkbenchTraceReadabilityConfig(JSON.stringify({
enabled: true,
runningCardTimeoutMs: 15_000,
disclosureActionTimeoutMs: 15_000,
disclosureOpenTimeoutMs: 10_000,
runningReadableTimeoutMs: 480_000,
terminalTimeoutMs: 480_000,
domStableQuietMs: 2_000,
domStableTimeoutMs: 6_000,
pollIntervalMs: 100,
})), {
enabled: true,
runningCardTimeoutMs: 15_000,
disclosureActionTimeoutMs: 15_000,
disclosureOpenTimeoutMs: 10_000,
runningReadableTimeoutMs: 480_000,
terminalTimeoutMs: 480_000,
domStableQuietMs: 2_000,
domStableTimeoutMs: 6_000,
pollIntervalMs: 100,
valuesRedacted: true,
});
assert.equal(helpers.workbenchTraceReadabilityTerminalStatus("completed"), true);
assert.equal(helpers.workbenchTraceReadabilityTerminalStatus("running"), false);
assert.deepEqual(helpers.workbenchTraceReadabilityFailures({
scope: { conversationCount: 1, debugPanelCountInsideConversation: 0 },
startedDuringRunning: true,
disclosure: { openBefore: true, autoReadable: true },
running: { observed: true, snapshot: { rowCount: 2, sourceAuthorityRowCount: 2, projectedAuthorityRowCount: 0 } },
terminalArrival: { observed: true },
terminal: {
stable: true,
snapshot: {
disclosure: { open: true },
rowCount: 3,
sourceAuthorityRowCount: 3,
readableSourceRowCount: 3,
projectedAuthorityRowCount: 0,
},
},
runningRetention: { passed: true },
retainedRunningRowCount: 2,
}), []);
assert.deepEqual(
helpers.workbenchTraceReadabilityFailures({
scope: { conversationCount: 1, debugPanelCountInsideConversation: 0 },
startedDuringRunning: true,
disclosure: { openBefore: false, autoReadable: false },
running: { observed: true, snapshot: { rowCount: 2, sourceAuthorityRowCount: 2, projectedAuthorityRowCount: 0 } },
terminalArrival: { observed: true },
terminal: { stable: true, snapshot: { disclosure: { open: true }, rowCount: 2, sourceAuthorityRowCount: 2, readableSourceRowCount: 2, projectedAuthorityRowCount: 0 } },
runningRetention: { passed: true },
retainedRunningRowCount: 1,
}).map((failure) => failure.code),
["trace_not_auto_expanded"],
);
assert.deepEqual(helpers.workbenchTraceReadabilityRetention({
rowIdentities: ["source:1:tool"],
disclosure: { eventCount: 121 },
sourceSeqMin: 1,
sourceSeqMax: 120,
}, {
rowIdentities: ["source:121:terminal"],
disclosure: { eventCount: 122, rowWindowed: true },
sourceSeqMin: 2,
sourceSeqMax: 121,
}), {
mode: "window-progress",
passed: true,
retainedRowCount: 0,
retainedConcreteRowCount: 0,
runningEventCount: 121,
terminalEventCount: 122,
runningSourceSeqMin: 1,
runningSourceSeqMax: 120,
terminalSourceSeqMin: 2,
terminalSourceSeqMax: 121,
valuesRedacted: true,
});
assert.deepEqual(helpers.workbenchTraceReadabilityRetention({
rowIdentities: ["source:18:trace-noise-summary"],
disclosure: { eventCount: 25, rowWindowed: false },
sourceSeqMin: 18,
sourceSeqMax: 18,
}, {
rowIdentities: ["source:21:event:evt_fixture", "source:42:terminal:evt_terminal"],
disclosure: { eventCount: 50, rowWindowed: false },
sourceSeqMin: 21,
sourceSeqMax: 42,
}), {
mode: "summary-progress",
passed: true,
retainedRowCount: 0,
retainedConcreteRowCount: 0,
runningEventCount: 25,
terminalEventCount: 50,
runningSourceSeqMin: 18,
runningSourceSeqMax: 18,
terminalSourceSeqMin: 21,
terminalSourceSeqMax: 42,
valuesRedacted: true,
});
assert.deepEqual(helpers.workbenchTraceReadabilityRetention({
rowIdentities: ["source:18:event:evt_running"],
disclosure: { eventCount: 25, rowWindowed: false },
sourceSeqMin: 18,
sourceSeqMax: 18,
}, {
rowIdentities: ["source:21:event:evt_terminal"],
disclosure: { eventCount: 50, rowWindowed: false },
sourceSeqMin: 21,
sourceSeqMax: 21,
}), {
mode: "identity-overlap",
passed: false,
retainedRowCount: 0,
retainedConcreteRowCount: 0,
valuesRedacted: true,
});
});
test("运行卡片命中后使用 traceId 稳定定位,不随状态变更漂移", async () => {
const source = nodeWebObserveRunnerTraceReadabilitySource();
const runningTimeline = {
status: "running",
getAttribute(name: string) {
return name === "data-status" ? this.status : null;
},
};
const cardElement = (traceId: string, timeline: typeof runningTimeline) => ({
getAttribute(name: string) {
return name === "data-trace-id" ? traceId : null;
},
querySelector() {
return timeline;
},
});
const selectedTraceId = "trc_current";
const stableCard = {
async count() { return 1; },
locator() { return { count: async () => 1 }; },
async getAttribute(name: string) { return name === "data-trace-id" ? selectedTraceId : null; },
};
const cards = {
async count() { return 2; },
async evaluateAll(callback: (elements: unknown[]) => unknown) {
return callback([
cardElement("trc_old", { ...runningTimeline, status: "completed" }),
cardElement(selectedTraceId, runningTimeline),
]);
},
};
const conversation = {
locator(selector: string) {
if (selector === ':scope > .message-card[data-role="agent"][data-trace-id]') return cards;
if (selector === '[data-testid="workbench-kafka-debug-panel"]') return { count: async () => 0 };
if (selector.includes(`[data-trace-id="${selectedTraceId}"]`)) return { first: () => stableCard };
throw new Error(`unexpected selector: ${selector}`);
},
};
const conversationRoot = {
first: () => conversation,
count: async () => 1,
};
const page = {
locator(selector: string) {
if (selector === "#conversation-list") return conversationRoot;
throw new Error(`unexpected page selector: ${selector}`);
},
};
const build = new Function("page", "sleep", `${source}\nreturn { workbenchTraceReadabilityWaitForCurrentProductCard };`) as (
pageValue: typeof page,
sleepValue: () => Promise<void>,
) => {
workbenchTraceReadabilityWaitForCurrentProductCard: (timeoutMs: number, pollIntervalMs: number) => Promise<Record<string, any>>;
};
const helpers = build(page, async () => {});
const target = await helpers.workbenchTraceReadabilityWaitForCurrentProductCard(1_000, 1);
runningTimeline.status = "completed";
assert.equal(target.traceId, selectedTraceId);
assert.equal(target.card, stableCard);
assert.equal(await target.card.getAttribute("data-trace-id"), selectedTraceId);
});
test("生成的 observer runner 包含产品 Trace 命令并通过 Node 语法检查", async () => {
const source = nodeWebObserveRunnerSource();
assert.match(source, /validateWorkbenchTraceReadability/u);
assert.match(source, /#conversation-list/u);
assert.match(source, /debugPanelCountInsideConversation/u);
assert.match(source, /trace_not_auto_expanded/u);
const child = Bun.spawn(["node", "--input-type=module", "--check", "-"], { stdin: "pipe", stdout: "pipe", stderr: "pipe" });
child.stdin.write(source);
child.stdin.end();
const [stderr, exitCode] = await Promise.all([new Response(child.stderr).text(), child.exited]);
assert.equal(exitCode, 0, stderr);
});