Database System 0.1.0
Advanced C++20 Database System with Multi-Backend Support
Loading...
Searching...
No Matches
test_service_registration.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
16#include <gtest/gtest.h>
17
18#include <kcenon/common/di/service_container.h>
19#include <kcenon/common/interfaces/database_interface.h>
20#include <kcenon/database/config/feature_flags.h>
21
22#if KCENON_HAS_COMMON_SYSTEM
23
24#include <kcenon/database/di/service_registration.h>
25
26#include <atomic>
27#include <memory>
28#include <string>
29#include <thread>
30#include <vector>
31
32using namespace kcenon::common::di;
33using namespace kcenon::database::di;
34using namespace kcenon::common::interfaces;
35
39class DatabaseServiceRegistrationTest : public ::testing::Test
40{
41protected:
42 service_container container_;
43
44 void TearDown() override { container_.clear(); }
45};
46
47// --- Registration Tests ---
48
49TEST_F(DatabaseServiceRegistrationTest, RegisterDatabaseServices_DefaultConfig_Succeeds)
50{
51 auto result = register_database_services(container_);
52
53 EXPECT_TRUE(result.is_ok());
54 EXPECT_TRUE(container_.is_registered<IDatabase>());
55}
56
57TEST_F(DatabaseServiceRegistrationTest, RegisterDatabaseServices_CustomConfig_Succeeds)
58{
59 database_registration_config config;
61 config.lifetime = service_lifetime::singleton;
62
63 auto result = register_database_services(container_, config);
64
65 EXPECT_TRUE(result.is_ok());
66 EXPECT_TRUE(container_.is_registered<IDatabase>());
67}
68
69TEST_F(DatabaseServiceRegistrationTest, RegisterDatabaseServices_TransientLifetime_Succeeds)
70{
71 database_registration_config config;
72 config.lifetime = service_lifetime::transient;
73
74 auto result = register_database_services(container_, config);
75
76 EXPECT_TRUE(result.is_ok());
77 EXPECT_TRUE(container_.is_registered<IDatabase>());
78}
79
80TEST_F(DatabaseServiceRegistrationTest, RegisterDatabaseServices_Duplicate_Fails)
81{
82 auto result1 = register_database_services(container_);
83 ASSERT_TRUE(result1.is_ok());
84
85 auto result2 = register_database_services(container_);
86 EXPECT_TRUE(result2.is_err());
87}
88
89TEST_F(DatabaseServiceRegistrationTest, RegisterAllDatabaseServices_Succeeds)
90{
91 auto result = register_all_database_services(container_);
92
93 EXPECT_TRUE(result.is_ok());
94 EXPECT_TRUE(container_.is_registered<IDatabase>());
95}
96
97TEST_F(DatabaseServiceRegistrationTest, RegisterAllDatabaseServices_Duplicate_Fails)
98{
99 auto result1 = register_all_database_services(container_);
100 ASSERT_TRUE(result1.is_ok());
101
102 auto result2 = register_all_database_services(container_);
103 EXPECT_TRUE(result2.is_err());
104}
105
106// --- Resolution Tests ---
107
108TEST_F(DatabaseServiceRegistrationTest, Resolve_DefaultSingleton_ReturnsSameInstance)
109{
110 register_database_services(container_);
111
112 auto result1 = container_.resolve<IDatabase>();
113 auto result2 = container_.resolve<IDatabase>();
114
115 ASSERT_TRUE(result1.is_ok());
116 ASSERT_TRUE(result2.is_ok());
117
118 // Default lifetime is singleton
119 EXPECT_EQ(result1.value(), result2.value());
120}
121
122TEST_F(DatabaseServiceRegistrationTest, Resolve_Transient_ReturnsDifferentInstances)
123{
124 database_registration_config config;
125 config.lifetime = service_lifetime::transient;
126
127 register_database_services(container_, config);
128
129 auto result1 = container_.resolve<IDatabase>();
130 auto result2 = container_.resolve<IDatabase>();
131
132 ASSERT_TRUE(result1.is_ok());
133 ASSERT_TRUE(result2.is_ok());
134
135 EXPECT_NE(result1.value(), result2.value());
136}
137
138TEST_F(DatabaseServiceRegistrationTest, Resolve_ReturnsValidIDatabase)
139{
140 register_database_services(container_);
141
142 auto result = container_.resolve<IDatabase>();
143
144 ASSERT_TRUE(result.is_ok());
145 EXPECT_NE(result.value(), nullptr);
146
147 // Initially not connected
148 EXPECT_FALSE(result.value()->is_connected());
149}
150
151TEST_F(DatabaseServiceRegistrationTest, ResolveOrNull_Unregistered_ReturnsNull)
152{
153 auto ptr = container_.resolve_or_null<IDatabase>();
154
155 EXPECT_EQ(ptr, nullptr);
156}
157
158TEST_F(DatabaseServiceRegistrationTest, ResolveOrNull_Registered_ReturnsInstance)
159{
160 register_database_services(container_);
161
162 auto ptr = container_.resolve_or_null<IDatabase>();
163
164 EXPECT_NE(ptr, nullptr);
165}
166
167// --- Instance Registration Tests ---
168
169TEST_F(DatabaseServiceRegistrationTest, RegisterInstance_Succeeds)
170{
171 auto manager = std::make_shared<::database::database_manager>(
172 std::make_shared<::database::database_context>());
173
174 auto result = register_database_instance(container_, manager);
175
176 EXPECT_TRUE(result.is_ok());
177 EXPECT_TRUE(container_.is_registered<IDatabase>());
178}
179
180TEST_F(DatabaseServiceRegistrationTest, RegisterInstance_NullManager_Fails)
181{
182 auto result = register_database_instance(container_, nullptr);
183
184 EXPECT_TRUE(result.is_err());
185}
186
187TEST_F(DatabaseServiceRegistrationTest, RegisterInstance_ResolveReturnsSameAdapter)
188{
189 auto manager = std::make_shared<::database::database_manager>(
190 std::make_shared<::database::database_context>());
191
192 register_database_instance(container_, manager);
193
194 auto result1 = container_.resolve<IDatabase>();
195 auto result2 = container_.resolve<IDatabase>();
196
197 ASSERT_TRUE(result1.is_ok());
198 ASSERT_TRUE(result2.is_ok());
199
200 // Instance registration is always singleton
201 EXPECT_EQ(result1.value(), result2.value());
202}
203
204// --- Unregister Tests ---
205
206TEST_F(DatabaseServiceRegistrationTest, UnregisterDatabaseServices_Succeeds)
207{
208 register_database_services(container_);
209 EXPECT_TRUE(container_.is_registered<IDatabase>());
210
211 auto result = unregister_database_services(container_);
212 EXPECT_TRUE(result.is_ok());
213 EXPECT_FALSE(container_.is_registered<IDatabase>());
214}
215
216TEST_F(DatabaseServiceRegistrationTest, UnregisterDatabaseServices_NotRegistered_Fails)
217{
218 auto result = unregister_database_services(container_);
219 EXPECT_TRUE(result.is_err());
220}
221
222// --- Underlying Manager Utility Tests ---
223
224TEST_F(DatabaseServiceRegistrationTest, GetUnderlyingManager_FactoryRegistered_ReturnsManager)
225{
226 register_database_services(container_);
227
228 auto db_result = container_.resolve<IDatabase>();
229 ASSERT_TRUE(db_result.is_ok());
230
231 auto manager = get_underlying_database_manager(db_result.value());
232 EXPECT_NE(manager, nullptr);
233}
234
235TEST_F(DatabaseServiceRegistrationTest, GetUnderlyingManager_InstanceRegistered_ReturnsOriginal)
236{
237 auto original_manager = std::make_shared<::database::database_manager>(
238 std::make_shared<::database::database_context>());
239
240 register_database_instance(container_, original_manager);
241
242 auto db_result = container_.resolve<IDatabase>();
243 ASSERT_TRUE(db_result.is_ok());
244
245 auto retrieved_manager = get_underlying_database_manager(db_result.value());
246 EXPECT_EQ(retrieved_manager, original_manager);
247}
248
249// --- Clear Tests ---
250
251TEST_F(DatabaseServiceRegistrationTest, Clear_RemovesRegistration)
252{
253 register_database_services(container_);
254 EXPECT_TRUE(container_.is_registered<IDatabase>());
255
256 container_.clear();
257
258 EXPECT_FALSE(container_.is_registered<IDatabase>());
259
260 auto result = container_.resolve<IDatabase>();
261 EXPECT_TRUE(result.is_err());
262}
263
264// --- Freeze Tests ---
265
266TEST_F(DatabaseServiceRegistrationTest, Freeze_PreventsRegistration)
267{
268 register_database_services(container_);
269 container_.freeze();
270
271 EXPECT_TRUE(container_.is_frozen());
272
273 // Trying to register another service after freeze should fail
274 auto result = container_.register_simple_factory<IDatabase>(
275 []() -> std::shared_ptr<IDatabase> { return nullptr; },
276 service_lifetime::singleton);
277
278 EXPECT_TRUE(result.is_err());
279}
280
281TEST_F(DatabaseServiceRegistrationTest, Freeze_AllowsResolution)
282{
283 register_database_services(container_);
284 container_.freeze();
285
286 auto result = container_.resolve<IDatabase>();
287 EXPECT_TRUE(result.is_ok());
288 EXPECT_NE(result.value(), nullptr);
289}
290
291// --- Thread Safety Tests ---
292
293TEST_F(DatabaseServiceRegistrationTest, ConcurrentResolve_Singleton_ThreadSafe)
294{
295 register_database_services(container_);
296
297 const int num_threads = 8;
298 std::vector<std::thread> threads;
299 std::vector<std::shared_ptr<IDatabase>> results(num_threads);
300
301 for (int i = 0; i < num_threads; ++i)
302 {
303 threads.emplace_back(
304 [this, &results, i]()
305 {
306 auto result = container_.resolve<IDatabase>();
307 if (result.is_ok())
308 {
309 results[i] = result.value();
310 }
311 });
312 }
313
314 for (auto& t : threads)
315 {
316 t.join();
317 }
318
319 // All threads should get the same singleton instance
320 EXPECT_NE(results[0], nullptr);
321 for (int i = 1; i < num_threads; ++i)
322 {
323 EXPECT_EQ(results[0], results[i]);
324 }
325}
326
327TEST_F(DatabaseServiceRegistrationTest, ConcurrentResolve_Transient_ThreadSafe)
328{
329 database_registration_config config;
330 config.lifetime = service_lifetime::transient;
331 register_database_services(container_, config);
332
333 const int num_threads = 8;
334 const int ops_per_thread = 50;
335 std::vector<std::thread> threads;
336 std::atomic<int> success_count{0};
337
338 for (int i = 0; i < num_threads; ++i)
339 {
340 threads.emplace_back(
341 [this, &success_count, ops_per_thread]()
342 {
343 for (int j = 0; j < ops_per_thread; ++j)
344 {
345 auto result = container_.resolve<IDatabase>();
346 if (result.is_ok())
347 {
348 ++success_count;
349 }
350 }
351 });
352 }
353
354 for (auto& t : threads)
355 {
356 t.join();
357 }
358
359 EXPECT_EQ(success_count.load(), num_threads * ops_per_thread);
360}
361
362// --- Service Descriptor Tests ---
363
364TEST_F(DatabaseServiceRegistrationTest, RegisteredServices_ReturnsDescriptors)
365{
366 register_database_services(container_);
367
368 auto services = container_.registered_services();
369 EXPECT_GE(services.size(), 1u);
370}
371
372// --- Scoped Container Tests ---
373
374TEST_F(DatabaseServiceRegistrationTest, ScopedContainer_InheritsRegistration)
375{
376 register_database_services(container_);
377
378 auto scope = container_.create_scope();
379 ASSERT_NE(scope, nullptr);
380
381 EXPECT_TRUE(scope->is_registered<IDatabase>());
382
383 auto result = scope->resolve<IDatabase>();
384 EXPECT_TRUE(result.is_ok());
385 EXPECT_NE(result.value(), nullptr);
386}
387
388#endif // KCENON_HAS_COMMON_SYSTEM
@ sqlite
Indicates a SQLite database.
TEST_F(AsyncResultTest, ConstructsFromFuture)
#define ASSERT_TRUE(condition, message)