294 lines
9.1 KiB
C++
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
|
|
}
|