LCOV - code coverage report
Current view: top level - /opt/artdaq/srcs/artdaq-database/test/CaseStudy/OtsDaqCPPInterface/test002 - DuplicateDetection_t.cc (source / functions) Coverage Total Hit
Test: artdaq.info.cleaned Lines: 100.0 % 145 145
Test Date: 2026-10-29 00:35:58 Functions: 100.0 % 13 13

            Line data    Source code
       1              : #define BOOST_TEST_DYN_LINK
       2              : #define BOOST_TEST_MODULE (DuplicateDetection test)
       3              : 
       4              : #include <boost/test/unit_test.hpp>
       5              : 
       6              : #include <cassert>
       7              : #include <cstdlib>
       8              : #include <ctime>
       9              : #include <iostream>
      10              : #include <memory>
      11              : #include <string>
      12              : 
      13              : #include "../DatabaseConfigurationInterface.h"
      14              : #include "artdaq-database/JsonDocument/JSONDocument.h"
      15              : 
      16              : #include "artdaq-database/ConfigurationDB/Multitasker.h"
      17              : #include "artdaq-database/ConfigurationDB/configuration_common.h"
      18              : #include "artdaq-database/ConfigurationDB/dispatch_common.h"
      19              : 
      20            4 : bool jsonContains(const std::string& json, const std::string& key, const std::string& value) {
      21            4 :   std::string pattern1 = "\"" + key + "\":\"" + value + "\"";
      22            4 :   std::string pattern2 = "\"" + key + "\" : \"" + value + "\"";
      23            8 :   return json.find(pattern1) != std::string::npos || json.find(pattern2) != std::string::npos;
      24            4 : }
      25              : 
      26              : struct DuplicateTestData {
      27            2 :   DuplicateTestData() {
      28            2 :     srand(time(nullptr));
      29            2 :     baseVersion = rand() % 99999 + 100000;
      30            2 :     std::cout << "=== DUPLICATE DETECTION TEST SUITE ===\n";
      31            2 :     std::cout << "Base version: " << baseVersion << "\n\n";
      32            2 :   }
      33              : 
      34            2 :   ~DuplicateTestData() { std::cout << "\n=== TEST SUITE COMPLETE ===\n"; }
      35              : 
      36              :   void updateConfigCount(int count) { oldConfigCount = count; }
      37              : 
      38              :   int baseVersion;
      39              :   int oldConfigCount = 0;
      40              : };
      41              : 
      42              : using namespace ots;
      43              : 
      44              : DuplicateTestData fixture;
      45              : 
      46              : BOOST_AUTO_TEST_SUITE(duplicate_detection_test)
      47              : 
      48            4 : BOOST_AUTO_TEST_CASE(duplicate_version_rejected) {
      49            2 :   std::cout << "\n=== TEST 1: Duplicate version rejected ===\n";
      50              : 
      51            2 :   auto ifc = DatabaseConfigurationInterface();
      52              : 
      53            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
      54              : 
      55            2 :   int testVersion = fixture.baseVersion;
      56            2 :   cfg1->getView().version = testVersion;
      57            4 :   cfg1->getView().fillFromJSON("{\"test\": \"first_save\"}");
      58              : 
      59            2 :   std::cout << "Step 1: Saving configuration version " << testVersion << " (first time)...\n";
      60              : 
      61            2 :   auto result1 = ifc.saveActiveVersion(cfg1.get());
      62              : 
      63            2 :   BOOST_CHECK_EQUAL(result1, 0);
      64            2 :   std::cout << "  Result: SUCCESS (as expected)\n";
      65              : 
      66            2 :   std::shared_ptr<ConfigurationBase> cfg2 = std::make_shared<TestConfiguration001>();
      67              : 
      68            2 :   cfg2->getView().version = testVersion;
      69            4 :   cfg2->getView().fillFromJSON("{\"test\": \"second_save\"}");
      70              : 
      71            2 :   std::cout << "Step 2: Attempting to save DUPLICATE version " << testVersion << " (second time)...\n";
      72              : 
      73            2 :   auto result2 = ifc.saveActiveVersion(cfg2.get());
      74              : 
      75            2 :   BOOST_CHECK_NE(result2, 0);
      76            2 :   std::cout << "  Result: FAILURE (as expected - duplicate rejected)\n";
      77              : 
      78            2 :   std::shared_ptr<ConfigurationBase> cfgRead = std::make_shared<TestConfiguration001>();
      79              : 
      80            2 :   auto resultRead = ifc.fill(cfgRead.get(), testVersion);
      81            2 :   BOOST_CHECK_EQUAL(resultRead, 0);
      82              : 
      83           10 :   BOOST_CHECK_MESSAGE(jsonContains(cfgRead->getView()._json, "test", "first_save"), "Expected 'first_save' but got: " << cfgRead->getView()._json);
      84              : 
      85            2 :   std::cout << "  Verified: Original version preserved\n";
      86            2 : }
      87              : 
      88            4 : BOOST_AUTO_TEST_CASE(duplicate_version_allowed_with_overwrite) {
      89            2 :   std::cout << "\n=== TEST 2: Duplicate version allowed with overwrite ===\n";
      90              : 
      91            2 :   auto ifc = DatabaseConfigurationInterface();
      92              : 
      93            2 :   int testVersion = fixture.baseVersion + 1;
      94              : 
      95            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
      96            2 :   cfg1->getView().version = testVersion;
      97            4 :   cfg1->getView().fillFromJSON("{\"data\": \"original\"}");
      98              : 
      99            2 :   std::cout << "Step 1: Saving original version " << testVersion << "\n";
     100            2 :   auto result1 = ifc.saveActiveVersion(cfg1.get());
     101            2 :   BOOST_CHECK_EQUAL(result1, 0);
     102              : 
     103            2 :   std::shared_ptr<ConfigurationBase> cfg2 = std::make_shared<TestConfiguration001>();
     104            2 :   cfg2->getView().version = testVersion;
     105            4 :   cfg2->getView().fillFromJSON("{\"data\": \"updated\"}");
     106              : 
     107            2 :   std::cout << "Step 2: Overwriting version " << testVersion << " with overwrite=true\n";
     108            2 :   auto result2 = ifc.saveActiveVersion(cfg2.get(), true);
     109              : 
     110            2 :   BOOST_CHECK_EQUAL(result2, 0);
     111            2 :   std::cout << "  Result: SUCCESS (overwrite allowed)\n";
     112              : 
     113            2 :   std::shared_ptr<ConfigurationBase> cfgRead = std::make_shared<TestConfiguration001>();
     114            2 :   auto resultRead = ifc.fill(cfgRead.get(), testVersion);
     115            2 :   BOOST_CHECK_EQUAL(resultRead, 0);
     116              : 
     117           10 :   BOOST_CHECK_MESSAGE(jsonContains(cfgRead->getView()._json, "data", "updated"), "Expected 'updated' but got: " << cfgRead->getView()._json);
     118            2 :   std::cout << "  Verified: Version successfully overwritten\n";
     119            2 : }
     120              : 
     121            4 : BOOST_AUTO_TEST_CASE(duplicate_composition_rejected) {
     122            2 :   std::cout << "\n=== TEST 3: Duplicate composition rejected ===\n";
     123              : 
     124            2 :   auto ifc = DatabaseConfigurationInterface();
     125              : 
     126            2 :   int version1 = fixture.baseVersion + 10;
     127            2 :   int version2 = fixture.baseVersion + 20;
     128              : 
     129            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
     130            2 :   cfg1->getView().version = version1;
     131            2 :   auto result1 = ifc.saveActiveVersion(cfg1.get());
     132            2 :   BOOST_CHECK_EQUAL(result1, 0);
     133              : 
     134            2 :   std::shared_ptr<ConfigurationBase> cfg2 = std::make_shared<TestConfiguration002>();
     135            2 :   cfg2->getView().version = version2;
     136            2 :   auto result2 = ifc.saveActiveVersion(cfg2.get());
     137            2 :   BOOST_CHECK_EQUAL(result2, 0);
     138              : 
     139            2 :   std::cout << "Created versions: " << version1 << ", " << version2 << "\n";
     140              : 
     141            2 :   auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     142            2 :   compositionMap[cfg1->getConfigurationName()] = version1;
     143            2 :   compositionMap[cfg2->getConfigurationName()] = version2;
     144              : 
     145            6 :   std::string compositionName = std::string{"TestComposition_"} + std::to_string(fixture.baseVersion);
     146              : 
     147            2 :   std::cout << "Step 2: Creating composition '" << compositionName << "' (first time)...\n";
     148              : 
     149            2 :   BOOST_CHECK_NO_THROW(ifc.storeGlobalConfiguration(compositionMap, compositionName));
     150            2 :   std::cout << "  Result: SUCCESS\n";
     151              : 
     152            2 :   std::cout << "Step 3: Attempting to create DUPLICATE composition '" << compositionName << "' (second time)...\n";
     153              : 
     154            4 :   BOOST_CHECK_THROW(ifc.storeGlobalConfiguration(compositionMap, compositionName), std::runtime_error);
     155            2 :   std::cout << "  Result: FAILURE (as expected - duplicate rejected)\n";
     156              : 
     157            2 :   auto loadedMap = ifc.loadGlobalConfiguration(compositionName);
     158              : 
     159            2 :   BOOST_CHECK_EQUAL(loadedMap.size(), 2);
     160            2 :   BOOST_CHECK_EQUAL(loadedMap[cfg1->getConfigurationName()], version1);
     161            2 :   BOOST_CHECK_EQUAL(loadedMap[cfg2->getConfigurationName()], version2);
     162              : 
     163            2 :   std::cout << "  Verified: Original composition preserved\n";
     164            2 : }
     165              : 
     166            4 : BOOST_AUTO_TEST_CASE(different_composition_names_allowed) {
     167            2 :   std::cout << "\n=== TEST 4: Different composition names allowed ===\n";
     168              : 
     169            2 :   auto ifc = DatabaseConfigurationInterface();
     170              : 
     171            2 :   int version1 = fixture.baseVersion + 100;
     172            2 :   int version2 = fixture.baseVersion + 200;
     173              : 
     174            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
     175            2 :   cfg1->getView().version = version1;
     176            2 :   ifc.saveActiveVersion(cfg1.get());
     177              : 
     178            2 :   std::shared_ptr<ConfigurationBase> cfg2 = std::make_shared<TestConfiguration002>();
     179            2 :   cfg2->getView().version = version2;
     180            2 :   ifc.saveActiveVersion(cfg2.get());
     181              : 
     182            2 :   auto compositionMap = DatabaseConfigurationInterface::config_version_map_t{};
     183            2 :   compositionMap[cfg1->getConfigurationName()] = version1;
     184            2 :   compositionMap[cfg2->getConfigurationName()] = version2;
     185              : 
     186            6 :   std::string compositionName1 = std::string{"Composition_A_"} + std::to_string(fixture.baseVersion);
     187              : 
     188            2 :   std::cout << "Creating composition '" << compositionName1 << "'\n";
     189            2 :   BOOST_CHECK_NO_THROW(ifc.storeGlobalConfiguration(compositionMap, compositionName1));
     190              : 
     191            6 :   std::string compositionName2 = std::string{"Composition_B_"} + std::to_string(fixture.baseVersion);
     192              : 
     193            2 :   std::cout << "Creating composition '" << compositionName2 << "' (same members, different name)\n";
     194              : 
     195            2 :   BOOST_CHECK_NO_THROW(ifc.storeGlobalConfiguration(compositionMap, compositionName2));
     196            2 :   std::cout << "  Result: SUCCESS (different names allowed)\n";
     197              : 
     198            2 :   auto configs = ifc.findAllGlobalConfigurations();
     199              : 
     200            2 :   bool found1 = (std::find(configs.begin(), configs.end(), compositionName1) != configs.end());
     201            2 :   bool found2 = (std::find(configs.begin(), configs.end(), compositionName2) != configs.end());
     202              : 
     203            2 :   BOOST_CHECK_EQUAL(found1, true);
     204            2 :   BOOST_CHECK_EQUAL(found2, true);
     205              : 
     206            2 :   std::cout << "  Verified: Both compositions exist independently\n";
     207            2 : }
     208              : 
     209            4 : BOOST_AUTO_TEST_CASE(composition_different_members_allowed) {
     210            2 :   std::cout << "\n=== TEST 5: Composition different members allowed ===\n";
     211              : 
     212            2 :   auto ifc = DatabaseConfigurationInterface();
     213              : 
     214            2 :   int version1 = fixture.baseVersion + 300;
     215            2 :   int version2 = fixture.baseVersion + 400;
     216            2 :   int version3 = fixture.baseVersion + 500;
     217              : 
     218            2 :   std::shared_ptr<ConfigurationBase> cfg1 = std::make_shared<TestConfiguration001>();
     219            2 :   cfg1->getView().version = version1;
     220            2 :   ifc.saveActiveVersion(cfg1.get());
     221              : 
     222            2 :   std::shared_ptr<ConfigurationBase> cfg2a = std::make_shared<TestConfiguration002>();
     223            2 :   cfg2a->getView().version = version2;
     224            2 :   ifc.saveActiveVersion(cfg2a.get());
     225              : 
     226            2 :   std::shared_ptr<ConfigurationBase> cfg2b = std::make_shared<TestConfiguration002>();
     227            2 :   cfg2b->getView().version = version3;
     228            2 :   ifc.saveActiveVersion(cfg2b.get());
     229              : 
     230            2 :   auto map1 = DatabaseConfigurationInterface::config_version_map_t{};
     231            2 :   map1[cfg1->getConfigurationName()] = version1;
     232            2 :   map1[cfg2a->getConfigurationName()] = version2;
     233              : 
     234            6 :   std::string compName1 = std::string{"CompA_"} + std::to_string(fixture.baseVersion);
     235              : 
     236            2 :   ifc.storeGlobalConfiguration(map1, compName1);
     237            2 :   std::cout << "Created composition '" << compName1 << "' with versions " << version1 << ", " << version2 << "\n";
     238              : 
     239            2 :   auto map2 = DatabaseConfigurationInterface::config_version_map_t{};
     240            2 :   map2[cfg1->getConfigurationName()] = version1;
     241            2 :   map2[cfg2b->getConfigurationName()] = version3;
     242              : 
     243            6 :   std::string compName2 = std::string{"CompB_"} + std::to_string(fixture.baseVersion);
     244              : 
     245            2 :   BOOST_CHECK_NO_THROW(ifc.storeGlobalConfiguration(map2, compName2));
     246            2 :   std::cout << "Created composition '" << compName2 << "' with versions " << version1 << ", " << version3 << " (SUCCESS)\n";
     247            2 : }
     248              : 
     249              : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1