286 lines
9.3 KiB
C++
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
|
|
}
|