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..eeea9e1 --- /dev/null +++ b/tests/test_cms_engine.cpp @@ -0,0 +1,379 @@ +#include +#include +#include "app.hpp" +#include +#include + +using namespace testing; + +// ============================================================================ +// Test Fixture for CmsEngine +// ============================================================================ +class CmsEngineTest : public ::testing::Test { +protected: + void SetUp() override { + engine = std::make_unique(); + } + + void TearDown() override { + engine.reset(); + } + + std::unique_ptr engine; +}; + +// ============================================================================ +// Tests for CmsEngine Constructor +// ============================================================================ + +/** + * @brief 测试构造函数初始化系统状态上下文 + * + * 验证构造函数正确初始化系统状态上下文,包括模式、一致性标记、切换时间和快照。 + */ +TEST_F(CmsEngineTest, testConstructorInitializesSystemContext) { + const auto& context = engine->getSystemContext(); + + EXPECT_EQ(context.currentMode, RunMode::Idle); + EXPECT_EQ(context.consistencyMark, 0); + + auto now = std::chrono::system_clock::now(); + auto diff = std::chrono::duration_cast(now - context.switchTime).count(); + EXPECT_GE(diff, 0); + EXPECT_LT(diff, 5); + + EXPECT_THAT(context.contextSnapshot, HasSubstr("\"mode\":\"Idle\"")); + EXPECT_THAT(context.contextSnapshot, HasSubstr("\"version\":\"1.0.0\"")); +} + +/** + * @brief 测试构造函数初始化空容器 + * + * 验证构造函数正确初始化所有容器为空。 + */ +TEST_F(CmsEngineTest, testConstructorInitializesEmptyContainers) { + EXPECT_EQ(engine->getAllPlans().size(), 0); + + std::string summary = engine->getSummary(); + EXPECT_THAT(summary, HasSubstr("Events : 0")); + EXPECT_THAT(summary, HasSubstr("Plans : 0")); + EXPECT_THAT(summary, HasSubstr("Templates : 0")); + EXPECT_THAT(summary, HasSubstr("Monitor Nodes : 0")); + EXPECT_THAT(summary, HasSubstr("Active Sessions : 0")); + EXPECT_THAT(summary, HasSubstr("Notifications : 0")); + EXPECT_THAT(summary, HasSubstr("Mode : Idle")); +} + +// ============================================================================ +// Tests for ingestEvent +// ============================================================================ + +/** + * @brief 测试正常事件接入 + * + * 验证具有有效 ID 和合法优先级的事件能够成功接入。 + */ +TEST_F(CmsEngineTest, testIngestEventValidEvent) { + EventRecord event; + event.id = "EVT-001"; + event.priority = 100; + event.status = EventStatus::Pending; + + bool result = engine->ingestEvent(event); + EXPECT_TRUE(result); + + const EventRecord* storedEvent = engine->findEventById("EVT-001"); + ASSERT_NE(storedEvent, nullptr); + EXPECT_EQ(storedEvent->id, "EVT-001"); + EXPECT_EQ(storedEvent->priority, 100); + EXPECT_EQ(storedEvent->status, EventStatus::Pending); +} + +/** + * @brief 测试接入优先级为 0 的事件 + * + * 验证优先级为 0(最小值)的事件能够成功接入。 + */ +TEST_F(CmsEngineTest, testIngestEventMinPriority) { + EventRecord event; + event.id = "EVT-002"; + event.priority = 0; + event.status = EventStatus::Pending; + + bool result = engine->ingestEvent(event); + EXPECT_TRUE(result); + + const EventRecord* storedEvent = engine->findEventById("EVT-002"); + ASSERT_NE(storedEvent, nullptr); + EXPECT_EQ(storedEvent->priority, 0); +} + +/** + * @brief 测试接入优先级为 255 的事件 + * + * 验证优先级为 255(最大值)的事件能够成功接入。 + */ +TEST_F(CmsEngineTest, testIngestEventMaxPriority) { + EventRecord event; + event.id = "EVT-003"; + event.priority = 255; + event.status = EventStatus::Pending; + + bool result = engine->ingestEvent(event); + EXPECT_TRUE(result); + + const EventRecord* storedEvent = engine->findEventById("EVT-003"); + ASSERT_NE(storedEvent, nullptr); + EXPECT_EQ(storedEvent->priority, 255); +} + +/** + * @brief 测试接入空 ID 事件 + * + * 验证 ID 为空的事件接入失败,返回 false。 + */ +TEST_F(CmsEngineTest, testIngestEventEmptyId) { + EventRecord event; + event.id = ""; + event.priority = 100; + event.status = EventStatus::Pending; + + bool result = engine->ingestEvent(event); + EXPECT_FALSE(result); + + const EventRecord* storedEvent = engine->findEventById(""); + EXPECT_EQ(storedEvent, nullptr); +} + +/** + * @brief 测试接入优先级超限事件 + * + * 验证优先级大于 255 的事件接入失败,返回 false。 + */ +TEST_F(CmsEngineTest, testIngestEventPriorityExceedsMax) { + EventRecord event; + event.id = "EVT-004"; + event.priority = 256; + event.status = EventStatus::Pending; + + bool result = engine->ingestEvent(event); + EXPECT_FALSE(result); + + const EventRecord* storedEvent = engine->findEventById("EVT-004"); + EXPECT_EQ(storedEvent, nullptr); +} + +/** + * @brief 测试接入多个事件 + * + * 验证多个事件能够依次成功接入,且事件列表正确增长。 + */ +TEST_F(CmsEngineTest, testIngestEventMultipleEvents) { + EventRecord event1; + event1.id = "EVT-005"; + event1.priority = 50; + event1.status = EventStatus::Pending; + + EventRecord event2; + event2.id = "EVT-006"; + event2.priority = 150; + event2.status = EventStatus::Pending; + + EXPECT_TRUE(engine->ingestEvent(event1)); + EXPECT_TRUE(engine->ingestEvent(event2)); + + const EventRecord* storedEvent1 = engine->findEventById("EVT-005"); + ASSERT_NE(storedEvent1, nullptr); + EXPECT_EQ(storedEvent1->priority, 50); + + const EventRecord* storedEvent2 = engine->findEventById("EVT-006"); + ASSERT_NE(storedEvent2, nullptr); + EXPECT_EQ(storedEvent2->priority, 150); +} + +/** + * @brief 测试接入事件后处理待处理事件 + * + * 验证接入 Pending 状态的事件后,processPendingEvents 能正确处理。 + */ +TEST_F(CmsEngineTest, testIngestEventAndProcessPending) { + EventRecord event; + event.id = "EVT-007"; + event.priority = 100; + event.status = EventStatus::Pending; + + EXPECT_TRUE(engine->ingestEvent(event)); + + size_t processedCount = engine->processPendingEvents(); + EXPECT_EQ(processedCount, 1); + + const EventRecord* storedEvent = engine->findEventById("EVT-007"); + ASSERT_NE(storedEvent, nullptr); + EXPECT_EQ(storedEvent->status, EventStatus::Generated); + + // 通过 getAllPlans 获取方案列表,验证自动生成的方案 + const auto& plans = engine->getAllPlans(); + ASSERT_EQ(plans.size(), 1); + EXPECT_EQ(plans[0].relatedEventId, "EVT-007"); + EXPECT_EQ(plans[0].name, "AutoPlan-EVT-007"); +} + +// ============================================================================ +// Tests for findPlanById +// ============================================================================ + +/** + * @brief 测试查找存在的方案 + * + * 验证能够通过 ID 查找到已创建的任务方案。 + */ +TEST_F(CmsEngineTest, testFindPlanByIdExistingPlan) { + TaskPlan plan; + plan.name = "TestPlan"; + plan.type = PlanType::Centralized; + plan.status = PlanStatus::Drafting; + plan.resourceQuota = 0.8; + plan.constraints = "测试约束"; + + EXPECT_TRUE(engine->createTaskPlan(plan)); + + std::string planId = plan.id; + EXPECT_FALSE(planId.empty()); + + const TaskPlan* foundPlan = engine->findPlanById(planId); + ASSERT_NE(foundPlan, nullptr); + EXPECT_EQ(foundPlan->id, planId); + EXPECT_EQ(foundPlan->name, "TestPlan"); + EXPECT_EQ(foundPlan->type, PlanType::Centralized); + EXPECT_EQ(foundPlan->status, PlanStatus::Drafting); + EXPECT_DOUBLE_EQ(foundPlan->resourceQuota, 0.8); + EXPECT_EQ(foundPlan->constraints, "测试约束"); +} + +/** + * @brief 测试查找不存在的方案 + * + * 验证查找不存在的 ID 时返回 nullptr。 + */ +TEST_F(CmsEngineTest, testFindPlanByIdNonExistentPlan) { + const TaskPlan* foundPlan = engine->findPlanById("NONEXISTENT"); + EXPECT_EQ(foundPlan, nullptr); +} + +/** + * @brief 测试查找空 ID 方案 + * + * 验证查找空字符串 ID 时返回 nullptr。 + */ +TEST_F(CmsEngineTest, testFindPlanByIdEmptyId) { + const TaskPlan* foundPlan = engine->findPlanById(""); + EXPECT_EQ(foundPlan, nullptr); +} + +/** + * @brief 测试查找多个方案中的特定方案 + * + * 验证在多个方案中能够正确查找到指定 ID 的方案。 + */ +TEST_F(CmsEngineTest, testFindPlanByIdAmongMultiplePlans) { + TaskPlan plan1; + plan1.name = "PlanAlpha"; + EXPECT_TRUE(engine->createTaskPlan(plan1)); + std::string id1 = plan1.id; + + TaskPlan plan2; + plan2.name = "PlanBeta"; + EXPECT_TRUE(engine->createTaskPlan(plan2)); + std::string id2 = plan2.id; + + TaskPlan plan3; + plan3.name = "PlanGamma"; + EXPECT_TRUE(engine->createTaskPlan(plan3)); + std::string id3 = plan3.id; + + EXPECT_NE(id1, id2); + EXPECT_NE(id2, id3); + EXPECT_NE(id1, id3); + + const TaskPlan* foundPlan = engine->findPlanById(id2); + ASSERT_NE(foundPlan, nullptr); + EXPECT_EQ(foundPlan->id, id2); + EXPECT_EQ(foundPlan->name, "PlanBeta"); + + foundPlan = engine->findPlanById(id1); + ASSERT_NE(foundPlan, nullptr); + EXPECT_EQ(foundPlan->id, id1); + EXPECT_EQ(foundPlan->name, "PlanAlpha"); + + foundPlan = engine->findPlanById(id3); + ASSERT_NE(foundPlan, nullptr); + EXPECT_EQ(foundPlan->id, id3); + EXPECT_EQ(foundPlan->name, "PlanGamma"); +} + +/** + * @brief 测试查找已更新状态的方案 + * + * 验证方案状态更新后仍能查找到,且状态正确。 + */ +TEST_F(CmsEngineTest, testFindPlanByIdAfterStatusUpdate) { + TaskPlan plan; + plan.name = "StatusTestPlan"; + EXPECT_TRUE(engine->createTaskPlan(plan)); + std::string planId = plan.id; + + EXPECT_TRUE(engine->updatePlanStatus(planId, PlanStatus::Approved)); + + const TaskPlan* foundPlan = engine->findPlanById(planId); + ASSERT_NE(foundPlan, nullptr); + EXPECT_EQ(foundPlan->status, PlanStatus::Approved); +} + +/** + * @brief 测试查找通过事件处理生成的方案 + * + * 验证通过 processPendingEvents 自动生成的方案能够被正确查找。 + */ +TEST_F(CmsEngineTest, testFindPlanByIdGeneratedFromEvent) { + EventRecord event; + event.id = "EVT-GEN-001"; + event.priority = 100; + event.status = EventStatus::Pending; + EXPECT_TRUE(engine->ingestEvent(event)); + + size_t processedCount = engine->processPendingEvents(); + EXPECT_EQ(processedCount, 1); + + // 通过 getAllPlans 获取方案列表,验证自动生成的方案 + const auto& plans = engine->getAllPlans(); + ASSERT_EQ(plans.size(), 1); + std::string planId = plans[0].id; + EXPECT_FALSE(planId.empty()); + + const TaskPlan* foundPlan = engine->findPlanById(planId); + ASSERT_NE(foundPlan, nullptr); + EXPECT_EQ(foundPlan->relatedEventId, "EVT-GEN-001"); + EXPECT_EQ(foundPlan->name, "AutoPlan-EVT-GEN-001"); + EXPECT_EQ(foundPlan->type, PlanType::Centralized); + EXPECT_EQ(foundPlan->status, PlanStatus::Drafting); + EXPECT_DOUBLE_EQ(foundPlan->resourceQuota, 0.5); + EXPECT_EQ(foundPlan->constraints, "自动生成约束"); +} + +/** + * @brief 测试查找方案时的常量正确性 + * + * 验证 const 版本的 findPlanById 返回的指针指向常量对象。 + */ +TEST_F(CmsEngineTest, testFindPlanByIdConstCorrectness) { + TaskPlan plan; + plan.name = "ConstTestPlan"; + EXPECT_TRUE(engine->createTaskPlan(plan)); + std::string planId = plan.id; + + const CmsEngine& constEngine = *engine; + const TaskPlan* foundPlan = constEngine.findPlanById(planId); + ASSERT_NE(foundPlan, nullptr); + EXPECT_EQ(foundPlan->id, planId); + EXPECT_EQ(foundPlan->name, "ConstTestPlan"); +}