320 lines
9.0 KiB
C++
320 lines
9.0 KiB
C++
|
|
/**
|
|||
|
|
* @file basic_test.cpp
|
|||
|
|
* @brief CMS 核心引擎基本单元测试
|
|||
|
|
*
|
|||
|
|
* 使用标准库 assert 进行验证,不依赖外部测试框架。
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
#include "app.hpp"
|
|||
|
|
#include <cassert>
|
|||
|
|
#include <iostream>
|
|||
|
|
#include <chrono>
|
|||
|
|
#include <string>
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 测试事件接入与处理
|
|||
|
|
*/
|
|||
|
|
static void testEventIngestion()
|
|||
|
|
{
|
|||
|
|
std::cout << "[TEST] testEventIngestion ... ";
|
|||
|
|
|
|||
|
|
CmsEngine engine;
|
|||
|
|
|
|||
|
|
// 空 ID 应被拒绝
|
|||
|
|
EventRecord bad;
|
|||
|
|
bad.id = "";
|
|||
|
|
assert(!engine.ingestEvent(bad));
|
|||
|
|
|
|||
|
|
// 有效事件应被接受
|
|||
|
|
EventRecord evt;
|
|||
|
|
evt.id = "EVT-TEST-001";
|
|||
|
|
evt.typeTag = "test_type";
|
|||
|
|
evt.priority = 100;
|
|||
|
|
evt.threatLevel = 3;
|
|||
|
|
evt.status = EventStatus::Pending;
|
|||
|
|
evt.receiveTime = std::chrono::system_clock::now();
|
|||
|
|
assert(engine.ingestEvent(evt));
|
|||
|
|
|
|||
|
|
// 查找事件
|
|||
|
|
const auto* found = engine.findEventById("EVT-TEST-001");
|
|||
|
|
assert(found != nullptr);
|
|||
|
|
assert(found->typeTag == "test_type");
|
|||
|
|
|
|||
|
|
// 处理待处理事件 → 应生成一个任务方案
|
|||
|
|
size_t count = engine.processPendingEvents();
|
|||
|
|
assert(count == 1);
|
|||
|
|
|
|||
|
|
// 事件状态已变为 Generated
|
|||
|
|
found = engine.findEventById("EVT-TEST-001");
|
|||
|
|
assert(found != nullptr);
|
|||
|
|
assert(found->status == EventStatus::Generated);
|
|||
|
|
|
|||
|
|
// 方案列表应有 1 个方案
|
|||
|
|
assert(engine.getAllPlans().size() == 1);
|
|||
|
|
|
|||
|
|
std::cout << "PASSED\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 测试任务方案管理
|
|||
|
|
*/
|
|||
|
|
static void testPlanManagement()
|
|||
|
|
{
|
|||
|
|
std::cout << "[TEST] testPlanManagement ... ";
|
|||
|
|
|
|||
|
|
CmsEngine engine;
|
|||
|
|
|
|||
|
|
// 空名称应被拒绝
|
|||
|
|
TaskPlan badPlan;
|
|||
|
|
badPlan.name = "";
|
|||
|
|
assert(!engine.createTaskPlan(badPlan));
|
|||
|
|
|
|||
|
|
// 有效方案
|
|||
|
|
TaskPlan plan;
|
|||
|
|
plan.name = "Operation-Storm";
|
|||
|
|
plan.type = PlanType::Distributed;
|
|||
|
|
plan.status = PlanStatus::Drafting;
|
|||
|
|
plan.topologyNodes = {"Node-1", "Node-2", "Node-3"};
|
|||
|
|
plan.resourceQuota = 0.8;
|
|||
|
|
plan.constraints = "max_delay=5s";
|
|||
|
|
assert(engine.createTaskPlan(plan));
|
|||
|
|
|
|||
|
|
// 创建后应有 ID
|
|||
|
|
assert(!plan.id.empty());
|
|||
|
|
|
|||
|
|
// 按 ID 查找
|
|||
|
|
const auto* found = engine.findPlanById(plan.id);
|
|||
|
|
assert(found != nullptr);
|
|||
|
|
assert(found->name == "Operation-Storm");
|
|||
|
|
assert(found->type == PlanType::Distributed);
|
|||
|
|
|
|||
|
|
// 更新状态
|
|||
|
|
assert(engine.updatePlanStatus(plan.id, PlanStatus::Confirmed));
|
|||
|
|
found = engine.findPlanById(plan.id);
|
|||
|
|
assert(found->status == PlanStatus::Confirmed);
|
|||
|
|
|
|||
|
|
// 不存在的 ID 更新应失败
|
|||
|
|
assert(!engine.updatePlanStatus("NONEXIST", PlanStatus::Completed));
|
|||
|
|
|
|||
|
|
std::cout << "PASSED\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 测试模板注册与匹配
|
|||
|
|
*/
|
|||
|
|
static void testTemplateMatching()
|
|||
|
|
{
|
|||
|
|
std::cout << "[TEST] testTemplateMatching ... ";
|
|||
|
|
|
|||
|
|
CmsEngine engine;
|
|||
|
|
|
|||
|
|
// 空库匹配应返回 nullptr
|
|||
|
|
assert(engine.matchTemplate("any") == nullptr);
|
|||
|
|
|
|||
|
|
TemplateInstance tmpl;
|
|||
|
|
tmpl.id = "TMPL-TEST";
|
|||
|
|
tmpl.scenario = "land_assault";
|
|||
|
|
tmpl.complexity = 0.8;
|
|||
|
|
tmpl.version = "1.0.0";
|
|||
|
|
tmpl.confidence = 0.95;
|
|||
|
|
engine.registerTemplate(tmpl);
|
|||
|
|
|
|||
|
|
const auto* matched = engine.matchTemplate("land_assault");
|
|||
|
|
assert(matched != nullptr);
|
|||
|
|
assert(matched->id == "TMPL-TEST");
|
|||
|
|
assert(matched->confidence == 0.95);
|
|||
|
|
|
|||
|
|
std::cout << "PASSED\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 测试执行监控与健康检查
|
|||
|
|
*/
|
|||
|
|
static void testExecutionMonitoring()
|
|||
|
|
{
|
|||
|
|
std::cout << "[TEST] testExecutionMonitoring ... ";
|
|||
|
|
|
|||
|
|
CmsEngine engine;
|
|||
|
|
|
|||
|
|
// 健康节点
|
|||
|
|
ExecutionStatus healthy;
|
|||
|
|
healthy.nodeId = "NODE-H";
|
|||
|
|
healthy.healthIndex = 0.95;
|
|||
|
|
healthy.errorCode = 0;
|
|||
|
|
healthy.lastReport = std::chrono::system_clock::now();
|
|||
|
|
engine.reportExecutionStatus(healthy);
|
|||
|
|
|
|||
|
|
// 异常节点(健康指数低)
|
|||
|
|
ExecutionStatus sick;
|
|||
|
|
sick.nodeId = "NODE-S";
|
|||
|
|
sick.healthIndex = 0.3;
|
|||
|
|
sick.errorCode = 0;
|
|||
|
|
sick.lastReport = std::chrono::system_clock::now();
|
|||
|
|
engine.reportExecutionStatus(sick);
|
|||
|
|
|
|||
|
|
// 异常节点(异常码非零)
|
|||
|
|
ExecutionStatus error;
|
|||
|
|
error.nodeId = "NODE-E";
|
|||
|
|
error.healthIndex = 0.9;
|
|||
|
|
error.errorCode = 0xA001;
|
|||
|
|
error.lastReport = std::chrono::system_clock::now();
|
|||
|
|
engine.reportExecutionStatus(error);
|
|||
|
|
|
|||
|
|
// 异常节点(心跳超时 - 45秒前)
|
|||
|
|
ExecutionStatus timeout;
|
|||
|
|
timeout.nodeId = "NODE-T";
|
|||
|
|
timeout.healthIndex = 0.9;
|
|||
|
|
timeout.errorCode = 0;
|
|||
|
|
timeout.lastReport = std::chrono::system_clock::now()
|
|||
|
|
- std::chrono::seconds(45);
|
|||
|
|
engine.reportExecutionStatus(timeout);
|
|||
|
|
|
|||
|
|
auto unhealthy = engine.checkHealth();
|
|||
|
|
// 应有 3 个异常节点(NODE-S, NODE-E, NODE-T)
|
|||
|
|
assert(unhealthy.size() == 3);
|
|||
|
|
|
|||
|
|
// 验证具体节点
|
|||
|
|
bool foundS = false, foundE = false, foundT = false;
|
|||
|
|
for (const auto& u : unhealthy) {
|
|||
|
|
if (u.nodeId == "NODE-S") foundS = true;
|
|||
|
|
if (u.nodeId == "NODE-E") foundE = true;
|
|||
|
|
if (u.nodeId == "NODE-T") foundT = true;
|
|||
|
|
}
|
|||
|
|
assert(foundS && foundE && foundT);
|
|||
|
|
|
|||
|
|
std::cout << "PASSED\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 测试用户会话与权限
|
|||
|
|
*/
|
|||
|
|
static void testUserSession()
|
|||
|
|
{
|
|||
|
|
std::cout << "[TEST] testUserSession ... ";
|
|||
|
|
|
|||
|
|
CmsEngine engine;
|
|||
|
|
|
|||
|
|
// 空用户 ID 应被拒绝
|
|||
|
|
UserSession bad;
|
|||
|
|
bad.userId = "";
|
|||
|
|
assert(!engine.createSession(bad));
|
|||
|
|
|
|||
|
|
// 创建有效用户
|
|||
|
|
UserSession user;
|
|||
|
|
user.userId = "USER-001";
|
|||
|
|
user.roleCode = "OPERATOR";
|
|||
|
|
user.permissionBitmap = 0x0000000000000003ULL; // 位0+位1
|
|||
|
|
assert(engine.createSession(user));
|
|||
|
|
|
|||
|
|
// 重复创建应失败
|
|||
|
|
assert(!engine.createSession(user));
|
|||
|
|
|
|||
|
|
// 权限校验
|
|||
|
|
assert(engine.checkPermission("USER-001", 0x0000000000000001ULL));
|
|||
|
|
assert(engine.checkPermission("USER-001", 0x0000000000000002ULL));
|
|||
|
|
assert(engine.checkPermission("USER-001", 0x0000000000000003ULL));
|
|||
|
|
assert(!engine.checkPermission("USER-001", 0x0000000000000004ULL));
|
|||
|
|
|
|||
|
|
// 不存在的用户
|
|||
|
|
assert(!engine.checkPermission("GHOST", 0x0000000000000001ULL));
|
|||
|
|
|
|||
|
|
std::cout << "PASSED\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 测试通知管理
|
|||
|
|
*/
|
|||
|
|
static void testNotification()
|
|||
|
|
{
|
|||
|
|
std::cout << "[TEST] testNotification ... ";
|
|||
|
|
|
|||
|
|
CmsEngine engine;
|
|||
|
|
|
|||
|
|
// 初始无未读通知
|
|||
|
|
assert(engine.getUnreadNotifications().empty());
|
|||
|
|
|
|||
|
|
NotificationMessage msg;
|
|||
|
|
msg.id = "NTF-001";
|
|||
|
|
msg.priority = 200;
|
|||
|
|
msg.type = NotificationType::Alert;
|
|||
|
|
msg.summary = "Test alert";
|
|||
|
|
msg.triggerTime = std::chrono::system_clock::now();
|
|||
|
|
msg.isRead = false;
|
|||
|
|
engine.pushNotification(msg);
|
|||
|
|
|
|||
|
|
assert(engine.getUnreadNotifications().size() == 1);
|
|||
|
|
|
|||
|
|
// 推送已读消息
|
|||
|
|
NotificationMessage readMsg;
|
|||
|
|
readMsg.id = "NTF-002";
|
|||
|
|
readMsg.priority = 50;
|
|||
|
|
readMsg.type = NotificationType::Hint;
|
|||
|
|
readMsg.summary = "Read message";
|
|||
|
|
readMsg.triggerTime = std::chrono::system_clock::now();
|
|||
|
|
readMsg.isRead = true;
|
|||
|
|
engine.pushNotification(readMsg);
|
|||
|
|
|
|||
|
|
// 未读数量仍为 1
|
|||
|
|
assert(engine.getUnreadNotifications().size() == 1);
|
|||
|
|
|
|||
|
|
std::cout << "PASSED\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 测试模式切换
|
|||
|
|
*/
|
|||
|
|
static void testModeSwitch()
|
|||
|
|
{
|
|||
|
|
std::cout << "[TEST] testModeSwitch ... ";
|
|||
|
|
|
|||
|
|
CmsEngine engine;
|
|||
|
|
|
|||
|
|
// 初始模式为 Idle
|
|||
|
|
assert(engine.getSystemContext().currentMode == RunMode::Idle);
|
|||
|
|
|
|||
|
|
// 正常切换
|
|||
|
|
assert(engine.switchMode(RunMode::HumanLoop));
|
|||
|
|
assert(engine.getSystemContext().currentMode == RunMode::HumanLoop);
|
|||
|
|
|
|||
|
|
assert(engine.switchMode(RunMode::AutoExec));
|
|||
|
|
assert(engine.getSystemContext().currentMode == RunMode::AutoExec);
|
|||
|
|
|
|||
|
|
// 进入降级模式
|
|||
|
|
assert(engine.switchMode(RunMode::Degraded));
|
|||
|
|
assert(engine.getSystemContext().currentMode == RunMode::Degraded);
|
|||
|
|
|
|||
|
|
// 降级模式下不允许切换回其他模式(安全策略)
|
|||
|
|
assert(!engine.switchMode(RunMode::Idle));
|
|||
|
|
assert(!engine.switchMode(RunMode::HumanLoop));
|
|||
|
|
|
|||
|
|
std::cout << "PASSED\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 主测试入口
|
|||
|
|
*
|
|||
|
|
* 依次运行所有测试用例,汇总测试结果。
|
|||
|
|
*
|
|||
|
|
* @return int 退出码(0 全部通过,1 有失败)
|
|||
|
|
*/
|
|||
|
|
int main()
|
|||
|
|
{
|
|||
|
|
std::cout << "╔══════════════════════════════════════════════╗\n";
|
|||
|
|
std::cout << "║ CMS 单元测试 ║\n";
|
|||
|
|
std::cout << "╚══════════════════════════════════════════════╝\n\n";
|
|||
|
|
|
|||
|
|
testEventIngestion();
|
|||
|
|
testPlanManagement();
|
|||
|
|
testTemplateMatching();
|
|||
|
|
testExecutionMonitoring();
|
|||
|
|
testUserSession();
|
|||
|
|
testNotification();
|
|||
|
|
testModeSwitch();
|
|||
|
|
|
|||
|
|
std::cout << "\n╔══════════════════════════════════════════════╗\n";
|
|||
|
|
std::cout << "║ 全部测试通过 (ALL TESTS PASSED) ║\n";
|
|||
|
|
std::cout << "╚══════════════════════════════════════════════╝\n";
|
|||
|
|
|
|||
|
|
return 0;
|
|||
|
|
}
|