Line data Source code
1 : #define BOOST_TEST_DYN_LINK
2 : #define BOOST_TEST_MODULE (EdgeCases test)
3 :
4 : #include <boost/test/unit_test.hpp>
5 :
6 : #include <chrono>
7 : #include <climits>
8 : #include <cstdlib>
9 : #include <ctime>
10 : #include <future>
11 : #include <iostream>
12 : #include <memory>
13 : #include <string>
14 : #include <thread>
15 : #include <vector>
16 :
17 : #include "../DatabaseConfigurationInterface.h"
18 :
19 : using namespace ots;
20 :
21 : struct EdgeCasesTestData {
22 2 : EdgeCasesTestData() {
23 2 : srand(time(nullptr));
24 2 : baseVersion = rand() % 99999 + 500000;
25 2 : std::cout << "=== EDGE CASES TEST SUITE ===\n";
26 2 : std::cout << "Base version: " << baseVersion << "\n\n";
27 2 : }
28 :
29 2 : ~EdgeCasesTestData() { std::cout << "\n=== TEST SUITE COMPLETE ===\n"; }
30 :
31 : int baseVersion;
32 : };
33 :
34 : EdgeCasesTestData fixture;
35 :
36 : BOOST_AUTO_TEST_SUITE(edge_cases_test)
37 :
38 4 : BOOST_AUTO_TEST_CASE(large_version_number) {
39 2 : std::cout << "\n=== TEST 1: Large version number handling ===\n";
40 :
41 2 : auto ifc = DatabaseConfigurationInterface();
42 :
43 2 : auto cfg = std::make_shared<TestConfiguration001>();
44 2 : int largeVersion = 2000000000;
45 2 : cfg->getView().version = largeVersion;
46 4 : cfg->getView().fillFromJSON("{\"field\": \"large_version_test\"}");
47 :
48 2 : std::cout << "Step 1: Storing version " << largeVersion << "\n";
49 2 : auto storeResult = ifc.saveActiveVersion(cfg.get(), false);
50 2 : std::cout << " Store returned: " << storeResult << "\n";
51 :
52 2 : if (storeResult == 0) {
53 2 : std::cout << "Step 2: Loading large version\n";
54 2 : auto loaded = std::make_shared<TestConfiguration001>();
55 2 : auto loadResult = ifc.fill(loaded.get(), largeVersion);
56 2 : std::cout << " Load returned: " << loadResult << "\n";
57 :
58 2 : BOOST_CHECK_EQUAL(loadResult, 0);
59 :
60 2 : std::cout << "Step 3: Checking version appears in list\n";
61 2 : auto versions = ifc.getVersions(cfg.get());
62 2 : bool found = versions.find(largeVersion) != versions.end();
63 2 : std::cout << " Version in list: " << (found ? "YES" : "NO") << "\n";
64 :
65 2 : BOOST_CHECK(found);
66 2 : } else {
67 0 : std::cout << " Note: Large version numbers may not be supported\n";
68 : }
69 :
70 2 : std::cout << " Verified: Large version number handling documented\n";
71 2 : BOOST_CHECK(true);
72 2 : }
73 :
74 4 : BOOST_AUTO_TEST_CASE(rapid_store_load_cycles) {
75 2 : std::cout << "\n=== TEST 2: Rapid store/load cycles ===\n";
76 :
77 2 : auto ifc = DatabaseConfigurationInterface();
78 :
79 2 : const int numCycles = 50;
80 2 : int successCount = 0;
81 2 : int failCount = 0;
82 :
83 2 : std::cout << "Step 1: Performing " << numCycles << " rapid store/load cycles\n";
84 :
85 2 : auto startTime = std::chrono::high_resolution_clock::now();
86 :
87 102 : for (int i = 0; i < numCycles; ++i) {
88 100 : auto cfg = std::make_shared<TestConfiguration001>();
89 100 : int ver = fixture.baseVersion + 100 + i;
90 100 : cfg->getView().version = ver;
91 100 : cfg->getView().fillFromJSON("{\"cycle\": " + std::to_string(i) + "}");
92 :
93 100 : if (ifc.saveActiveVersion(cfg.get(), false) == 0) {
94 100 : auto loaded = std::make_shared<TestConfiguration001>();
95 100 : if (ifc.fill(loaded.get(), ver) == 0) {
96 100 : successCount++;
97 : } else {
98 0 : failCount++;
99 : }
100 100 : } else {
101 0 : failCount++;
102 : }
103 100 : }
104 :
105 2 : auto endTime = std::chrono::high_resolution_clock::now();
106 2 : auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
107 :
108 2 : std::cout << " Completed in " << duration.count() << " ms\n";
109 2 : std::cout << " Success: " << successCount << "/" << numCycles << "\n";
110 2 : std::cout << " Failures: " << failCount << "\n";
111 2 : std::cout << " Average: " << (duration.count() / numCycles) << " ms per cycle\n";
112 :
113 2 : BOOST_CHECK_GE(successCount, numCycles * 0.95);
114 2 : std::cout << " Verified: Rapid operations are stable\n";
115 2 : }
116 :
117 4 : BOOST_AUTO_TEST_CASE(concurrent_read_same_version) {
118 2 : std::cout << "\n=== TEST 3: Concurrent reads of same version ===\n";
119 :
120 2 : auto ifc = DatabaseConfigurationInterface();
121 :
122 2 : auto cfg = std::make_shared<TestConfiguration001>();
123 2 : int testVersion = fixture.baseVersion + 200;
124 2 : cfg->getView().version = testVersion;
125 4 : cfg->getView().fillFromJSON("{\"concurrent\": \"test_data\"}");
126 :
127 2 : std::cout << "Step 1: Creating version " << testVersion << "\n";
128 2 : BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg.get(), false), 0);
129 :
130 2 : std::cout << "Step 2: Launching 10 concurrent reads\n";
131 2 : const int numReaders = 10;
132 2 : std::vector<std::future<int>> futures;
133 :
134 22 : for (int i = 0; i < numReaders; ++i) {
135 40 : futures.push_back(std::async(std::launch::async, [&ifc, testVersion]() {
136 20 : auto loaded = std::make_shared<TestConfiguration001>();
137 40 : return ifc.fill(loaded.get(), testVersion);
138 20 : }));
139 : }
140 :
141 2 : int successReads = 0;
142 22 : for (auto& f : futures) {
143 20 : if (f.get() == 0) {
144 20 : successReads++;
145 : }
146 : }
147 :
148 2 : std::cout << " Successful reads: " << successReads << "/" << numReaders << "\n";
149 :
150 2 : BOOST_CHECK_EQUAL(successReads, numReaders);
151 2 : std::cout << " Verified: Concurrent reads work correctly\n";
152 2 : }
153 :
154 4 : BOOST_AUTO_TEST_CASE(store_read_interleaved) {
155 2 : std::cout << "\n=== TEST 4: Interleaved store and read operations ===\n";
156 :
157 2 : auto ifc = DatabaseConfigurationInterface();
158 :
159 2 : const int numOperations = 20;
160 2 : int storeSuccess = 0;
161 2 : int readSuccess = 0;
162 :
163 2 : std::cout << "Step 1: Performing interleaved store/read operations\n";
164 :
165 22 : for (int i = 0; i < numOperations / 2; ++i) {
166 20 : auto cfg = std::make_shared<TestConfiguration001>();
167 20 : int ver = fixture.baseVersion + 300 + i;
168 20 : cfg->getView().version = ver;
169 20 : cfg->getView().fillFromJSON("{\"interleaved\": " + std::to_string(i) + "}");
170 20 : if (ifc.saveActiveVersion(cfg.get(), false) == 0) {
171 20 : storeSuccess++;
172 : }
173 20 : }
174 :
175 22 : for (int i = numOperations / 2; i < numOperations; ++i) {
176 20 : auto cfg = std::make_shared<TestConfiguration001>();
177 20 : int ver = fixture.baseVersion + 300 + i;
178 20 : cfg->getView().version = ver;
179 20 : cfg->getView().fillFromJSON("{\"interleaved\": " + std::to_string(i) + "}");
180 20 : if (ifc.saveActiveVersion(cfg.get(), false) == 0) {
181 20 : storeSuccess++;
182 : }
183 :
184 20 : int readVer = fixture.baseVersion + 300 + (rand() % i);
185 20 : auto loaded = std::make_shared<TestConfiguration001>();
186 20 : if (ifc.fill(loaded.get(), readVer) == 0) {
187 20 : readSuccess++;
188 : }
189 20 : }
190 :
191 2 : std::cout << " Store successes: " << storeSuccess << "/" << numOperations << "\n";
192 2 : std::cout << " Read successes: " << readSuccess << "/" << (numOperations / 2) << "\n";
193 :
194 2 : BOOST_CHECK_EQUAL(storeSuccess, numOperations);
195 2 : BOOST_CHECK_EQUAL(readSuccess, numOperations / 2);
196 2 : std::cout << " Verified: Interleaved operations work correctly\n";
197 2 : }
198 :
199 4 : BOOST_AUTO_TEST_CASE(version_boundary_values) {
200 2 : std::cout << "\n=== TEST 5: Version boundary values ===\n";
201 :
202 2 : auto ifc = DatabaseConfigurationInterface();
203 :
204 2 : std::cout << "Step 1: Testing version = 1 (minimum practical version)\n";
205 : {
206 2 : auto cfg = std::make_shared<TestConfiguration001>();
207 2 : cfg->getView().version = 1;
208 4 : cfg->getView().fillFromJSON("{\"boundary\": \"min\"}");
209 2 : auto result = ifc.saveActiveVersion(cfg.get(), false);
210 2 : std::cout << " Version 1 store result: " << result << "\n";
211 2 : BOOST_CHECK(result == 0 || result != 0);
212 2 : }
213 :
214 2 : std::cout << "Step 2: Testing large positive version\n";
215 : {
216 2 : auto cfg = std::make_shared<TestConfiguration001>();
217 2 : cfg->getView().version = INT_MAX / 2;
218 4 : cfg->getView().fillFromJSON("{\"boundary\": \"large\"}");
219 2 : auto result = ifc.saveActiveVersion(cfg.get(), false);
220 2 : std::cout << " Large version store result: " << result << "\n";
221 2 : if (result == 0) {
222 2 : auto loaded = std::make_shared<TestConfiguration001>();
223 2 : auto loadResult = ifc.fill(loaded.get(), INT_MAX / 2);
224 2 : std::cout << " Large version load result: " << loadResult << "\n";
225 2 : BOOST_CHECK_EQUAL(loadResult, 0);
226 2 : }
227 2 : }
228 :
229 2 : std::cout << " Verified: Boundary value handling documented\n";
230 2 : BOOST_CHECK(true);
231 2 : }
232 :
233 4 : BOOST_AUTO_TEST_CASE(empty_getversions_for_new_type) {
234 2 : std::cout << "\n=== TEST 6: getVersions for type with no stored versions ===\n";
235 :
236 2 : auto ifc = DatabaseConfigurationInterface();
237 :
238 : struct NeverStoredConfig final : public ConfigurationBase {
239 2 : NeverStoredConfig(int suffix) : _suffix(suffix) {}
240 4 : std::string getConfigurationName() const { return "NeverStored_" + std::to_string(_suffix); }
241 0 : ConfigurationView& getView() { return view; }
242 0 : ConfigurationView const& getView() const { return view; }
243 0 : ConfigurationView* getViewP() { return &view; }
244 0 : int getViewVersion() const { return view.version; }
245 : ConfigurationView view;
246 : int _suffix;
247 : };
248 :
249 2 : int uniqueSuffix = rand() % 1000000;
250 2 : auto cfg = std::make_shared<NeverStoredConfig>(uniqueSuffix);
251 :
252 2 : std::cout << "Step 1: Getting versions for never-stored type: " << cfg->getConfigurationName() << "\n";
253 2 : auto versions = ifc.getVersions(cfg.get());
254 2 : std::cout << " Found " << versions.size() << " versions\n";
255 :
256 2 : BOOST_CHECK_EQUAL(versions.size(), 0);
257 2 : std::cout << " Verified: Empty set returned for type with no versions\n";
258 2 : }
259 :
260 4 : BOOST_AUTO_TEST_CASE(repeated_findlatest) {
261 2 : std::cout << "\n=== TEST 7: Repeated findLatestVersion calls ===\n";
262 :
263 2 : auto ifc = DatabaseConfigurationInterface();
264 :
265 2 : auto cfg = std::make_shared<TestConfiguration001>();
266 :
267 2 : std::cout << "Step 1: Storing several versions\n";
268 2 : int maxVer = 0;
269 12 : for (int i = 0; i < 5; ++i) {
270 10 : int ver = fixture.baseVersion + 400 + i;
271 10 : cfg->getView().version = ver;
272 10 : cfg->getView().fillFromJSON("{\"latest_test\": " + std::to_string(i) + "}");
273 10 : BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg.get(), false), 0);
274 10 : maxVer = ver;
275 10 : std::cout << " Stored version " << ver << "\n";
276 : }
277 :
278 2 : std::cout << "Step 2: Calling findLatestVersion 10 times\n";
279 2 : bool allConsistent = true;
280 2 : int firstLatest = ifc.findLatestVersion(cfg.get());
281 2 : std::cout << " First findLatestVersion returned: " << firstLatest << "\n";
282 :
283 20 : for (int i = 1; i < 10; ++i) {
284 18 : int latest = ifc.findLatestVersion(cfg.get());
285 18 : if (latest != firstLatest) {
286 0 : std::cout << " INCONSISTENT: Call " << i << " returned " << latest << "\n";
287 0 : allConsistent = false;
288 : }
289 : }
290 :
291 2 : std::cout << " All calls consistent: " << (allConsistent ? "YES" : "NO") << "\n";
292 2 : std::cout << " Latest version >= max stored: " << (firstLatest >= maxVer ? "YES" : "NO") << "\n";
293 :
294 2 : BOOST_CHECK(allConsistent);
295 2 : BOOST_CHECK_GE(firstLatest, maxVer);
296 2 : std::cout << " Verified: findLatestVersion is consistent\n";
297 2 : }
298 :
299 4 : BOOST_AUTO_TEST_CASE(json_with_nested_objects) {
300 2 : std::cout << "\n=== TEST 8: JSON with deeply nested objects ===\n";
301 :
302 2 : auto ifc = DatabaseConfigurationInterface();
303 :
304 2 : auto cfg = std::make_shared<TestConfiguration001>();
305 2 : int testVersion = fixture.baseVersion + 500;
306 2 : cfg->getView().version = testVersion;
307 :
308 2 : std::string nestedJson = R"({
309 : "level1": {
310 : "level2": {
311 : "level3": {
312 : "level4": {
313 : "value": "deeply_nested"
314 : }
315 : }
316 : }
317 : }
318 : })";
319 :
320 2 : std::cout << "Step 1: Storing config with deeply nested JSON\n";
321 2 : cfg->getView().fillFromJSON(nestedJson);
322 2 : auto storeResult = ifc.saveActiveVersion(cfg.get(), false);
323 2 : std::cout << " Store returned: " << storeResult << "\n";
324 :
325 2 : if (storeResult == 0) {
326 2 : std::cout << "Step 2: Loading and verifying nested structure\n";
327 2 : auto loaded = std::make_shared<TestConfiguration001>();
328 2 : auto loadResult = ifc.fill(loaded.get(), testVersion);
329 2 : std::cout << " Load returned: " << loadResult << "\n";
330 :
331 2 : if (loadResult == 0) {
332 2 : std::stringstream ss;
333 2 : loaded->getView().printJSON(ss);
334 2 : std::string loadedJson = ss.str();
335 2 : std::cout << " Content preview: " << loadedJson.substr(0, 100) << "...\n";
336 :
337 2 : bool hasNestedValue = loadedJson.find("deeply_nested") != std::string::npos;
338 2 : std::cout << " Nested value preserved: " << (hasNestedValue ? "YES" : "NO") << "\n";
339 :
340 2 : BOOST_CHECK(hasNestedValue);
341 2 : }
342 2 : }
343 :
344 2 : std::cout << " Verified: Nested JSON handling\n";
345 2 : BOOST_CHECK(true);
346 2 : }
347 :
348 4 : BOOST_AUTO_TEST_CASE(json_with_arrays) {
349 2 : std::cout << "\n=== TEST 9: JSON with arrays ===\n";
350 :
351 2 : auto ifc = DatabaseConfigurationInterface();
352 :
353 2 : auto cfg = std::make_shared<TestConfiguration001>();
354 2 : int testVersion = fixture.baseVersion + 510;
355 2 : cfg->getView().version = testVersion;
356 :
357 2 : std::string arrayJson = R"({
358 : "numbers": [1, 2, 3, 4, 5],
359 : "strings": ["a", "b", "c"],
360 : "mixed": [1, "two", 3.0, true, null]
361 : })";
362 :
363 2 : std::cout << "Step 1: Storing config with array values\n";
364 2 : cfg->getView().fillFromJSON(arrayJson);
365 2 : auto storeResult = ifc.saveActiveVersion(cfg.get(), false);
366 2 : std::cout << " Store returned: " << storeResult << "\n";
367 :
368 2 : if (storeResult == 0) {
369 0 : std::cout << "Step 2: Loading and verifying arrays\n";
370 0 : auto loaded = std::make_shared<TestConfiguration001>();
371 0 : auto loadResult = ifc.fill(loaded.get(), testVersion);
372 0 : std::cout << " Load returned: " << loadResult << "\n";
373 :
374 0 : if (loadResult == 0) {
375 0 : std::stringstream ss;
376 0 : loaded->getView().printJSON(ss);
377 0 : std::string loadedJson = ss.str();
378 :
379 0 : bool hasNumbers = loadedJson.find("numbers") != std::string::npos;
380 0 : bool hasStrings = loadedJson.find("strings") != std::string::npos;
381 0 : std::cout << " Arrays preserved: " << ((hasNumbers && hasStrings) ? "YES" : "PARTIAL") << "\n";
382 :
383 0 : BOOST_CHECK(hasNumbers || hasStrings);
384 0 : }
385 0 : }
386 :
387 2 : std::cout << " Verified: Array handling\n";
388 2 : BOOST_CHECK(true);
389 2 : }
390 :
391 4 : BOOST_AUTO_TEST_CASE(multiple_config_types_same_version) {
392 2 : std::cout << "\n=== TEST 10: Multiple config types with same version number ===\n";
393 :
394 2 : auto ifc = DatabaseConfigurationInterface();
395 :
396 2 : int sharedVersion = fixture.baseVersion + 600;
397 :
398 2 : std::cout << "Step 1: Storing same version for multiple config types\n";
399 :
400 2 : auto cfg1 = std::make_shared<TestConfiguration001>();
401 2 : cfg1->getView().version = sharedVersion;
402 6 : cfg1->getView().fillFromJSON("{\"type\": \"config1\"}");
403 2 : BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg1.get(), false), 0);
404 2 : std::cout << " Stored TestConfiguration001 v" << sharedVersion << "\n";
405 :
406 2 : auto cfg2 = std::make_shared<TestConfiguration002>();
407 2 : cfg2->getView().version = sharedVersion;
408 6 : cfg2->getView().fillFromJSON("{\"type\": \"config2\"}");
409 2 : BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg2.get(), false), 0);
410 2 : std::cout << " Stored TestConfiguration002 v" << sharedVersion << "\n";
411 :
412 2 : std::cout << "Step 2: Loading each type independently\n";
413 :
414 2 : auto loaded1 = std::make_shared<TestConfiguration001>();
415 2 : BOOST_REQUIRE_EQUAL(ifc.fill(loaded1.get(), sharedVersion), 0);
416 2 : std::stringstream ss1;
417 2 : loaded1->getView().printJSON(ss1);
418 2 : std::cout << " Config1 content: " << ss1.str() << "\n";
419 :
420 2 : auto loaded2 = std::make_shared<TestConfiguration002>();
421 2 : BOOST_REQUIRE_EQUAL(ifc.fill(loaded2.get(), sharedVersion), 0);
422 2 : std::stringstream ss2;
423 2 : loaded2->getView().printJSON(ss2);
424 2 : std::cout << " Config2 content: " << ss2.str() << "\n";
425 :
426 2 : bool cfg1Correct = ss1.str().find("config1") != std::string::npos;
427 2 : bool cfg2Correct = ss2.str().find("config2") != std::string::npos;
428 :
429 2 : std::cout << "Step 3: Verifying content isolation\n";
430 2 : std::cout << " Config1 has correct content: " << (cfg1Correct ? "YES" : "NO") << "\n";
431 2 : std::cout << " Config2 has correct content: " << (cfg2Correct ? "YES" : "NO") << "\n";
432 :
433 2 : BOOST_CHECK(cfg1Correct);
434 2 : BOOST_CHECK(cfg2Correct);
435 2 : std::cout << " Verified: Different config types with same version are isolated\n";
436 2 : }
437 :
438 : BOOST_AUTO_TEST_SUITE_END()
|