PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
test_support.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021-2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
19#pragma once
20
21#include "service_interfaces.h"
23
26
27#include <kcenon/common/di/service_container.h>
28
29#include <functional>
30#include <map>
31#include <memory>
32#include <mutex>
33#include <string>
34#include <vector>
35
37
38// =============================================================================
39// Mock Storage Implementation
40// =============================================================================
41
64class MockStorage : public IDicomStorage {
65public:
66 MockStorage() = default;
67 ~MockStorage() override = default;
68
69 // =========================================================================
70 // storage_interface Implementation
71 // =========================================================================
72
73 [[nodiscard]] storage::VoidResult store(const core::dicom_dataset& dataset) override {
74 std::lock_guard<std::mutex> lock(mutex_);
75
77 if (uid.empty()) {
78 return kcenon::common::make_error<std::monostate>(
79 -1, "Dataset missing SOP Instance UID", "MockStorage");
80 }
81
82 datasets_[uid] = dataset;
83 store_calls_.push_back(uid);
84
85 if (on_store_) {
86 on_store_(dataset);
87 }
88
89 return storage::VoidResult::ok({});
90 }
91
93 retrieve(std::string_view sop_instance_uid) override {
94 std::lock_guard<std::mutex> lock(mutex_);
95
96 auto it = datasets_.find(std::string(sop_instance_uid));
97 if (it == datasets_.end()) {
98 return kcenon::common::make_error<core::dicom_dataset>(
99 -1, "Instance not found", "MockStorage");
100 }
101
103 }
104
105 [[nodiscard]] storage::VoidResult remove(std::string_view sop_instance_uid) override {
106 std::lock_guard<std::mutex> lock(mutex_);
107 datasets_.erase(std::string(sop_instance_uid));
108 return storage::VoidResult::ok({});
109 }
110
111 [[nodiscard]] bool exists(std::string_view sop_instance_uid) const override {
112 std::lock_guard<std::mutex> lock(mutex_);
113 return datasets_.find(std::string(sop_instance_uid)) != datasets_.end();
114 }
115
117 find(const core::dicom_dataset& /*query*/) override {
118 std::lock_guard<std::mutex> lock(mutex_);
119
120 std::vector<core::dicom_dataset> results;
121 for (const auto& [uid, dataset] : datasets_) {
122 results.push_back(dataset);
123 }
124 return storage::Result<std::vector<core::dicom_dataset>>::ok(std::move(results));
125 }
126
127 [[nodiscard]] storage::storage_statistics get_statistics() const override {
128 std::lock_guard<std::mutex> lock(mutex_);
129
131 stats.total_instances = datasets_.size();
132 return stats;
133 }
134
135 [[nodiscard]] storage::VoidResult verify_integrity() override {
136 return storage::VoidResult::ok({});
137 }
138
139 // =========================================================================
140 // Test Utilities
141 // =========================================================================
142
144 [[nodiscard]] std::size_t store_count() const {
145 std::lock_guard<std::mutex> lock(mutex_);
146 return store_calls_.size();
147 }
148
150 [[nodiscard]] std::vector<std::string> stored_uids() const {
151 std::lock_guard<std::mutex> lock(mutex_);
152 return store_calls_;
153 }
154
156 void clear() {
157 std::lock_guard<std::mutex> lock(mutex_);
158 datasets_.clear();
159 store_calls_.clear();
160 }
161
163 void on_store(std::function<void(const core::dicom_dataset&)> callback) {
164 on_store_ = std::move(callback);
165 }
166
167private:
168 mutable std::mutex mutex_;
169 std::map<std::string, core::dicom_dataset> datasets_;
170 std::vector<std::string> store_calls_;
171 std::function<void(const core::dicom_dataset&)> on_store_;
172};
173
174// =============================================================================
175// Mock Network Implementation
176// =============================================================================
177
187public:
188 MockNetwork() = default;
189 ~MockNetwork() override = default;
190
191 [[nodiscard]] std::unique_ptr<network::dicom_server> create_server(
192 const network::server_config& config,
193 const integration::tls_config& /*tls_cfg*/) override {
194 std::lock_guard<std::mutex> lock(mutex_);
195 server_configs_.push_back(config);
196
197 // Return nullptr for mock - actual server creation not needed in tests
198 return nullptr;
199 }
200
202 connect(const integration::connection_config& config) override {
203 std::lock_guard<std::mutex> lock(mutex_);
204 connection_attempts_.push_back({config.host, config.port});
205
206 if (fail_connections_) {
207 return integration::error_info{"Connection refused (mock)"};
208 }
209
210 // Return nullptr session for mock
212 }
213
215 connect(const std::string& host, uint16_t port,
216 std::chrono::milliseconds /*timeout*/) override {
218 config.host = host;
219 config.port = port;
220 return connect(config);
221 }
222
223 // =========================================================================
224 // Test Utilities
225 // =========================================================================
226
228 [[nodiscard]] std::size_t connection_attempt_count() const {
229 std::lock_guard<std::mutex> lock(mutex_);
230 return connection_attempts_.size();
231 }
232
234 [[nodiscard]] std::size_t server_creation_count() const {
235 std::lock_guard<std::mutex> lock(mutex_);
236 return server_configs_.size();
237 }
238
240 void set_fail_connections(bool fail) {
241 std::lock_guard<std::mutex> lock(mutex_);
242 fail_connections_ = fail;
243 }
244
246 void clear() {
247 std::lock_guard<std::mutex> lock(mutex_);
248 connection_attempts_.clear();
249 server_configs_.clear();
250 }
251
252private:
253 mutable std::mutex mutex_;
254 std::vector<std::pair<std::string, uint16_t>> connection_attempts_;
255 std::vector<network::server_config> server_configs_;
256 bool fail_connections_ = false;
257};
258
259// =============================================================================
260// Test Container Builder
261// =============================================================================
262
282public:
284
288 TestContainerBuilder& with_storage(std::shared_ptr<MockStorage> storage) {
289 mock_storage_ = std::move(storage);
290 return *this;
291 }
292
297 mock_storage_ = std::make_shared<MockStorage>();
298 return *this;
299 }
300
304 TestContainerBuilder& with_network(std::shared_ptr<MockNetwork> network) {
305 mock_network_ = std::move(network);
306 return *this;
307 }
308
313 mock_network_ = std::make_shared<MockNetwork>();
314 return *this;
315 }
316
322 [[nodiscard]] std::shared_ptr<kcenon::common::di::service_container> build() {
323 auto container = std::make_shared<kcenon::common::di::service_container>();
324
325 if (mock_storage_) {
326 container->register_instance<IDicomStorage>(mock_storage_);
327 }
328
329 if (mock_network_) {
330 container->register_instance<IDicomNetwork>(mock_network_);
331 }
332
333 return container;
334 }
335
339 [[nodiscard]] std::shared_ptr<MockStorage> storage() const {
340 return mock_storage_;
341 }
342
346 [[nodiscard]] std::shared_ptr<MockNetwork> network() const {
347 return mock_network_;
348 }
349
350private:
351 std::shared_ptr<MockStorage> mock_storage_;
352 std::shared_ptr<MockNetwork> mock_network_;
353};
354
355// =============================================================================
356// Convenience Functions
357// =============================================================================
358
364[[nodiscard]] inline std::shared_ptr<kcenon::common::di::service_container>
371
379 kcenon::common::di::IServiceContainer& container,
380 std::shared_ptr<MockStorage> mock) {
381 container.register_instance<IDicomStorage>(std::move(mock));
382}
383
391 kcenon::common::di::IServiceContainer& container,
392 std::shared_ptr<MockNetwork> mock) {
393 container.register_instance<IDicomNetwork>(std::move(mock));
394}
395
396} // namespace kcenon::pacs::di::test
auto get_string(dicom_tag tag, std::string_view default_value="") const -> std::string
Get the string value of an element.
Network service interface for DICOM communication.
Mock network service for testing.
std::vector< std::pair< std::string, uint16_t > > connection_attempts_
integration::Result< integration::network_adapter::session_ptr > connect(const std::string &host, uint16_t port, std::chrono::milliseconds) override
Connect to a remote DICOM peer (simplified)
std::vector< network::server_config > server_configs_
std::size_t server_creation_count() const
Get server creation count.
void set_fail_connections(bool fail)
Configure connections to fail.
std::unique_ptr< network::dicom_server > create_server(const network::server_config &config, const integration::tls_config &) override
Create a DICOM server.
void clear()
Clear call history.
std::size_t connection_attempt_count() const
Get connection attempt count.
integration::Result< integration::network_adapter::session_ptr > connect(const integration::connection_config &config) override
Connect to a remote DICOM peer.
storage::Result< core::dicom_dataset > retrieve(std::string_view sop_instance_uid) override
Retrieve a DICOM dataset by SOP Instance UID.
storage::VoidResult store(const core::dicom_dataset &dataset) override
Store a DICOM dataset.
void on_store(std::function< void(const core::dicom_dataset &)> callback)
Set callback for store operations (for assertions)
std::vector< std::string > store_calls_
bool exists(std::string_view sop_instance_uid) const override
Check if a DICOM instance exists.
storage::Result< std::vector< core::dicom_dataset > > find(const core::dicom_dataset &) override
Find DICOM datasets matching query criteria.
std::size_t store_count() const
Get the number of store() calls.
void clear()
Clear all stored data and call history.
std::function< void(const core::dicom_dataset &)> on_store_
storage::storage_statistics get_statistics() const override
Get storage statistics.
std::map< std::string, core::dicom_dataset > datasets_
storage::VoidResult verify_integrity() override
Verify storage integrity.
storage::VoidResult remove(std::string_view sop_instance_uid) override
Remove a DICOM dataset by SOP Instance UID.
std::vector< std::string > stored_uids() const
Get all stored UIDs in order.
std::shared_ptr< kcenon::common::di::service_container > build()
Build the configured container.
TestContainerBuilder & with_mock_storage()
Create and use a new mock storage.
std::shared_ptr< MockNetwork > mock_network_
TestContainerBuilder & with_network(std::shared_ptr< MockNetwork > network)
Use a specific mock network instance.
TestContainerBuilder & with_mock_network()
Create and use a new mock network.
std::shared_ptr< MockStorage > mock_storage_
std::shared_ptr< MockNetwork > network() const
Get the mock network instance (for test assertions)
TestContainerBuilder & with_storage(std::shared_ptr< MockStorage > storage)
Use a specific mock storage instance.
std::shared_ptr< MockStorage > storage() const
Get the mock storage instance (for test assertions)
Simple result type for error handling when common_system is unavailable.
std::shared_ptr< dicom_session > session_ptr
Session pointer type.
DICOM Dataset - ordered collection of Data Elements.
Compile-time constants for commonly used DICOM tags.
constexpr dicom_tag sop_instance_uid
SOP Instance UID.
void register_mock_network(kcenon::common::di::IServiceContainer &container, std::shared_ptr< MockNetwork > mock)
Register mock network with a container.
std::shared_ptr< kcenon::common::di::service_container > create_test_container()
Create a test container with all mock services.
void register_mock_storage(kcenon::common::di::IServiceContainer &container, std::shared_ptr< MockStorage > mock)
Register mock storage with a container.
Service interface aliases for dependency injection.
ServiceContainer registration module for PACS services.
Configuration for client connections.
Simple error info for fallback when common_system is unavailable.
Configuration for TLS/SSL secure transport.
std::string_view uid