guanghulab/ops-agent/tests/smoke.test.js
Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

201 lines
8.1 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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);
}
})();