aissia/tests/modules/StorageModuleTests.cpp

294 lines
9.1 KiB
C++

/**
* @file StorageModuleTests.cpp
* @brief Integration tests for StorageModule (10 TI)
*/
#include <catch2/catch_test_macros.hpp>
#include "mocks/MockIO.hpp"
#include "utils/TimeSimulator.hpp"
#include "utils/TestHelpers.hpp"
#include "modules/StorageModule.h"
#include <grove/JsonDataNode.h>
using namespace aissia;
using namespace aissia::tests;
// ============================================================================
// Test Fixture
// ============================================================================
class StorageTestFixture {
public:
MockIO io;
TimeSimulator time;
StorageModule module;
void configure(const json& config = json::object()) {
json fullConfig = json::object();
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_STORAGE_001: Task Completed Saves Session
// ============================================================================
TEST_CASE("TI_STORAGE_001_TaskCompletedSavesSession", "[storage][integration]") {
StorageTestFixture f;
f.configure();
// Receive task completed
f.io.injectMessage("scheduler:task_completed", {
{"taskId", "task-1"},
{"taskName", "Coding session"},
{"durationMinutes", 45},
{"hyperfocus", false}
});
f.process();
// Verify save_session published
REQUIRE(f.io.wasPublished("storage:save_session"));
auto msg = f.io.getLastPublished("storage:save_session");
REQUIRE(msg["taskName"] == "Coding session");
REQUIRE(msg["durationMinutes"] == 45);
}
// ============================================================================
// TI_STORAGE_002: App Changed Saves Usage
// ============================================================================
TEST_CASE("TI_STORAGE_002_AppChangedSavesUsage", "[storage][integration]") {
StorageTestFixture f;
f.configure();
// Receive app changed with duration
f.io.injectMessage("monitoring:app_changed", {
{"appName", "Code"},
{"oldApp", "Discord"},
{"duration", 120},
{"classification", "productive"}
});
f.process();
// Verify save_app_usage published
REQUIRE(f.io.wasPublished("storage:save_app_usage"));
auto msg = f.io.getLastPublished("storage:save_app_usage");
REQUIRE(msg["appName"] == "Discord"); // Old app that ended
REQUIRE(msg["durationSeconds"] == 120);
}
// ============================================================================
// TI_STORAGE_003: Session Saved Updates Last ID
// ============================================================================
TEST_CASE("TI_STORAGE_003_SessionSavedUpdatesLastId", "[storage][integration]") {
StorageTestFixture f;
f.configure();
// Receive session saved confirmation
f.io.injectMessage("storage:session_saved", {
{"sessionId", 42}
});
f.process();
// Verify state updated
auto state = f.module.getState();
// TODO: Verify lastSessionId == 42
SUCCEED(); // Placeholder
}
// ============================================================================
// TI_STORAGE_004: Storage Error Handled
// ============================================================================
TEST_CASE("TI_STORAGE_004_StorageErrorHandled", "[storage][integration]") {
StorageTestFixture f;
f.configure();
// Receive storage error
f.io.injectMessage("storage:error", {
{"message", "Database locked"}
});
// Should not throw
REQUIRE_NOTHROW(f.process());
}
// ============================================================================
// TI_STORAGE_005: Pending Saves Tracking
// ============================================================================
TEST_CASE("TI_STORAGE_005_PendingSavesTracking", "[storage][integration]") {
StorageTestFixture f;
f.configure();
// Trigger save
f.io.injectMessage("scheduler:task_completed", {
{"taskId", "t1"},
{"taskName", "Task"},
{"durationMinutes", 10}
});
f.process();
// Verify pending incremented
auto state = f.module.getState();
// TODO: Verify pendingSaves == 1
SUCCEED(); // Placeholder
}
// ============================================================================
// TI_STORAGE_006: Total Saved Tracking
// ============================================================================
TEST_CASE("TI_STORAGE_006_TotalSavedTracking", "[storage][integration]") {
StorageTestFixture f;
f.configure();
// Save and confirm multiple times
for (int i = 0; i < 3; i++) {
f.io.injectMessage("scheduler:task_completed", {
{"taskId", "t" + std::to_string(i)},
{"taskName", "Task"},
{"durationMinutes", 10}
});
f.process();
f.io.injectMessage("storage:session_saved", {{"sessionId", i}});
f.process();
}
// Verify total
auto state = f.module.getState();
// TODO: Verify totalSaved == 3
SUCCEED(); // Placeholder
}
// ============================================================================
// TI_STORAGE_007: Tool Query Notes
// ============================================================================
TEST_CASE("TI_STORAGE_007_ToolQueryNotes", "[storage][integration]") {
StorageTestFixture f;
f.configure();
// Add a note first
f.io.injectMessage("storage:command", {
{"action", "save_note"},
{"content", "Test note"},
{"tags", json::array({"test", "important"})}
});
f.process();
f.io.clearPublished();
// Query notes
f.io.injectMessage("storage:query", {
{"action", "query_notes"},
{"correlation_id", "query-1"}
});
f.process();
// Verify response
REQUIRE(f.io.wasPublished("storage:response"));
auto resp = f.io.getLastPublished("storage:response");
REQUIRE(resp["correlation_id"] == "query-1");
}
// ============================================================================
// TI_STORAGE_008: Tool Command Save Note
// ============================================================================
TEST_CASE("TI_STORAGE_008_ToolCommandSaveNote", "[storage][integration]") {
StorageTestFixture f;
f.configure();
// Save note
f.io.injectMessage("storage:command", {
{"action", "save_note"},
{"content", "Remember to check logs"},
{"tags", json::array({"reminder"})}
});
f.process();
// Verify note added to state
auto state = f.module.getState();
// TODO: Verify notes contains the new note
SUCCEED(); // Placeholder
}
// ============================================================================
// TI_STORAGE_009: Note Tags Filtering
// ============================================================================
TEST_CASE("TI_STORAGE_009_NoteTagsFiltering", "[storage][integration]") {
StorageTestFixture f;
f.configure();
// Add notes with different tags
f.io.injectMessage("storage:command", {
{"action", "save_note"},
{"content", "Work note"},
{"tags", json::array({"work"})}
});
f.process();
f.io.injectMessage("storage:command", {
{"action", "save_note"},
{"content", "Personal note"},
{"tags", json::array({"personal"})}
});
f.process();
f.io.clearPublished();
// Query with tag filter
f.io.injectMessage("storage:query", {
{"action", "query_notes"},
{"tags", json::array({"work"})},
{"correlation_id", "filter-1"}
});
f.process();
// Verify filtered response
REQUIRE(f.io.wasPublished("storage:response"));
auto resp = f.io.getLastPublished("storage:response");
// TODO: Verify only work notes returned
SUCCEED(); // Placeholder
}
// ============================================================================
// TI_STORAGE_010: State Serialization
// ============================================================================
TEST_CASE("TI_STORAGE_010_StateSerialization", "[storage][integration]") {
StorageTestFixture f;
f.configure();
// Build state with notes
f.io.injectMessage("storage:command", {
{"action", "save_note"},
{"content", "Test note for serialization"},
{"tags", json::array({"test"})}
});
f.process();
// Get state
auto state = f.module.getState();
REQUIRE(state != nullptr);
// Restore
StorageModule 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
}