guanghulab/ops-agent/tests/smoke.test.js

201 lines
8.1 KiB
JavaScript
Raw Permalink Normal View History

#!/usr/bin/env node
/**
* 铸渊运维守卫 · 冒烟测试
* 编号: ZY-OPS-TEST-001
* 版权: 国作登字-2026-A-00037559
*
* 验证所有模块能正确加载和基本功能正常
*/
'use strict';
let passed = 0;
let failed = 0;
function assert(condition, name) {
if (condition) {
console.log(`${name}`);
passed++;
} else {
console.log(`${name}`);
failed++;
}
}
console.log('\n🛡 铸渊运维守卫 · 冒烟测试\n');
// ── 模块加载 ──────────────────────────────
console.log('📦 模块加载:');
let llm, memory, healthChecker, repairEngine, notifier;
try {
llm = require('../llm-client');
assert(true, 'llm-client 加载成功');
} catch (e) {
assert(false, `llm-client 加载失败: ${e.message}`);
}
try {
memory = require('../memory');
assert(true, 'memory 加载成功');
} catch (e) {
assert(false, `memory 加载失败: ${e.message}`);
}
try {
healthChecker = require('../health-checker');
assert(true, 'health-checker 加载成功');
} catch (e) {
assert(false, `health-checker 加载失败: ${e.message}`);
}
try {
repairEngine = require('../repair-engine');
assert(true, 'repair-engine 加载成功');
} catch (e) {
assert(false, `repair-engine 加载失败: ${e.message}`);
}
try {
notifier = require('../notifier');
assert(true, 'notifier 加载成功');
} catch (e) {
assert(false, `notifier 加载失败: ${e.message}`);
}
// ── LLM 客户端 ──────────────────────────
console.log('\n🧠 LLM 客户端:');
assert(typeof llm.diagnoseByPattern === 'function', 'diagnoseByPattern 是函数');
assert(typeof llm.chat === 'function', 'chat 是函数');
assert(typeof llm.detectIntent === 'function', 'detectIntent 是函数');
assert(typeof llm.getSession === 'function', 'getSession 是函数');
assert(typeof llm.getSessionHistory === 'function', 'getSessionHistory 是函数');
assert(typeof llm.listSessions === 'function', 'listSessions 是函数');
assert(typeof llm.callLLM === 'function', 'callLLM 是函数');
// 模式匹配测试
const patterns = llm.diagnoseByPattern('ECONNREFUSED on port 3800');
assert(patterns.length > 0, '模式匹配: ECONNREFUSED 被识别');
assert(patterns[0].category === 'service', '模式匹配: 正确分类为 service');
const emptyPatterns = llm.diagnoseByPattern('一切正常');
assert(emptyPatterns.length === 0, '模式匹配: 正常文本无匹配');
// 意图识别测试
const intent1 = llm.detectIntent('MCP连不上了怎么办');
assert(intent1.intent === 'diagnose', '意图识别: "连不上" → diagnose');
assert(intent1.action === 'health_check', '意图识别: diagnose → health_check');
const intent2 = llm.detectIntent('服务器内存够用吗');
assert(intent2.intent === 'resources', '意图识别: "内存" → resources');
const intent3 = llm.detectIntent('看看日志');
assert(intent3.intent === 'logs', '意图识别: "日志" → logs');
const intent4 = llm.detectIntent('你好呀');
assert(intent4.intent === 'general', '意图识别: 普通问候 → general');
// 会话管理测试
const { sessionId: sid1, session: s1 } = llm.getSession();
assert(sid1.startsWith('s-'), '会话管理: 新会话ID格式正确');
assert(Array.isArray(s1.messages), '会话管理: messages 是数组');
const { sessionId: sid2 } = llm.getSession(sid1);
assert(sid2 === sid1, '会话管理: 同一ID返回同一会话');
const { sessionId: sid3 } = llm.getSession();
assert(sid3 !== sid1, '会话管理: 无ID时创建新会话');
const sessions = llm.listSessions();
assert(sessions.length >= 2, '会话管理: listSessions 返回正确数量');
// ── 记忆系统 ──────────────────────────────
console.log('\n💾 记忆系统:');
assert(typeof memory.logEvent === 'function', 'logEvent 是函数');
assert(typeof memory.queryEvents === 'function', 'queryEvents 是函数');
assert(typeof memory.buildMemoryContext === 'function', 'buildMemoryContext 是函数');
assert(typeof memory.createTicket === 'function', 'createTicket 是函数');
assert(typeof memory.findOpenTicketByKey === 'function', 'findOpenTicketByKey 是函数');
const ctx = memory.buildMemoryContext();
assert(typeof ctx === 'string', 'buildMemoryContext 返回字符串');
assert(ctx.includes('运维守卫记忆'), 'buildMemoryContext 包含正确标题');
// ── 健康检查 ──────────────────────────────
console.log('\n🔍 健康检查:');
assert(typeof healthChecker.quickCheck === 'function', 'quickCheck 是函数');
assert(typeof healthChecker.deepCheck === 'function', 'deepCheck 是函数');
assert(typeof healthChecker.getSystemResources === 'function', 'getSystemResources 是函数');
assert(typeof healthChecker.getPM2Status === 'function', 'getPM2Status 是函数');
assert(typeof healthChecker.getNginxStatus === 'function', 'getNginxStatus 是函数');
assert(Array.isArray(healthChecker.SERVICES), 'SERVICES 是数组');
assert(healthChecker.SERVICES.length > 0, 'SERVICES 不为空');
const resources = healthChecker.getSystemResources();
assert(typeof resources.memory.used_pct === 'number', 'getSystemResources: 内存百分比是数字');
assert(resources.cpus > 0, 'getSystemResources: CPU核数 > 0');
// ── 修复引擎 ──────────────────────────────
console.log('\n🔧 修复引擎:');
assert(typeof repairEngine.autoRepair === 'function', 'autoRepair 是函数');
assert(typeof repairEngine.sanitizeProcessName === 'function', 'sanitizeProcessName 是函数');
assert(typeof repairEngine.sanitizePath === 'function', 'sanitizePath 是函数');
assert(repairEngine.sanitizeProcessName('zhuyuan-server') === 'zhuyuan-server', '白名单: zhuyuan-server 允许');
assert(repairEngine.sanitizeProcessName('rm -rf /') === null, '白名单: 恶意输入被拒绝');
assert(repairEngine.sanitizeProcessName('unknown-process') === null, '白名单: 未知进程被拒绝');
assert(repairEngine.sanitizePath('/opt/zhuyuan/app/test') === '/opt/zhuyuan/app/test', '路径: 合法路径允许');
assert(repairEngine.sanitizePath('/etc/passwd') === null, '路径: 敏感路径被拒绝');
assert(repairEngine.sanitizePath('/opt/zhuyuan/../etc/passwd') === null, '路径: 路径遍历被拒绝');
// ── 通知系统 ──────────────────────────────
console.log('\n📧 通知系统:');
assert(typeof notifier.sendAlertEmail === 'function', 'sendAlertEmail 是函数');
assert(typeof notifier.alertOnIssues === 'function', 'alertOnIssues 是函数');
assert(typeof notifier.notifyTicketCreated === 'function', 'notifyTicketCreated 是函数');
assert(typeof notifier.sendDailyReport === 'function', 'sendDailyReport 是函数');
// SMTP 未配置时不崩溃
const transporter = notifier.getSmtpTransporter();
assert(transporter === null || typeof transporter === 'object', 'SMTP 未配置时返回 null');
// kill switch: OPS_EMAIL_ENABLED=false 时 sendAlertEmail no-op
(async () => {
const prev = process.env.OPS_EMAIL_ENABLED;
process.env.OPS_EMAIL_ENABLED = 'false';
try {
assert(notifier.isEmailEnabled() === false, 'isEmailEnabled: false 时返回 false');
const r = await notifier.sendAlertEmail('test', '<p>x</p>');
assert(r && r.sent === false && r.reason === 'disabled', 'sendAlertEmail: kill switch 返回 {sent:false, reason:"disabled"}');
} finally {
if (prev === undefined) delete process.env.OPS_EMAIL_ENABLED;
else process.env.OPS_EMAIL_ENABLED = prev;
}
// ── 结果汇总 ──────────────────────────────
console.log('\n─────────────────────────────');
console.log(`结果: ${passed} 通过, ${failed} 失败`);
if (failed > 0) {
console.log('❌ 冒烟测试未全部通过\n');
process.exit(1);
} else {
console.log('✅ 冒烟测试全部通过\n');
process.exit(0);
}
})();