diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..a6dba10 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.10.0) +project(test_plan_execute_t1) +include(FetchContent) +if (MSVC) + add_compile_options(/utf-8) +endif() +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include) + +include(CTest) +enable_testing() + +add_executable(test_plan_execute_t1 test_cms_engine.cpp ../src/app.cpp) + +target_link_libraries(test_plan_execute_t1 gtest gmock gtest_main) +include(GoogleTest) +gtest_discover_tests(test_plan_execute_t1) \ No newline at end of file diff --git a/tests/test_cms_engine.cpp b/tests/test_cms_engine.cpp new file mode 100644 index 0000000..b398315 --- /dev/null +++ b/tests/test_cms_engine.cpp @@ -0,0 +1,240 @@ +#include +#include "app.hpp" +#include +#include + +class CmsEngineTest : public ::testing::Test { +protected: + CmsEngine engine; + + EventRecord makeEvent(const std::string& id, int priority, EventStatus status) { + EventRecord evt; + evt.id = id; + evt.priority = priority; + evt.status = status; + return evt; + } + + TaskPlan makePlan(const std::string& name, PlanType type, PlanStatus status) { + TaskPlan plan; + plan.name = name; + plan.type = type; + plan.status = status; + return plan; + } + + TemplateInstance makeTemplate(const std::string& id, double confidence) { + TemplateInstance tmpl; + tmpl.id = id; + tmpl.confidence = confidence; + return tmpl; + } + + ExecutionStatus makeExecStatus(const std::string& nodeId, double health, int errorCode, std::chrono::system_clock::time_point lastReport) { + ExecutionStatus s; + s.nodeId = nodeId; + s.healthIndex = health; + s.errorCode = errorCode; + s.lastReport = lastReport; + return s; + } + + UserSession makeSession(const std::string& userId, uint64_t perm) { + UserSession s; + s.userId = userId; + s.permissionBitmap = perm; + return s; + } + + NotificationMessage makeNotification(const std::string& id, bool isRead) { + NotificationMessage msg; + msg.id = id; + msg.isRead = isRead; + return msg; + } +}; + +// 1. CmsEngine 构造函数测试 +TEST_F(CmsEngineTest, testConstructorInitializesCorrectly) { + EXPECT_EQ(engine.getSystemContext().currentMode, RunMode::Idle); + EXPECT_EQ(engine.getSystemContext().consistencyMark, 0); +} + +// 2. generateNextId (私有函数,通过公开接口间接测试) +TEST_F(CmsEngineTest, testGenerateNextIdIndirect) { + TaskPlan plan = makePlan("TestPlan", PlanType::Centralized, PlanStatus::Drafting); + EXPECT_TRUE(engine.createTaskPlan(plan)); + EXPECT_FALSE(plan.id.empty()); + EXPECT_EQ(plan.id.substr(0, 3), "ID-"); +} + +// 3. ingestEvent 正常输入 +TEST_F(CmsEngineTest, testIngestEventValidInput) { + EventRecord evt = makeEvent("EVT1", 100, EventStatus::Pending); + EXPECT_TRUE(engine.ingestEvent(evt)); + EXPECT_NE(engine.findEventById("EVT1"), nullptr); +} + +// 3. ingestEvent 边界值:空ID +TEST_F(CmsEngineTest, testIngestEventEmptyId) { + EventRecord evt = makeEvent("", 50, EventStatus::Pending); + EXPECT_FALSE(engine.ingestEvent(evt)); +} + +// 3. ingestEvent 异常输入:优先级越界 +TEST_F(CmsEngineTest, testIngestEventHighPriority) { + EventRecord evt = makeEvent("EVT2", 256, EventStatus::Pending); + EXPECT_FALSE(engine.ingestEvent(evt)); +} + +// 4. processPendingEvents 正常处理 +TEST_F(CmsEngineTest, testProcessPendingEventsMultiple) { + engine.ingestEvent(makeEvent("EVT1", 10, EventStatus::Pending)); + engine.ingestEvent(makeEvent("EVT2", 20, EventStatus::Pending)); + EXPECT_EQ(engine.processPendingEvents(), 2); + EXPECT_EQ(engine.findEventById("EVT1")->status, EventStatus::Generated); + EXPECT_EQ(engine.getAllPlans().size(), 2); +} + +// 5. findEventById 未找到场景 +TEST_F(CmsEngineTest, testFindEventByIdNotFound) { + EXPECT_EQ(engine.findEventById("NONEXIST"), nullptr); +} + +// 6. createTaskPlan 正常输入 +TEST_F(CmsEngineTest, testCreateTaskPlanValid) { + TaskPlan plan = makePlan("ValidPlan", PlanType::Centralized, PlanStatus::Drafting); + EXPECT_TRUE(engine.createTaskPlan(plan)); + EXPECT_FALSE(plan.id.empty()); +} + +// 6. createTaskPlan 边界值:空名称 +TEST_F(CmsEngineTest, testCreateTaskPlanEmptyName) { + TaskPlan plan = makePlan("", PlanType::Centralized, PlanStatus::Drafting); + EXPECT_FALSE(engine.createTaskPlan(plan)); +} + +// 7. getAllPlans 正常获取 +TEST_F(CmsEngineTest, testGetAllPlans) { + engine.createTaskPlan(makePlan("P1", PlanType::Centralized, PlanStatus::Drafting)); + const auto& plans = engine.getAllPlans(); + EXPECT_EQ(plans.size(), 1); + EXPECT_EQ(plans[0].name, "P1"); +} + +// 8. findPlanById 正常查找 +TEST_F(CmsEngineTest, testFindPlanById) { + TaskPlan plan = makePlan("P1", PlanType::Centralized, PlanStatus::Drafting); + engine.createTaskPlan(plan); + const auto* found = engine.findPlanById(plan.id); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->name, "P1"); +} + +// 9. updatePlanStatus 正常更新 +TEST_F(CmsEngineTest, testUpdatePlanStatusSuccess) { + TaskPlan plan = makePlan("P1", PlanType::Centralized, PlanStatus::Drafting); + engine.createTaskPlan(plan); + auto planId = engine.getAllPlans()[0].id; + EXPECT_TRUE(engine.updatePlanStatus(planId, static_cast(1))); + EXPECT_EQ(engine.findPlanById(planId)->status, static_cast(1)); +} + +// 10. registerTemplate 正常注册 +TEST_F(CmsEngineTest, testRegisterTemplate) { + TemplateInstance tmpl = makeTemplate("T1", 0.8); + engine.registerTemplate(tmpl); + EXPECT_EQ(engine.matchTemplate("any")->id, "T1"); +} + +// 11. matchTemplate 空列表边界 +TEST_F(CmsEngineTest, testMatchTemplateEmptyList) { + EXPECT_EQ(engine.matchTemplate("scenario"), nullptr); +} + +// 11. matchTemplate 最高置信度匹配 +TEST_F(CmsEngineTest, testMatchTemplateHighestConfidence) { + engine.registerTemplate(makeTemplate("T1", 0.7)); + engine.registerTemplate(makeTemplate("T2", 0.9)); + const auto* best = engine.matchTemplate("any"); + ASSERT_NE(best, nullptr); + EXPECT_EQ(best->id, "T2"); +} + +// 12. reportExecutionStatus 正常上报 +TEST_F(CmsEngineTest, testReportExecutionStatus) { + auto now = std::chrono::system_clock::now(); + ExecutionStatus s = makeExecStatus("N1", 0.9, 0, now); + engine.reportExecutionStatus(s); + EXPECT_EQ(engine.checkHealth().size(), 0); +} + +// 13. checkHealth 异常节点检测 +TEST_F(CmsEngineTest, testCheckHealthUnhealthyNode) { + auto now = std::chrono::system_clock::now(); + engine.reportExecutionStatus(makeExecStatus("N1", 0.4, 0, now)); + auto unhealthy = engine.checkHealth(); + EXPECT_EQ(unhealthy.size(), 1); + EXPECT_EQ(unhealthy[0].nodeId, "N1"); +} + +// 14. createSession 正常创建与重复拦截 +TEST_F(CmsEngineTest, testCreateSessionValidAndDuplicate) { + UserSession s1 = makeSession("U1", 0xFF); + UserSession s2 = makeSession("U1", 0xAA); + EXPECT_TRUE(engine.createSession(s1)); + EXPECT_FALSE(engine.createSession(s2)); +} + +// 15. checkPermission 权限校验 +TEST_F(CmsEngineTest, testCheckPermission) { + engine.createSession(makeSession("U1", 0x0F)); + EXPECT_TRUE(engine.checkPermission("U1", 0x03)); + EXPECT_FALSE(engine.checkPermission("U1", 0x10)); + EXPECT_FALSE(engine.checkPermission("UNKNOWN", 0x01)); +} + +// 16. pushNotification 正常推送 +TEST_F(CmsEngineTest, testPushNotification) { + engine.pushNotification(makeNotification("MSG1", false)); + EXPECT_EQ(engine.getUnreadNotifications().size(), 1); +} + +// 17. getUnreadNotifications 过滤已读 +TEST_F(CmsEngineTest, testGetUnreadNotificationsFilter) { + engine.pushNotification(makeNotification("MSG1", false)); + engine.pushNotification(makeNotification("MSG2", true)); + auto unread = engine.getUnreadNotifications(); + EXPECT_EQ(unread.size(), 1); + EXPECT_EQ(unread[0].id, "MSG1"); +} + +// 18. switchMode 正常切换 +TEST_F(CmsEngineTest, testSwitchModeNormal) { + EXPECT_TRUE(engine.switchMode(RunMode::HumanLoop)); + EXPECT_EQ(engine.getSystemContext().currentMode, RunMode::HumanLoop); +} + +// 18. switchMode 降级模式限制 +TEST_F(CmsEngineTest, testSwitchModeDegradedRestriction) { + engine.switchMode(RunMode::Degraded); + EXPECT_FALSE(engine.switchMode(RunMode::AutoExec)); + EXPECT_TRUE(engine.switchMode(RunMode::Degraded)); +} + +// 19. getSystemContext 状态获取 +TEST_F(CmsEngineTest, testGetSystemContext) { + const auto& ctx = engine.getSystemContext(); + EXPECT_EQ(ctx.currentMode, RunMode::Idle); + EXPECT_FALSE(ctx.contextSnapshot.empty()); +} + +// 20. getSummary 摘要生成 +TEST_F(CmsEngineTest, testGetSummaryContent) { + engine.ingestEvent(makeEvent("E1", 10, EventStatus::Pending)); + engine.createTaskPlan(makePlan("P1", PlanType::Centralized, PlanStatus::Drafting)); + auto summary = engine.getSummary(); + EXPECT_NE(summary.find("Events : 1"), std::string::npos); + EXPECT_NE(summary.find("Plans : 1"), std::string::npos); + EXPECT_NE(summary.find("Mode : Idle"), std::string::npos); +} \ No newline at end of file