427 lines
22 KiB
JavaScript
427 lines
22 KiB
JavaScript
|
|
// scripts/agents/tests/governance.test.js
|
||
|
|
// Governance System · 治理系统综合测试
|
||
|
|
// ZY-TEST-GOV-001 · Phase 1
|
||
|
|
// 版权:国作登字-2026-A-00037559
|
||
|
|
|
||
|
|
'use strict';
|
||
|
|
|
||
|
|
const path = require('path');
|
||
|
|
const fs = require('fs');
|
||
|
|
|
||
|
|
let passed = 0;
|
||
|
|
let failed = 0;
|
||
|
|
|
||
|
|
function assert(condition, message) {
|
||
|
|
if (condition) {
|
||
|
|
passed++;
|
||
|
|
console.log(` ✅ ${message}`);
|
||
|
|
} else {
|
||
|
|
failed++;
|
||
|
|
console.error(` ❌ ${message}`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('🏛️ Governance System 综合测试\n');
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 1: Twin Merge Engine
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
console.log('── 测试 1: Twin Merge Engine ──');
|
||
|
|
|
||
|
|
const { merge, computeComposite } = require('../twin-merge');
|
||
|
|
|
||
|
|
const leftMetrics = { taskCompletion: 0.9, testPassRate: 0.85, codeCoverage: 1.0, securityScore: 1.0 };
|
||
|
|
const rightMetrics = { taskCompletion: 0.95, testPassRate: 0.9, codeCoverage: 1.0, securityScore: 1.0 };
|
||
|
|
|
||
|
|
const mergeResult = merge(leftMetrics, rightMetrics);
|
||
|
|
|
||
|
|
assert(typeof mergeResult.left === 'object', 'merge 返回 left 对象');
|
||
|
|
assert(typeof mergeResult.right === 'object', 'merge 返回 right 对象');
|
||
|
|
assert(typeof mergeResult.balance === 'number', 'merge 返回 balance 数值');
|
||
|
|
assert(mergeResult.balance >= 0 && mergeResult.balance <= 1, 'balance 在 0-1 范围内');
|
||
|
|
assert(typeof mergeResult.timestamp === 'string', 'merge 返回 timestamp');
|
||
|
|
|
||
|
|
// 验证 composite 计算
|
||
|
|
const expectedLeft = Math.round((0.4*0.9 + 0.25*0.85 + 0.2*1.0 + 0.15*1.0) * 10000) / 10000;
|
||
|
|
assert(mergeResult.left.composite === expectedLeft, `左翼 composite = ${expectedLeft} (实际: ${mergeResult.left.composite})`);
|
||
|
|
|
||
|
|
// 完全相同的指标 → balance = 1.0
|
||
|
|
const perfectMerge = merge(leftMetrics, leftMetrics);
|
||
|
|
assert(perfectMerge.balance === 1, '相同指标 → 完美平衡 (balance=1.0)');
|
||
|
|
|
||
|
|
// computeComposite 单独测试
|
||
|
|
const comp = computeComposite({ taskCompletion: 1, testPassRate: 1, codeCoverage: 1, securityScore: 1 });
|
||
|
|
assert(comp === 1, '全满分 composite = 1.0');
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 2: Balance Checker
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
console.log('\n── 测试 2: Balance Checker ──');
|
||
|
|
|
||
|
|
const { check } = require('../balance-checker');
|
||
|
|
|
||
|
|
// 完美平衡
|
||
|
|
const balancedResult = check(perfectMerge);
|
||
|
|
assert(balancedResult.balanced === true, '完美平衡 → balanced=true');
|
||
|
|
assert(balancedResult.alert === null, '完美平衡 → alert=null');
|
||
|
|
assert(balancedResult.drift === 0, '完美平衡 → drift=0');
|
||
|
|
|
||
|
|
// 有偏差
|
||
|
|
const driftResult = check(mergeResult);
|
||
|
|
assert(typeof driftResult.balanced === 'boolean', 'check 返回 balanced 布尔值');
|
||
|
|
assert(typeof driftResult.drift === 'number', 'check 返回 drift 数值');
|
||
|
|
|
||
|
|
// 大偏差测试
|
||
|
|
const bigDrift = merge(
|
||
|
|
{ taskCompletion: 0.5, testPassRate: 0.5, codeCoverage: 0.5, securityScore: 0.5 },
|
||
|
|
{ taskCompletion: 1.0, testPassRate: 1.0, codeCoverage: 1.0, securityScore: 1.0 }
|
||
|
|
);
|
||
|
|
const bigCheck = check(bigDrift);
|
||
|
|
assert(bigCheck.balanced === false, '大偏差 → balanced=false');
|
||
|
|
assert(bigCheck.alert !== null, '大偏差 → 有告警');
|
||
|
|
assert(bigCheck.alert.level === 'high', '大偏差 (0.5) → high 级别');
|
||
|
|
|
||
|
|
// 小偏差测试
|
||
|
|
const smallDrift = merge(
|
||
|
|
{ taskCompletion: 0.98, testPassRate: 0.98, codeCoverage: 1.0, securityScore: 1.0 },
|
||
|
|
{ taskCompletion: 1.0, testPassRate: 1.0, codeCoverage: 1.0, securityScore: 1.0 }
|
||
|
|
);
|
||
|
|
const smallCheck = check(smallDrift);
|
||
|
|
if (!smallCheck.balanced && smallCheck.alert) {
|
||
|
|
assert(smallCheck.alert.level === 'low', '小偏差 → low 级别');
|
||
|
|
} else {
|
||
|
|
assert(true, '小偏差 → 近似平衡');
|
||
|
|
}
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 3: Auto-Repair Classifier
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
console.log('\n── 测试 3: Auto-Repair Classifier ──');
|
||
|
|
|
||
|
|
const { diagnose, classify, verify } = require('../auto-repair');
|
||
|
|
|
||
|
|
const lowAlert = { level: 'low', laggingSide: 'left', deficit: 0.02 };
|
||
|
|
const medAlert = { level: 'medium', laggingSide: 'right', deficit: 0.1 };
|
||
|
|
const highAlert = { level: 'high', laggingSide: 'left', deficit: 0.3 };
|
||
|
|
|
||
|
|
const lowDiag = diagnose(lowAlert);
|
||
|
|
assert(lowDiag.severity === 'low', 'low alert → severity=low');
|
||
|
|
|
||
|
|
const medDiag = diagnose(medAlert);
|
||
|
|
assert(medDiag.severity === 'medium', 'medium alert → severity=medium');
|
||
|
|
|
||
|
|
const highDiag = diagnose(highAlert);
|
||
|
|
assert(highDiag.severity === 'high', 'high alert → severity=high');
|
||
|
|
|
||
|
|
const lowClass = classify(lowDiag);
|
||
|
|
assert(lowClass.level === 'L1', 'low severity → L1 自动修复');
|
||
|
|
assert(lowClass.autoFixable === true, 'L1 → autoFixable=true');
|
||
|
|
|
||
|
|
const medClass = classify(medDiag);
|
||
|
|
assert(medClass.level === 'L2', 'medium severity → L2 技术干预');
|
||
|
|
assert(medClass.autoFixable === false, 'L2 → autoFixable=false');
|
||
|
|
|
||
|
|
const highClass = classify(highDiag);
|
||
|
|
assert(highClass.level === 'L3', 'high severity → L3 架构干预');
|
||
|
|
|
||
|
|
// verify 测试
|
||
|
|
const verifySuccess = verify({ success: true });
|
||
|
|
assert(verifySuccess.verified === true, '成功修复 → verified=true');
|
||
|
|
|
||
|
|
const verifyFail = verify({ success: false, level: 'L2' });
|
||
|
|
assert(verifyFail.verified === false, '未修复 → verified=false');
|
||
|
|
|
||
|
|
// null alert
|
||
|
|
const nullDiag = diagnose(null);
|
||
|
|
assert(nullDiag.severity === 'none', 'null alert → severity=none');
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 4: Escalation Router
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
console.log('\n── 测试 4: Escalation Router ──');
|
||
|
|
|
||
|
|
const { classify: escClassify, route, escalate } = require('../escalation-router');
|
||
|
|
|
||
|
|
// L1 类型
|
||
|
|
assert(escClassify({ type: 'test_flaky' }).level === 'L1', 'test_flaky → L1');
|
||
|
|
assert(escClassify({ type: 'lint_error' }).level === 'L1', 'lint_error → L1');
|
||
|
|
assert(escClassify({ type: 'stub_missing' }).level === 'L1', 'stub_missing → L1');
|
||
|
|
assert(escClassify({ type: 'format_issue' }).level === 'L1', 'format_issue → L1');
|
||
|
|
|
||
|
|
// L2 类型
|
||
|
|
assert(escClassify({ type: 'test_persistent_fail' }).level === 'L2', 'test_persistent_fail → L2');
|
||
|
|
assert(escClassify({ type: 'dependency_conflict' }).level === 'L2', 'dependency_conflict → L2');
|
||
|
|
assert(escClassify({ type: 'security_alert' }).level === 'L2', 'security_alert → L2');
|
||
|
|
|
||
|
|
// L3 类型
|
||
|
|
assert(escClassify({ type: 'balance_long_drift' }).level === 'L3', 'balance_long_drift → L3');
|
||
|
|
assert(escClassify({ type: 'ontology_conflict' }).level === 'L3', 'ontology_conflict → L3');
|
||
|
|
assert(escClassify({ type: 'cross_system_architecture' }).level === 'L3', 'cross_system_architecture → L3');
|
||
|
|
|
||
|
|
// 路由测试
|
||
|
|
const l1Route = route({ type: 'test_flaky' });
|
||
|
|
assert(l1Route.handler === 'auto_repair', 'L1 handler = auto_repair');
|
||
|
|
assert(l1Route.notify === null, 'L1 notify = null');
|
||
|
|
|
||
|
|
const l2Route = route({ type: 'security_alert' });
|
||
|
|
assert(l2Route.notify !== null, 'L2 有通知目标');
|
||
|
|
|
||
|
|
// 完整升级流程
|
||
|
|
const escResult = escalate({ type: 'ontology_conflict', description: '测试' });
|
||
|
|
assert(escResult.classification.level === 'L3', 'escalate 返回正确分类');
|
||
|
|
assert(typeof escResult.timestamp === 'string', 'escalate 返回 timestamp');
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 5: Bulletin Manager
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
console.log('\n── 测试 5: Bulletin Manager ──');
|
||
|
|
|
||
|
|
const { addEvent, updateStatus, getActive, getRecent, prune, loadBulletin, saveBulletin } = require('../bulletin-manager');
|
||
|
|
|
||
|
|
// 清理测试环境
|
||
|
|
const ROOT = path.resolve(__dirname, '../../..');
|
||
|
|
const bulletinTestPath = path.join(ROOT, '.github/tianyen/bulletin-data.json');
|
||
|
|
const bulletinBackup = fs.existsSync(bulletinTestPath) ? fs.readFileSync(bulletinTestPath, 'utf8') : null;
|
||
|
|
|
||
|
|
// 重置
|
||
|
|
saveBulletin({ events: [] });
|
||
|
|
|
||
|
|
// 添加事件
|
||
|
|
const added1 = addEvent({ id: 'TEST-001', event: '测试事件1', status: 'active', handler: 'auto', priority: 1 });
|
||
|
|
assert(added1 === true, 'addEvent 成功添加');
|
||
|
|
|
||
|
|
const added2 = addEvent({ id: 'TEST-002', event: '测试事件2', status: 'active', handler: 'manual', priority: 2 });
|
||
|
|
assert(added2 === true, 'addEvent 添加第二个事件');
|
||
|
|
|
||
|
|
// 去重
|
||
|
|
const addDup = addEvent({ id: 'TEST-001', event: '重复事件', status: 'active' });
|
||
|
|
assert(addDup === false, 'addEvent 去重:相同 id 不重复添加');
|
||
|
|
|
||
|
|
// 获取活跃事件
|
||
|
|
const active = getActive();
|
||
|
|
assert(active.length === 2, `getActive 返回 2 个活跃事件 (实际: ${active.length})`);
|
||
|
|
|
||
|
|
// 更新状态
|
||
|
|
const updated = updateStatus('TEST-001', 'resolved');
|
||
|
|
assert(updated === true, 'updateStatus 成功更新');
|
||
|
|
|
||
|
|
const activeAfter = getActive();
|
||
|
|
assert(activeAfter.length === 1, `resolved 后活跃事件 = 1 (实际: ${activeAfter.length})`);
|
||
|
|
|
||
|
|
// 获取最近事件
|
||
|
|
const recent = getRecent(5);
|
||
|
|
assert(recent.length === 2, `getRecent 返回所有事件 (含 resolved)`);
|
||
|
|
|
||
|
|
// 清理过期事件(用极小 maxAge 测试)
|
||
|
|
addEvent({ id: 'TEST-OLD', timestamp: '2020-01-01T00:00:00Z', event: '旧事件', status: 'active' });
|
||
|
|
const pruned = prune(1000); // 1秒前的都清理
|
||
|
|
assert(pruned >= 1, `prune 清理了至少 1 条过期事件 (清理了: ${pruned})`);
|
||
|
|
|
||
|
|
// 恢复测试数据
|
||
|
|
if (bulletinBackup) {
|
||
|
|
fs.writeFileSync(bulletinTestPath, bulletinBackup, 'utf8');
|
||
|
|
} else {
|
||
|
|
saveBulletin({ events: [] });
|
||
|
|
}
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 6: Actions Parser
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
console.log('\n── 测试 6: Actions Parser ──');
|
||
|
|
|
||
|
|
const { parseWorkflowOutput, parsePRMerge, parseTestReport } = require('../actions-parser');
|
||
|
|
|
||
|
|
// parseWorkflowOutput
|
||
|
|
const wfOutput = ' ✅ test1\n ✅ test2\n ❌ test3\n Error: something\n';
|
||
|
|
const wfResult = parseWorkflowOutput(wfOutput);
|
||
|
|
assert(wfResult.tests.passed === 2, 'parseWorkflowOutput: 2 passed');
|
||
|
|
assert(wfResult.tests.failed === 1, 'parseWorkflowOutput: 1 failed');
|
||
|
|
assert(wfResult.tests.total === 3, 'parseWorkflowOutput: 3 total');
|
||
|
|
assert(wfResult.errors.length >= 1, 'parseWorkflowOutput: 检测到错误行');
|
||
|
|
|
||
|
|
// 空输入
|
||
|
|
const emptyResult = parseWorkflowOutput('');
|
||
|
|
assert(emptyResult.tests.total === 0, 'parseWorkflowOutput: 空输入 → 0 total');
|
||
|
|
|
||
|
|
const nullResult = parseWorkflowOutput(null);
|
||
|
|
assert(nullResult.tests.total === 0, 'parseWorkflowOutput: null 输入 → 0 total');
|
||
|
|
|
||
|
|
// parsePRMerge
|
||
|
|
const prResult = parsePRMerge({ title: 'Fix bug', number: 42, author: 'test', files: ['a.js', 'b.js'] });
|
||
|
|
assert(prResult.filesChanged === 2, 'parsePRMerge: 2 files changed');
|
||
|
|
assert(prResult.summary.includes('#42'), 'parsePRMerge: 包含 PR 编号');
|
||
|
|
|
||
|
|
const nullPr = parsePRMerge(null);
|
||
|
|
assert(nullPr.filesChanged === 0, 'parsePRMerge: null → 0 files');
|
||
|
|
|
||
|
|
// parseTestReport
|
||
|
|
const reportResult = parseTestReport('✅ a\n✅ b\n❌ c');
|
||
|
|
assert(reportResult.passed === 2, 'parseTestReport: 2 passed');
|
||
|
|
assert(reportResult.failed === 1, 'parseTestReport: 1 failed');
|
||
|
|
assert(reportResult.passRate > 0.6 && reportResult.passRate < 0.7, 'parseTestReport: passRate ~0.6667');
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 7: TianYen Scheduler
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
console.log('\n── 测试 7: TianYen Scheduler ──');
|
||
|
|
|
||
|
|
const { evaluateSchedule, setFrequency, FREQUENCY_LEVELS } = require('../../tianyen/scheduler');
|
||
|
|
|
||
|
|
// 无指标 → default
|
||
|
|
const defEval = evaluateSchedule('AG-TEST', null);
|
||
|
|
assert(defEval.level === 'default', '无指标 → default 频率');
|
||
|
|
|
||
|
|
// 高偏差 → max
|
||
|
|
const maxEval = evaluateSchedule('AG-TEST', { drift: 0.2 });
|
||
|
|
assert(maxEval.level === 'max', '高偏差 (0.2) → max 频率');
|
||
|
|
|
||
|
|
// 中偏差 → high
|
||
|
|
const highEval = evaluateSchedule('AG-TEST', { drift: 0.08 });
|
||
|
|
assert(highEval.level === 'high', '中偏差 (0.08) → high 频率');
|
||
|
|
|
||
|
|
// 完美平衡 → low
|
||
|
|
const lowEval = evaluateSchedule('AG-TEST', { balance: 1.0, drift: 0 });
|
||
|
|
assert(lowEval.level === 'low', '完美平衡 → low 频率');
|
||
|
|
|
||
|
|
// 活跃变更 → high/medium
|
||
|
|
const activeEval = evaluateSchedule('AG-TEST', { recentChanges: 15 });
|
||
|
|
assert(activeEval.level === 'high', '大量变更 → high 频率');
|
||
|
|
|
||
|
|
const medEval = evaluateSchedule('AG-TEST', { recentChanges: 5 });
|
||
|
|
assert(medEval.level === 'medium', '中等变更 → medium 频率');
|
||
|
|
|
||
|
|
// FREQUENCY_LEVELS 完整性
|
||
|
|
assert(Object.keys(FREQUENCY_LEVELS).length === 5, '5 个频率级别');
|
||
|
|
assert(FREQUENCY_LEVELS.max.cron === '*/30 * * * *', 'max cron 正确');
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 8: Agent Checkin Module
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
console.log('\n── 测试 8: Agent Checkin Module ──');
|
||
|
|
|
||
|
|
const { checkin, checkout, getLastCheckin, detectTimeout, loadCheckinLog, saveCheckinLog } = require('../../tianyen/agent-checkin-module');
|
||
|
|
|
||
|
|
// 备份
|
||
|
|
const checkinLogPath = path.join(ROOT, '.github/tianyen/checkin-log.json');
|
||
|
|
const checkinBackup = fs.existsSync(checkinLogPath) ? fs.readFileSync(checkinLogPath, 'utf8') : null;
|
||
|
|
|
||
|
|
// 重置
|
||
|
|
saveCheckinLog({ version: '1.0.0', checkins: {} });
|
||
|
|
|
||
|
|
// checkin
|
||
|
|
const ci = checkin('AG-TEST-001');
|
||
|
|
assert(ci.agentId === 'AG-TEST-001', 'checkin 返回正确 agentId');
|
||
|
|
assert(ci.status === 'running', 'checkin status = running');
|
||
|
|
assert(typeof ci.timestamp === 'string', 'checkin 返回 timestamp');
|
||
|
|
|
||
|
|
// getLastCheckin
|
||
|
|
const last = getLastCheckin('AG-TEST-001');
|
||
|
|
assert(last !== null, 'getLastCheckin 返回记录');
|
||
|
|
assert(last.status === 'running', 'getLastCheckin status = running');
|
||
|
|
|
||
|
|
// detectTimeout — 刚签到,不应超时
|
||
|
|
const timeoutCheck = detectTimeout('AG-TEST-001', 600000);
|
||
|
|
assert(timeoutCheck.timedOut === false, '刚签到 → 未超时');
|
||
|
|
|
||
|
|
// detectTimeout — 用极小 maxDuration 强制超时
|
||
|
|
const shortTimeout = detectTimeout('AG-TEST-001', 0);
|
||
|
|
assert(shortTimeout.timedOut === true, '0ms maxDuration → 超时');
|
||
|
|
|
||
|
|
// checkout
|
||
|
|
const co = checkout('AG-TEST-001', 'success', { testsRun: 10 });
|
||
|
|
assert(co.status === 'success', 'checkout status = success');
|
||
|
|
assert(typeof co.duration === 'number', 'checkout 返回 duration');
|
||
|
|
|
||
|
|
// checkout 后不再检测为运行中
|
||
|
|
const afterCo = detectTimeout('AG-TEST-001', 0);
|
||
|
|
assert(afterCo.timedOut === false, 'checkout 后不超时');
|
||
|
|
|
||
|
|
// 不存在的 Agent
|
||
|
|
const noAgent = getLastCheckin('AG-NONEXISTENT');
|
||
|
|
assert(noAgent === null, '不存在的 Agent → null');
|
||
|
|
|
||
|
|
const noTimeout = detectTimeout('AG-NONEXISTENT');
|
||
|
|
assert(noTimeout.timedOut === false, '不存在的 Agent → 不超时');
|
||
|
|
|
||
|
|
// 恢复
|
||
|
|
if (checkinBackup) {
|
||
|
|
fs.writeFileSync(checkinLogPath, checkinBackup, 'utf8');
|
||
|
|
} else {
|
||
|
|
saveCheckinLog({ version: '1.0.0', checkins: {} });
|
||
|
|
}
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 9: Twin Collector (不执行实际测试,只验证模块导出)
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
console.log('\n── 测试 9: Twin Collector 模块导出 ──');
|
||
|
|
|
||
|
|
const collector = require('../twin-collector');
|
||
|
|
assert(typeof collector.collectMetrics === 'function', 'collectMetrics 是函数');
|
||
|
|
assert(typeof collector.collectLeft === 'function', 'collectLeft 是函数');
|
||
|
|
assert(typeof collector.collectRight === 'function', 'collectRight 是函数');
|
||
|
|
assert(typeof collector.collectBoth === 'function', 'collectBoth 是函数');
|
||
|
|
assert(typeof collector.WEIGHTS === 'object', 'WEIGHTS 已导出');
|
||
|
|
assert(collector.WEIGHTS.taskCompletion === 0.4, 'taskCompletion 权重 = 0.4');
|
||
|
|
assert(collector.WEIGHTS.testPassRate === 0.25, 'testPassRate 权重 = 0.25');
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 10: README Generator (SKINS 和布局)
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
console.log('\n── 测试 10: README Generator ──');
|
||
|
|
|
||
|
|
const {
|
||
|
|
SKINS, selectSkin, generateLayout, renderDashboard,
|
||
|
|
renderTwinBalance, renderBulletin, generateReadme
|
||
|
|
} = require('../readme-generator');
|
||
|
|
|
||
|
|
assert(Object.keys(SKINS).length === 5, '5 种皮肤');
|
||
|
|
assert(SKINS.CALM.badgeColor === 'blue', 'CALM badgeColor = blue');
|
||
|
|
assert(SKINS.EMERGENCY.badgeColor === 'red', 'EMERGENCY badgeColor = red');
|
||
|
|
|
||
|
|
// selectSkin
|
||
|
|
assert(selectSkin(null).name === SKINS.CALM.name, 'null state → CALM');
|
||
|
|
assert(selectSkin({ alert: { level: 'high' } }).name === SKINS.EMERGENCY.name, 'high alert → EMERGENCY');
|
||
|
|
assert(selectSkin({ alert: { level: 'medium' } }).name === SKINS.ATTENTION.name, 'medium alert → ATTENTION');
|
||
|
|
assert(selectSkin({ building: true }).name === SKINS.BUILDING.name, 'building → BUILDING');
|
||
|
|
|
||
|
|
// generateLayout
|
||
|
|
const emergencyLayout = generateLayout({ alert: { level: 'high' }, balanced: false });
|
||
|
|
assert(emergencyLayout[0] === 'critical_alert', '紧急布局首项 = critical_alert');
|
||
|
|
|
||
|
|
const calmLayout = generateLayout({});
|
||
|
|
assert(calmLayout.includes('dashboard'), '常规布局包含 dashboard');
|
||
|
|
|
||
|
|
// renderDashboard
|
||
|
|
const dashMd = renderDashboard({});
|
||
|
|
assert(dashMd.includes('系统状态'), 'dashboard 包含系统状态');
|
||
|
|
|
||
|
|
// renderTwinBalance
|
||
|
|
const twinMd = renderTwinBalance({ metrics: { left: { composite: 0.9, dimensions: { taskCompletion: 0.9, testPassRate: 0.8, codeCoverage: 1.0, securityScore: 1.0 } }, right: { composite: 0.95, dimensions: { taskCompletion: 0.95, testPassRate: 0.9, codeCoverage: 1.0, securityScore: 1.0 } } }, balance: 0.95 });
|
||
|
|
assert(twinMd.includes('双子天平'), 'twin balance 包含标题');
|
||
|
|
assert(twinMd.includes('EXE-Engine'), 'twin balance 包含 EXE-Engine');
|
||
|
|
|
||
|
|
// renderBulletin
|
||
|
|
const bulletinMd = renderBulletin([]);
|
||
|
|
assert(bulletinMd.includes('暂无公告'), '空公告 → 暂无公告');
|
||
|
|
|
||
|
|
const bulletinMd2 = renderBulletin([{ timestamp: '2026-01-01T00:00:00Z', event: '测试', status: 'active', handler: 'auto' }]);
|
||
|
|
assert(bulletinMd2.includes('测试'), '公告包含事件内容');
|
||
|
|
|
||
|
|
// generateReadme
|
||
|
|
const readme = generateReadme({});
|
||
|
|
assert(readme.includes('光湖'), 'README 包含光湖');
|
||
|
|
assert(readme.includes('版权'), 'README 包含版权');
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 结果汇总
|
||
|
|
// ══════════════════════════════════════════════════════════════════════════
|
||
|
|
console.log(`\n${'═'.repeat(60)}`);
|
||
|
|
console.log(`🏛️ 治理系统测试结果: ${passed} passed, ${failed} failed`);
|
||
|
|
|
||
|
|
if (failed > 0) {
|
||
|
|
console.log(`\n❌ ${failed} 个测试失败`);
|
||
|
|
process.exit(1);
|
||
|
|
} else {
|
||
|
|
console.log(`\n✅ 全部 ${passed} 个测试通过`);
|
||
|
|
}
|