LCOV - code coverage report
Current view: top level - /opt/artdaq/srcs/artdaq-database/test/CaseStudy/OtsDaqTests/test004 - ThreeVersionsBug_t.cc (source / functions) Coverage Total Hit
Test: artdaq.info.cleaned Lines: 82.9 % 858 711
Test Date: 2026-10-29 00:35:58 Functions: 94.1 % 51 48

            Line data    Source code
       1              : #define BOOST_TEST_DYN_LINK
       2              : #define BOOST_TEST_MODULE (ThreeVersionsBug test)
       3              : 
       4              : #include <boost/test/unit_test.hpp>
       5              : 
       6              : #include <dirent.h>
       7              : #include <atomic>
       8              : #include <chrono>
       9              : #include <fstream>
      10              : #include <functional>
      11              : #include <iostream>
      12              : #include <map>
      13              : #include <memory>
      14              : #include <mutex>
      15              : #include <regex>
      16              : #include <set>
      17              : #include <sstream>
      18              : #include <string>
      19              : #include <thread>
      20              : #include <vector>
      21              : 
      22              : #include "../DatabaseConfigurationInterface.h"
      23              : 
      24              : using namespace ots;
      25              : 
      26              : struct BugTestData {
      27            2 :   BugTestData() {
      28            2 :     srand(time(nullptr));
      29            2 :     baseVersion = rand() % 99999 + 100000;
      30            2 :     std::cout << "=== INVESTIGATING THREE VERSIONS BUG ===\n";
      31            2 :     std::cout << "Base version: " << baseVersion << "\n\n";
      32              : 
      33            2 :     auto uri = std::getenv("ARTDAQ_DATABASE_URI");
      34            2 :     if (uri) {
      35            2 :       std::string uriStr(uri);
      36            2 :       if (uriStr.find("filesystemdb:///") == 0) {
      37            1 :         dbPath = uriStr.substr(16);
      38            1 :       } else if (uriStr.find("filesystemdb://") == 0) {
      39            0 :         dbPath = uriStr.substr(15);
      40              :       } else {
      41            1 :         dbPath = uriStr;
      42              :       }
      43            2 :       std::cout << "Database path: " << dbPath << "\n";
      44            2 :     }
      45            2 :   }
      46              : 
      47            2 :   ~BugTestData() { std::cout << "\n=== INVESTIGATION COMPLETE ===\n"; }
      48              : 
      49              :   int baseVersion;
      50              :   std::string dbPath;
      51              : };
      52              : 
      53              : BugTestData fixture;
      54              : 
      55            1 : std::string readFileContent(const std::string& filepath) {
      56            1 :   std::ifstream file(filepath);
      57            1 :   if (!file.is_open()) {
      58            2 :     return "";
      59              :   }
      60            0 :   std::stringstream buffer;
      61            0 :   buffer << file.rdbuf();
      62            0 :   return buffer.str();
      63            1 : }
      64              : 
      65            6 : std::vector<std::string> listJsonFiles(const std::string& dirPath) {
      66            6 :   std::vector<std::string> files;
      67              : 
      68            6 :   DIR* dir = opendir(dirPath.c_str());
      69            6 :   if (dir == nullptr) {
      70            6 :     return files;
      71              :   }
      72              : 
      73              :   struct dirent* entry;
      74            0 :   while ((entry = readdir(dir)) != nullptr) {
      75            0 :     std::string name = entry->d_name;
      76            0 :     if (name.size() > 5 && name.substr(name.size() - 5) == ".json" && name != "index.json") {
      77            0 :       files.push_back(dirPath + "/" + name);
      78              :     }
      79            0 :   }
      80            0 :   closedir(dir);
      81            0 :   return files;
      82            0 : }
      83              : 
      84            0 : int countConfigTypeOccurrences(const std::string& jsonContent, const std::string& configType) {
      85            0 :   std::regex pattern("\"name\"\\s*:\\s*\"" + configType + "\"");
      86              : 
      87            0 :   auto begin = std::sregex_iterator(jsonContent.begin(), jsonContent.end(), pattern);
      88            0 :   auto end = std::sregex_iterator();
      89              : 
      90            0 :   return std::distance(begin, end);
      91            0 : }
      92              : 
      93            0 : std::vector<std::string> extractConfigNames(const std::string& jsonContent) {
      94            0 :   std::vector<std::string> names;
      95            0 :   std::regex pattern("\"name\"\\s*:\\s*\"([^\"]+)\"");
      96              : 
      97            0 :   auto begin = std::sregex_iterator(jsonContent.begin(), jsonContent.end(), pattern);
      98            0 :   auto end = std::sregex_iterator();
      99              : 
     100            0 :   for (auto it = begin; it != end; ++it) {
     101            0 :     names.push_back((*it)[1].str());
     102            0 :   }
     103            0 :   return names;
     104            0 : }
     105              : 
     106            4 : bool isFileSystemBackend() {
     107            4 :   auto uri = std::getenv("ARTDAQ_DATABASE_URI");
     108            4 :   if (uri) {
     109            4 :     std::string uriStr(uri);
     110            4 :     return uriStr.find("filesystemdb") != std::string::npos;
     111            4 :   }
     112            0 :   return false;
     113              : }
     114              : 
     115            1 : std::set<std::string> extractOidsFromIndex(const std::string& indexPath) {
     116            1 :   std::set<std::string> oids;
     117            1 :   std::string content = readFileContent(indexPath);
     118            1 :   if (content.empty()) {
     119            1 :     return oids;
     120              :   }
     121              : 
     122            0 :   std::regex pattern("\"oid\"\\s*:\\s*\"([a-f0-9]+)\"");
     123              : 
     124            0 :   auto begin = std::sregex_iterator(content.begin(), content.end(), pattern);
     125            0 :   auto end = std::sregex_iterator();
     126              : 
     127            0 :   for (auto it = begin; it != end; ++it) {
     128            0 :     oids.insert((*it)[1].str());
     129            0 :   }
     130            0 :   return oids;
     131            1 : }
     132              : 
     133            0 : std::string getOidFromFilename(const std::string& filepath) {
     134            0 :   size_t lastSlash = filepath.rfind('/');
     135            0 :   std::string filename = (lastSlash != std::string::npos) ? filepath.substr(lastSlash + 1) : filepath;
     136              : 
     137            0 :   if (filename.size() > 5 && filename.substr(filename.size() - 5) == ".json") {
     138            0 :     return filename.substr(0, filename.size() - 5);
     139              :   }
     140            0 :   return "";
     141            0 : }
     142              : 
     143              : BOOST_AUTO_TEST_SUITE(three_versions_bug_test)
     144              : 
     145            4 : BOOST_AUTO_TEST_CASE(attempt_three_versions_same_type) {
     146            2 :   std::cout << "\n=== TEST 1: Attempting to add three versions ===\n";
     147              : 
     148            2 :   auto ifc = DatabaseConfigurationInterface();
     149              : 
     150            2 :   int version1 = fixture.baseVersion;
     151            2 :   int version2 = fixture.baseVersion + 1;
     152            2 :   int version3 = fixture.baseVersion + 2;
     153              : 
     154            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
     155            2 :   cfg1->getView().version = version1;
     156            2 :   ifc.saveActiveVersion(cfg1.get());
     157            2 :   std::cout << "Created version 1: " << version1 << "\n";
     158              : 
     159            2 :   std::shared_ptr<ConfigurationBase> cfg2 = std::make_shared<TestConfiguration001>();
     160            2 :   cfg2->getView().version = version2;
     161            2 :   ifc.saveActiveVersion(cfg2.get());
     162            2 :   std::cout << "Created version 2: " << version2 << "\n";
     163              : 
     164            2 :   std::shared_ptr<ConfigurationBase> cfg3 = std::make_shared<TestConfiguration001>();
     165            2 :   cfg3->getView().version = version3;
     166            2 :   ifc.saveActiveVersion(cfg3.get());
     167            2 :   std::cout << "Created version 3: " << version3 << "\n";
     168              : 
     169            2 :   std::cout << "\nAttempt 1: Using std::map...\n";
     170            2 :   auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     171              : 
     172            2 :   compositionMap[cfg1->getConfigurationName()] = version1;
     173            2 :   std::cout << "  Added version1, map size: " << compositionMap.size() << "\n";
     174              : 
     175            2 :   compositionMap[cfg2->getConfigurationName()] = version2;
     176            2 :   std::cout << "  Added version2, map size: " << compositionMap.size() << " (should still be 1)\n";
     177              : 
     178            2 :   compositionMap[cfg3->getConfigurationName()] = version3;
     179            2 :   std::cout << "  Added version3, map size: " << compositionMap.size() << " (should still be 1)\n";
     180              : 
     181            2 :   BOOST_CHECK_EQUAL(compositionMap.size(), 1);
     182            2 :   BOOST_CHECK_EQUAL(compositionMap[cfg1->getConfigurationName()], version3);
     183              : 
     184            2 :   std::cout << "  Result: Map correctly contains only last version: " << version3 << "\n";
     185              : 
     186            6 :   std::string compName = std::string{"BugTest1_"} + std::to_string(fixture.baseVersion);
     187              : 
     188            2 :   ifc.storeGlobalConfiguration(compositionMap, compName);
     189            2 :   std::cout << "  Created composition: " << compName << "\n";
     190              : 
     191            2 :   auto loadedMap = ifc.loadGlobalConfiguration(compName);
     192            2 :   BOOST_CHECK_EQUAL(loadedMap.size(), 1);
     193              : 
     194            2 :   std::cout << "  Verified: Composition has " << loadedMap.size() << " member (correct)\n";
     195            2 : }
     196              : 
     197            4 : BOOST_AUTO_TEST_CASE(inspect_database_storage_format) {
     198            2 :   std::cout << "\n=== TEST 2: Inspecting database storage format ===\n";
     199              : 
     200            2 :   auto ifc = DatabaseConfigurationInterface();
     201              : 
     202            2 :   int version1 = fixture.baseVersion + 10;
     203            2 :   int version2 = fixture.baseVersion + 11;
     204              : 
     205            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
     206            2 :   cfg1->getView().version = version1;
     207            2 :   ifc.saveActiveVersion(cfg1.get());
     208              : 
     209            2 :   std::shared_ptr<ConfigurationBase> cfg2 = std::make_shared<TestConfiguration002>();
     210            2 :   cfg2->getView().version = version2;
     211            2 :   ifc.saveActiveVersion(cfg2.get());
     212              : 
     213            2 :   auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     214            2 :   compositionMap[cfg1->getConfigurationName()] = version1;
     215            2 :   compositionMap[cfg2->getConfigurationName()] = version2;
     216              : 
     217            6 :   std::string compName = std::string{"BugTest2_"} + std::to_string(fixture.baseVersion);
     218              : 
     219            2 :   ifc.storeGlobalConfiguration(compositionMap, compName);
     220              : 
     221            2 :   auto loadedMap = ifc.loadGlobalConfiguration(compName);
     222              : 
     223            2 :   std::cout << "Composition '" << compName << "' contains:\n";
     224            6 :   for (auto const& entry : loadedMap) {
     225            4 :     std::cout << "  " << entry.first << " -> version " << entry.second << "\n";
     226              :   }
     227              : 
     228            2 :   BOOST_CHECK_EQUAL(loadedMap.size(), 2);
     229              : 
     230            2 :   int count001 = loadedMap.count(cfg1->getConfigurationName());
     231            2 :   int count002 = loadedMap.count(cfg2->getConfigurationName());
     232              : 
     233            2 :   BOOST_CHECK_EQUAL(count001, 1);
     234            2 :   BOOST_CHECK_EQUAL(count002, 1);
     235              : 
     236            2 :   std::cout << "  Verified: Each configuration type appears exactly once\n";
     237            2 : }
     238              : 
     239            4 : BOOST_AUTO_TEST_CASE(verify_raw_storage_no_duplicates) {
     240            2 :   std::cout << "\n=== TEST 3: Verifying raw storage has no duplicates ===\n";
     241              : 
     242            2 :   auto ifc = DatabaseConfigurationInterface();
     243              : 
     244            2 :   int version1 = fixture.baseVersion + 20;
     245            2 :   int version2 = fixture.baseVersion + 21;
     246              : 
     247            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
     248            2 :   cfg1->getView().version = version1;
     249            2 :   ifc.saveActiveVersion(cfg1.get());
     250              : 
     251            2 :   std::shared_ptr<ConfigurationBase> cfg2 = std::make_shared<TestConfiguration002>();
     252            2 :   cfg2->getView().version = version2;
     253            2 :   ifc.saveActiveVersion(cfg2.get());
     254              : 
     255            2 :   auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     256            2 :   compositionMap[cfg1->getConfigurationName()] = version1;
     257            2 :   compositionMap[cfg2->getConfigurationName()] = version2;
     258              : 
     259            6 :   std::string compName = std::string{"RawStorageTest_"} + std::to_string(fixture.baseVersion);
     260            2 :   ifc.storeGlobalConfiguration(compositionMap, compName);
     261              : 
     262            2 :   std::cout << "Created composition: " << compName << "\n";
     263              : 
     264            2 :   std::string globalConfigDir = fixture.dbPath + "/GlobalConfigurations";
     265            2 :   std::cout << "Scanning: " << globalConfigDir << "\n";
     266              : 
     267            2 :   auto jsonFiles = listJsonFiles(globalConfigDir);
     268            2 :   std::cout << "Found " << jsonFiles.size() << " JSON files\n";
     269              : 
     270            2 :   bool foundOurComposition = false;
     271            2 :   int totalDuplicatesFound = 0;
     272              : 
     273            2 :   for (const auto& filepath : jsonFiles) {
     274            0 :     std::string content = readFileContent(filepath);
     275            0 :     if (content.empty()) {
     276            0 :       continue;
     277              :     }
     278              : 
     279            0 :     if (content.find(compName) != std::string::npos) {
     280            0 :       foundOurComposition = true;
     281            0 :       std::cout << "\nFound our composition in: " << filepath << "\n";
     282              : 
     283            0 :       auto configNames = extractConfigNames(content);
     284            0 :       std::cout << "  Configuration entries found: " << configNames.size() << "\n";
     285              : 
     286            0 :       std::set<std::string> uniqueNames;
     287            0 :       std::map<std::string, int> nameCounts;
     288              : 
     289            0 :       for (const auto& name : configNames) {
     290            0 :         nameCounts[name]++;
     291            0 :         uniqueNames.insert(name);
     292              :       }
     293              : 
     294            0 :       for (const auto& entry : nameCounts) {
     295            0 :         if (entry.second > 1) {
     296            0 :           std::cout << "  DUPLICATE DETECTED: '" << entry.first << "' appears " << entry.second << " times!\n";
     297            0 :           totalDuplicatesFound++;
     298              :         }
     299              :       }
     300              : 
     301            0 :       int count001 = countConfigTypeOccurrences(content, cfg1->getConfigurationName());
     302            0 :       int count002 = countConfigTypeOccurrences(content, cfg2->getConfigurationName());
     303              : 
     304            0 :       std::cout << "  " << cfg1->getConfigurationName() << " appears " << count001 << " time(s)\n";
     305            0 :       std::cout << "  " << cfg2->getConfigurationName() << " appears " << count002 << " time(s)\n";
     306              : 
     307            0 :       BOOST_CHECK_EQUAL(count001, 1);
     308            0 :       BOOST_CHECK_EQUAL(count002, 1);
     309            0 :     }
     310            0 :   }
     311              : 
     312            2 :   BOOST_CHECK_MESSAGE(foundOurComposition || jsonFiles.empty(), "Composition should be found in raw storage (or directory doesn't exist)");
     313              : 
     314            2 :   BOOST_CHECK_EQUAL(totalDuplicatesFound, 0);
     315              : 
     316            2 :   std::cout << "\n  Verified: Raw storage contains no duplicate configuration types\n";
     317            2 : }
     318              : 
     319            4 : BOOST_AUTO_TEST_CASE(scan_all_compositions_for_duplicates) {
     320            2 :   std::cout << "\n=== TEST 4: Scanning all compositions for duplicate entries ===\n";
     321              : 
     322            2 :   std::string globalConfigDir = fixture.dbPath + "/GlobalConfigurations";
     323            2 :   auto jsonFiles = listJsonFiles(globalConfigDir);
     324              : 
     325            2 :   std::cout << "Scanning " << jsonFiles.size() << " composition files in: " << globalConfigDir << "\n";
     326              : 
     327            2 :   int filesWithDuplicates = 0;
     328            2 :   int totalFilesScanned = 0;
     329              : 
     330            2 :   for (const auto& filepath : jsonFiles) {
     331            0 :     std::string content = readFileContent(filepath);
     332            0 :     if (content.empty()) {
     333            0 :       continue;
     334              :     }
     335              : 
     336            0 :     totalFilesScanned++;
     337              : 
     338            0 :     auto configNames = extractConfigNames(content);
     339              : 
     340            0 :     std::map<std::string, int> nameCounts;
     341            0 :     for (const auto& name : configNames) {
     342            0 :       nameCounts[name]++;
     343              :     }
     344              : 
     345            0 :     bool hasDuplicates = false;
     346            0 :     for (const auto& entry : nameCounts) {
     347            0 :       if (entry.second > 1) {
     348            0 :         if (!hasDuplicates) {
     349            0 :           std::cout << "\nDuplicates in: " << filepath << "\n";
     350            0 :           hasDuplicates = true;
     351            0 :           filesWithDuplicates++;
     352              :         }
     353            0 :         std::cout << "  '" << entry.first << "' appears " << entry.second << " times\n";
     354              :       }
     355              :     }
     356            0 :   }
     357              : 
     358            2 :   std::cout << "\nSummary:\n";
     359            2 :   std::cout << "  Files scanned: " << totalFilesScanned << "\n";
     360            2 :   std::cout << "  Files with duplicates: " << filesWithDuplicates << "\n";
     361              : 
     362            2 :   BOOST_CHECK_EQUAL(filesWithDuplicates, 0);
     363              : 
     364            2 :   std::cout << "\n  Verified: No compositions contain duplicate configuration types\n";
     365            2 : }
     366              : 
     367            4 : BOOST_AUTO_TEST_CASE(map_interface_prevents_duplicates) {
     368            2 :   std::cout << "\n=== TEST 5: Map interface prevents duplicates ===\n";
     369              : 
     370            2 :   auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     371              : 
     372            2 :   std::string typeName = "TestConfiguration001";
     373              : 
     374            2 :   compositionMap[typeName] = 100;
     375            2 :   std::cout << "Added version 100, map size: " << compositionMap.size() << "\n";
     376            2 :   BOOST_CHECK_EQUAL(compositionMap.size(), 1);
     377            2 :   BOOST_CHECK_EQUAL(compositionMap[typeName], 100);
     378              : 
     379            2 :   compositionMap[typeName] = 200;
     380            2 :   std::cout << "Added version 200, map size: " << compositionMap.size() << "\n";
     381            2 :   BOOST_CHECK_EQUAL(compositionMap.size(), 1);
     382            2 :   BOOST_CHECK_EQUAL(compositionMap[typeName], 200);
     383              : 
     384            2 :   compositionMap[typeName] = 300;
     385            2 :   std::cout << "Added version 300, map size: " << compositionMap.size() << "\n";
     386            2 :   BOOST_CHECK_EQUAL(compositionMap.size(), 1);
     387            2 :   BOOST_CHECK_EQUAL(compositionMap[typeName], 300);
     388              : 
     389            2 :   std::cout << "  Verified: std::map automatically prevents duplicates\n";
     390            2 :   std::cout << "  Final value: " << compositionMap[typeName] << " (should be 300)\n";
     391            2 : }
     392              : 
     393            4 : BOOST_AUTO_TEST_CASE(test_duplicate_composition_behavior) {
     394            2 :   std::cout << "\n=== TEST 6: Test duplicate composition behavior ===\n";
     395              : 
     396            2 :   auto ifc = DatabaseConfigurationInterface();
     397              : 
     398            2 :   int version1 = fixture.baseVersion + 1000;
     399              : 
     400            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
     401            2 :   cfg1->getView().version = version1;
     402            2 :   ifc.saveActiveVersion(cfg1.get());
     403              : 
     404            6 :   std::string compName = std::string{"DupCompTest_"} + std::to_string(fixture.baseVersion);
     405              : 
     406            2 :   auto map1 = DatabaseConfigurationInterface::config_version_map_t{};
     407            2 :   map1[cfg1->getConfigurationName()] = version1;
     408              : 
     409            2 :   ifc.storeGlobalConfiguration(map1, compName);
     410            2 :   std::cout << "Created initial composition: " << compName << "\n";
     411              : 
     412            2 :   bool secondWriteFailed = false;
     413              :   try {
     414            2 :     ifc.storeGlobalConfiguration(map1, compName);
     415            0 :     std::cout << "  Second write succeeded (API allows duplicate composition names)\n";
     416            2 :   } catch (std::exception const& e) {
     417            2 :     secondWriteFailed = true;
     418            2 :     std::cout << "  Second write rejected: " << e.what() << "\n";
     419            2 :   }
     420              : 
     421            2 :   if (secondWriteFailed) {
     422            2 :     std::cout << "  API BEHAVIOR: Duplicate composition names are REJECTED\n";
     423              :   } else {
     424            0 :     std::cout << "  API BEHAVIOR: Duplicate composition names are ALLOWED (overwrite)\n";
     425              :   }
     426              : 
     427            2 :   auto allComps = ifc.findAllGlobalConfigurations();
     428            2 :   bool found = (allComps.find(compName) != allComps.end());
     429              : 
     430            2 :   BOOST_CHECK_EQUAL(found, true);
     431              : 
     432            2 :   std::cout << "  Verified: Composition exists after duplicate attempt\n";
     433            2 : }
     434              : 
     435            4 : BOOST_AUTO_TEST_CASE(different_compositions_same_config_types) {
     436            2 :   std::cout << "\n=== TEST 7: Different compositions can have same config types ===\n";
     437              : 
     438            2 :   auto ifc = DatabaseConfigurationInterface();
     439              : 
     440            2 :   int version1 = fixture.baseVersion + 2000;
     441              : 
     442            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
     443            2 :   cfg1->getView().version = version1;
     444            2 :   ifc.saveActiveVersion(cfg1.get());
     445              : 
     446            6 :   std::string compName1 = std::string{"MultiComp1_"} + std::to_string(fixture.baseVersion);
     447              : 
     448            2 :   auto map1 = DatabaseConfigurationInterface::config_version_map_t{};
     449            2 :   map1[cfg1->getConfigurationName()] = version1;
     450              : 
     451            2 :   ifc.storeGlobalConfiguration(map1, compName1);
     452            2 :   std::cout << "Created composition 1: " << compName1 << "\n";
     453              : 
     454            6 :   std::string compName2 = std::string{"MultiComp2_"} + std::to_string(fixture.baseVersion);
     455              : 
     456            2 :   auto map2 = DatabaseConfigurationInterface::config_version_map_t{};
     457            2 :   map2[cfg1->getConfigurationName()] = version1;
     458              : 
     459            2 :   bool secondSucceeded = true;
     460              :   try {
     461            2 :     ifc.storeGlobalConfiguration(map2, compName2);
     462            2 :     std::cout << "Created composition 2: " << compName2 << "\n";
     463            0 :   } catch (std::exception const& e) {
     464            0 :     secondSucceeded = false;
     465            0 :     std::cout << "  Second composition failed (unexpected): " << e.what() << "\n";
     466            0 :   }
     467              : 
     468            2 :   BOOST_CHECK_EQUAL(secondSucceeded, true);
     469              : 
     470            2 :   auto allComps = ifc.findAllGlobalConfigurations();
     471              : 
     472            2 :   bool found1 = (allComps.find(compName1) != allComps.end());
     473            2 :   bool found2 = (allComps.find(compName2) != allComps.end());
     474              : 
     475            2 :   BOOST_CHECK_EQUAL(found1, true);
     476            2 :   BOOST_CHECK_EQUAL(found2, true);
     477              : 
     478            2 :   std::cout << "  Verified: Both compositions exist independently\n";
     479            2 : }
     480              : 
     481            4 : BOOST_AUTO_TEST_CASE(sequential_multi_operation) {
     482            2 :   std::cout << "\n=== TEST 8: Sequential multi-operation test ===\n";
     483              : 
     484            2 :   const int NUM_OPERATIONS = 5;
     485            2 :   int successCount = 0;
     486            2 :   int failCount = 0;
     487              : 
     488            2 :   auto ifc = DatabaseConfigurationInterface();
     489              : 
     490            2 :   auto startTime = std::chrono::high_resolution_clock::now();
     491              : 
     492           12 :   for (int i = 0; i < NUM_OPERATIONS; i++) {
     493              :     try {
     494           10 :       int version = fixture.baseVersion + 3000 + (i * 10);
     495              : 
     496           10 :       std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
     497           10 :       cfg->getView().version = version;
     498           10 :       ifc.saveActiveVersion(cfg.get());
     499              : 
     500           10 :       auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     501           10 :       compositionMap[cfg->getConfigurationName()] = version;
     502              : 
     503           30 :       std::string compName = std::string{"SeqTest_"} + std::to_string(fixture.baseVersion) + "_op" + std::to_string(i);
     504              : 
     505           10 :       ifc.storeGlobalConfiguration(compositionMap, compName);
     506              : 
     507           10 :       std::cout << "  Operation " << i << ": Created composition " << compName << "\n";
     508              : 
     509           10 :       successCount++;
     510           10 :     } catch (std::exception const& e) {
     511            0 :       std::cout << "  Operation " << i << " failed: " << e.what() << "\n";
     512            0 :       failCount++;
     513            0 :     }
     514              :   }
     515              : 
     516            2 :   auto endTime = std::chrono::high_resolution_clock::now();
     517            2 :   auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
     518              : 
     519            2 :   std::cout << "  Completed in " << duration.count() << "ms\n";
     520            2 :   std::cout << "  Success: " << successCount << ", Failed: " << failCount << "\n";
     521              : 
     522            2 :   BOOST_CHECK_EQUAL(successCount, NUM_OPERATIONS);
     523              : 
     524            2 :   auto allComps = ifc.findAllGlobalConfigurations();
     525              : 
     526            2 :   int foundCount = 0;
     527           12 :   for (int i = 0; i < NUM_OPERATIONS; i++) {
     528           30 :     std::string compName = std::string{"SeqTest_"} + std::to_string(fixture.baseVersion) + "_op" + std::to_string(i);
     529           10 :     if (allComps.find(compName) != allComps.end()) {
     530           10 :       foundCount++;
     531              :     }
     532           10 :   }
     533              : 
     534            2 :   BOOST_CHECK_EQUAL(foundCount, NUM_OPERATIONS);
     535            2 :   std::cout << "  Verified: All " << foundCount << " sequential compositions exist\n";
     536            2 : }
     537              : 
     538            4 : BOOST_AUTO_TEST_CASE(performance_store_load) {
     539            2 :   std::cout << "\n=== TEST 9: Performance test (store/load) ===\n";
     540              : 
     541            2 :   auto ifc = DatabaseConfigurationInterface();
     542              : 
     543            2 :   const int NUM_CONFIGS = 2;
     544            2 :   int baseVer = fixture.baseVersion + 4000;
     545              : 
     546            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
     547            2 :   cfg1->getView().version = baseVer;
     548            2 :   ifc.saveActiveVersion(cfg1.get());
     549              : 
     550            2 :   std::shared_ptr<ConfigurationBase> cfg2 = std::make_shared<TestConfiguration002>();
     551            2 :   cfg2->getView().version = baseVer + 1;
     552            2 :   ifc.saveActiveVersion(cfg2.get());
     553              : 
     554            2 :   auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     555            2 :   compositionMap[cfg1->getConfigurationName()] = baseVer;
     556            2 :   compositionMap[cfg2->getConfigurationName()] = baseVer + 1;
     557              : 
     558            6 :   std::string compName = std::string{"PerfTest_"} + std::to_string(fixture.baseVersion);
     559              : 
     560            2 :   auto storeStart = std::chrono::high_resolution_clock::now();
     561            2 :   ifc.storeGlobalConfiguration(compositionMap, compName);
     562            2 :   auto storeEnd = std::chrono::high_resolution_clock::now();
     563            2 :   auto storeDuration = std::chrono::duration_cast<std::chrono::milliseconds>(storeEnd - storeStart);
     564              : 
     565            2 :   std::cout << "  Store time: " << storeDuration.count() << "ms\n";
     566              : 
     567            2 :   const int LOAD_ITERATIONS = 10;
     568            2 :   auto loadStart = std::chrono::high_resolution_clock::now();
     569           22 :   for (int i = 0; i < LOAD_ITERATIONS; i++) {
     570           20 :     auto loadedMap = ifc.loadGlobalConfiguration(compName);
     571           20 :     BOOST_CHECK_EQUAL(loadedMap.size(), NUM_CONFIGS);
     572           20 :   }
     573            2 :   auto loadEnd = std::chrono::high_resolution_clock::now();
     574            2 :   auto loadDuration = std::chrono::duration_cast<std::chrono::milliseconds>(loadEnd - loadStart);
     575              : 
     576            2 :   std::cout << "  Load time (" << LOAD_ITERATIONS << " iterations): " << loadDuration.count() << "ms\n";
     577            2 :   std::cout << "  Average load time: " << (loadDuration.count() / LOAD_ITERATIONS) << "ms\n";
     578              : 
     579            2 :   BOOST_CHECK_LT(storeDuration.count(), 5000);
     580            2 :   BOOST_CHECK_LT(loadDuration.count(), 5000);
     581              : 
     582            2 :   std::cout << "  Verified: Performance within acceptable limits\n";
     583            2 : }
     584              : 
     585            4 : BOOST_AUTO_TEST_CASE(empty_composition_handling) {
     586            2 :   std::cout << "\n=== TEST 10: Empty composition handling ===\n";
     587              : 
     588            2 :   std::cout << "  API BEHAVIOR: Empty compositions are NOT allowed (assertion enforced)\n";
     589            2 :   std::cout << "  The storeGlobalConfiguration API requires at least one configuration.\n";
     590              : 
     591            2 :   auto ifc = DatabaseConfigurationInterface();
     592              : 
     593            2 :   int version = fixture.baseVersion + 4500;
     594            2 :   std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
     595            2 :   cfg->getView().version = version;
     596            2 :   ifc.saveActiveVersion(cfg.get());
     597              : 
     598            2 :   auto minimalMap = DatabaseConfigurationInterface::config_version_map_t{};
     599            2 :   minimalMap[cfg->getConfigurationName()] = version;
     600              : 
     601            6 :   std::string compName = std::string{"MinimalCompTest_"} + std::to_string(fixture.baseVersion);
     602              : 
     603            2 :   bool storeSucceeded = false;
     604              :   try {
     605            2 :     ifc.storeGlobalConfiguration(minimalMap, compName);
     606            2 :     storeSucceeded = true;
     607            2 :     std::cout << "  Minimal (single-entry) composition: succeeded\n";
     608            0 :   } catch (std::exception const& e) {
     609            0 :     std::cout << "  Minimal composition failed: " << e.what() << "\n";
     610            0 :   }
     611              : 
     612            2 :   BOOST_CHECK_EQUAL(storeSucceeded, true);
     613            2 :   std::cout << "  Verified: Minimal compositions are allowed\n";
     614            2 : }
     615              : 
     616            4 : BOOST_AUTO_TEST_CASE(version_boundary_values) {
     617            2 :   std::cout << "\n=== TEST 11: Version boundary values ===\n";
     618              : 
     619            2 :   auto ifc = DatabaseConfigurationInterface();
     620              : 
     621            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
     622            2 :   cfg1->getView().version = 1;
     623              : 
     624            2 :   bool v1Succeeded = true;
     625              :   try {
     626            2 :     ifc.saveActiveVersion(cfg1.get());
     627            2 :     std::cout << "  Version 1: Save succeeded\n";
     628            0 :   } catch (std::exception const& e) {
     629            0 :     v1Succeeded = false;
     630            0 :     std::cout << "  Version 1: Save failed - " << e.what() << "\n";
     631            0 :   }
     632              : 
     633            2 :   std::shared_ptr<ConfigurationBase> cfg2 = std::make_shared<TestConfiguration001>();
     634            2 :   cfg2->getView().version = 999999;
     635              : 
     636            2 :   bool vLargeSucceeded = true;
     637              :   try {
     638            2 :     ifc.saveActiveVersion(cfg2.get());
     639            2 :     std::cout << "  Version 999999: Save succeeded\n";
     640            0 :   } catch (std::exception const& e) {
     641            0 :     vLargeSucceeded = false;
     642            0 :     std::cout << "  Version 999999: Save failed - " << e.what() << "\n";
     643            0 :   }
     644              : 
     645            2 :   if (v1Succeeded && vLargeSucceeded) {
     646            2 :     auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     647            2 :     compositionMap[cfg1->getConfigurationName()] = 999999;
     648              : 
     649            6 :     std::string compName = std::string{"BoundaryTest_"} + std::to_string(fixture.baseVersion);
     650              : 
     651            2 :     bool compSucceeded = true;
     652              :     try {
     653            2 :       ifc.storeGlobalConfiguration(compositionMap, compName);
     654            2 :       std::cout << "  Composition with large version: succeeded\n";
     655            0 :     } catch (std::exception const& e) {
     656            0 :       compSucceeded = false;
     657            0 :       std::cout << "  Composition with large version: failed - " << e.what() << "\n";
     658            0 :     }
     659              : 
     660            2 :     BOOST_CHECK_EQUAL(compSucceeded, true);
     661            2 :   }
     662              : 
     663            2 :   BOOST_CHECK_EQUAL(v1Succeeded, true);
     664              : 
     665            2 :   std::cout << "  Verified: Boundary version values handled\n";
     666            2 : }
     667              : 
     668            4 : BOOST_AUTO_TEST_CASE(index_consistency_validation) {
     669            2 :   std::cout << "\n=== TEST 12: Index consistency validation ===\n";
     670              : 
     671            2 :   if (!isFileSystemBackend()) {
     672            1 :     std::cout << "  Skipping: Not using FileSystemDB backend\n";
     673            1 :     BOOST_CHECK(true);
     674            1 :     return;
     675              :   }
     676              : 
     677            1 :   auto ifc = DatabaseConfigurationInterface();
     678              : 
     679            1 :   int version = fixture.baseVersion + 5000;
     680              : 
     681            1 :   std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
     682            1 :   cfg->getView().version = version;
     683            1 :   ifc.saveActiveVersion(cfg.get());
     684              : 
     685            1 :   auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     686            1 :   compositionMap[cfg->getConfigurationName()] = version;
     687              : 
     688            3 :   std::string compName = std::string{"IndexTest_"} + std::to_string(fixture.baseVersion);
     689            1 :   ifc.storeGlobalConfiguration(compositionMap, compName);
     690              : 
     691            1 :   std::string globalConfigDir = fixture.dbPath + "/GlobalConfigurations";
     692            1 :   std::string indexPath = globalConfigDir + "/index.json";
     693              : 
     694            1 :   std::cout << "  Checking index: " << indexPath << "\n";
     695              : 
     696            1 :   auto indexOids = extractOidsFromIndex(indexPath);
     697            1 :   std::cout << "  OIDs in index: " << indexOids.size() << "\n";
     698              : 
     699            1 :   auto jsonFiles = listJsonFiles(globalConfigDir);
     700            1 :   std::set<std::string> fileOids;
     701            1 :   for (const auto& filepath : jsonFiles) {
     702            0 :     std::string oid = getOidFromFilename(filepath);
     703            0 :     if (!oid.empty() && oid != "index") {
     704            0 :       fileOids.insert(oid);
     705              :     }
     706            0 :   }
     707            1 :   std::cout << "  Files on disk: " << fileOids.size() << "\n";
     708              : 
     709            1 :   int orphanedFiles = 0;
     710            1 :   for (const auto& fileOid : fileOids) {
     711            0 :     if (indexOids.find(fileOid) == indexOids.end()) {
     712            0 :       std::cout << "  WARNING: Orphaned file: " << fileOid << ".json\n";
     713            0 :       orphanedFiles++;
     714              :     }
     715              :   }
     716              : 
     717            1 :   int missingFiles = 0;
     718            1 :   for (const auto& indexOid : indexOids) {
     719            0 :     if (fileOids.find(indexOid) == fileOids.end()) {
     720            0 :       std::cout << "  WARNING: Missing file: " << indexOid << ".json\n";
     721            0 :       missingFiles++;
     722              :     }
     723              :   }
     724              : 
     725            1 :   std::cout << "  Orphaned files: " << orphanedFiles << "\n";
     726            1 :   std::cout << "  Missing files: " << missingFiles << "\n";
     727              : 
     728            1 :   BOOST_WARN_EQUAL(orphanedFiles, 0);
     729            1 :   BOOST_WARN_EQUAL(missingFiles, 0);
     730              : 
     731            1 :   BOOST_CHECK_EQUAL(missingFiles, 0);
     732              : 
     733            1 :   std::cout << "  Verified: Index consistency checked\n";
     734            1 : }
     735              : 
     736            4 : BOOST_AUTO_TEST_CASE(stress_test_rapid_operations) {
     737            2 :   std::cout << "\n=== TEST 13: Stress test (rapid operations) ===\n";
     738              : 
     739            2 :   auto ifc = DatabaseConfigurationInterface();
     740              : 
     741            2 :   const int NUM_OPERATIONS = 20;
     742            2 :   int successCount = 0;
     743            2 :   int failCount = 0;
     744              : 
     745            2 :   auto startTime = std::chrono::high_resolution_clock::now();
     746              : 
     747           42 :   for (int i = 0; i < NUM_OPERATIONS; i++) {
     748              :     try {
     749           40 :       int version = fixture.baseVersion + 6000 + i;
     750              : 
     751           40 :       std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
     752           40 :       cfg->getView().version = version;
     753           40 :       ifc.saveActiveVersion(cfg.get());
     754              : 
     755           40 :       auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     756           40 :       compositionMap[cfg->getConfigurationName()] = version;
     757              : 
     758          120 :       std::string compName = std::string{"StressTest_"} + std::to_string(fixture.baseVersion) + "_" + std::to_string(i);
     759           40 :       ifc.storeGlobalConfiguration(compositionMap, compName);
     760              : 
     761           40 :       successCount++;
     762           40 :     } catch (std::exception const& e) {
     763            0 :       std::cout << "  Operation " << i << " failed: " << e.what() << "\n";
     764            0 :       failCount++;
     765            0 :     }
     766              :   }
     767              : 
     768            2 :   auto endTime = std::chrono::high_resolution_clock::now();
     769            2 :   auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
     770              : 
     771            2 :   std::cout << "  Completed " << NUM_OPERATIONS << " operations in " << duration.count() << "ms\n";
     772            2 :   std::cout << "  Average: " << (duration.count() / NUM_OPERATIONS) << "ms per operation\n";
     773            2 :   std::cout << "  Success: " << successCount << ", Failed: " << failCount << "\n";
     774              : 
     775            2 :   BOOST_CHECK_EQUAL(successCount, NUM_OPERATIONS);
     776            2 :   BOOST_CHECK_EQUAL(failCount, 0);
     777              : 
     778            2 :   BOOST_CHECK_LT(duration.count(), 30000);
     779              : 
     780            2 :   if (isFileSystemBackend()) {
     781            1 :     std::string globalConfigDir = fixture.dbPath + "/GlobalConfigurations";
     782            1 :     auto jsonFiles = listJsonFiles(globalConfigDir);
     783              : 
     784            1 :     int totalDuplicates = 0;
     785            1 :     for (const auto& filepath : jsonFiles) {
     786            0 :       std::string content = readFileContent(filepath);
     787            0 :       if (content.empty()) continue;
     788              : 
     789            0 :       auto configNames = extractConfigNames(content);
     790            0 :       std::map<std::string, int> nameCounts;
     791            0 :       for (const auto& name : configNames) {
     792            0 :         nameCounts[name]++;
     793              :       }
     794              : 
     795            0 :       for (const auto& entry : nameCounts) {
     796            0 :         if (entry.second > 1) {
     797            0 :           totalDuplicates++;
     798              :         }
     799              :       }
     800            0 :     }
     801              : 
     802            1 :     BOOST_CHECK_EQUAL(totalDuplicates, 0);
     803            1 :     std::cout << "  Verified: No duplicates in storage after stress test\n";
     804            1 :   }
     805            2 : }
     806              : 
     807            4 : BOOST_AUTO_TEST_CASE(composition_name_edge_cases) {
     808            2 :   std::cout << "\n=== TEST 14: Composition name edge cases ===\n";
     809              : 
     810            2 :   auto ifc = DatabaseConfigurationInterface();
     811              : 
     812            2 :   int version = fixture.baseVersion + 7000;
     813              : 
     814            2 :   std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
     815            2 :   cfg->getView().version = version;
     816            2 :   ifc.saveActiveVersion(cfg.get());
     817              : 
     818            2 :   auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     819            2 :   compositionMap[cfg->getConfigurationName()] = version;
     820              : 
     821            6 :   std::string name1 = std::string{"Test___Multiple___Underscores_"} + std::to_string(fixture.baseVersion);
     822            2 :   bool name1Succeeded = false;
     823              :   try {
     824            2 :     ifc.storeGlobalConfiguration(compositionMap, name1);
     825            2 :     name1Succeeded = true;
     826            2 :     std::cout << "  Underscore-heavy name: succeeded\n";
     827            0 :   } catch (std::exception const& e) {
     828            0 :     std::cout << "  Underscore-heavy name: failed - " << e.what() << "\n";
     829            0 :   }
     830              : 
     831            2 :   std::string name2 = std::to_string(fixture.baseVersion) + "12345";
     832            2 :   bool name2Succeeded = false;
     833              :   try {
     834            2 :     cfg->getView().version = version + 1;
     835            2 :     ifc.saveActiveVersion(cfg.get());
     836            2 :     compositionMap[cfg->getConfigurationName()] = version + 1;
     837              : 
     838            2 :     ifc.storeGlobalConfiguration(compositionMap, name2);
     839            2 :     name2Succeeded = true;
     840            2 :     std::cout << "  Numeric name: succeeded\n";
     841            0 :   } catch (std::exception const& e) {
     842            0 :     std::cout << "  Numeric name: failed - " << e.what() << "\n";
     843            0 :   }
     844              : 
     845            6 :   std::string name3 = std::string{"VeryLongCompositionNameThatMightCauseIssues_"} + std::to_string(fixture.baseVersion);
     846            2 :   bool name3Succeeded = false;
     847              :   try {
     848            2 :     cfg->getView().version = version + 2;
     849            2 :     ifc.saveActiveVersion(cfg.get());
     850            2 :     compositionMap[cfg->getConfigurationName()] = version + 2;
     851              : 
     852            2 :     ifc.storeGlobalConfiguration(compositionMap, name3);
     853            2 :     name3Succeeded = true;
     854            2 :     std::cout << "  Long name: succeeded\n";
     855            0 :   } catch (std::exception const& e) {
     856            0 :     std::cout << "  Long name: failed - " << e.what() << "\n";
     857            0 :   }
     858              : 
     859            2 :   BOOST_CHECK_EQUAL(name1Succeeded, true);
     860            2 :   BOOST_CHECK_MESSAGE(true, (name2Succeeded ? "Numeric names allowed" : "Numeric names rejected"));
     861            2 :   BOOST_CHECK_MESSAGE(true, (name3Succeeded ? "Long names allowed" : "Long names rejected"));
     862              : 
     863            2 :   std::cout << "  Verified: Composition name edge cases tested\n";
     864            2 : }
     865              : 
     866            4 : BOOST_AUTO_TEST_CASE(overwrite_updates_version) {
     867            2 :   std::cout << "\n=== TEST 15: Overwrite updates version (unassigns old) ===\n";
     868              : 
     869            2 :   auto ifc = DatabaseConfigurationInterface();
     870              : 
     871            2 :   int versionOld = fixture.baseVersion + 8000;
     872            2 :   int versionNew = fixture.baseVersion + 8001;
     873              : 
     874            2 :   std::shared_ptr<ConfigurationBase> cfgOld = std::make_shared<TestConfiguration001>();
     875            2 :   cfgOld->getView().version = versionOld;
     876            2 :   ifc.saveActiveVersion(cfgOld.get());
     877            2 :   std::cout << "  Created old version: " << versionOld << "\n";
     878              : 
     879            2 :   std::shared_ptr<ConfigurationBase> cfgNew = std::make_shared<TestConfiguration001>();
     880            2 :   cfgNew->getView().version = versionNew;
     881            2 :   ifc.saveActiveVersion(cfgNew.get());
     882            2 :   std::cout << "  Created new version: " << versionNew << "\n";
     883              : 
     884            6 :   std::string compName = std::string{"OverwriteVersionTest_"} + std::to_string(fixture.baseVersion);
     885              : 
     886            2 :   auto mapOld = DatabaseConfigurationInterface::config_version_map_t{};
     887            2 :   mapOld[cfgOld->getConfigurationName()] = versionOld;
     888              : 
     889            2 :   ifc.storeGlobalConfiguration(mapOld, compName);
     890            2 :   std::cout << "  Created composition with version " << versionOld << "\n";
     891              : 
     892            2 :   auto loaded1 = ifc.loadGlobalConfiguration(compName);
     893            2 :   BOOST_CHECK_EQUAL(loaded1.size(), 1);
     894            2 :   BOOST_CHECK_EQUAL(loaded1[cfgOld->getConfigurationName()], versionOld);
     895            2 :   std::cout << "  Verified: Initial composition has version " << versionOld << "\n";
     896              : 
     897            2 :   auto mapNew = DatabaseConfigurationInterface::config_version_map_t{};
     898            2 :   mapNew[cfgNew->getConfigurationName()] = versionNew;
     899              : 
     900            2 :   ifc.storeGlobalConfiguration(mapNew, compName, true);
     901            2 :   std::cout << "  Overwrote composition with version " << versionNew << "\n";
     902              : 
     903            2 :   auto loaded2 = ifc.loadGlobalConfiguration(compName);
     904            2 :   BOOST_CHECK_EQUAL(loaded2.size(), 1);
     905            2 :   BOOST_CHECK_EQUAL(loaded2[cfgNew->getConfigurationName()], versionNew);
     906            2 :   std::cout << "  Verified: Composition now has version " << versionNew << "\n";
     907              : 
     908            2 :   auto compsWithOld = ifc.findCompositionsContaining(cfgOld->getConfigurationName(), std::to_string(versionOld));
     909            2 :   bool oldInComp = (compsWithOld.find(compName) != compsWithOld.end());
     910            2 :   BOOST_CHECK_EQUAL(oldInComp, false);
     911            2 :   std::cout << "  Verified: Old version " << versionOld << " is NOT in composition (correctly unassigned)\n";
     912              : 
     913            2 :   auto compsWithNew = ifc.findCompositionsContaining(cfgNew->getConfigurationName(), std::to_string(versionNew));
     914            2 :   bool newInComp = (compsWithNew.find(compName) != compsWithNew.end());
     915            2 :   BOOST_CHECK_EQUAL(newInComp, true);
     916            2 :   std::cout << "  Verified: New version " << versionNew << " IS in composition\n";
     917              : 
     918            2 :   std::cout << "  TEST PASSED: Overwrite correctly unassigned old version\n";
     919            2 : }
     920              : 
     921            4 : BOOST_AUTO_TEST_CASE(overwrite_removes_members) {
     922            2 :   std::cout << "\n=== TEST 16: Overwrite removes members ===\n";
     923              : 
     924            2 :   auto ifc = DatabaseConfigurationInterface();
     925              : 
     926            2 :   int version1 = fixture.baseVersion + 8100;
     927            2 :   int version2 = fixture.baseVersion + 8101;
     928            2 :   int version3 = fixture.baseVersion + 8102;
     929              : 
     930            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
     931            2 :   cfg1->getView().version = version1;
     932            2 :   ifc.saveActiveVersion(cfg1.get());
     933              : 
     934            2 :   std::shared_ptr<ConfigurationBase> cfg2 = std::make_shared<TestConfiguration002>();
     935            2 :   cfg2->getView().version = version2;
     936            2 :   ifc.saveActiveVersion(cfg2.get());
     937              : 
     938            2 :   std::shared_ptr<ConfigurationBase> cfg3 = std::make_shared<TestConfiguration003>();
     939            2 :   cfg3->getView().version = version3;
     940            2 :   ifc.saveActiveVersion(cfg3.get());
     941              : 
     942            2 :   std::cout << "  Created 3 config versions: " << version1 << ", " << version2 << ", " << version3 << "\n";
     943              : 
     944            6 :   std::string compName = std::string{"OverwriteRemoveTest_"} + std::to_string(fixture.baseVersion);
     945              : 
     946            2 :   auto mapAll = DatabaseConfigurationInterface::config_version_map_t{};
     947            2 :   mapAll[cfg1->getConfigurationName()] = version1;
     948            2 :   mapAll[cfg2->getConfigurationName()] = version2;
     949            2 :   mapAll[cfg3->getConfigurationName()] = version3;
     950              : 
     951            2 :   ifc.storeGlobalConfiguration(mapAll, compName);
     952            2 :   std::cout << "  Created composition with 3 members\n";
     953              : 
     954            2 :   auto loaded1 = ifc.loadGlobalConfiguration(compName);
     955            2 :   BOOST_CHECK_EQUAL(loaded1.size(), 3);
     956            2 :   std::cout << "  Verified: Initial composition has 3 members\n";
     957              : 
     958            2 :   auto mapOne = DatabaseConfigurationInterface::config_version_map_t{};
     959            2 :   mapOne[cfg1->getConfigurationName()] = version1;
     960              : 
     961            2 :   ifc.storeGlobalConfiguration(mapOne, compName, true);
     962            2 :   std::cout << "  Overwrote composition with only 1 member\n";
     963              : 
     964            2 :   auto loaded2 = ifc.loadGlobalConfiguration(compName);
     965            2 :   BOOST_CHECK_EQUAL(loaded2.size(), 1);
     966            2 :   BOOST_CHECK_EQUAL(loaded2[cfg1->getConfigurationName()], version1);
     967            2 :   std::cout << "  Verified: Composition now has 1 member\n";
     968              : 
     969            2 :   auto compsWithCfg2 = ifc.findCompositionsContaining(cfg2->getConfigurationName(), std::to_string(version2));
     970            2 :   bool cfg2InComp = (compsWithCfg2.find(compName) != compsWithCfg2.end());
     971            2 :   BOOST_CHECK_EQUAL(cfg2InComp, false);
     972            2 :   std::cout << "  Verified: " << cfg2->getConfigurationName() << " is NOT in composition\n";
     973              : 
     974            2 :   auto compsWithCfg3 = ifc.findCompositionsContaining(cfg3->getConfigurationName(), std::to_string(version3));
     975            2 :   bool cfg3InComp = (compsWithCfg3.find(compName) != compsWithCfg3.end());
     976            2 :   BOOST_CHECK_EQUAL(cfg3InComp, false);
     977            2 :   std::cout << "  Verified: " << cfg3->getConfigurationName() << " is NOT in composition\n";
     978              : 
     979            2 :   auto compsWithCfg1 = ifc.findCompositionsContaining(cfg1->getConfigurationName(), std::to_string(version1));
     980            2 :   bool cfg1InComp = (compsWithCfg1.find(compName) != compsWithCfg1.end());
     981            2 :   BOOST_CHECK_EQUAL(cfg1InComp, true);
     982            2 :   std::cout << "  Verified: " << cfg1->getConfigurationName() << " IS still in composition\n";
     983              : 
     984            2 :   std::cout << "  TEST PASSED: Overwrite correctly removed members\n";
     985            2 : }
     986              : 
     987            4 : BOOST_AUTO_TEST_CASE(overwrite_with_partial_changes) {
     988            2 :   std::cout << "\n=== TEST 17: Overwrite with partial changes (full merge) ===\n";
     989              : 
     990            2 :   auto ifc = DatabaseConfigurationInterface();
     991              : 
     992            2 :   int vA1 = fixture.baseVersion + 8200;
     993            2 :   int vA5 = fixture.baseVersion + 8205;
     994            2 :   int vB2 = fixture.baseVersion + 8202;
     995            2 :   int vC3 = fixture.baseVersion + 8203;
     996              : 
     997            2 :   std::shared_ptr<ConfigurationBase> cfgA1 = std::make_shared<TestConfiguration001>();
     998            2 :   cfgA1->getView().version = vA1;
     999            2 :   ifc.saveActiveVersion(cfgA1.get());
    1000              : 
    1001            2 :   std::shared_ptr<ConfigurationBase> cfgA5 = std::make_shared<TestConfiguration001>();
    1002            2 :   cfgA5->getView().version = vA5;
    1003            2 :   ifc.saveActiveVersion(cfgA5.get());
    1004              : 
    1005            2 :   std::shared_ptr<ConfigurationBase> cfgB2 = std::make_shared<TestConfiguration002>();
    1006            2 :   cfgB2->getView().version = vB2;
    1007            2 :   ifc.saveActiveVersion(cfgB2.get());
    1008              : 
    1009            2 :   std::shared_ptr<ConfigurationBase> cfgC3 = std::make_shared<TestConfiguration003>();
    1010            2 :   cfgC3->getView().version = vC3;
    1011            2 :   ifc.saveActiveVersion(cfgC3.get());
    1012              : 
    1013            2 :   std::cout << "  Created versions: A/v" << vA1 << ", A/v" << vA5 << ", B/v" << vB2 << ", C/v" << vC3 << "\n";
    1014              : 
    1015            6 :   std::string compName = std::string{"PartialChangeTest_"} + std::to_string(fixture.baseVersion);
    1016              : 
    1017            2 :   auto mapOld = DatabaseConfigurationInterface::config_version_map_t{};
    1018            2 :   mapOld[cfgA1->getConfigurationName()] = vA1;
    1019            2 :   mapOld[cfgB2->getConfigurationName()] = vB2;
    1020            2 :   mapOld[cfgC3->getConfigurationName()] = vC3;
    1021              : 
    1022            2 :   ifc.storeGlobalConfiguration(mapOld, compName);
    1023            2 :   std::cout << "  Created OLD composition: {A/v" << vA1 << ", B/v" << vB2 << ", C/v" << vC3 << "}\n";
    1024              : 
    1025            2 :   auto loaded1 = ifc.loadGlobalConfiguration(compName);
    1026            2 :   BOOST_CHECK_EQUAL(loaded1.size(), 3);
    1027              : 
    1028            2 :   auto mapNew = DatabaseConfigurationInterface::config_version_map_t{};
    1029            2 :   mapNew[cfgA5->getConfigurationName()] = vA5;
    1030            2 :   mapNew[cfgB2->getConfigurationName()] = vB2;
    1031              : 
    1032            2 :   ifc.storeGlobalConfiguration(mapNew, compName, true);
    1033            2 :   std::cout << "  Overwrote with NEW composition: {A/v" << vA5 << ", B/v" << vB2 << "}\n";
    1034              : 
    1035            2 :   auto loaded2 = ifc.loadGlobalConfiguration(compName);
    1036            2 :   BOOST_CHECK_EQUAL(loaded2.size(), 2);
    1037            2 :   BOOST_CHECK_EQUAL(loaded2[cfgA5->getConfigurationName()], vA5);
    1038            2 :   BOOST_CHECK_EQUAL(loaded2[cfgB2->getConfigurationName()], vB2);
    1039            2 :   std::cout << "  Verified: Composition has 2 members with correct versions\n";
    1040              : 
    1041            2 :   auto compsWithA1 = ifc.findCompositionsContaining(cfgA1->getConfigurationName(), std::to_string(vA1));
    1042            2 :   bool A1InComp = (compsWithA1.find(compName) != compsWithA1.end());
    1043            2 :   BOOST_CHECK_EQUAL(A1InComp, false);
    1044            2 :   std::cout << "  Verified: A/v" << vA1 << " is NOT in composition (replaced)\n";
    1045              : 
    1046            2 :   auto compsWithA5 = ifc.findCompositionsContaining(cfgA5->getConfigurationName(), std::to_string(vA5));
    1047            2 :   bool A5InComp = (compsWithA5.find(compName) != compsWithA5.end());
    1048            2 :   BOOST_CHECK_EQUAL(A5InComp, true);
    1049            2 :   std::cout << "  Verified: A/v" << vA5 << " IS in composition (new version)\n";
    1050              : 
    1051            2 :   auto compsWithB2 = ifc.findCompositionsContaining(cfgB2->getConfigurationName(), std::to_string(vB2));
    1052            2 :   bool B2InComp = (compsWithB2.find(compName) != compsWithB2.end());
    1053            2 :   BOOST_CHECK_EQUAL(B2InComp, true);
    1054            2 :   std::cout << "  Verified: B/v" << vB2 << " IS in composition (unchanged)\n";
    1055              : 
    1056            2 :   auto compsWithC3 = ifc.findCompositionsContaining(cfgC3->getConfigurationName(), std::to_string(vC3));
    1057            2 :   bool C3InComp = (compsWithC3.find(compName) != compsWithC3.end());
    1058            2 :   BOOST_CHECK_EQUAL(C3InComp, false);
    1059            2 :   std::cout << "  Verified: C/v" << vC3 << " is NOT in composition (removed)\n";
    1060              : 
    1061            2 :   std::cout << "  TEST PASSED: Partial changes correctly applied\n";
    1062            2 : }
    1063              : 
    1064            4 : BOOST_AUTO_TEST_CASE(load_after_overwrite_returns_only_new_members) {
    1065            2 :   std::cout << "\n=== TEST 18: Load after overwrite returns only new members ===\n";
    1066              : 
    1067            2 :   auto ifc = DatabaseConfigurationInterface();
    1068              : 
    1069            2 :   int v1 = fixture.baseVersion + 8300;
    1070            2 :   int v2 = fixture.baseVersion + 8301;
    1071            2 :   int v3 = fixture.baseVersion + 8302;
    1072            2 :   int v4 = fixture.baseVersion + 8303;
    1073              : 
    1074            2 :   std::shared_ptr<ConfigurationBase> cfg1a = std::make_shared<TestConfiguration001>();
    1075            2 :   cfg1a->getView().version = v1;
    1076            2 :   ifc.saveActiveVersion(cfg1a.get());
    1077              : 
    1078            2 :   std::shared_ptr<ConfigurationBase> cfg1b = std::make_shared<TestConfiguration001>();
    1079            2 :   cfg1b->getView().version = v2;
    1080            2 :   ifc.saveActiveVersion(cfg1b.get());
    1081              : 
    1082            2 :   std::shared_ptr<ConfigurationBase> cfg2a = std::make_shared<TestConfiguration002>();
    1083            2 :   cfg2a->getView().version = v3;
    1084            2 :   ifc.saveActiveVersion(cfg2a.get());
    1085              : 
    1086            2 :   std::shared_ptr<ConfigurationBase> cfg2b = std::make_shared<TestConfiguration002>();
    1087            2 :   cfg2b->getView().version = v4;
    1088            2 :   ifc.saveActiveVersion(cfg2b.get());
    1089              : 
    1090            6 :   std::string compName = std::string{"LoadAfterOverwriteTest_"} + std::to_string(fixture.baseVersion);
    1091              : 
    1092            2 :   auto map1 = DatabaseConfigurationInterface::config_version_map_t{};
    1093            2 :   map1[cfg1a->getConfigurationName()] = v1;
    1094            2 :   map1[cfg2a->getConfigurationName()] = v3;
    1095              : 
    1096            2 :   ifc.storeGlobalConfiguration(map1, compName);
    1097            2 :   std::cout << "  Created initial: {Type1/v" << v1 << ", Type2/v" << v3 << "}\n";
    1098              : 
    1099            2 :   auto map2 = DatabaseConfigurationInterface::config_version_map_t{};
    1100            2 :   map2[cfg1b->getConfigurationName()] = v2;
    1101            2 :   map2[cfg2b->getConfigurationName()] = v4;
    1102              : 
    1103            2 :   ifc.storeGlobalConfiguration(map2, compName, true);
    1104            2 :   std::cout << "  Overwrote with: {Type1/v" << v2 << ", Type2/v" << v4 << "}\n";
    1105              : 
    1106            2 :   auto loaded = ifc.loadGlobalConfiguration(compName);
    1107              : 
    1108            2 :   BOOST_CHECK_EQUAL(loaded.size(), 2);
    1109            2 :   BOOST_CHECK_EQUAL(loaded[cfg1b->getConfigurationName()], v2);
    1110            2 :   BOOST_CHECK_EQUAL(loaded[cfg2b->getConfigurationName()], v4);
    1111              : 
    1112            2 :   BOOST_CHECK(loaded[cfg1a->getConfigurationName()] != v1);
    1113            2 :   BOOST_CHECK(loaded[cfg2a->getConfigurationName()] != v3);
    1114              : 
    1115            2 :   std::cout << "  Verified: Load returns exactly {Type1/v" << v2 << ", Type2/v" << v4 << "}\n";
    1116            2 :   std::cout << "  TEST PASSED: Load after overwrite returns only new members\n";
    1117            2 : }
    1118              : 
    1119            4 : BOOST_AUTO_TEST_CASE(reverse_lookup_correct_after_overwrite) {
    1120            2 :   std::cout << "\n=== TEST 19: Reverse lookup correct after overwrite ===\n";
    1121              : 
    1122            2 :   auto ifc = DatabaseConfigurationInterface();
    1123              : 
    1124            2 :   int vOld = fixture.baseVersion + 8400;
    1125            2 :   int vNew = fixture.baseVersion + 8401;
    1126              : 
    1127            2 :   std::shared_ptr<ConfigurationBase> cfgOld = std::make_shared<TestConfiguration001>();
    1128            2 :   cfgOld->getView().version = vOld;
    1129            2 :   ifc.saveActiveVersion(cfgOld.get());
    1130              : 
    1131            2 :   std::shared_ptr<ConfigurationBase> cfgNew = std::make_shared<TestConfiguration001>();
    1132            2 :   cfgNew->getView().version = vNew;
    1133            2 :   ifc.saveActiveVersion(cfgNew.get());
    1134              : 
    1135            6 :   std::string compName = std::string{"ReverseLookupOverwriteTest_"} + std::to_string(fixture.baseVersion);
    1136              : 
    1137            2 :   auto mapOld = DatabaseConfigurationInterface::config_version_map_t{};
    1138            2 :   mapOld[cfgOld->getConfigurationName()] = vOld;
    1139            2 :   ifc.storeGlobalConfiguration(mapOld, compName);
    1140            2 :   std::cout << "  Created composition with version " << vOld << "\n";
    1141              : 
    1142            2 :   auto comps1 = ifc.findCompositionsContaining(cfgOld->getConfigurationName(), std::to_string(vOld));
    1143            2 :   BOOST_CHECK(comps1.find(compName) != comps1.end());
    1144            2 :   std::cout << "  Verified: Reverse lookup finds old version\n";
    1145              : 
    1146            2 :   auto mapNew = DatabaseConfigurationInterface::config_version_map_t{};
    1147            2 :   mapNew[cfgNew->getConfigurationName()] = vNew;
    1148            2 :   ifc.storeGlobalConfiguration(mapNew, compName, true);
    1149            2 :   std::cout << "  Overwrote composition with version " << vNew << "\n";
    1150              : 
    1151            2 :   auto comps2 = ifc.findCompositionsContaining(cfgOld->getConfigurationName(), std::to_string(vOld));
    1152            2 :   bool oldFound = (comps2.find(compName) != comps2.end());
    1153            2 :   BOOST_CHECK_EQUAL(oldFound, false);
    1154            2 :   std::cout << "  Verified: Reverse lookup for old version returns EMPTY (correctly unassigned)\n";
    1155              : 
    1156            2 :   auto comps3 = ifc.findCompositionsContaining(cfgNew->getConfigurationName(), std::to_string(vNew));
    1157            2 :   bool newFound = (comps3.find(compName) != comps3.end());
    1158            2 :   BOOST_CHECK_EQUAL(newFound, true);
    1159            2 :   std::cout << "  Verified: Reverse lookup for new version finds it\n";
    1160              : 
    1161            2 :   std::cout << "  TEST PASSED: Reverse lookup is correct after overwrite\n";
    1162            2 : }
    1163              : 
    1164            4 : BOOST_AUTO_TEST_CASE(overwrite_prevents_too_many_results) {
    1165            2 :   std::cout << "\n=== TEST 20: Overwrite prevents 'too many results' error ===\n";
    1166              : 
    1167            2 :   auto ifc = DatabaseConfigurationInterface();
    1168              : 
    1169            6 :   std::string compName = std::string{"TooManyResultsTest_"} + std::to_string(fixture.baseVersion);
    1170              : 
    1171            2 :   const int NUM_OVERWRITES = 5;
    1172            2 :   int baseVer = fixture.baseVersion + 8500;
    1173              : 
    1174            2 :   std::cout << "  Performing " << NUM_OVERWRITES << " sequential overwrites...\n";
    1175              : 
    1176           12 :   for (int i = 0; i < NUM_OVERWRITES; i++) {
    1177           10 :     int version = baseVer + i;
    1178              : 
    1179           10 :     std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
    1180           10 :     cfg->getView().version = version;
    1181           10 :     ifc.saveActiveVersion(cfg.get());
    1182              : 
    1183           10 :     auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
    1184           10 :     compositionMap[cfg->getConfigurationName()] = version;
    1185              : 
    1186           10 :     bool allowOverwrite = (i > 0);
    1187           10 :     ifc.storeGlobalConfiguration(compositionMap, compName, allowOverwrite);
    1188              : 
    1189           10 :     std::cout << "    Overwrite " << i << ": Set to version " << version << "\n";
    1190           10 :   }
    1191              : 
    1192            2 :   bool loadSucceeded = false;
    1193            2 :   int loadedVersion = 0;
    1194            2 :   std::string errorMsg;
    1195              : 
    1196              :   try {
    1197            2 :     auto loaded = ifc.loadGlobalConfiguration(compName);
    1198            2 :     loadSucceeded = true;
    1199              : 
    1200            2 :     BOOST_CHECK_EQUAL(loaded.size(), 1);
    1201              : 
    1202            2 :     int expectedVersion = baseVer + NUM_OVERWRITES - 1;
    1203            2 :     loadedVersion = loaded.begin()->second;
    1204            2 :     BOOST_CHECK_EQUAL(loadedVersion, expectedVersion);
    1205              : 
    1206            2 :     std::cout << "  Load succeeded! Got version " << loadedVersion << "\n";
    1207            2 :   } catch (std::exception const& e) {
    1208            0 :     errorMsg = e.what();
    1209            0 :     std::cout << "  Load FAILED: " << errorMsg << "\n";
    1210            0 :   }
    1211              : 
    1212            2 :   BOOST_CHECK_MESSAGE(loadSucceeded,
    1213              :                       "loadGlobalConfiguration should NOT fail after multiple overwrites. "
    1214              :                       "Error: " +
    1215              :                           errorMsg);
    1216              : 
    1217            2 :   if (loadSucceeded) {
    1218            2 :     std::cout << "  Verified: No 'too many results' error after " << NUM_OVERWRITES << " overwrites\n";
    1219            2 :     std::cout << "  Verified: Final version is " << loadedVersion << " (expected " << (baseVer + NUM_OVERWRITES - 1) << ")\n";
    1220              :   }
    1221              : 
    1222            2 :   std::cout << "  TEST PASSED: Overwrite correctly prevents 'too many results' error\n";
    1223            2 : }
    1224              : 
    1225            4 : BOOST_AUTO_TEST_CASE(overwrite_with_identical_composition) {
    1226            2 :   std::cout << "\n=== TEST 21: Overwrite with identical composition (no-op) ===\n";
    1227              : 
    1228            2 :   auto ifc = DatabaseConfigurationInterface();
    1229              : 
    1230            2 :   int version1 = fixture.baseVersion + 8600;
    1231            2 :   int version2 = fixture.baseVersion + 8601;
    1232              : 
    1233            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
    1234            2 :   cfg1->getView().version = version1;
    1235            2 :   ifc.saveActiveVersion(cfg1.get());
    1236              : 
    1237            2 :   std::shared_ptr<ConfigurationBase> cfg2 = std::make_shared<TestConfiguration002>();
    1238            2 :   cfg2->getView().version = version2;
    1239            2 :   ifc.saveActiveVersion(cfg2.get());
    1240              : 
    1241            6 :   std::string compName = std::string{"IdenticalOverwriteTest_"} + std::to_string(fixture.baseVersion);
    1242              : 
    1243            2 :   auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
    1244            2 :   compositionMap[cfg1->getConfigurationName()] = version1;
    1245            2 :   compositionMap[cfg2->getConfigurationName()] = version2;
    1246              : 
    1247            2 :   ifc.storeGlobalConfiguration(compositionMap, compName);
    1248            2 :   std::cout << "  Created initial composition\n";
    1249              : 
    1250            2 :   bool overwriteSucceeded = false;
    1251              :   try {
    1252            2 :     ifc.storeGlobalConfiguration(compositionMap, compName, true);
    1253            2 :     overwriteSucceeded = true;
    1254            2 :     std::cout << "  Overwrote with identical composition: succeeded\n";
    1255            0 :   } catch (std::exception const& e) {
    1256            0 :     std::cout << "  Overwrote with identical composition: FAILED - " << e.what() << "\n";
    1257            0 :   }
    1258              : 
    1259            2 :   BOOST_CHECK_EQUAL(overwriteSucceeded, true);
    1260              : 
    1261            2 :   auto loaded = ifc.loadGlobalConfiguration(compName);
    1262            2 :   BOOST_CHECK_EQUAL(loaded.size(), 2);
    1263            2 :   BOOST_CHECK_EQUAL(loaded[cfg1->getConfigurationName()], version1);
    1264            2 :   BOOST_CHECK_EQUAL(loaded[cfg2->getConfigurationName()], version2);
    1265              : 
    1266            2 :   std::cout << "  Verified: Composition unchanged after identical overwrite\n";
    1267            2 :   std::cout << "  TEST PASSED: Identical overwrite is a successful no-op\n";
    1268            2 : }
    1269              : 
    1270              : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1