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()
|