mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-09-25 02:40:56 +02:00
feat: add configurable tool call blocking and monitoring
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const vm = require('node:vm');
|
||||
|
||||
const source = fs.readFileSync('web/static/js/monitor.js', 'utf8');
|
||||
const statsSource = source.slice(source.indexOf('const MCP_STATS_TOP_N'), source.indexOf('function renderMonitorExecutions('));
|
||||
|
||||
function harness() {
|
||||
const container = { innerHTML: '' };
|
||||
const escapeHtml = (value) => String(value).replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
||||
const context = vm.createContext({
|
||||
window: { __locale: 'zh-CN' },
|
||||
document: { getElementById: (id) => id === 'monitor-stats' ? container : null },
|
||||
localStorage: { getItem: () => null },
|
||||
monitorState: {},
|
||||
escapeHtml,
|
||||
escapeAttrLocal: escapeHtml,
|
||||
formatMonitorToolName: (name) => name,
|
||||
monitorToolNamesEqual: (a, b) => a === b,
|
||||
});
|
||||
vm.runInContext(statsSource, context);
|
||||
Object.assign(context, {
|
||||
bindMonitorStatsPanelEvents() {},
|
||||
bindMcpStatsTimelineEvents() {},
|
||||
updateMonitorStatsSubtitle() {},
|
||||
});
|
||||
return { context, container };
|
||||
}
|
||||
|
||||
test('安全拦截计入总调用量,但不归入失败或终止', () => {
|
||||
const { context } = harness();
|
||||
const totals = context.buildMonitorTotals({ totalCalls: 10, successCalls: 4, failedCalls: 1, blockedCalls: 3 });
|
||||
assert.deepEqual({ ...totals }, { total: 10, success: 4, failed: 1, blocked: 3, neutral: 2, lastCallTime: null });
|
||||
assert.equal(context.buildMonitorTotals({ totalCalls: 3, blockedCalls: 3 }).neutral, 0);
|
||||
assert.equal(context.buildMonitorTotals({ totalCalls: 2, successCalls: 1, failedCalls: 1 }).blocked, 0);
|
||||
});
|
||||
|
||||
test('概览成功率排除安全拦截,只有拦截时不显示失败率或终止标签', () => {
|
||||
const { context, container } = harness();
|
||||
context.renderMonitorStats({ totalCalls: 5, successCalls: 1, failedCalls: 1, blockedCalls: 3 });
|
||||
assert.match(container.innerHTML, />50\.0%<\/span>/);
|
||||
assert.match(container.innerHTML, /is-blocked">安全拦截 3<\/span>/);
|
||||
assert.doesNotMatch(container.innerHTML, /is-neutral/);
|
||||
|
||||
context.renderMonitorStats({ totalCalls: 3, blockedCalls: 3 });
|
||||
assert.match(container.innerHTML, /value--rate is-muted">-<\/span>/);
|
||||
assert.match(container.innerHTML, /is-fail">失败 0<\/span>/);
|
||||
assert.doesNotMatch(container.innerHTML, /is-danger|is-neutral|0\.0%/);
|
||||
});
|
||||
|
||||
test('工具统计独立展示安全拦截,拦截不降低工具成功率', () => {
|
||||
const { context } = harness();
|
||||
for (const render of [context.renderMcpStatsToolTable, context.renderMcpStatsToolsPanel]) {
|
||||
const blockedOnly = render([{ toolName: 'safe-tool', totalCalls: 4, blockedCalls: 4 }], { total: 4 });
|
||||
assert.match(blockedOnly, /安全拦截 4/);
|
||||
assert.match(blockedOnly, /is-muted">-<\/span>/);
|
||||
assert.doesNotMatch(blockedOnly, /is-danger|>0\.0%<\/span>/);
|
||||
|
||||
const mixed = render([{ toolName: 'safe-tool', totalCalls: 10, successCalls: 3, failedCalls: 1, blockedCalls: 6 }], { total: 10 });
|
||||
assert.match(mixed, /75\.0%/);
|
||||
assert.match(mixed, /安全拦截 6/);
|
||||
assert.match(mixed, /失败 1/);
|
||||
}
|
||||
});
|
||||
|
||||
test('趋势图区分安全拦截和失败,并保留悬停的独立计数', () => {
|
||||
const { context } = harness();
|
||||
const points = [{ t: '2026-09-07T00:00:00Z', total: 3, failed: 0, blocked: 3 }];
|
||||
const html = context.renderMcpStatsTimelineBody({ range: '24h', points, summary: { totalCalls: 3, peak: 3 } });
|
||||
assert.match(html, /legend-item--blocked">安全拦截/);
|
||||
assert.match(html, /mcp-stats-timeline-bar-blocked/);
|
||||
assert.match(html, /mcp-stats-timeline-line--blocked/);
|
||||
assert.match(html, /data-total="3" data-failed="0" data-blocked="3"/);
|
||||
assert.doesNotMatch(html, /legend-item--fail|mcp-stats-timeline-bar-fail/);
|
||||
|
||||
const mixed = context.buildMcpTimelineSvg([{ ...points[0], total: 4, failed: 1, blocked: 2 }], '24h');
|
||||
assert.match(mixed, /data-total="4" data-failed="1" data-blocked="2"/);
|
||||
assert.match(mixed, /mcp-stats-timeline-bar-fail/);
|
||||
assert.match(mixed, /mcp-stats-timeline-bar-blocked/);
|
||||
});
|
||||
Reference in New Issue
Block a user