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..b6ac609 --- /dev/null +++ b/tests/test_cms_engine.cpp @@ -0,0 +1,399 @@ +#include +#include +#include +#include +#include "app.hpp" + +// ============================================================================ +// Test Fixture for CmsEngine +// ============================================================================ +class CmsEngineTest : public ::testing::Test { +protected: + void SetUp() override { + engine = new CmsEngine(); + } + + void TearDown() override { + delete engine; + } + + CmsEngine* engine; +}; + +// ============================================================================ +// Tests for CmsEngine constructor +// ============================================================================ + +/** + * @brief Test constructor initializes system context correctly + */ +TEST_F(CmsEngineTest, testConstructorNormalInitialization) { + const SystemStateContext& ctx = engine->getSystemContext(); + EXPECT_EQ(ctx.currentMode, RunMode::Idle); + EXPECT_EQ(ctx.consistencyMark, 0); + EXPECT_EQ(ctx.contextSnapshot, R"({"mode":"Idle","version":"1.0.0"})"); +} + +/** + * @brief Test constructor initializes empty containers + */ +TEST_F(CmsEngineTest, testConstructorEmptyContainers) { + EXPECT_TRUE(engine->getAllPlans().empty()); + std::string summary = engine->getSummary(); + EXPECT_NE(summary.find("Events : 0"), std::string::npos); +} + +// ============================================================================ +// Tests for ingestEvent +// ============================================================================ + +/** + * @brief Test ingestEvent with valid event + */ +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* found = engine->findEventById("EVT-001"); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->priority, 100); + EXPECT_EQ(found->status, EventStatus::Pending); +} + +/** + * @brief Test ingestEvent with empty event ID + */ +TEST_F(CmsEngineTest, testIngestEventEmptyId) { + EventRecord event; + event.id = ""; + event.priority = 100; + + bool result = engine->ingestEvent(event); + EXPECT_FALSE(result); + + const EventRecord* found = engine->findEventById(""); + EXPECT_EQ(found, nullptr); +} + +/** + * @brief Test ingestEvent with priority exceeding maximum + */ +TEST_F(CmsEngineTest, testIngestEventPriorityExceedsMax) { + EventRecord event; + event.id = "EVT-002"; + event.priority = 256; + + bool result = engine->ingestEvent(event); + EXPECT_FALSE(result); + + const EventRecord* found = engine->findEventById("EVT-002"); + EXPECT_EQ(found, nullptr); +} + +/** + * @brief Test ingestEvent with priority at boundary (255) + */ +TEST_F(CmsEngineTest, testIngestEventPriorityAtBoundary) { + EventRecord event; + event.id = "EVT-003"; + event.priority = 255; + + bool result = engine->ingestEvent(event); + EXPECT_TRUE(result); + + const EventRecord* found = engine->findEventById("EVT-003"); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->priority, 255); +} + +/** + * @brief Test ingestEvent with priority at zero + */ +TEST_F(CmsEngineTest, testIngestEventPriorityZero) { + EventRecord event; + event.id = "EVT-004"; + event.priority = 0; + + bool result = engine->ingestEvent(event); + EXPECT_TRUE(result); + + const EventRecord* found = engine->findEventById("EVT-004"); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->priority, 0); +} + +/** + * @brief Test ingestEvent with multiple events + */ +TEST_F(CmsEngineTest, testIngestEventMultipleEvents) { + EventRecord event1; + event1.id = "EVT-001"; + event1.priority = 50; + + EventRecord event2; + event2.id = "EVT-002"; + event2.priority = 150; + + EXPECT_TRUE(engine->ingestEvent(event1)); + EXPECT_TRUE(engine->ingestEvent(event2)); + + const EventRecord* found1 = engine->findEventById("EVT-001"); + const EventRecord* found2 = engine->findEventById("EVT-002"); + ASSERT_NE(found1, nullptr); + ASSERT_NE(found2, nullptr); + EXPECT_EQ(found1->priority, 50); + EXPECT_EQ(found2->priority, 150); +} + +// ============================================================================ +// Tests for findPlanById +// ============================================================================ + +/** + * @brief Test findPlanById with existing plan + */ +TEST_F(CmsEngineTest, testFindPlanByIdExistingPlan) { + TaskPlan plan; + plan.name = "Test Plan"; + plan.type = PlanType::Centralized; + plan.status = PlanStatus::Drafting; + plan.resourceQuota = 0.8; + plan.constraints = "Test constraints"; + + EXPECT_TRUE(engine->createTaskPlan(plan)); + ASSERT_FALSE(plan.id.empty()); + + const TaskPlan* found = engine->findPlanById(plan.id); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->name, "Test Plan"); + EXPECT_EQ(found->type, PlanType::Centralized); + EXPECT_EQ(found->status, PlanStatus::Drafting); + EXPECT_DOUBLE_EQ(found->resourceQuota, 0.8); + EXPECT_EQ(found->constraints, "Test constraints"); +} + +/** + * @brief Test findPlanById with non-existent plan + */ +TEST_F(CmsEngineTest, testFindPlanByIdNonExistent) { + const TaskPlan* found = engine->findPlanById("NONEXISTENT"); + EXPECT_EQ(found, nullptr); +} + +/** + * @brief Test findPlanById with empty string + */ +TEST_F(CmsEngineTest, testFindPlanByIdEmptyString) { + const TaskPlan* found = engine->findPlanById(""); + EXPECT_EQ(found, nullptr); +} + +/** + * @brief Test findPlanById after multiple plans created + */ +TEST_F(CmsEngineTest, testFindPlanByIdMultiplePlans) { + TaskPlan plan1; + plan1.name = "Plan Alpha"; + engine->createTaskPlan(plan1); + + TaskPlan plan2; + plan2.name = "Plan Beta"; + engine->createTaskPlan(plan2); + + const TaskPlan* found1 = engine->findPlanById(plan1.id); + const TaskPlan* found2 = engine->findPlanById(plan2.id); + ASSERT_NE(found1, nullptr); + ASSERT_NE(found2, nullptr); + EXPECT_EQ(found1->name, "Plan Alpha"); + EXPECT_EQ(found2->name, "Plan Beta"); +} + +// ============================================================================ +// Tests for updatePlanStatus +// ============================================================================ + +/** + * @brief Test updatePlanStatus with valid plan + */ +TEST_F(CmsEngineTest, testUpdatePlanStatusValidPlan) { + TaskPlan plan; + plan.name = "Status Test"; + plan.status = PlanStatus::Drafting; + engine->createTaskPlan(plan); + + bool result = engine->updatePlanStatus(plan.id, PlanStatus::Approved); + EXPECT_TRUE(result); + + const TaskPlan* updated = engine->findPlanById(plan.id); + ASSERT_NE(updated, nullptr); + EXPECT_EQ(updated->status, PlanStatus::Approved); +} + +/** + * @brief Test updatePlanStatus with non-existent plan + */ +TEST_F(CmsEngineTest, testUpdatePlanStatusNonExistent) { + bool result = engine->updatePlanStatus("NONEXISTENT", PlanStatus::Approved); + EXPECT_FALSE(result); +} + +/** + * @brief Test updatePlanStatus with empty plan ID + */ +TEST_F(CmsEngineTest, testUpdatePlanStatusEmptyId) { + bool result = engine->updatePlanStatus("", PlanStatus::Approved); + EXPECT_FALSE(result); +} + +/** + * @brief Test updatePlanStatus multiple times on same plan + */ +TEST_F(CmsEngineTest, testUpdatePlanStatusMultipleUpdates) { + TaskPlan plan; + plan.name = "Multi Update"; + plan.status = PlanStatus::Drafting; + engine->createTaskPlan(plan); + + EXPECT_TRUE(engine->updatePlanStatus(plan.id, PlanStatus::Approved)); + EXPECT_TRUE(engine->updatePlanStatus(plan.id, PlanStatus::Active)); + EXPECT_TRUE(engine->updatePlanStatus(plan.id, PlanStatus::Completed)); + + const TaskPlan* updated = engine->findPlanById(plan.id); + ASSERT_NE(updated, nullptr); + EXPECT_EQ(updated->status, PlanStatus::Completed); +} + +/** + * @brief Test updatePlanStatus does not affect other plans + */ +TEST_F(CmsEngineTest, testUpdatePlanStatusIsolation) { + TaskPlan plan1; + plan1.name = "Plan One"; + plan1.status = PlanStatus::Drafting; + engine->createTaskPlan(plan1); + + TaskPlan plan2; + plan2.name = "Plan Two"; + plan2.status = PlanStatus::Drafting; + engine->createTaskPlan(plan2); + + engine->updatePlanStatus(plan1.id, PlanStatus::Approved); + + const TaskPlan* updated1 = engine->findPlanById(plan1.id); + const TaskPlan* updated2 = engine->findPlanById(plan2.id); + ASSERT_NE(updated1, nullptr); + ASSERT_NE(updated2, nullptr); + EXPECT_EQ(updated1->status, PlanStatus::Approved); + EXPECT_EQ(updated2->status, PlanStatus::Drafting); +} + +// ============================================================================ +// Tests for registerTemplate +// ============================================================================ + +/** + * @brief Test registerTemplate with valid template + */ +TEST_F(CmsEngineTest, testRegisterTemplateValid) { + TemplateInstance tmpl; + tmpl.templateId = "TMPL-001"; + tmpl.confidence = 0.85; + tmpl.scenario = "combat"; + + engine->registerTemplate(tmpl); + + const TemplateInstance* matched = engine->matchTemplate("combat"); + ASSERT_NE(matched, nullptr); + EXPECT_EQ(matched->templateId, "TMPL-001"); + EXPECT_DOUBLE_EQ(matched->confidence, 0.85); +} + +/** + * @brief Test registerTemplate with multiple templates + */ +TEST_F(CmsEngineTest, testRegisterTemplateMultiple) { + TemplateInstance tmpl1; + tmpl1.templateId = "TMPL-A"; + tmpl1.confidence = 0.7; + + TemplateInstance tmpl2; + tmpl2.templateId = "TMPL-B"; + tmpl2.confidence = 0.9; + + engine->registerTemplate(tmpl1); + engine->registerTemplate(tmpl2); + + const TemplateInstance* matched = engine->matchTemplate("any"); + ASSERT_NE(matched, nullptr); + EXPECT_EQ(matched->templateId, "TMPL-B"); + EXPECT_DOUBLE_EQ(matched->confidence, 0.9); +} + +/** + * @brief Test registerTemplate with empty template ID + */ +TEST_F(CmsEngineTest, testRegisterTemplateEmptyId) { + TemplateInstance tmpl; + tmpl.templateId = ""; + tmpl.confidence = 0.5; + + engine->registerTemplate(tmpl); + + const TemplateInstance* matched = engine->matchTemplate("any"); + ASSERT_NE(matched, nullptr); + EXPECT_TRUE(matched->templateId.empty()); +} + +/** + * @brief Test registerTemplate with zero confidence + */ +TEST_F(CmsEngineTest, testRegisterTemplateZeroConfidence) { + TemplateInstance tmpl; + tmpl.templateId = "TMPL-LOW"; + tmpl.confidence = 0.0; + + engine->registerTemplate(tmpl); + + const TemplateInstance* matched = engine->matchTemplate("any"); + ASSERT_NE(matched, nullptr); + EXPECT_DOUBLE_EQ(matched->confidence, 0.0); +} + +/** + * @brief Test registerTemplate with maximum confidence + */ +TEST_F(CmsEngineTest, testRegisterTemplateMaxConfidence) { + TemplateInstance tmpl; + tmpl.templateId = "TMPL-HIGH"; + tmpl.confidence = 1.0; + + engine->registerTemplate(tmpl); + + const TemplateInstance* matched = engine->matchTemplate("any"); + ASSERT_NE(matched, nullptr); + EXPECT_DOUBLE_EQ(matched->confidence, 1.0); +} + +/** + * @brief Test registerTemplate does not affect existing templates + */ +TEST_F(CmsEngineTest, testRegisterTemplateNoSideEffects) { + TemplateInstance tmpl1; + tmpl1.templateId = "TMPL-FIRST"; + tmpl1.confidence = 0.6; + engine->registerTemplate(tmpl1); + + TemplateInstance tmpl2; + tmpl2.templateId = "TMPL-SECOND"; + tmpl2.confidence = 0.8; + engine->registerTemplate(tmpl2); + + const TemplateInstance* matched = engine->matchTemplate("any"); + ASSERT_NE(matched, nullptr); + EXPECT_EQ(matched->templateId, "TMPL-SECOND"); +}