Line data Source code
1 : #define BOOST_TEST_DYN_LINK
2 : #define BOOST_TEST_MODULE (OverwriteVersion 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 OverwriteVersionTestData {
17 2 : OverwriteVersionTestData() {
18 2 : srand(time(nullptr));
19 2 : baseVersion = rand() % 99999 + 100000;
20 2 : std::cout << "=== OVERWRITE VERSION TEST SUITE ===\n";
21 2 : std::cout << "Base version: " << baseVersion << "\n\n";
22 2 : }
23 :
24 2 : ~OverwriteVersionTestData() { std::cout << "\n=== TEST SUITE COMPLETE ===\n"; }
25 :
26 : int baseVersion;
27 : };
28 :
29 : OverwriteVersionTestData fixture;
30 :
31 : BOOST_AUTO_TEST_SUITE(overwrite_version_test)
32 :
33 4 : BOOST_AUTO_TEST_CASE(basic_overwrite_succeeds) {
34 2 : std::cout << "\n=== TEST 1: Basic overwrite succeeds ===\n";
35 :
36 2 : auto ifc = DatabaseConfigurationInterface();
37 :
38 2 : auto cfg = std::make_shared<TestConfiguration001>();
39 2 : int testVersion = fixture.baseVersion + 1;
40 2 : cfg->getView().version = testVersion;
41 4 : cfg->getView().fillFromJSON("{\"field\": \"initial_value\"}");
42 :
43 2 : std::cout << "Step 1: Creating initial version " << testVersion << " with 'initial_value'\n";
44 2 : auto createResult = ifc.saveActiveVersion(cfg.get(), false);
45 2 : BOOST_REQUIRE_EQUAL(createResult, 0);
46 2 : std::cout << " Initial version created successfully\n";
47 :
48 2 : std::cout << "Step 2: Overwriting with 'updated_value'\n";
49 4 : cfg->getView().fillFromJSON("{\"field\": \"updated_value\"}");
50 2 : auto overwriteResult = ifc.saveActiveVersion(cfg.get(), true);
51 2 : BOOST_REQUIRE_EQUAL(overwriteResult, 0);
52 2 : std::cout << " Overwrite completed successfully\n";
53 :
54 2 : std::cout << "Step 3: Loading version and verifying content\n";
55 2 : auto loaded = std::make_shared<TestConfiguration001>();
56 2 : auto loadResult = ifc.fill(loaded.get(), testVersion);
57 2 : BOOST_REQUIRE_EQUAL(loadResult, 0);
58 :
59 2 : std::stringstream ss;
60 2 : loaded->getView().printJSON(ss);
61 2 : std::string loadedJson = ss.str();
62 2 : std::cout << " Loaded content: " << loadedJson << "\n";
63 :
64 2 : BOOST_CHECK(loadedJson.find("updated_value") != std::string::npos);
65 2 : std::cout << " Verified: Content was updated to 'updated_value'\n";
66 2 : }
67 :
68 4 : BOOST_AUTO_TEST_CASE(overwrite_nonexistent_behavior) {
69 2 : std::cout << "\n=== TEST 2: Overwrite non-existent version behavior ===\n";
70 :
71 2 : auto ifc = DatabaseConfigurationInterface();
72 :
73 2 : auto cfg = std::make_shared<TestConfiguration001>();
74 2 : cfg->getView().version = 999999997;
75 4 : cfg->getView().fillFromJSON("{\"field\": \"value\"}");
76 :
77 2 : std::cout << "Step 1: Attempting to overwrite non-existent version 999999997\n";
78 2 : auto overwriteResult = ifc.saveActiveVersion(cfg.get(), true);
79 2 : std::cout << " Overwrite returned: " << overwriteResult << "\n";
80 :
81 2 : if (overwriteResult == 0) {
82 1 : std::cout << " Observation: Overwrite on non-existent version SUCCEEDED (upsert behavior)\n";
83 1 : auto versions = ifc.getVersions(cfg.get());
84 1 : bool found = versions.find(999999997) != versions.end();
85 1 : std::cout << " Version 999999997 exists after overwrite: " << (found ? "yes" : "no") << "\n";
86 1 : BOOST_CHECK(found);
87 1 : } else {
88 1 : std::cout << " Observation: Overwrite on non-existent version FAILED as expected\n";
89 1 : BOOST_CHECK(true);
90 : }
91 2 : }
92 :
93 4 : BOOST_AUTO_TEST_CASE(overwrite_preserves_version_number) {
94 2 : std::cout << "\n=== TEST 3: Overwrite preserves version number ===\n";
95 :
96 2 : auto ifc = DatabaseConfigurationInterface();
97 :
98 2 : auto cfg = std::make_shared<TestConfiguration001>();
99 2 : int testVersion = fixture.baseVersion + 2;
100 2 : cfg->getView().version = testVersion;
101 4 : cfg->getView().fillFromJSON("{\"field\": \"original\"}");
102 :
103 2 : std::cout << "Step 1: Creating version " << testVersion << "\n";
104 2 : BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg.get(), false), 0);
105 :
106 2 : std::cout << "Step 2: Getting versions before overwrite\n";
107 2 : auto versionsBefore = ifc.getVersions(cfg.get());
108 2 : std::cout << " Versions count before: " << versionsBefore.size() << "\n";
109 2 : BOOST_REQUIRE(versionsBefore.find(testVersion) != versionsBefore.end());
110 :
111 2 : std::cout << "Step 3: Overwriting version " << testVersion << "\n";
112 6 : cfg->getView().fillFromJSON("{\"field\": \"overwritten\"}");
113 2 : BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg.get(), true), 0);
114 :
115 2 : std::cout << "Step 4: Getting versions after overwrite\n";
116 2 : auto versionsAfter = ifc.getVersions(cfg.get());
117 2 : std::cout << " Versions count after: " << versionsAfter.size() << "\n";
118 :
119 2 : BOOST_CHECK_EQUAL(versionsBefore.size(), versionsAfter.size());
120 2 : BOOST_CHECK(versionsAfter.find(testVersion) != versionsAfter.end());
121 2 : std::cout << " Verified: No new version was created, version " << testVersion << " still exists\n";
122 2 : }
123 :
124 4 : BOOST_AUTO_TEST_CASE(overwrite_readonly_behavior) {
125 2 : std::cout << "\n=== TEST 4: Overwrite read-only version behavior ===\n";
126 :
127 2 : auto ifc = DatabaseConfigurationInterface();
128 :
129 2 : auto cfg = std::make_shared<TestConfiguration001>();
130 2 : int testVersion = fixture.baseVersion + 3;
131 2 : cfg->getView().version = testVersion;
132 4 : cfg->getView().fillFromJSON("{\"field\": \"readonly_test\"}");
133 :
134 2 : std::cout << "Step 1: Creating version " << testVersion << "\n";
135 2 : BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg.get(), false), 0);
136 :
137 2 : std::cout << "Step 2: Attempting to mark version as read-only\n";
138 2 : auto markResult = ifc.markActiveVersionReadonly(cfg.get());
139 2 : std::cout << " markActiveVersionReadonly returned: " << markResult << "\n";
140 :
141 2 : if (markResult != 0) {
142 0 : std::cout << " Observation: markVersionReadonly returned error for existing version\n";
143 0 : std::cout << " SKIPPING overwrite test - cannot verify readonly protection\n";
144 0 : BOOST_CHECK(true);
145 0 : return;
146 : }
147 :
148 2 : std::cout << " Version marked read-only successfully\n";
149 :
150 2 : std::cout << "Step 3: Attempting to overwrite read-only version\n";
151 4 : cfg->getView().fillFromJSON("{\"field\": \"should_fail\"}");
152 2 : auto overwriteResult = ifc.saveActiveVersion(cfg.get(), true);
153 2 : std::cout << " Overwrite returned: " << overwriteResult << "\n";
154 :
155 2 : if (overwriteResult != 0) {
156 0 : std::cout << " Verified: Overwrite of read-only version correctly rejected\n";
157 : } else {
158 2 : std::cout << " Observation: Overwrite of read-only version was ALLOWED\n";
159 : }
160 2 : BOOST_CHECK(true);
161 2 : }
162 :
163 4 : BOOST_AUTO_TEST_CASE(multiple_overwrites_work) {
164 2 : std::cout << "\n=== TEST 5: Multiple overwrites work correctly ===\n";
165 :
166 2 : auto ifc = DatabaseConfigurationInterface();
167 :
168 2 : auto cfg = std::make_shared<TestConfiguration001>();
169 2 : int testVersion = fixture.baseVersion + 4;
170 2 : cfg->getView().version = testVersion;
171 4 : cfg->getView().fillFromJSON("{\"counter\": 0}");
172 :
173 2 : std::cout << "Step 1: Creating initial version " << testVersion << " with counter=0\n";
174 2 : BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg.get(), false), 0);
175 :
176 8 : for (int i = 1; i <= 3; ++i) {
177 6 : std::cout << "Step " << (i + 1) << ": Overwriting with counter=" << i << "\n";
178 6 : std::string newJson = "{\"counter\": " + std::to_string(i) + "}";
179 6 : cfg->getView().fillFromJSON(newJson);
180 6 : auto result = ifc.saveActiveVersion(cfg.get(), true);
181 6 : BOOST_REQUIRE_EQUAL(result, 0);
182 6 : std::cout << " Overwrite " << i << " succeeded\n";
183 6 : }
184 :
185 2 : std::cout << "Step 5: Verifying final content\n";
186 2 : auto loaded = std::make_shared<TestConfiguration001>();
187 2 : BOOST_REQUIRE_EQUAL(ifc.fill(loaded.get(), testVersion), 0);
188 :
189 2 : std::stringstream ss;
190 2 : loaded->getView().printJSON(ss);
191 2 : std::string finalJson = ss.str();
192 2 : std::cout << " Final content: " << finalJson << "\n";
193 :
194 2 : BOOST_CHECK(finalJson.find("3") != std::string::npos);
195 2 : std::cout << " Verified: Final content has counter=3\n";
196 :
197 2 : std::cout << "Step 6: Verifying only one version exists\n";
198 2 : auto versions = ifc.getVersions(cfg.get());
199 2 : auto count = std::count(versions.begin(), versions.end(), testVersion);
200 2 : BOOST_CHECK_EQUAL(count, 1);
201 2 : std::cout << " Verified: Version " << testVersion << " appears exactly once\n";
202 2 : }
203 :
204 4 : BOOST_AUTO_TEST_CASE(overwrite_does_not_create_new_version) {
205 2 : std::cout << "\n=== TEST 6: Overwrite does not create a new version ===\n";
206 :
207 2 : auto ifc = DatabaseConfigurationInterface();
208 :
209 2 : auto cfg = std::make_shared<TestConfiguration001>();
210 2 : int testVersion = fixture.baseVersion + 5;
211 2 : cfg->getView().version = testVersion;
212 4 : cfg->getView().fillFromJSON("{\"data\": \"test\"}");
213 :
214 2 : std::cout << "Step 1: Creating version " << testVersion << "\n";
215 2 : BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg.get(), false), 0);
216 :
217 2 : std::cout << "Step 2: Recording version count before overwrite\n";
218 2 : auto versionsBefore = ifc.getVersions(cfg.get());
219 2 : size_t countBefore = versionsBefore.size();
220 2 : std::cout << " Version count before: " << countBefore << "\n";
221 :
222 2 : std::cout << "Step 3: Performing 5 overwrites\n";
223 12 : for (int i = 0; i < 5; ++i) {
224 10 : cfg->getView().fillFromJSON("{\"data\": \"overwrite_" + std::to_string(i) + "\"}");
225 10 : BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg.get(), true), 0);
226 : }
227 2 : std::cout << " 5 overwrites completed\n";
228 :
229 2 : std::cout << "Step 4: Checking version count after overwrites\n";
230 2 : auto versionsAfter = ifc.getVersions(cfg.get());
231 2 : size_t countAfter = versionsAfter.size();
232 2 : std::cout << " Version count after: " << countAfter << "\n";
233 :
234 2 : BOOST_CHECK_EQUAL(countBefore, countAfter);
235 2 : std::cout << " Verified: No new versions were created during overwrites\n";
236 2 : }
237 :
238 4 : BOOST_AUTO_TEST_CASE(overwrite_different_config_types_independent) {
239 2 : std::cout << "\n=== TEST 7: Overwrite of different config types are independent ===\n";
240 :
241 2 : auto ifc = DatabaseConfigurationInterface();
242 :
243 2 : int testVersion = fixture.baseVersion + 6;
244 :
245 2 : auto cfg1 = std::make_shared<TestConfiguration001>();
246 2 : cfg1->getView().version = testVersion;
247 4 : cfg1->getView().fillFromJSON("{\"type\": \"config1_original\"}");
248 :
249 2 : auto cfg2 = std::make_shared<TestConfiguration002>();
250 2 : cfg2->getView().version = testVersion;
251 4 : cfg2->getView().fillFromJSON("{\"type\": \"config2_original\"}");
252 :
253 2 : std::cout << "Step 1: Creating both config types with version " << testVersion << "\n";
254 2 : BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg1.get(), false), 0);
255 2 : BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg2.get(), false), 0);
256 :
257 2 : std::cout << "Step 2: Overwriting only TestConfiguration001\n";
258 6 : cfg1->getView().fillFromJSON("{\"type\": \"config1_updated\"}");
259 2 : BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg1.get(), true), 0);
260 :
261 2 : std::cout << "Step 3: Verifying TestConfiguration002 is unchanged\n";
262 2 : auto loaded2 = std::make_shared<TestConfiguration002>();
263 2 : BOOST_REQUIRE_EQUAL(ifc.fill(loaded2.get(), testVersion), 0);
264 :
265 2 : std::stringstream ss2;
266 2 : loaded2->getView().printJSON(ss2);
267 2 : std::string loaded2Json = ss2.str();
268 2 : std::cout << " TestConfiguration002 content: " << loaded2Json << "\n";
269 :
270 2 : BOOST_CHECK(loaded2Json.find("config2_original") != std::string::npos);
271 2 : std::cout << " Verified: TestConfiguration002 was not affected by TestConfiguration001 overwrite\n";
272 :
273 2 : std::cout << "Step 4: Verifying TestConfiguration001 was updated\n";
274 2 : auto loaded1 = std::make_shared<TestConfiguration001>();
275 2 : BOOST_REQUIRE_EQUAL(ifc.fill(loaded1.get(), testVersion), 0);
276 :
277 2 : std::stringstream ss1;
278 2 : loaded1->getView().printJSON(ss1);
279 2 : std::string loaded1Json = ss1.str();
280 2 : std::cout << " TestConfiguration001 content: " << loaded1Json << "\n";
281 :
282 2 : BOOST_CHECK(loaded1Json.find("config1_updated") != std::string::npos);
283 2 : std::cout << " Verified: TestConfiguration001 was correctly updated\n";
284 2 : }
285 :
286 : BOOST_AUTO_TEST_SUITE_END()
|