aissia/tests/modules/MonitoringModuleTests.cpp

286 lines
9.3 KiB
C++

/**
* @file MonitoringModuleTests.cpp
* @brief Integration tests for MonitoringModule (10 TI)
*/
#include <catch2/catch_test_macros.hpp>
#include "mocks/MockIO.hpp"
#include "utils/TimeSimulator.hpp"
#include "utils/TestHelpers.hpp"
#include "modules/MonitoringModule.h"
#include <grove/JsonDataNode.h>
using namespace aissia;
using namespace aissia::tests;
// ============================================================================
// Test Fixture
// ============================================================================
class MonitoringTestFixture {
public:
MockIO io;
TimeSimulator time;
MonitoringModule module;
void configure(const json& config = json::object()) {
json fullConfig = {
{"enabled", true},
{"productive_apps", json::array({"Code", "CLion", "Visual Studio"})},
{"distracting_apps", json::array({"Discord", "Steam", "YouTube"})}
};
fullConfig.merge_patch(config);
grove::JsonDataNode configNode("config", fullConfig);
module.setConfiguration(configNode, &io, nullptr);
}
void process() {
grove::JsonDataNode input("input", time.createInput());
module.process(input);
}
};
// ============================================================================
// TI_MONITOR_001: App Changed
// ============================================================================
TEST_CASE("TI_MONITOR_001_AppChanged", "[monitoring][integration]") {
MonitoringTestFixture f;
f.configure();
// Inject window change
f.io.injectMessage("platform:window_changed", {
{"oldApp", ""},
{"newApp", "Code"},
{"duration", 0}
});
f.process();
// Verify app_changed published
REQUIRE(f.io.wasPublished("monitoring:app_changed"));
auto msg = f.io.getLastPublished("monitoring:app_changed");
REQUIRE(msg["appName"] == "Code");
}
// ============================================================================
// TI_MONITOR_002: Productive App Classification
// ============================================================================
TEST_CASE("TI_MONITOR_002_ProductiveAppClassification", "[monitoring][integration]") {
MonitoringTestFixture f;
f.configure();
f.io.injectMessage("platform:window_changed", {
{"oldApp", ""},
{"newApp", "Code"},
{"duration", 0}
});
f.process();
REQUIRE(f.io.wasPublished("monitoring:app_changed"));
auto msg = f.io.getLastPublished("monitoring:app_changed");
REQUIRE(msg["classification"] == "productive");
}
// ============================================================================
// TI_MONITOR_003: Distracting App Classification
// ============================================================================
TEST_CASE("TI_MONITOR_003_DistractingAppClassification", "[monitoring][integration]") {
MonitoringTestFixture f;
f.configure();
f.io.injectMessage("platform:window_changed", {
{"oldApp", ""},
{"newApp", "Discord"},
{"duration", 0}
});
f.process();
REQUIRE(f.io.wasPublished("monitoring:app_changed"));
auto msg = f.io.getLastPublished("monitoring:app_changed");
REQUIRE(msg["classification"] == "distracting");
}
// ============================================================================
// TI_MONITOR_004: Neutral App Classification
// ============================================================================
TEST_CASE("TI_MONITOR_004_NeutralAppClassification", "[monitoring][integration]") {
MonitoringTestFixture f;
f.configure();
f.io.injectMessage("platform:window_changed", {
{"oldApp", ""},
{"newApp", "Notepad"},
{"duration", 0}
});
f.process();
REQUIRE(f.io.wasPublished("monitoring:app_changed"));
auto msg = f.io.getLastPublished("monitoring:app_changed");
REQUIRE(msg["classification"] == "neutral");
}
// ============================================================================
// TI_MONITOR_005: Duration Tracking
// ============================================================================
TEST_CASE("TI_MONITOR_005_DurationTracking", "[monitoring][integration]") {
MonitoringTestFixture f;
f.configure();
// Start with Code
f.io.injectMessage("platform:window_changed", {
{"oldApp", ""},
{"newApp", "Code"},
{"duration", 0}
});
f.process();
f.io.clearPublished();
// Switch after 60 seconds
f.io.injectMessage("platform:window_changed", {
{"oldApp", "Code"},
{"newApp", "Discord"},
{"duration", 60}
});
f.process();
// Verify duration tracked
auto state = f.module.getState();
// TODO: Verify appDurations["Code"] == 60
SUCCEED(); // Placeholder
}
// ============================================================================
// TI_MONITOR_006: Idle Detected Pauses Tracking
// ============================================================================
TEST_CASE("TI_MONITOR_006_IdleDetectedPausesTracking", "[monitoring][integration]") {
MonitoringTestFixture f;
f.configure();
// Start tracking
f.io.injectMessage("platform:window_changed", {
{"oldApp", ""},
{"newApp", "Code"},
{"duration", 0}
});
f.process();
// Go idle
f.io.injectMessage("platform:idle_detected", {{"idleSeconds", 300}});
f.process();
// Verify idle state
auto state = f.module.getState();
// TODO: Verify isIdle == true
SUCCEED(); // Placeholder
}
// ============================================================================
// TI_MONITOR_007: Activity Resumed Resumes Tracking
// ============================================================================
TEST_CASE("TI_MONITOR_007_ActivityResumedResumesTracking", "[monitoring][integration]") {
MonitoringTestFixture f;
f.configure();
// Setup idle state
f.io.injectMessage("platform:window_changed", {{"oldApp", ""}, {"newApp", "Code"}, {"duration", 0}});
f.process();
f.io.injectMessage("platform:idle_detected", {});
f.process();
// Resume
f.io.injectMessage("platform:activity_resumed", {});
f.process();
// Verify not idle
auto state = f.module.getState();
// TODO: Verify isIdle == false
SUCCEED(); // Placeholder
}
// ============================================================================
// TI_MONITOR_008: Productivity Stats
// ============================================================================
TEST_CASE("TI_MONITOR_008_ProductivityStats", "[monitoring][integration]") {
MonitoringTestFixture f;
f.configure();
// Use productive app for 60s
f.io.injectMessage("platform:window_changed", {{"oldApp", ""}, {"newApp", "Code"}, {"duration", 0}});
f.process();
f.io.injectMessage("platform:window_changed", {{"oldApp", "Code"}, {"newApp", "Discord"}, {"duration", 60}});
f.process();
// Use distracting app for 30s
f.io.injectMessage("platform:window_changed", {{"oldApp", "Discord"}, {"newApp", "Code"}, {"duration", 30}});
f.process();
// Verify stats
auto state = f.module.getState();
// TODO: Verify totalProductiveSeconds == 60, totalDistractingSeconds == 30
SUCCEED(); // Placeholder
}
// ============================================================================
// TI_MONITOR_009: Tool Query Get Current App
// ============================================================================
TEST_CASE("TI_MONITOR_009_ToolQueryGetCurrentApp", "[monitoring][integration]") {
MonitoringTestFixture f;
f.configure();
// Set current app
f.io.injectMessage("platform:window_changed", {{"oldApp", ""}, {"newApp", "Code"}, {"duration", 0}});
f.process();
f.io.clearPublished();
// Query
f.io.injectMessage("monitoring:query", {
{"action", "get_current_app"},
{"correlation_id", "test-456"}
});
f.process();
// Verify response
REQUIRE(f.io.wasPublished("monitoring:response"));
auto resp = f.io.getLastPublished("monitoring:response");
REQUIRE(resp["correlation_id"] == "test-456");
}
// ============================================================================
// TI_MONITOR_010: State Serialization
// ============================================================================
TEST_CASE("TI_MONITOR_010_StateSerialization", "[monitoring][integration]") {
MonitoringTestFixture f;
f.configure();
// Build up some state
f.io.injectMessage("platform:window_changed", {{"oldApp", ""}, {"newApp", "Code"}, {"duration", 0}});
f.process();
f.io.injectMessage("platform:window_changed", {{"oldApp", "Code"}, {"newApp", "Discord"}, {"duration", 120}});
f.process();
// Get state
auto state = f.module.getState();
REQUIRE(state != nullptr);
// Restore to new module
MonitoringModule module2;
grove::JsonDataNode configNode2("config", json::object());
module2.setConfiguration(configNode2, &f.io, nullptr);
module2.setState(*state);
auto state2 = module2.getState();
REQUIRE(state2 != nullptr);
SUCCEED(); // Placeholder
}