LCOV - code coverage report
Current view: top level - /opt/artdaq/srcs/artdaq-database/test/CaseStudy/OtsDaqTests/test003 - ReverseLookup_t.cc (source / functions) Coverage Total Hit
Test: artdaq.info.cleaned Lines: 91.8 % 463 425
Test Date: 2026-10-29 00:35:58 Functions: 92.3 % 39 36

            Line data    Source code
       1              : #define BOOST_TEST_DYN_LINK
       2              : #define BOOST_TEST_MODULE (ReverseLookup test)
       3              : 
       4              : #include <boost/test/unit_test.hpp>
       5              : 
       6              : #include <dirent.h>
       7              : #include <algorithm>
       8              : #include <chrono>
       9              : #include <fstream>
      10              : #include <iostream>
      11              : #include <iterator>
      12              : #include <map>
      13              : #include <memory>
      14              : #include <regex>
      15              : #include <set>
      16              : #include <sstream>
      17              : #include <string>
      18              : #include <vector>
      19              : 
      20              : #include "../DatabaseConfigurationInterface.h"
      21              : 
      22            1 : std::string readFileContent(const std::string& filepath) {
      23            1 :   std::ifstream file(filepath);
      24            1 :   if (!file.is_open()) {
      25            0 :     return "";
      26              :   }
      27            1 :   std::stringstream buffer;
      28            1 :   buffer << file.rdbuf();
      29            1 :   return buffer.str();
      30            1 : }
      31              : 
      32            1 : std::vector<std::string> listJsonFiles(const std::string& dirPath) {
      33            1 :   std::vector<std::string> files;
      34              : 
      35            1 :   DIR* dir = opendir(dirPath.c_str());
      36            1 :   if (dir == nullptr) {
      37            0 :     return files;
      38              :   }
      39              : 
      40              :   struct dirent* entry;
      41           11 :   while ((entry = readdir(dir)) != nullptr) {
      42           10 :     std::string name = entry->d_name;
      43           10 :     if (name.size() > 5 && name.substr(name.size() - 5) == ".json" && name != "index.json") {
      44            7 :       files.push_back(dirPath + "/" + name);
      45              :     }
      46           10 :   }
      47            1 :   closedir(dir);
      48            1 :   return files;
      49            0 : }
      50              : 
      51            4 : bool isFileSystemBackend() {
      52            4 :   auto uri = std::getenv("ARTDAQ_DATABASE_URI");
      53            4 :   if (uri) {
      54            4 :     std::string uriStr(uri);
      55            4 :     return uriStr.find("filesystemdb") != std::string::npos;
      56            4 :   }
      57            0 :   return false;
      58              : }
      59              : 
      60            2 : std::string getDatabasePath() {
      61            2 :   auto uri = std::getenv("ARTDAQ_DATABASE_URI");
      62            2 :   if (uri) {
      63            2 :     std::string uriStr(uri);
      64            2 :     if (uriStr.find("filesystemdb:///") == 0) {
      65            1 :       return uriStr.substr(16);
      66            1 :     } else if (uriStr.find("filesystemdb://") == 0) {
      67            0 :       return uriStr.substr(15);
      68              :     }
      69            1 :     return uriStr;
      70            2 :   }
      71            0 :   return "";
      72              : }
      73              : 
      74            0 : std::string extractCompositionName(const std::string& jsonContent) {
      75            0 :   std::regex pattern("\"name\"\\s*:\\s*\"([^\"]+)\"");
      76            0 :   std::smatch match;
      77              : 
      78            0 :   if (std::regex_search(jsonContent, match, pattern)) {
      79            0 :     return match[1].str();
      80              :   }
      81            0 :   return "";
      82            0 : }
      83              : 
      84            0 : bool compositionContains(const std::string& jsonContent, const std::string& configType, const std::string& version) {
      85            0 :   bool hasConfigType = jsonContent.find("\"" + configType + "\"") != std::string::npos;
      86              : 
      87            0 :   std::regex versionPattern("\"version\"\\s*:\\s*\"" + version + "\"");
      88            0 :   bool hasVersion = std::regex_search(jsonContent, versionPattern);
      89              : 
      90            0 :   return hasConfigType && hasVersion;
      91            0 : }
      92              : 
      93            0 : int countCompositionsContaining(const std::string& globalConfigDir, const std::string& configType, const std::string& version) {
      94            0 :   auto files = listJsonFiles(globalConfigDir);
      95            0 :   int count = 0;
      96              : 
      97            0 :   for (const auto& filepath : files) {
      98            0 :     std::string content = readFileContent(filepath);
      99            0 :     if (content.empty()) continue;
     100              : 
     101            0 :     if (compositionContains(content, configType, version)) {
     102            0 :       count++;
     103              :     }
     104            0 :   }
     105            0 :   return count;
     106            0 : }
     107              : 
     108            1 : std::set<std::string> extractOidsFromIndex(const std::string& indexPath) {
     109            1 :   std::set<std::string> oids;
     110            1 :   std::string content = readFileContent(indexPath);
     111            1 :   if (content.empty()) {
     112            0 :     return oids;
     113              :   }
     114              : 
     115            1 :   std::regex pattern("\"oid\"\\s*:\\s*\"([a-f0-9]+)\"");
     116            1 :   auto begin = std::sregex_iterator(content.begin(), content.end(), pattern);
     117            1 :   auto end = std::sregex_iterator();
     118              : 
     119            1 :   for (auto it = begin; it != end; ++it) {
     120            0 :     oids.insert((*it)[1].str());
     121            1 :   }
     122            1 :   return oids;
     123            1 : }
     124              : 
     125            7 : std::string getOidFromFilename(const std::string& filepath) {
     126            7 :   size_t lastSlash = filepath.rfind('/');
     127            7 :   std::string filename = (lastSlash != std::string::npos) ? filepath.substr(lastSlash + 1) : filepath;
     128              : 
     129            7 :   if (filename.size() > 5 && filename.substr(filename.size() - 5) == ".json") {
     130            7 :     return filename.substr(0, filename.size() - 5);
     131              :   }
     132            0 :   return "";
     133            7 : }
     134              : 
     135              : using namespace ots;
     136              : 
     137              : struct ReverseLookupTestData {
     138            2 :   ReverseLookupTestData() {
     139            2 :     srand(time(nullptr));
     140            2 :     baseVersion = rand() % 99999 + 100000;
     141            2 :     dbPath = getDatabasePath();
     142            2 :     std::cout << "=== REVERSE LOOKUP TEST SUITE ===\n";
     143            2 :     std::cout << "Base version: " << baseVersion << "\n";
     144            2 :     std::cout << "Database path: " << dbPath << "\n\n";
     145            2 :   }
     146              : 
     147            2 :   ~ReverseLookupTestData() { std::cout << "\n=== TEST SUITE COMPLETE ===\n"; }
     148              : 
     149              :   int baseVersion;
     150              :   std::string dbPath;
     151              : };
     152              : 
     153              : ReverseLookupTestData fixture;
     154              : 
     155              : BOOST_AUTO_TEST_SUITE(reverse_lookup_test)
     156              : 
     157            4 : BOOST_AUTO_TEST_CASE(basic_reverse_lookup) {
     158            2 :   std::cout << "\n=== TEST 1: Basic reverse lookup ===\n";
     159              : 
     160            2 :   auto ifc = DatabaseConfigurationInterface();
     161              : 
     162            2 :   int version1 = fixture.baseVersion;
     163            2 :   int version2 = fixture.baseVersion + 1;
     164              : 
     165            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
     166            2 :   cfg1->getView().version = version1;
     167            2 :   auto result1 = ifc.saveActiveVersion(cfg1.get());
     168            2 :   BOOST_CHECK_EQUAL(result1, 0);
     169              : 
     170            2 :   std::shared_ptr<ConfigurationBase> cfg2 = std::make_shared<TestConfiguration002>();
     171            2 :   cfg2->getView().version = version2;
     172            2 :   auto result2 = ifc.saveActiveVersion(cfg2.get());
     173            2 :   BOOST_CHECK_EQUAL(result2, 0);
     174              : 
     175            2 :   std::cout << "Created versions: " << version1 << ", " << version2 << "\n";
     176              : 
     177            2 :   auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     178            2 :   compositionMap[cfg1->getConfigurationName()] = version1;
     179            2 :   compositionMap[cfg2->getConfigurationName()] = version2;
     180              : 
     181            6 :   std::string compositionName = std::string{"TestComp_"} + std::to_string(fixture.baseVersion);
     182              : 
     183            2 :   ifc.storeGlobalConfiguration(compositionMap, compositionName);
     184            2 :   std::cout << "Created composition: " << compositionName << "\n";
     185              : 
     186            2 :   std::cout << "Performing reverse lookup for " << cfg1->getConfigurationName() << " v" << version1 << "\n";
     187              : 
     188            2 :   auto foundCompositions = ifc.findCompositionsContaining(cfg1->getConfigurationName(), std::to_string(version1));
     189              : 
     190            2 :   std::cout << "Found " << foundCompositions.size() << " composition(s)\n";
     191            4 :   for (auto const& comp : foundCompositions) {
     192            2 :     std::cout << "  - " << comp << "\n";
     193              :   }
     194              : 
     195            2 :   bool found = (foundCompositions.find(compositionName) != foundCompositions.end());
     196              : 
     197            2 :   BOOST_CHECK_EQUAL(found, true);
     198            2 :   std::cout << "  Verified: Composition found via reverse lookup\n";
     199            2 : }
     200              : 
     201            4 : BOOST_AUTO_TEST_CASE(multiple_compositions_same_version) {
     202            2 :   std::cout << "\n=== TEST 2: Multiple compositions same version ===\n";
     203              : 
     204            2 :   auto ifc = DatabaseConfigurationInterface();
     205              : 
     206            2 :   int sharedVersion = fixture.baseVersion + 10;
     207              : 
     208            2 :   std::shared_ptr<ConfigurationBase> cfgShared = std::make_shared<TestConfiguration001>();
     209            2 :   cfgShared->getView().version = sharedVersion;
     210            2 :   ifc.saveActiveVersion(cfgShared.get());
     211              : 
     212            2 :   std::cout << "Created shared version: " << sharedVersion << "\n";
     213              : 
     214            2 :   auto map1 = DatabaseConfigurationInterface::config_version_map_t{};
     215            2 :   map1[cfgShared->getConfigurationName()] = sharedVersion;
     216              : 
     217            6 :   std::string comp1Name = std::string{"Comp1_"} + std::to_string(fixture.baseVersion);
     218            2 :   ifc.storeGlobalConfiguration(map1, comp1Name);
     219            2 :   std::cout << "Created composition 1: " << comp1Name << "\n";
     220              : 
     221            2 :   auto map2 = DatabaseConfigurationInterface::config_version_map_t{};
     222            2 :   map2[cfgShared->getConfigurationName()] = sharedVersion;
     223              : 
     224            6 :   std::string comp2Name = std::string{"Comp2_"} + std::to_string(fixture.baseVersion);
     225            2 :   ifc.storeGlobalConfiguration(map2, comp2Name);
     226            2 :   std::cout << "Created composition 2: " << comp2Name << "\n";
     227              : 
     228            2 :   auto foundCompositions = ifc.findCompositionsContaining(cfgShared->getConfigurationName(), std::to_string(sharedVersion));
     229              : 
     230            2 :   std::cout << "Reverse lookup found " << foundCompositions.size() << " compositions\n";
     231              : 
     232            2 :   BOOST_CHECK_EQUAL(foundCompositions.size(), 2);
     233              : 
     234            2 :   bool found1 = (foundCompositions.find(comp1Name) != foundCompositions.end());
     235            2 :   bool found2 = (foundCompositions.find(comp2Name) != foundCompositions.end());
     236              : 
     237            2 :   BOOST_CHECK_EQUAL(found1, true);
     238            2 :   BOOST_CHECK_EQUAL(found2, true);
     239              : 
     240            2 :   std::cout << "  Verified: Both compositions found\n";
     241            2 : }
     242              : 
     243            4 : BOOST_AUTO_TEST_CASE(reverse_lookup_no_matches) {
     244            2 :   std::cout << "\n=== TEST 3: Reverse lookup no matches ===\n";
     245              : 
     246            2 :   auto ifc = DatabaseConfigurationInterface();
     247              : 
     248            2 :   int orphanVersion = fixture.baseVersion + 100;
     249              : 
     250            2 :   std::shared_ptr<ConfigurationBase> cfgOrphan = std::make_shared<TestConfiguration001>();
     251            2 :   cfgOrphan->getView().version = orphanVersion;
     252            2 :   ifc.saveActiveVersion(cfgOrphan.get());
     253              : 
     254            2 :   std::cout << "Created orphan version (not in any composition): " << orphanVersion << "\n";
     255              : 
     256            2 :   auto foundCompositions = ifc.findCompositionsContaining(cfgOrphan->getConfigurationName(), std::to_string(orphanVersion));
     257              : 
     258            2 :   std::cout << "Reverse lookup found " << foundCompositions.size() << " compositions\n";
     259              : 
     260            2 :   BOOST_CHECK_EQUAL(foundCompositions.size(), 0);
     261            2 :   std::cout << "  Verified: No compositions found (as expected)\n";
     262            2 : }
     263              : 
     264            4 : BOOST_AUTO_TEST_CASE(version_multiple_positions_same_composition) {
     265            2 :   std::cout << "\n=== TEST 4: Version multiple positions same composition ===\n";
     266              : 
     267            2 :   auto ifc = DatabaseConfigurationInterface();
     268              : 
     269            2 :   int version1 = fixture.baseVersion + 200;
     270            2 :   int version2 = fixture.baseVersion + 201;
     271              : 
     272            2 :   std::shared_ptr<ConfigurationBase> cfg1a = std::make_shared<TestConfiguration001>();
     273            2 :   cfg1a->getView().version = version1;
     274            2 :   ifc.saveActiveVersion(cfg1a.get());
     275              : 
     276            2 :   std::shared_ptr<ConfigurationBase> cfg1b = std::make_shared<TestConfiguration001>();
     277            2 :   cfg1b->getView().version = version2;
     278            2 :   ifc.saveActiveVersion(cfg1b.get());
     279              : 
     280            2 :   auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     281            2 :   compositionMap[cfg1a->getConfigurationName()] = version1;
     282              : 
     283            6 :   std::string compName = std::string{"SingleTypeComp_"} + std::to_string(fixture.baseVersion);
     284              : 
     285            2 :   ifc.storeGlobalConfiguration(compositionMap, compName);
     286              : 
     287            2 :   auto found1 = ifc.findCompositionsContaining(cfg1a->getConfigurationName(), std::to_string(version1));
     288              : 
     289            2 :   BOOST_CHECK_EQUAL(found1.size(), 1);
     290              : 
     291            2 :   auto found2 = ifc.findCompositionsContaining(cfg1b->getConfigurationName(), std::to_string(version2));
     292              : 
     293            2 :   BOOST_CHECK_EQUAL(found2.size(), 0);
     294              : 
     295            2 :   std::cout << "  Verified: Only the stored version is found\n";
     296            2 : }
     297              : 
     298            4 : BOOST_AUTO_TEST_CASE(performance_many_compositions) {
     299            2 :   std::cout << "\n=== TEST 5: Performance many compositions ===\n";
     300              : 
     301            2 :   auto ifc = DatabaseConfigurationInterface();
     302              : 
     303            2 :   int targetVersion = fixture.baseVersion + 500;
     304              : 
     305            2 :   std::shared_ptr<ConfigurationBase> cfgTarget = std::make_shared<TestConfiguration001>();
     306            2 :   cfgTarget->getView().version = targetVersion;
     307            2 :   ifc.saveActiveVersion(cfgTarget.get());
     308              : 
     309            2 :   std::string targetCompName;
     310              : 
     311           22 :   for (int i = 0; i < 10; ++i) {
     312           20 :     int otherVersion = fixture.baseVersion + 600 + i;
     313              : 
     314           20 :     std::shared_ptr<ConfigurationBase> cfgOther = std::make_shared<TestConfiguration002>();
     315           20 :     cfgOther->getView().version = otherVersion;
     316           20 :     ifc.saveActiveVersion(cfgOther.get());
     317              : 
     318           20 :     auto map = DatabaseConfigurationInterface::config_version_map_t{};
     319              : 
     320           20 :     if (i == 5) {
     321            2 :       map[cfgTarget->getConfigurationName()] = targetVersion;
     322            2 :       map[cfgOther->getConfigurationName()] = otherVersion;
     323            6 :       targetCompName = std::string{"PerfComp_"} + std::to_string(i) + "_" + std::to_string(fixture.baseVersion);
     324              :     } else {
     325           18 :       map[cfgOther->getConfigurationName()] = otherVersion;
     326              :     }
     327              : 
     328           60 :     std::string compName = std::string{"PerfComp_"} + std::to_string(i) + "_" + std::to_string(fixture.baseVersion);
     329           20 :     ifc.storeGlobalConfiguration(map, compName);
     330           20 :   }
     331              : 
     332            2 :   std::cout << "Created 10 compositions, target is in: " << targetCompName << "\n";
     333              : 
     334            2 :   auto start = std::chrono::high_resolution_clock::now();
     335              : 
     336            2 :   auto foundCompositions = ifc.findCompositionsContaining(cfgTarget->getConfigurationName(), std::to_string(targetVersion));
     337              : 
     338            2 :   auto end = std::chrono::high_resolution_clock::now();
     339            2 :   auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
     340              : 
     341            2 :   std::cout << "Reverse lookup completed in " << duration.count() << "ms\n";
     342              : 
     343            2 :   BOOST_CHECK_EQUAL(foundCompositions.size(), 1);
     344              : 
     345            2 :   bool foundTarget = (foundCompositions.find(targetCompName) != foundCompositions.end());
     346            2 :   BOOST_CHECK_EQUAL(foundTarget, true);
     347              : 
     348            2 :   std::cout << "  Verified: Correct composition found efficiently\n";
     349              : 
     350            2 :   BOOST_CHECK_LT(duration.count(), 5000);
     351            2 : }
     352              : 
     353            4 : BOOST_AUTO_TEST_CASE(raw_storage_verify_composition) {
     354            2 :   std::cout << "\n=== TEST 6: Raw storage verification ===\n";
     355              : 
     356            2 :   if (!isFileSystemBackend()) {
     357            1 :     std::cout << "  Skipping: Not using FileSystemDB backend\n";
     358            1 :     BOOST_CHECK(true);
     359            1 :     return;
     360              :   }
     361              : 
     362            1 :   auto ifc = DatabaseConfigurationInterface();
     363              : 
     364            1 :   int version1 = fixture.baseVersion + 700;
     365            1 :   int version2 = fixture.baseVersion + 701;
     366              : 
     367            1 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
     368            1 :   cfg1->getView().version = version1;
     369            1 :   ifc.saveActiveVersion(cfg1.get());
     370              : 
     371            1 :   std::shared_ptr<ConfigurationBase> cfg2 = std::make_shared<TestConfiguration002>();
     372            1 :   cfg2->getView().version = version2;
     373            1 :   ifc.saveActiveVersion(cfg2.get());
     374              : 
     375            1 :   auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     376            1 :   compositionMap[cfg1->getConfigurationName()] = version1;
     377            1 :   compositionMap[cfg2->getConfigurationName()] = version2;
     378              : 
     379            3 :   std::string compName = std::string{"RawStorageComp_"} + std::to_string(fixture.baseVersion);
     380            1 :   ifc.storeGlobalConfiguration(compositionMap, compName);
     381              : 
     382            1 :   std::cout << "Created composition: " << compName << "\n";
     383              : 
     384            1 :   auto apiResults = ifc.findCompositionsContaining(cfg1->getConfigurationName(), std::to_string(version1));
     385              : 
     386            1 :   std::cout << "  API found " << apiResults.size() << " composition(s) containing v" << version1 << "\n";
     387              : 
     388            1 :   BOOST_CHECK_GE(apiResults.size(), static_cast<size_t>(1));
     389            1 :   bool foundOurs = (apiResults.find(compName) != apiResults.end());
     390            1 :   BOOST_CHECK_EQUAL(foundOurs, true);
     391              : 
     392            1 :   std::cout << "  Verified: Composition accessible via API\n";
     393            1 : }
     394              : 
     395            4 : BOOST_AUTO_TEST_CASE(index_consistency_validation) {
     396            2 :   std::cout << "\n=== TEST 7: Index consistency validation ===\n";
     397              : 
     398            2 :   if (!isFileSystemBackend()) {
     399            1 :     std::cout << "  Skipping: Not using FileSystemDB backend\n";
     400            1 :     BOOST_CHECK(true);
     401            1 :     return;
     402              :   }
     403              : 
     404            1 :   std::string collectionDir = fixture.dbPath + "/TestConfiguration001";
     405            1 :   std::string indexPath = collectionDir + "/index.json";
     406              : 
     407            1 :   auto indexOids = extractOidsFromIndex(indexPath);
     408            1 :   std::cout << "  OIDs in index: " << indexOids.size() << "\n";
     409              : 
     410            1 :   auto jsonFiles = listJsonFiles(collectionDir);
     411            1 :   std::set<std::string> fileOids;
     412            8 :   for (const auto& filepath : jsonFiles) {
     413            7 :     std::string oid = getOidFromFilename(filepath);
     414            7 :     if (!oid.empty() && oid != "index") {
     415            7 :       fileOids.insert(oid);
     416              :     }
     417            7 :   }
     418            1 :   std::cout << "  Files on disk: " << fileOids.size() << "\n";
     419              : 
     420            1 :   int orphaned = 0;
     421            8 :   for (const auto& fileOid : fileOids) {
     422            7 :     if (indexOids.find(fileOid) == indexOids.end()) {
     423            7 :       std::cout << "  WARNING: Orphaned file: " << fileOid << ".json\n";
     424            7 :       orphaned++;
     425              :     }
     426              :   }
     427              : 
     428            1 :   int missing = 0;
     429            1 :   for (const auto& indexOid : indexOids) {
     430            0 :     if (fileOids.find(indexOid) == fileOids.end()) {
     431            0 :       std::cout << "  WARNING: Missing file: " << indexOid << ".json\n";
     432            0 :       missing++;
     433              :     }
     434              :   }
     435              : 
     436            1 :   std::cout << "  Orphaned files: " << orphaned << "\n";
     437            1 :   std::cout << "  Missing files: " << missing << "\n";
     438              : 
     439            1 :   BOOST_WARN_EQUAL(orphaned, 0);
     440            1 :   BOOST_CHECK_EQUAL(missing, 0);
     441              : 
     442            1 :   std::cout << "  Verified: Index consistency checked\n";
     443            1 : }
     444              : 
     445            4 : BOOST_AUTO_TEST_CASE(stress_test_many_compositions) {
     446            2 :   std::cout << "\n=== TEST 8: Stress test - many compositions ===\n";
     447              : 
     448            2 :   auto ifc = DatabaseConfigurationInterface();
     449              : 
     450            2 :   const int NUM_COMPOSITIONS = 20;
     451            2 :   int baseVer = fixture.baseVersion + 800;
     452              : 
     453            2 :   std::shared_ptr<ConfigurationBase> cfgShared = std::make_shared<TestConfiguration001>();
     454            2 :   cfgShared->getView().version = baseVer;
     455            2 :   ifc.saveActiveVersion(cfgShared.get());
     456              : 
     457            2 :   auto startCreate = std::chrono::high_resolution_clock::now();
     458              : 
     459            2 :   int compositionsWithShared = 0;
     460           42 :   for (int i = 0; i < NUM_COMPOSITIONS; i++) {
     461           40 :     int otherVersion = baseVer + 100 + i;
     462              : 
     463           40 :     std::shared_ptr<ConfigurationBase> cfgOther = std::make_shared<TestConfiguration002>();
     464           40 :     cfgOther->getView().version = otherVersion;
     465           40 :     ifc.saveActiveVersion(cfgOther.get());
     466              : 
     467           40 :     auto map = DatabaseConfigurationInterface::config_version_map_t{};
     468              : 
     469           40 :     if (i % 2 == 0) {
     470           20 :       map[cfgShared->getConfigurationName()] = baseVer;
     471           20 :       compositionsWithShared++;
     472              :     }
     473           40 :     map[cfgOther->getConfigurationName()] = otherVersion;
     474              : 
     475          120 :     std::string compName = std::string{"StressComp_"} + std::to_string(i) + "_" + std::to_string(fixture.baseVersion);
     476           40 :     ifc.storeGlobalConfiguration(map, compName);
     477           40 :   }
     478              : 
     479            2 :   auto endCreate = std::chrono::high_resolution_clock::now();
     480            2 :   auto createDuration = std::chrono::duration_cast<std::chrono::milliseconds>(endCreate - startCreate);
     481              : 
     482            2 :   std::cout << "  Created " << NUM_COMPOSITIONS << " compositions in " << createDuration.count() << "ms\n";
     483            2 :   std::cout << "  " << compositionsWithShared << " compositions contain shared version\n";
     484              : 
     485            2 :   auto startLookup = std::chrono::high_resolution_clock::now();
     486              : 
     487            2 :   auto foundCompositions = ifc.findCompositionsContaining(cfgShared->getConfigurationName(), std::to_string(baseVer));
     488              : 
     489            2 :   auto endLookup = std::chrono::high_resolution_clock::now();
     490            2 :   auto lookupDuration = std::chrono::duration_cast<std::chrono::milliseconds>(endLookup - startLookup);
     491              : 
     492            2 :   std::cout << "  Reverse lookup completed in " << lookupDuration.count() << "ms\n";
     493            2 :   std::cout << "  Found " << foundCompositions.size() << " compositions\n";
     494              : 
     495            2 :   BOOST_CHECK_EQUAL(foundCompositions.size(), static_cast<size_t>(compositionsWithShared));
     496              : 
     497            2 :   BOOST_CHECK_LT(lookupDuration.count(), 10000);
     498              : 
     499            2 :   std::cout << "  Verified: Stress test passed\n";
     500            2 : }
     501              : 
     502            4 : BOOST_AUTO_TEST_CASE(edge_case_config_type_not_in_compositions) {
     503            2 :   std::cout << "\n=== TEST 9: Edge case - config type not in compositions ===\n";
     504              : 
     505            2 :   auto ifc = DatabaseConfigurationInterface();
     506              : 
     507            2 :   int testVersion = fixture.baseVersion + 900;
     508              : 
     509            2 :   std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
     510            2 :   cfg->getView().version = testVersion;
     511            2 :   ifc.saveActiveVersion(cfg.get());
     512              : 
     513            2 :   std::cout << "Created standalone version " << testVersion << " (not in any composition)\n";
     514              : 
     515            2 :   auto foundCompositions = ifc.findCompositionsContaining(cfg->getConfigurationName(), std::to_string(testVersion));
     516              : 
     517            2 :   BOOST_CHECK_EQUAL(foundCompositions.size(), 0);
     518            2 :   std::cout << "  Verified: Empty result for version not in any composition\n";
     519            2 : }
     520              : 
     521            4 : BOOST_AUTO_TEST_CASE(edge_case_nonexistent_version) {
     522            2 :   std::cout << "\n=== TEST 10: Edge case - nonexistent version ===\n";
     523              : 
     524            2 :   auto ifc = DatabaseConfigurationInterface();
     525              : 
     526            2 :   int nonexistentVersion = 999888777;
     527              : 
     528            6 :   auto foundCompositions = ifc.findCompositionsContaining("TestConfiguration001", std::to_string(nonexistentVersion));
     529              : 
     530            2 :   std::cout << "  Searched for nonexistent version " << nonexistentVersion << "\n";
     531            2 :   std::cout << "  Found " << foundCompositions.size() << " compositions\n";
     532              : 
     533            2 :   BOOST_CHECK_EQUAL(foundCompositions.size(), 0);
     534            2 :   std::cout << "  Verified: Empty result for nonexistent version\n";
     535            2 : }
     536              : 
     537            4 : BOOST_AUTO_TEST_CASE(reverse_lookup_different_config_types) {
     538            2 :   std::cout << "\n=== TEST 11: Reverse lookup different config types ===\n";
     539              : 
     540            2 :   auto ifc = DatabaseConfigurationInterface();
     541              : 
     542            2 :   int version1 = fixture.baseVersion + 1100;
     543            2 :   int version2 = fixture.baseVersion + 1101;
     544              : 
     545            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
     546            2 :   cfg1->getView().version = version1;
     547            2 :   ifc.saveActiveVersion(cfg1.get());
     548              : 
     549            2 :   std::shared_ptr<ConfigurationBase> cfg2 = std::make_shared<TestConfiguration002>();
     550            2 :   cfg2->getView().version = version2;
     551            2 :   ifc.saveActiveVersion(cfg2.get());
     552              : 
     553            2 :   auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     554            2 :   compositionMap[cfg1->getConfigurationName()] = version1;
     555            2 :   compositionMap[cfg2->getConfigurationName()] = version2;
     556              : 
     557            6 :   std::string compName = std::string{"MultiTypeComp_"} + std::to_string(fixture.baseVersion);
     558            2 :   ifc.storeGlobalConfiguration(compositionMap, compName);
     559              : 
     560            2 :   std::cout << "Created composition with both config types\n";
     561              : 
     562            2 :   auto found1 = ifc.findCompositionsContaining(cfg1->getConfigurationName(), std::to_string(version1));
     563            2 :   BOOST_CHECK_EQUAL(found1.size(), 1);
     564            2 :   BOOST_CHECK(found1.find(compName) != found1.end());
     565              : 
     566            2 :   auto found2 = ifc.findCompositionsContaining(cfg2->getConfigurationName(), std::to_string(version2));
     567            2 :   BOOST_CHECK_EQUAL(found2.size(), 1);
     568            2 :   BOOST_CHECK(found2.find(compName) != found2.end());
     569              : 
     570            2 :   std::cout << "  Verified: Both config types correctly find the composition\n";
     571            2 : }
     572              : 
     573            4 : BOOST_AUTO_TEST_CASE(performance_multiple_lookups) {
     574            2 :   std::cout << "\n=== TEST 12: Performance - multiple reverse lookups ===\n";
     575              : 
     576            2 :   auto ifc = DatabaseConfigurationInterface();
     577              : 
     578            2 :   const int NUM_LOOKUPS = 20;
     579            2 :   int baseVer = fixture.baseVersion + 1200;
     580              : 
     581            2 :   std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
     582            2 :   cfg->getView().version = baseVer;
     583            2 :   ifc.saveActiveVersion(cfg.get());
     584              : 
     585            2 :   auto map = DatabaseConfigurationInterface::config_version_map_t{};
     586            2 :   map[cfg->getConfigurationName()] = baseVer;
     587              : 
     588            6 :   std::string compName = std::string{"PerfLookupComp_"} + std::to_string(fixture.baseVersion);
     589            2 :   ifc.storeGlobalConfiguration(map, compName);
     590              : 
     591            2 :   auto startTime = std::chrono::high_resolution_clock::now();
     592              : 
     593           42 :   for (int i = 0; i < NUM_LOOKUPS; i++) {
     594           40 :     auto found = ifc.findCompositionsContaining(cfg->getConfigurationName(), std::to_string(baseVer));
     595           40 :     BOOST_CHECK_EQUAL(found.size(), 1);
     596           40 :   }
     597              : 
     598            2 :   auto endTime = std::chrono::high_resolution_clock::now();
     599            2 :   auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
     600              : 
     601            2 :   std::cout << "  Completed " << NUM_LOOKUPS << " reverse lookups in " << duration.count() << "ms\n";
     602            2 :   std::cout << "  Average: " << (duration.count() / NUM_LOOKUPS) << "ms per lookup\n";
     603              : 
     604            2 :   BOOST_CHECK_LT(duration.count(), 10000);
     605              : 
     606            2 :   std::cout << "  Verified: Multiple lookups performance acceptable\n";
     607            2 : }
     608              : 
     609            4 : BOOST_AUTO_TEST_CASE(api_consistency_multiple_compositions) {
     610            2 :   std::cout << "\n=== TEST 13: API consistency - multiple compositions ===\n";
     611              : 
     612            2 :   auto ifc = DatabaseConfigurationInterface();
     613              : 
     614            2 :   int sharedVersion = fixture.baseVersion + 1300;
     615            2 :   const int NUM_COMPOSITIONS = 3;
     616              : 
     617            2 :   std::shared_ptr<ConfigurationBase> cfgShared = std::make_shared<TestConfiguration001>();
     618            2 :   cfgShared->getView().version = sharedVersion;
     619            2 :   ifc.saveActiveVersion(cfgShared.get());
     620              : 
     621            2 :   std::vector<std::string> createdComps;
     622            8 :   for (int i = 0; i < NUM_COMPOSITIONS; i++) {
     623            6 :     int otherVersion = sharedVersion + 100 + i;
     624              : 
     625            6 :     std::shared_ptr<ConfigurationBase> cfgOther = std::make_shared<TestConfiguration002>();
     626            6 :     cfgOther->getView().version = otherVersion;
     627            6 :     ifc.saveActiveVersion(cfgOther.get());
     628              : 
     629            6 :     auto map = DatabaseConfigurationInterface::config_version_map_t{};
     630            6 :     map[cfgShared->getConfigurationName()] = sharedVersion;
     631            6 :     map[cfgOther->getConfigurationName()] = otherVersion;
     632              : 
     633           18 :     std::string compName = std::string{"ConsistencyComp_"} + std::to_string(i) + "_" + std::to_string(fixture.baseVersion);
     634            6 :     ifc.storeGlobalConfiguration(map, compName);
     635            6 :     createdComps.push_back(compName);
     636            6 :   }
     637              : 
     638            2 :   std::cout << "  Created " << NUM_COMPOSITIONS << " compositions with shared version\n";
     639              : 
     640            2 :   auto apiResults = ifc.findCompositionsContaining(cfgShared->getConfigurationName(), std::to_string(sharedVersion));
     641            2 :   std::cout << "  API found " << apiResults.size() << " compositions\n";
     642              : 
     643            2 :   BOOST_CHECK_GE(apiResults.size(), static_cast<size_t>(NUM_COMPOSITIONS));
     644              : 
     645            2 :   int foundCount = 0;
     646            8 :   for (const auto& comp : createdComps) {
     647            6 :     bool found = apiResults.find(comp) != apiResults.end();
     648            6 :     if (found) foundCount++;
     649            6 :     BOOST_CHECK_MESSAGE(found, "Composition " << comp << " should be in API results");
     650              :   }
     651              : 
     652            2 :   std::cout << "  Found " << foundCount << " of " << NUM_COMPOSITIONS << " expected compositions\n";
     653            2 :   std::cout << "  Verified: API returns all expected compositions\n";
     654            2 : }
     655              : 
     656            4 : BOOST_AUTO_TEST_CASE(comprehensive_reverse_lookup_verification) {
     657            2 :   std::cout << "\n=== TEST 14: Comprehensive reverse lookup verification ===\n";
     658              : 
     659            2 :   auto ifc = DatabaseConfigurationInterface();
     660              : 
     661            2 :   int baseVer = fixture.baseVersion + 1400;
     662            2 :   const int NUM_CONFIGS = 3;
     663            2 :   const int NUM_COMPOSITIONS = 4;
     664              : 
     665            2 :   std::vector<int> createdVersions;
     666            8 :   for (int i = 0; i < NUM_CONFIGS; i++) {
     667            6 :     int version = baseVer + i;
     668            6 :     std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
     669            6 :     cfg->getView().version = version;
     670            6 :     ifc.saveActiveVersion(cfg.get());
     671            6 :     createdVersions.push_back(version);
     672            6 :   }
     673              : 
     674            2 :   std::map<std::string, std::vector<int>> compVersions;
     675              : 
     676           10 :   for (int i = 0; i < NUM_COMPOSITIONS; i++) {
     677            8 :     auto map = DatabaseConfigurationInterface::config_version_map_t{};
     678              : 
     679            8 :     int v1 = createdVersions[i % NUM_CONFIGS];
     680           16 :     map["TestConfiguration001"] = v1;
     681              : 
     682            8 :     int v2 = baseVer + 100 + i;
     683            8 :     std::shared_ptr<ConfigurationBase> cfg2 = std::make_shared<TestConfiguration002>();
     684            8 :     cfg2->getView().version = v2;
     685            8 :     ifc.saveActiveVersion(cfg2.get());
     686           16 :     map["TestConfiguration002"] = v2;
     687              : 
     688           24 :     std::string compName = std::string{"ComprehensiveComp_"} + std::to_string(i) + "_" + std::to_string(fixture.baseVersion);
     689            8 :     ifc.storeGlobalConfiguration(map, compName);
     690            8 :     compVersions[compName] = {v1, v2};
     691            8 :   }
     692              : 
     693            2 :   std::cout << "  Created " << NUM_COMPOSITIONS << " compositions for comprehensive scan\n";
     694              : 
     695            2 :   int successfulLookups = 0;
     696            2 :   int failedLookups = 0;
     697              : 
     698           10 :   for (const auto& [compName, versions] : compVersions) {
     699           24 :     auto results = ifc.findCompositionsContaining("TestConfiguration001", std::to_string(versions[0]));
     700            8 :     bool found = results.find(compName) != results.end();
     701            8 :     if (found) {
     702            8 :       successfulLookups++;
     703              :     } else {
     704            0 :       failedLookups++;
     705            0 :       std::cout << "  WARNING: Composition " << compName << " not found via v" << versions[0] << "\n";
     706              :     }
     707            8 :   }
     708              : 
     709            2 :   std::cout << "  Successful lookups: " << successfulLookups << "\n";
     710            2 :   std::cout << "  Failed lookups: " << failedLookups << "\n";
     711              : 
     712            2 :   BOOST_CHECK_EQUAL(failedLookups, 0);
     713            2 :   std::cout << "  Verified: Comprehensive scan complete\n";
     714            2 : }
     715              : 
     716              : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1