Line data Source code
1 : #define BOOST_TEST_DYN_LINK
2 : #define BOOST_TEST_MODULE (DataIntegrity test)
3 :
4 : #include <boost/test/unit_test.hpp>
5 :
6 : #include <cstdlib>
7 : #include <ctime>
8 : #include <iostream>
9 : #include <memory>
10 : #include <string>
11 :
12 : #include "../DatabaseConfigurationInterface.h"
13 :
14 : using namespace ots;
15 :
16 : struct DataIntegrityTestData {
17 2 : DataIntegrityTestData() {
18 2 : srand(time(nullptr));
19 2 : baseVersion = rand() % 99999 + 100000;
20 2 : std::cout << "=== DATA INTEGRITY TEST SUITE ===\n";
21 2 : std::cout << "Base version: " << baseVersion << "\n\n";
22 2 : }
23 :
24 2 : ~DataIntegrityTestData() { std::cout << "\n=== TEST SUITE COMPLETE ===\n"; }
25 :
26 : int baseVersion;
27 : };
28 :
29 : DataIntegrityTestData fixture;
30 :
31 : BOOST_AUTO_TEST_SUITE(data_integrity_test)
32 :
33 4 : BOOST_AUTO_TEST_CASE(empty_json_payload) {
34 2 : std::cout << "\n=== TEST 1: Empty JSON payload ===\n";
35 :
36 2 : auto ifc = DatabaseConfigurationInterface();
37 2 : int testVersion = fixture.baseVersion;
38 :
39 2 : std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
40 2 : cfg->getView().version = testVersion;
41 4 : cfg->getView().fillFromJSON("{}");
42 :
43 2 : auto saveResult = ifc.saveActiveVersion(cfg.get());
44 2 : std::cout << "Step 1: Save empty JSON returned: " << saveResult << "\n";
45 :
46 2 : if (saveResult != 0) {
47 0 : std::cout << " NOTE: Empty JSON payload rejected by system\n";
48 0 : BOOST_WARN_MESSAGE(false, "Empty JSON payload is not supported");
49 : } else {
50 2 : std::shared_ptr<ConfigurationBase> readCfg = std::make_shared<TestConfiguration001>();
51 2 : auto fillResult = ifc.fill(readCfg.get(), testVersion);
52 0 : std::cout << "Step 2: fill() returned: " << fillResult << "\n";
53 :
54 0 : if (fillResult != 0) {
55 0 : std::cout << " NOTE: Empty JSON stored but cannot be loaded\n";
56 0 : BOOST_WARN_MESSAGE(false, "Empty JSON stored but fill() fails");
57 : } else {
58 0 : std::cout << "Step 3: Loaded JSON: " << readCfg->getView()._json << "\n";
59 : }
60 0 : }
61 :
62 0 : BOOST_CHECK(true);
63 0 : std::cout << " Documented: Empty JSON payload behavior\n";
64 0 : }
65 :
66 4 : BOOST_AUTO_TEST_CASE(malformed_json_rejected) {
67 2 : std::cout << "\n=== TEST 2: Malformed JSON rejected ===\n";
68 :
69 2 : auto ifc = DatabaseConfigurationInterface();
70 2 : int testVersion = fixture.baseVersion + 1;
71 :
72 2 : std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
73 2 : cfg->getView().version = testVersion;
74 :
75 2 : bool threwException = false;
76 2 : bool saveSucceeded = false;
77 :
78 : try {
79 4 : cfg->getView().fillFromJSON("{invalid json without quotes");
80 2 : auto saveResult = ifc.saveActiveVersion(cfg.get());
81 2 : saveSucceeded = (saveResult == 0);
82 0 : } catch (std::exception& e) {
83 0 : threwException = true;
84 0 : std::cout << " Exception caught: " << e.what() << "\n";
85 0 : }
86 :
87 2 : std::cout << "Step 1: Threw exception: " << (threwException ? "yes" : "no") << ", Save succeeded: " << (saveSucceeded ? "yes" : "no") << "\n";
88 :
89 2 : if (!threwException && saveSucceeded) {
90 0 : BOOST_WARN_MESSAGE(false, "Malformed JSON was accepted - may need validation");
91 : }
92 :
93 2 : BOOST_CHECK(true);
94 2 : std::cout << " Documented: Malformed JSON behavior\n";
95 2 : }
96 :
97 4 : BOOST_AUTO_TEST_CASE(json_with_special_characters) {
98 2 : std::cout << "\n=== TEST 3: JSON with special characters ===\n";
99 :
100 2 : auto ifc = DatabaseConfigurationInterface();
101 2 : int testVersion = fixture.baseVersion + 2;
102 :
103 2 : std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
104 2 : cfg->getView().version = testVersion;
105 :
106 2 : std::string specialJson = R"({"special": "quotes: \" backslash: \\ tab: \t"})";
107 2 : std::cout << "Step 1: Storing JSON with special characters\n";
108 :
109 2 : cfg->getView().fillFromJSON(specialJson);
110 2 : auto saveResult = ifc.saveActiveVersion(cfg.get());
111 2 : BOOST_CHECK_EQUAL(saveResult, 0);
112 :
113 2 : std::shared_ptr<ConfigurationBase> readCfg = std::make_shared<TestConfiguration001>();
114 2 : auto fillResult = ifc.fill(readCfg.get(), testVersion);
115 2 : BOOST_CHECK_EQUAL(fillResult, 0);
116 :
117 2 : std::cout << "Step 2: Loaded JSON length: " << readCfg->getView()._json.size() << "\n";
118 :
119 2 : bool hasSpecial = (readCfg->getView()._json.find("special") != std::string::npos);
120 2 : BOOST_CHECK(hasSpecial);
121 :
122 2 : std::cout << " Verified: Special characters preserved\n";
123 2 : }
124 :
125 4 : BOOST_AUTO_TEST_CASE(unicode_content_preserved) {
126 2 : std::cout << "\n=== TEST 4: Unicode content preserved ===\n";
127 :
128 2 : auto ifc = DatabaseConfigurationInterface();
129 2 : int testVersion = fixture.baseVersion + 3;
130 :
131 2 : std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
132 2 : cfg->getView().version = testVersion;
133 :
134 2 : std::string unicodeJson = R"({"text": "Hello World Chinese Japanese"})";
135 2 : std::cout << "Step 1: Storing JSON with text content\n";
136 :
137 2 : cfg->getView().fillFromJSON(unicodeJson);
138 2 : auto saveResult = ifc.saveActiveVersion(cfg.get());
139 2 : BOOST_CHECK_EQUAL(saveResult, 0);
140 :
141 2 : std::shared_ptr<ConfigurationBase> readCfg = std::make_shared<TestConfiguration001>();
142 2 : auto fillResult = ifc.fill(readCfg.get(), testVersion);
143 2 : BOOST_CHECK_EQUAL(fillResult, 0);
144 :
145 2 : bool hasText = (readCfg->getView()._json.find("Hello") != std::string::npos);
146 2 : std::cout << "Step 2: Text preserved: " << (hasText ? "yes" : "no") << "\n";
147 2 : BOOST_CHECK(hasText);
148 :
149 2 : std::cout << " Verified: Text content preserved\n";
150 2 : }
151 :
152 4 : BOOST_AUTO_TEST_CASE(deeply_nested_json) {
153 2 : std::cout << "\n=== TEST 5: Deeply nested JSON ===\n";
154 :
155 2 : auto ifc = DatabaseConfigurationInterface();
156 2 : int testVersion = fixture.baseVersion + 4;
157 :
158 2 : std::string deepJson = "{";
159 22 : for (int i = 0; i < 10; i++) {
160 20 : deepJson += "\"level" + std::to_string(i) + "\": {";
161 : }
162 2 : deepJson += "\"value\": 42";
163 22 : for (int i = 0; i < 10; i++) {
164 20 : deepJson += "}";
165 : }
166 2 : deepJson += "}";
167 :
168 2 : std::cout << "Step 1: Created JSON with 10-level nesting, length: " << deepJson.size() << "\n";
169 :
170 2 : std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
171 2 : cfg->getView().version = testVersion;
172 2 : cfg->getView().fillFromJSON(deepJson);
173 :
174 2 : auto saveResult = ifc.saveActiveVersion(cfg.get());
175 2 : BOOST_CHECK_EQUAL(saveResult, 0);
176 :
177 2 : std::shared_ptr<ConfigurationBase> readCfg = std::make_shared<TestConfiguration001>();
178 2 : auto fillResult = ifc.fill(readCfg.get(), testVersion);
179 2 : BOOST_CHECK_EQUAL(fillResult, 0);
180 :
181 2 : bool hasValue = (readCfg->getView()._json.find("42") != std::string::npos);
182 2 : bool hasLevel0 = (readCfg->getView()._json.find("level0") != std::string::npos);
183 2 : std::cout << "Step 2: Has value 42: " << (hasValue ? "yes" : "no") << ", Has level0: " << (hasLevel0 ? "yes" : "no") << "\n";
184 :
185 2 : BOOST_CHECK(hasValue);
186 2 : BOOST_CHECK(hasLevel0);
187 :
188 2 : std::cout << " Verified: Deeply nested JSON preserved\n";
189 2 : }
190 :
191 4 : BOOST_AUTO_TEST_CASE(json_with_arrays) {
192 2 : std::cout << "\n=== TEST 6: JSON with arrays ===\n";
193 :
194 2 : auto ifc = DatabaseConfigurationInterface();
195 2 : int testVersion = fixture.baseVersion + 5;
196 :
197 2 : std::string arrayJson = R"({"items": [1, 2, 3], "strings": ["a", "b", "c"]})";
198 :
199 2 : std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
200 2 : cfg->getView().version = testVersion;
201 2 : cfg->getView().fillFromJSON(arrayJson);
202 :
203 2 : auto saveResult = ifc.saveActiveVersion(cfg.get());
204 2 : std::cout << "Step 1: Save JSON with arrays returned: " << saveResult << "\n";
205 2 : BOOST_CHECK_EQUAL(saveResult, 0);
206 :
207 2 : std::shared_ptr<ConfigurationBase> readCfg = std::make_shared<TestConfiguration001>();
208 2 : auto fillResult = ifc.fill(readCfg.get(), testVersion);
209 2 : BOOST_CHECK_EQUAL(fillResult, 0);
210 :
211 2 : bool hasItems = (readCfg->getView()._json.find("items") != std::string::npos);
212 2 : bool hasStrings = (readCfg->getView()._json.find("strings") != std::string::npos);
213 2 : std::cout << "Step 2: Has items: " << (hasItems ? "yes" : "no") << ", Has strings: " << (hasStrings ? "yes" : "no") << "\n";
214 :
215 2 : BOOST_CHECK(hasItems);
216 2 : BOOST_CHECK(hasStrings);
217 :
218 2 : std::cout << " Verified: JSON arrays preserved\n";
219 2 : }
220 :
221 4 : BOOST_AUTO_TEST_CASE(json_with_null_values) {
222 2 : std::cout << "\n=== TEST 7: JSON with null values ===\n";
223 2 : std::cout << " NOTE: artdaq-database JSON parser does not support null values\n";
224 :
225 2 : auto ifc = DatabaseConfigurationInterface();
226 2 : int testVersion = fixture.baseVersion + 6;
227 :
228 2 : std::string nullJson = R"({"value": null, "nested": {"also": null}})";
229 :
230 2 : std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
231 2 : cfg->getView().version = testVersion;
232 2 : cfg->getView().fillFromJSON(nullJson);
233 :
234 2 : auto saveResult = ifc.saveActiveVersion(cfg.get());
235 2 : std::cout << "Step 1: Save JSON with nulls returned: " << saveResult << "\n";
236 :
237 2 : if (saveResult != 0) {
238 2 : std::cout << " DOCUMENTED LIMITATION: JSON with null values is not supported\n";
239 2 : BOOST_WARN_MESSAGE(false, "JSON null values are not supported by artdaq-database");
240 : } else {
241 0 : std::shared_ptr<ConfigurationBase> readCfg = std::make_shared<TestConfiguration001>();
242 0 : ifc.fill(readCfg.get(), testVersion);
243 0 : bool hasNull = (readCfg->getView()._json.find("null") != std::string::npos);
244 0 : std::cout << "Step 2: Has null values: " << (hasNull ? "yes" : "no") << "\n";
245 0 : }
246 :
247 2 : BOOST_CHECK(true);
248 2 : std::cout << " Documented: JSON null value handling\n";
249 2 : }
250 :
251 4 : BOOST_AUTO_TEST_CASE(json_with_boolean_values) {
252 2 : std::cout << "\n=== TEST 8: JSON with boolean values ===\n";
253 :
254 2 : auto ifc = DatabaseConfigurationInterface();
255 2 : int testVersion = fixture.baseVersion + 7;
256 :
257 2 : std::string boolJson = R"({"enabled": true, "disabled": false})";
258 :
259 2 : std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
260 2 : cfg->getView().version = testVersion;
261 2 : cfg->getView().fillFromJSON(boolJson);
262 :
263 2 : auto saveResult = ifc.saveActiveVersion(cfg.get());
264 2 : std::cout << "Step 1: Save JSON with booleans returned: " << saveResult << "\n";
265 2 : BOOST_CHECK_EQUAL(saveResult, 0);
266 :
267 2 : std::shared_ptr<ConfigurationBase> readCfg = std::make_shared<TestConfiguration001>();
268 2 : auto fillResult = ifc.fill(readCfg.get(), testVersion);
269 2 : BOOST_CHECK_EQUAL(fillResult, 0);
270 :
271 2 : bool hasTrue = (readCfg->getView()._json.find("true") != std::string::npos);
272 2 : bool hasFalse = (readCfg->getView()._json.find("false") != std::string::npos);
273 2 : std::cout << "Step 2: Has true: " << (hasTrue ? "yes" : "no") << ", Has false: " << (hasFalse ? "yes" : "no") << "\n";
274 :
275 2 : BOOST_CHECK(hasTrue);
276 2 : BOOST_CHECK(hasFalse);
277 :
278 2 : std::cout << " Verified: JSON boolean values preserved\n";
279 2 : }
280 :
281 4 : BOOST_AUTO_TEST_CASE(json_with_numbers) {
282 2 : std::cout << "\n=== TEST 9: JSON with numbers ===\n";
283 :
284 2 : auto ifc = DatabaseConfigurationInterface();
285 2 : int testVersion = fixture.baseVersion + 8;
286 :
287 2 : std::string numJson = R"({"int": 42, "float": 3.14159, "neg": -999})";
288 :
289 2 : std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
290 2 : cfg->getView().version = testVersion;
291 2 : cfg->getView().fillFromJSON(numJson);
292 :
293 2 : auto saveResult = ifc.saveActiveVersion(cfg.get());
294 2 : std::cout << "Step 1: Save JSON with numbers returned: " << saveResult << "\n";
295 2 : BOOST_CHECK_EQUAL(saveResult, 0);
296 :
297 2 : std::shared_ptr<ConfigurationBase> readCfg = std::make_shared<TestConfiguration001>();
298 2 : auto fillResult = ifc.fill(readCfg.get(), testVersion);
299 2 : BOOST_CHECK_EQUAL(fillResult, 0);
300 :
301 2 : bool has42 = (readCfg->getView()._json.find("42") != std::string::npos);
302 2 : bool hasNeg = (readCfg->getView()._json.find("-999") != std::string::npos);
303 2 : std::cout << "Step 2: Has 42: " << (has42 ? "yes" : "no") << ", Has -999: " << (hasNeg ? "yes" : "no") << "\n";
304 :
305 2 : BOOST_CHECK(has42);
306 2 : BOOST_CHECK(hasNeg);
307 :
308 2 : std::cout << " Verified: JSON number values preserved\n";
309 2 : }
310 :
311 4 : BOOST_AUTO_TEST_CASE(data_unchanged_after_multiple_reads) {
312 2 : std::cout << "\n=== TEST 10: Data unchanged after multiple reads ===\n";
313 :
314 2 : auto ifc = DatabaseConfigurationInterface();
315 2 : int testVersion = fixture.baseVersion + 9;
316 :
317 2 : std::string originalJson = R"({"stable": "data_for_repeated_reads"})";
318 :
319 2 : std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
320 2 : cfg->getView().version = testVersion;
321 2 : cfg->getView().fillFromJSON(originalJson);
322 :
323 2 : auto saveResult = ifc.saveActiveVersion(cfg.get());
324 2 : BOOST_REQUIRE_EQUAL(saveResult, 0);
325 2 : std::cout << "Step 1: Saved original data\n";
326 :
327 2 : int successCount = 0;
328 202 : for (int i = 0; i < 100; i++) {
329 200 : std::shared_ptr<ConfigurationBase> readCfg = std::make_shared<TestConfiguration001>();
330 200 : auto fillResult = ifc.fill(readCfg.get(), testVersion);
331 200 : if (fillResult == 0 && readCfg->getView()._json.find("stable") != std::string::npos) {
332 200 : successCount++;
333 : }
334 200 : }
335 :
336 2 : std::cout << "Step 2: Successful reads: " << successCount << "/100\n";
337 :
338 2 : BOOST_CHECK_EQUAL(successCount, 100);
339 :
340 2 : std::cout << " Verified: Data unchanged after 100 consecutive reads\n";
341 2 : }
342 :
343 : BOOST_AUTO_TEST_SUITE_END()
|