Implémentation complète du scénario 11 (IO System Stress Test) avec correction majeure de l'architecture de routing IntraIO. ## Nouveaux Modules de Test (Scenario 11) - ProducerModule: Publie messages pour tests IO - ConsumerModule: Consomme et valide messages reçus - BroadcastModule: Test multi-subscriber broadcasting - BatchModule: Test low-frequency batching - IOStressModule: Tests de charge concurrents ## Test d'Intégration - test_11_io_system.cpp: 6 tests validant: * Basic Publish-Subscribe * Pattern Matching avec wildcards * Multi-Module Routing (1-to-many) * Low-Frequency Subscriptions (batching) * Backpressure & Queue Overflow * Thread Safety (concurrent pub/pull) ## Fix Architecture Critique: IntraIO Routing **Problème**: IntraIO::publish() et subscribe() n'utilisaient PAS IntraIOManager pour router entre modules. **Solution**: Utilisation de JSON comme format de transport intermédiaire - IntraIO::publish() → extrait JSON → IntraIOManager::routeMessage() - IntraIO::subscribe() → enregistre au IntraIOManager::registerSubscription() - IntraIOManager::routeMessage() → copie JSON pour chaque subscriber → deliverMessage() **Bénéfices**: - ✅ Routing centralisé fonctionnel - ✅ Support 1-to-many (copie JSON au lieu de move unique_ptr) - ✅ Pas besoin d'implémenter IDataNode::clone() - ✅ Compatible futur NetworkIO (JSON sérialisable) ## Modules Scenario 13 (Cross-System) - ConfigWatcherModule, PlayerModule, EconomyModule, MetricsModule - test_13_cross_system.cpp (stub) ## Documentation - CLAUDE_NEXT_SESSION.md: Instructions détaillées pour build/test 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
230 lines
9.6 KiB
C++
230 lines
9.6 KiB
C++
#include "grove/JsonDataNode.h"
|
|
#include "grove/JsonDataTree.h"
|
|
#include "../helpers/TestMetrics.h"
|
|
#include "../helpers/TestAssertions.h"
|
|
#include "../helpers/TestReporter.h"
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <filesystem>
|
|
#include <thread>
|
|
#include <chrono>
|
|
|
|
using namespace grove;
|
|
|
|
int main() {
|
|
TestReporter reporter("DataNode Integration Test");
|
|
TestMetrics metrics;
|
|
|
|
std::cout << "================================================================================\n";
|
|
std::cout << "TEST: DataNode Integration Test\n";
|
|
std::cout << "================================================================================\n\n";
|
|
|
|
// === SETUP ===
|
|
std::cout << "Setup: Creating test directories...\n";
|
|
std::filesystem::create_directories("test_data/config");
|
|
std::filesystem::create_directories("test_data/data");
|
|
|
|
// Créer IDataTree
|
|
auto tree = std::make_unique<JsonDataTree>("test_data");
|
|
|
|
// ========================================================================
|
|
// TEST 1: Typed Setters & Getters
|
|
// ========================================================================
|
|
std::cout << "\n=== TEST 1: Typed Setters & Getters ===\n";
|
|
|
|
auto dataRoot = tree->getDataRoot();
|
|
|
|
// Create player node and add it to data root
|
|
auto playerNode = std::make_unique<JsonDataNode>("player", nlohmann::json::object());
|
|
|
|
// Test setters on the node before adding to tree
|
|
playerNode->setInt("score", 100);
|
|
playerNode->setString("name", "Player1");
|
|
playerNode->setBool("active", true);
|
|
playerNode->setDouble("ratio", 3.14);
|
|
|
|
// Add to tree
|
|
dataRoot->setChild("player", std::move(playerNode));
|
|
|
|
// Retrieve and test getters
|
|
auto retrievedPlayer = dataRoot->getChild("player");
|
|
ASSERT_TRUE(retrievedPlayer != nullptr, "Player node should exist");
|
|
|
|
ASSERT_EQ(retrievedPlayer->getInt("score"), 100, "setInt should work");
|
|
std::cout << " ✓ setInt/getInt works\n";
|
|
|
|
ASSERT_EQ(retrievedPlayer->getString("name"), "Player1", "setString should work");
|
|
std::cout << " ✓ setString/getString works\n";
|
|
|
|
ASSERT_EQ(retrievedPlayer->getBool("active"), true, "setBool should work");
|
|
std::cout << " ✓ setBool/getBool works\n";
|
|
|
|
double ratio = retrievedPlayer->getDouble("ratio");
|
|
ASSERT_TRUE(std::abs(ratio - 3.14) < 0.001, "setDouble should work");
|
|
std::cout << " ✓ setDouble/getDouble works\n";
|
|
|
|
reporter.addAssertion("typed_setters", true);
|
|
std::cout << "✓ TEST 1 PASSED\n";
|
|
|
|
// ========================================================================
|
|
// TEST 2: Data Hash
|
|
// ========================================================================
|
|
std::cout << "\n=== TEST 2: Data Hash ===\n";
|
|
|
|
auto testNode = std::make_unique<JsonDataNode>("test", nlohmann::json{
|
|
{"value", 42}
|
|
});
|
|
|
|
std::string hash1 = testNode->getDataHash();
|
|
std::cout << " Hash 1: " << hash1.substr(0, 16) << "...\n";
|
|
|
|
// Modify data
|
|
testNode->setInt("value", 43);
|
|
|
|
std::string hash2 = testNode->getDataHash();
|
|
std::cout << " Hash 2: " << hash2.substr(0, 16) << "...\n";
|
|
|
|
ASSERT_TRUE(hash1 != hash2, "Hashes should differ after data change");
|
|
|
|
reporter.addAssertion("data_hash", true);
|
|
std::cout << "✓ TEST 2 PASSED\n";
|
|
|
|
// ========================================================================
|
|
// TEST 3: Tree Hash
|
|
// ========================================================================
|
|
std::cout << "\n=== TEST 3: Tree Hash ===\n";
|
|
|
|
// Use data root to have a writable node
|
|
auto hashTestRoot = tree->getDataRoot();
|
|
|
|
auto child1 = std::make_unique<JsonDataNode>("child1", nlohmann::json{{"data", 1}});
|
|
auto child2 = std::make_unique<JsonDataNode>("child2", nlohmann::json{{"data", 2}});
|
|
|
|
hashTestRoot->setChild("child1", std::move(child1));
|
|
hashTestRoot->setChild("child2", std::move(child2));
|
|
|
|
std::string treeHash1 = hashTestRoot->getTreeHash();
|
|
std::cout << " Tree Hash 1: " << treeHash1.substr(0, 16) << "...\n";
|
|
|
|
// Modify child1: retrieve, modify, and put back
|
|
auto child1Retrieved = hashTestRoot->getChild("child1");
|
|
child1Retrieved->setInt("data", 999);
|
|
hashTestRoot->setChild("child1", std::move(child1Retrieved));
|
|
|
|
std::string treeHash2 = hashTestRoot->getTreeHash();
|
|
std::cout << " Tree Hash 2: " << treeHash2.substr(0, 16) << "...\n";
|
|
|
|
ASSERT_TRUE(treeHash1 != treeHash2, "Tree hash should change when child changes");
|
|
|
|
reporter.addAssertion("tree_hash", true);
|
|
std::cout << "✓ TEST 3 PASSED\n";
|
|
|
|
// ========================================================================
|
|
// TEST 4: Property Queries
|
|
// ========================================================================
|
|
std::cout << "\n=== TEST 4: Property Queries ===\n";
|
|
|
|
// Create an isolated vehicles container
|
|
auto vehiclesNode = std::make_unique<JsonDataNode>("vehicles", nlohmann::json::object());
|
|
|
|
// Create vehicles with different armor values
|
|
auto tank1 = std::make_unique<JsonDataNode>("tank1", nlohmann::json{{"armor", 150}});
|
|
auto tank2 = std::make_unique<JsonDataNode>("tank2", nlohmann::json{{"armor", 180}});
|
|
auto scout = std::make_unique<JsonDataNode>("scout", nlohmann::json{{"armor", 50}});
|
|
|
|
vehiclesNode->setChild("tank1", std::move(tank1));
|
|
vehiclesNode->setChild("tank2", std::move(tank2));
|
|
vehiclesNode->setChild("scout", std::move(scout));
|
|
|
|
// Query: armor > 100
|
|
// Note: queryByProperty searches recursively in the subtree
|
|
auto armoredVehicles = vehiclesNode->queryByProperty("armor",
|
|
[](const IDataValue& val) {
|
|
return val.isNumber() && val.asInt() > 100;
|
|
});
|
|
|
|
std::cout << " Vehicles with armor > 100: " << armoredVehicles.size() << "\n";
|
|
for (const auto& node : armoredVehicles) {
|
|
int armor = node->getInt("armor");
|
|
std::cout << " - " << node->getName() << " (armor=" << armor << ")\n";
|
|
ASSERT_TRUE(armor > 100, "All queried vehicles should have armor > 100");
|
|
}
|
|
ASSERT_EQ(armoredVehicles.size(), 2, "Should find 2 armored vehicles");
|
|
|
|
reporter.addAssertion("property_queries", true);
|
|
std::cout << "✓ TEST 4 PASSED\n";
|
|
|
|
// ========================================================================
|
|
// TEST 5: Pattern Matching
|
|
// ========================================================================
|
|
std::cout << "\n=== TEST 5: Pattern Matching ===\n";
|
|
|
|
// Create an isolated units container
|
|
auto unitsNode = std::make_unique<JsonDataNode>("units", nlohmann::json::object());
|
|
|
|
auto heavy_mk1 = std::make_unique<JsonDataNode>("heavy_mk1", nlohmann::json{{"type", "tank"}});
|
|
auto heavy_mk2 = std::make_unique<JsonDataNode>("heavy_mk2", nlohmann::json{{"type", "tank"}});
|
|
auto heavy_trooper = std::make_unique<JsonDataNode>("heavy_trooper", nlohmann::json{{"type", "infantry"}});
|
|
auto light_scout = std::make_unique<JsonDataNode>("light_scout", nlohmann::json{{"type", "vehicle"}});
|
|
|
|
unitsNode->setChild("heavy_mk1", std::move(heavy_mk1));
|
|
unitsNode->setChild("heavy_mk2", std::move(heavy_mk2));
|
|
unitsNode->setChild("heavy_trooper", std::move(heavy_trooper));
|
|
unitsNode->setChild("light_scout", std::move(light_scout));
|
|
|
|
// Pattern: *heavy*
|
|
// Note: getChildrenByNameMatch searches recursively in the entire subtree
|
|
// It will match children whose names contain "heavy"
|
|
auto heavyUnits = unitsNode->getChildrenByNameMatch("*heavy*");
|
|
std::cout << " Pattern '*heavy*' matched: " << heavyUnits.size() << " units\n";
|
|
for (const auto& node : heavyUnits) {
|
|
std::cout << " - " << node->getName() << "\n";
|
|
}
|
|
ASSERT_EQ(heavyUnits.size(), 3, "Should match 3 'heavy' units");
|
|
reporter.addMetric("pattern_heavy_count", heavyUnits.size());
|
|
|
|
// Pattern: *_mk*
|
|
auto mkUnits = unitsNode->getChildrenByNameMatch("*_mk*");
|
|
std::cout << " Pattern '*_mk*' matched: " << mkUnits.size() << " units\n";
|
|
ASSERT_EQ(mkUnits.size(), 2, "Should match 2 '_mk' units");
|
|
|
|
reporter.addAssertion("pattern_matching", true);
|
|
std::cout << "✓ TEST 5 PASSED\n";
|
|
|
|
// ========================================================================
|
|
// TEST 6: Defaults
|
|
// ========================================================================
|
|
std::cout << "\n=== TEST 6: Type Defaults ===\n";
|
|
|
|
auto configNode = std::make_unique<JsonDataNode>("config", nlohmann::json{
|
|
{"existing", 42}
|
|
});
|
|
|
|
// Test defaults for missing properties
|
|
int missing = configNode->getInt("missing", 100);
|
|
ASSERT_EQ(missing, 100, "getInt with default should return default");
|
|
|
|
std::string missingStr = configNode->getString("missing", "default");
|
|
ASSERT_EQ(missingStr, "default", "getString with default should return default");
|
|
|
|
bool missingBool = configNode->getBool("missing", true);
|
|
ASSERT_EQ(missingBool, true, "getBool with default should return default");
|
|
|
|
reporter.addAssertion("type_defaults", true);
|
|
std::cout << "✓ TEST 6 PASSED\n";
|
|
|
|
// ========================================================================
|
|
// CLEANUP
|
|
// ========================================================================
|
|
std::filesystem::remove_all("test_data");
|
|
|
|
// ========================================================================
|
|
// RAPPORT FINAL
|
|
// ========================================================================
|
|
|
|
metrics.printReport();
|
|
reporter.printFinalReport();
|
|
|
|
return reporter.getExitCode();
|
|
}
|