spikard 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +674 -674
- data/ext/spikard_rb/Cargo.toml +17 -17
- data/ext/spikard_rb/extconf.rb +13 -10
- data/ext/spikard_rb/src/lib.rs +6 -6
- data/lib/spikard/app.rb +405 -405
- data/lib/spikard/background.rb +27 -27
- data/lib/spikard/config.rb +396 -396
- data/lib/spikard/converters.rb +13 -13
- data/lib/spikard/handler_wrapper.rb +113 -113
- data/lib/spikard/provide.rb +214 -214
- data/lib/spikard/response.rb +173 -173
- data/lib/spikard/schema.rb +243 -243
- data/lib/spikard/sse.rb +111 -111
- data/lib/spikard/streaming_response.rb +44 -44
- data/lib/spikard/testing.rb +256 -256
- data/lib/spikard/upload_file.rb +131 -131
- data/lib/spikard/version.rb +5 -5
- data/lib/spikard/websocket.rb +59 -59
- data/lib/spikard.rb +43 -43
- data/sig/spikard.rbs +366 -366
- data/vendor/crates/spikard-bindings-shared/Cargo.toml +63 -63
- data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +132 -132
- data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +752 -752
- data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +194 -194
- data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +246 -246
- data/vendor/crates/spikard-bindings-shared/src/error_response.rs +401 -401
- data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +238 -238
- data/vendor/crates/spikard-bindings-shared/src/lib.rs +24 -24
- data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +292 -292
- data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +616 -616
- data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +305 -305
- data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +248 -248
- data/vendor/crates/spikard-bindings-shared/src/validation_helpers.rs +351 -351
- data/vendor/crates/spikard-bindings-shared/tests/comprehensive_coverage.rs +454 -454
- data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +383 -383
- data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +280 -280
- data/vendor/crates/spikard-core/Cargo.toml +40 -40
- data/vendor/crates/spikard-core/src/bindings/mod.rs +3 -3
- data/vendor/crates/spikard-core/src/bindings/response.rs +133 -133
- data/vendor/crates/spikard-core/src/debug.rs +127 -127
- data/vendor/crates/spikard-core/src/di/container.rs +702 -702
- data/vendor/crates/spikard-core/src/di/dependency.rs +273 -273
- data/vendor/crates/spikard-core/src/di/error.rs +118 -118
- data/vendor/crates/spikard-core/src/di/factory.rs +534 -534
- data/vendor/crates/spikard-core/src/di/graph.rs +506 -506
- data/vendor/crates/spikard-core/src/di/mod.rs +192 -192
- data/vendor/crates/spikard-core/src/di/resolved.rs +405 -405
- data/vendor/crates/spikard-core/src/di/value.rs +281 -281
- data/vendor/crates/spikard-core/src/errors.rs +69 -69
- data/vendor/crates/spikard-core/src/http.rs +415 -415
- data/vendor/crates/spikard-core/src/lib.rs +29 -29
- data/vendor/crates/spikard-core/src/lifecycle.rs +1186 -1186
- data/vendor/crates/spikard-core/src/metadata.rs +389 -389
- data/vendor/crates/spikard-core/src/parameters.rs +2525 -2525
- data/vendor/crates/spikard-core/src/problem.rs +344 -344
- data/vendor/crates/spikard-core/src/request_data.rs +1154 -1154
- data/vendor/crates/spikard-core/src/router.rs +510 -510
- data/vendor/crates/spikard-core/src/schema_registry.rs +183 -183
- data/vendor/crates/spikard-core/src/type_hints.rs +304 -304
- data/vendor/crates/spikard-core/src/validation/error_mapper.rs +696 -688
- data/vendor/crates/spikard-core/src/validation/mod.rs +457 -457
- data/vendor/crates/spikard-http/Cargo.toml +62 -64
- data/vendor/crates/spikard-http/examples/sse-notifications.rs +148 -148
- data/vendor/crates/spikard-http/examples/websocket-chat.rs +92 -92
- data/vendor/crates/spikard-http/src/auth.rs +296 -296
- data/vendor/crates/spikard-http/src/background.rs +1860 -1860
- data/vendor/crates/spikard-http/src/bindings/mod.rs +3 -3
- data/vendor/crates/spikard-http/src/bindings/response.rs +1 -1
- data/vendor/crates/spikard-http/src/body_metadata.rs +8 -8
- data/vendor/crates/spikard-http/src/cors.rs +1005 -1005
- data/vendor/crates/spikard-http/src/debug.rs +128 -128
- data/vendor/crates/spikard-http/src/di_handler.rs +1668 -1668
- data/vendor/crates/spikard-http/src/handler_response.rs +901 -901
- data/vendor/crates/spikard-http/src/handler_trait.rs +838 -830
- data/vendor/crates/spikard-http/src/handler_trait_tests.rs +290 -290
- data/vendor/crates/spikard-http/src/lib.rs +534 -534
- data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +230 -230
- data/vendor/crates/spikard-http/src/lifecycle.rs +1193 -1193
- data/vendor/crates/spikard-http/src/middleware/mod.rs +560 -540
- data/vendor/crates/spikard-http/src/middleware/multipart.rs +912 -912
- data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +513 -513
- data/vendor/crates/spikard-http/src/middleware/validation.rs +768 -735
- data/vendor/crates/spikard-http/src/openapi/mod.rs +309 -309
- data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +535 -535
- data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +1363 -1363
- data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +665 -665
- data/vendor/crates/spikard-http/src/query_parser.rs +793 -793
- data/vendor/crates/spikard-http/src/response.rs +720 -720
- data/vendor/crates/spikard-http/src/server/handler.rs +1650 -1650
- data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +234 -234
- data/vendor/crates/spikard-http/src/server/mod.rs +1593 -1502
- data/vendor/crates/spikard-http/src/server/request_extraction.rs +789 -770
- data/vendor/crates/spikard-http/src/server/routing_factory.rs +629 -599
- data/vendor/crates/spikard-http/src/sse.rs +1409 -1409
- data/vendor/crates/spikard-http/src/testing/form.rs +52 -52
- data/vendor/crates/spikard-http/src/testing/multipart.rs +64 -60
- data/vendor/crates/spikard-http/src/testing/test_client.rs +311 -283
- data/vendor/crates/spikard-http/src/testing.rs +406 -377
- data/vendor/crates/spikard-http/src/websocket.rs +1404 -1375
- data/vendor/crates/spikard-http/tests/background_behavior.rs +832 -832
- data/vendor/crates/spikard-http/tests/common/handlers.rs +309 -309
- data/vendor/crates/spikard-http/tests/common/mod.rs +26 -26
- data/vendor/crates/spikard-http/tests/di_integration.rs +192 -192
- data/vendor/crates/spikard-http/tests/doc_snippets.rs +5 -5
- data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +1093 -1093
- data/vendor/crates/spikard-http/tests/multipart_behavior.rs +656 -656
- data/vendor/crates/spikard-http/tests/server_config_builder.rs +314 -314
- data/vendor/crates/spikard-http/tests/sse_behavior.rs +620 -620
- data/vendor/crates/spikard-http/tests/websocket_behavior.rs +663 -663
- data/vendor/crates/spikard-rb/Cargo.toml +48 -48
- data/vendor/crates/spikard-rb/build.rs +199 -199
- data/vendor/crates/spikard-rb/src/background.rs +63 -63
- data/vendor/crates/spikard-rb/src/config/mod.rs +5 -5
- data/vendor/crates/spikard-rb/src/config/server_config.rs +285 -285
- data/vendor/crates/spikard-rb/src/conversion.rs +554 -554
- data/vendor/crates/spikard-rb/src/di/builder.rs +100 -100
- data/vendor/crates/spikard-rb/src/di/mod.rs +375 -375
- data/vendor/crates/spikard-rb/src/handler.rs +618 -618
- data/vendor/crates/spikard-rb/src/integration/mod.rs +3 -3
- data/vendor/crates/spikard-rb/src/lib.rs +1806 -1810
- data/vendor/crates/spikard-rb/src/lifecycle.rs +275 -275
- data/vendor/crates/spikard-rb/src/metadata/mod.rs +5 -5
- data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +442 -447
- data/vendor/crates/spikard-rb/src/runtime/mod.rs +5 -5
- data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +324 -324
- data/vendor/crates/spikard-rb/src/server.rs +305 -308
- data/vendor/crates/spikard-rb/src/sse.rs +231 -231
- data/vendor/crates/spikard-rb/src/testing/client.rs +538 -551
- data/vendor/crates/spikard-rb/src/testing/mod.rs +7 -7
- data/vendor/crates/spikard-rb/src/testing/sse.rs +143 -143
- data/vendor/crates/spikard-rb/src/testing/websocket.rs +608 -635
- data/vendor/crates/spikard-rb/src/websocket.rs +377 -374
- metadata +15 -1
|
@@ -1,1668 +1,1668 @@
|
|
|
1
|
-
//! Dependency Injection Handler Wrapper
|
|
2
|
-
//!
|
|
3
|
-
//! This module provides a handler wrapper that integrates the DI system with the HTTP
|
|
4
|
-
//! handler pipeline. It follows the same composition pattern as `ValidatingHandler`.
|
|
5
|
-
//!
|
|
6
|
-
//! # Architecture
|
|
7
|
-
//!
|
|
8
|
-
//! The `DependencyInjectingHandler` wraps any `Handler` and:
|
|
9
|
-
//! 1. Resolves required dependencies in parallel batches before calling the handler
|
|
10
|
-
//! 2. Attaches resolved dependencies to `RequestData`
|
|
11
|
-
//! 3. Calls the inner handler with the enriched request data
|
|
12
|
-
//! 4. Cleans up dependencies after the handler completes (async Drop pattern)
|
|
13
|
-
//!
|
|
14
|
-
//! # Performance
|
|
15
|
-
//!
|
|
16
|
-
//! - **Zero overhead when no DI**: If no container is provided, DI is skipped entirely
|
|
17
|
-
//! - **Parallel resolution**: Independent dependencies are resolved concurrently
|
|
18
|
-
//! - **Efficient caching**: Singleton and per-request caching minimize redundant work
|
|
19
|
-
//! - **Composable**: Works seamlessly with `ValidatingHandler` and lifecycle hooks
|
|
20
|
-
//!
|
|
21
|
-
//! # Examples
|
|
22
|
-
//!
|
|
23
|
-
//! ```ignore
|
|
24
|
-
//! use spikard_http::di_handler::DependencyInjectingHandler;
|
|
25
|
-
//! use spikard_core::di::DependencyContainer;
|
|
26
|
-
//! use std::sync::Arc;
|
|
27
|
-
//!
|
|
28
|
-
//! # tokio_test::block_on(async {
|
|
29
|
-
//! let container = Arc::new(DependencyContainer::new());
|
|
30
|
-
//! let handler = Arc::new(MyHandler::new());
|
|
31
|
-
//!
|
|
32
|
-
//! let di_handler = DependencyInjectingHandler::new(
|
|
33
|
-
//! handler,
|
|
34
|
-
//! container,
|
|
35
|
-
//! vec!["database".to_string(), "cache".to_string()],
|
|
36
|
-
//! );
|
|
37
|
-
//! # });
|
|
38
|
-
//! ```
|
|
39
|
-
|
|
40
|
-
use crate::handler_trait::{Handler, HandlerResult, RequestData};
|
|
41
|
-
use axum::body::Body;
|
|
42
|
-
use axum::http::{Request, StatusCode};
|
|
43
|
-
use spikard_core::di::{DependencyContainer, DependencyError};
|
|
44
|
-
use std::future::Future;
|
|
45
|
-
use std::pin::Pin;
|
|
46
|
-
use std::sync::Arc;
|
|
47
|
-
use tracing::{debug, info_span, instrument};
|
|
48
|
-
|
|
49
|
-
/// Handler wrapper that resolves dependencies before calling the inner handler
|
|
50
|
-
///
|
|
51
|
-
/// This wrapper follows the composition pattern used by `ValidatingHandler`:
|
|
52
|
-
/// it wraps an existing handler and enriches the request with resolved dependencies.
|
|
53
|
-
///
|
|
54
|
-
/// # Thread Safety
|
|
55
|
-
///
|
|
56
|
-
/// This struct is `Send + Sync` and can be safely shared across threads.
|
|
57
|
-
/// The container is shared via `Arc`, and all dependencies must be `Send + Sync`.
|
|
58
|
-
pub struct DependencyInjectingHandler {
|
|
59
|
-
/// The wrapped handler that will receive the enriched request
|
|
60
|
-
inner: Arc<dyn Handler>,
|
|
61
|
-
/// Shared dependency container for resolution
|
|
62
|
-
container: Arc<DependencyContainer>,
|
|
63
|
-
/// List of dependency names required by this handler
|
|
64
|
-
required_dependencies: Vec<String>,
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
impl DependencyInjectingHandler {
|
|
68
|
-
/// Create a new dependency-injecting handler wrapper
|
|
69
|
-
///
|
|
70
|
-
/// # Arguments
|
|
71
|
-
///
|
|
72
|
-
/// * `handler` - The handler to wrap
|
|
73
|
-
/// * `container` - Shared dependency container
|
|
74
|
-
/// * `required_dependencies` - Names of dependencies to resolve for this handler
|
|
75
|
-
///
|
|
76
|
-
/// # Examples
|
|
77
|
-
///
|
|
78
|
-
/// ```ignore
|
|
79
|
-
/// use spikard_http::di_handler::DependencyInjectingHandler;
|
|
80
|
-
/// use spikard_core::di::DependencyContainer;
|
|
81
|
-
/// use std::sync::Arc;
|
|
82
|
-
///
|
|
83
|
-
/// # tokio_test::block_on(async {
|
|
84
|
-
/// let container = Arc::new(DependencyContainer::new());
|
|
85
|
-
/// let handler = Arc::new(MyHandler::new());
|
|
86
|
-
///
|
|
87
|
-
/// let di_handler = DependencyInjectingHandler::new(
|
|
88
|
-
/// handler,
|
|
89
|
-
/// container,
|
|
90
|
-
/// vec!["db".to_string()],
|
|
91
|
-
/// );
|
|
92
|
-
/// # });
|
|
93
|
-
/// ```
|
|
94
|
-
pub fn new(
|
|
95
|
-
handler: Arc<dyn Handler>,
|
|
96
|
-
container: Arc<DependencyContainer>,
|
|
97
|
-
required_dependencies: Vec<String>,
|
|
98
|
-
) -> Self {
|
|
99
|
-
Self {
|
|
100
|
-
inner: handler,
|
|
101
|
-
container,
|
|
102
|
-
required_dependencies,
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/// Get the list of required dependencies
|
|
107
|
-
pub fn required_dependencies(&self) -> &[String] {
|
|
108
|
-
&self.required_dependencies
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
impl Handler for DependencyInjectingHandler {
|
|
113
|
-
#[instrument(
|
|
114
|
-
skip(self, request, request_data),
|
|
115
|
-
fields(
|
|
116
|
-
required_deps = %self.required_dependencies.len(),
|
|
117
|
-
deps = ?self.required_dependencies
|
|
118
|
-
)
|
|
119
|
-
)]
|
|
120
|
-
fn call(
|
|
121
|
-
&self,
|
|
122
|
-
request: Request<Body>,
|
|
123
|
-
mut request_data: RequestData,
|
|
124
|
-
) -> Pin<Box<dyn Future<Output = HandlerResult> + Send + '_>> {
|
|
125
|
-
tracing::debug!(
|
|
126
|
-
target = "spikard::di",
|
|
127
|
-
required_deps = ?self.required_dependencies,
|
|
128
|
-
"entering DI handler"
|
|
129
|
-
);
|
|
130
|
-
let inner = self.inner.clone();
|
|
131
|
-
let container = self.container.clone();
|
|
132
|
-
let required_dependencies = self.required_dependencies.clone();
|
|
133
|
-
|
|
134
|
-
Box::pin(async move {
|
|
135
|
-
debug!(
|
|
136
|
-
"DI handler invoked for {} deps; container keys: {:?}",
|
|
137
|
-
required_dependencies.len(),
|
|
138
|
-
container.keys()
|
|
139
|
-
);
|
|
140
|
-
let resolution_span = info_span!(
|
|
141
|
-
"resolve_dependencies",
|
|
142
|
-
count = %required_dependencies.len()
|
|
143
|
-
);
|
|
144
|
-
let _enter = resolution_span.enter();
|
|
145
|
-
|
|
146
|
-
debug!(
|
|
147
|
-
"Resolving {} dependencies: {:?}",
|
|
148
|
-
required_dependencies.len(),
|
|
149
|
-
required_dependencies
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
let start = std::time::Instant::now();
|
|
153
|
-
|
|
154
|
-
let core_request_data = spikard_core::RequestData {
|
|
155
|
-
path_params: Arc::clone(&request_data.path_params),
|
|
156
|
-
query_params: request_data.query_params.clone(),
|
|
157
|
-
validated_params: request_data.validated_params.clone(),
|
|
158
|
-
raw_query_params: Arc::clone(&request_data.raw_query_params),
|
|
159
|
-
body: request_data.body.clone(),
|
|
160
|
-
raw_body: request_data.raw_body.clone(),
|
|
161
|
-
headers: Arc::clone(&request_data.headers),
|
|
162
|
-
cookies: Arc::clone(&request_data.cookies),
|
|
163
|
-
method: request_data.method.clone(),
|
|
164
|
-
path: request_data.path.clone(),
|
|
165
|
-
#[cfg(feature = "di")]
|
|
166
|
-
dependencies: None,
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
let (parts, _body) = request.into_parts();
|
|
170
|
-
let core_request = Request::from_parts(parts.clone(), ());
|
|
171
|
-
|
|
172
|
-
let request = Request::from_parts(parts, axum::body::Body::default());
|
|
173
|
-
|
|
174
|
-
let resolved = match container
|
|
175
|
-
.resolve_for_handler(&required_dependencies, &core_request, &core_request_data)
|
|
176
|
-
.await
|
|
177
|
-
{
|
|
178
|
-
Ok(resolved) => resolved,
|
|
179
|
-
Err(e) => {
|
|
180
|
-
debug!("DI error: {}", e);
|
|
181
|
-
|
|
182
|
-
let (status, json_body) = match e {
|
|
183
|
-
DependencyError::NotFound { ref key } => {
|
|
184
|
-
let body = serde_json::json!({
|
|
185
|
-
"detail": "Required dependency not found",
|
|
186
|
-
"errors": [{
|
|
187
|
-
"dependency_key": key,
|
|
188
|
-
"msg": format!("Dependency '{}' is not registered", key),
|
|
189
|
-
"type": "missing_dependency"
|
|
190
|
-
}],
|
|
191
|
-
"status": 500,
|
|
192
|
-
"title": "Dependency Resolution Failed",
|
|
193
|
-
"type": "https://spikard.dev/errors/dependency-error"
|
|
194
|
-
});
|
|
195
|
-
(StatusCode::INTERNAL_SERVER_ERROR, body)
|
|
196
|
-
}
|
|
197
|
-
DependencyError::CircularDependency { ref cycle } => {
|
|
198
|
-
let body = serde_json::json!({
|
|
199
|
-
"detail": "Circular dependency detected",
|
|
200
|
-
"errors": [{
|
|
201
|
-
"cycle": cycle,
|
|
202
|
-
"msg": "Circular dependency detected in dependency graph",
|
|
203
|
-
"type": "circular_dependency"
|
|
204
|
-
}],
|
|
205
|
-
"status": 500,
|
|
206
|
-
"title": "Dependency Resolution Failed",
|
|
207
|
-
"type": "https://spikard.dev/errors/dependency-error"
|
|
208
|
-
});
|
|
209
|
-
(StatusCode::INTERNAL_SERVER_ERROR, body)
|
|
210
|
-
}
|
|
211
|
-
DependencyError::ResolutionFailed { ref message } => {
|
|
212
|
-
let body = serde_json::json!({
|
|
213
|
-
"detail": "Dependency resolution failed",
|
|
214
|
-
"errors": [{
|
|
215
|
-
"msg": message,
|
|
216
|
-
"type": "resolution_failed"
|
|
217
|
-
}],
|
|
218
|
-
"status": 503,
|
|
219
|
-
"title": "Service Unavailable",
|
|
220
|
-
"type": "https://spikard.dev/errors/dependency-error"
|
|
221
|
-
});
|
|
222
|
-
(StatusCode::SERVICE_UNAVAILABLE, body)
|
|
223
|
-
}
|
|
224
|
-
_ => {
|
|
225
|
-
let body = serde_json::json!({
|
|
226
|
-
"detail": "Dependency resolution failed",
|
|
227
|
-
"errors": [{
|
|
228
|
-
"msg": e.to_string(),
|
|
229
|
-
"type": "unknown"
|
|
230
|
-
}],
|
|
231
|
-
"status": 500,
|
|
232
|
-
"title": "Dependency Resolution Failed",
|
|
233
|
-
"type": "https://spikard.dev/errors/dependency-error"
|
|
234
|
-
});
|
|
235
|
-
(StatusCode::INTERNAL_SERVER_ERROR, body)
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
let response = axum::http::Response::builder()
|
|
240
|
-
.status(status)
|
|
241
|
-
.header("Content-Type", "application/json")
|
|
242
|
-
.body(Body::from(json_body.to_string()))
|
|
243
|
-
.unwrap();
|
|
244
|
-
|
|
245
|
-
return Ok(response);
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
let duration = start.elapsed();
|
|
250
|
-
debug!(
|
|
251
|
-
"Dependencies resolved in {:?} ({} dependencies)",
|
|
252
|
-
duration,
|
|
253
|
-
required_dependencies.len()
|
|
254
|
-
);
|
|
255
|
-
|
|
256
|
-
drop(_enter);
|
|
257
|
-
|
|
258
|
-
let deps = Arc::new(resolved);
|
|
259
|
-
request_data.dependencies = Some(Arc::clone(&deps));
|
|
260
|
-
|
|
261
|
-
let result = inner.call(request, request_data).await;
|
|
262
|
-
|
|
263
|
-
if let Ok(deps) = Arc::try_unwrap(deps) {
|
|
264
|
-
let cleanup_span = info_span!("cleanup_dependencies");
|
|
265
|
-
let _enter = cleanup_span.enter();
|
|
266
|
-
|
|
267
|
-
debug!("Running dependency cleanup tasks");
|
|
268
|
-
deps.cleanup().await;
|
|
269
|
-
} else {
|
|
270
|
-
debug!("Skipping cleanup: dependencies still shared");
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
result
|
|
274
|
-
})
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
#[cfg(test)]
|
|
279
|
-
mod tests {
|
|
280
|
-
use super::*;
|
|
281
|
-
use crate::handler_trait::RequestData;
|
|
282
|
-
use axum::http::Response;
|
|
283
|
-
use spikard_core::di::ValueDependency;
|
|
284
|
-
use std::collections::HashMap;
|
|
285
|
-
|
|
286
|
-
/// Test handler that checks for dependency presence
|
|
287
|
-
struct TestHandler;
|
|
288
|
-
|
|
289
|
-
impl Handler for TestHandler {
|
|
290
|
-
fn call(
|
|
291
|
-
&self,
|
|
292
|
-
_request: Request<Body>,
|
|
293
|
-
request_data: RequestData,
|
|
294
|
-
) -> Pin<Box<dyn Future<Output = HandlerResult> + Send + '_>> {
|
|
295
|
-
Box::pin(async move {
|
|
296
|
-
if request_data.dependencies.is_some() {
|
|
297
|
-
let response = Response::builder()
|
|
298
|
-
.status(StatusCode::OK)
|
|
299
|
-
.body(Body::from("dependencies present"))
|
|
300
|
-
.unwrap();
|
|
301
|
-
Ok(response)
|
|
302
|
-
} else {
|
|
303
|
-
Err((StatusCode::INTERNAL_SERVER_ERROR, "no dependencies".to_string()))
|
|
304
|
-
}
|
|
305
|
-
})
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/// Handler that returns error to test error propagation
|
|
310
|
-
struct ErrorHandler;
|
|
311
|
-
|
|
312
|
-
impl Handler for ErrorHandler {
|
|
313
|
-
fn call(
|
|
314
|
-
&self,
|
|
315
|
-
_request: Request<Body>,
|
|
316
|
-
_request_data: RequestData,
|
|
317
|
-
) -> Pin<Box<dyn Future<Output = HandlerResult> + Send + '_>> {
|
|
318
|
-
Box::pin(async move { Err((StatusCode::INTERNAL_SERVER_ERROR, "inner handler error".to_string())) })
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
/// Handler that reads and validates dependency values
|
|
323
|
-
struct ReadDependencyHandler;
|
|
324
|
-
|
|
325
|
-
impl Handler for ReadDependencyHandler {
|
|
326
|
-
fn call(
|
|
327
|
-
&self,
|
|
328
|
-
_request: Request<Body>,
|
|
329
|
-
request_data: RequestData,
|
|
330
|
-
) -> Pin<Box<dyn Future<Output = HandlerResult> + Send + '_>> {
|
|
331
|
-
Box::pin(async move {
|
|
332
|
-
if request_data.dependencies.is_some() {
|
|
333
|
-
let response = Response::builder()
|
|
334
|
-
.status(StatusCode::OK)
|
|
335
|
-
.body(Body::from("dependencies resolved and accessible"))
|
|
336
|
-
.unwrap();
|
|
337
|
-
Ok(response)
|
|
338
|
-
} else {
|
|
339
|
-
Err((StatusCode::INTERNAL_SERVER_ERROR, "no dependencies".to_string()))
|
|
340
|
-
}
|
|
341
|
-
})
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/// Helper function to create a basic RequestData
|
|
346
|
-
fn create_request_data() -> RequestData {
|
|
347
|
-
RequestData {
|
|
348
|
-
path_params: Arc::new(HashMap::new()),
|
|
349
|
-
query_params: serde_json::Value::Null,
|
|
350
|
-
validated_params: None,
|
|
351
|
-
raw_query_params: Arc::new(HashMap::new()),
|
|
352
|
-
body: serde_json::Value::Null,
|
|
353
|
-
raw_body: None,
|
|
354
|
-
headers: Arc::new(HashMap::new()),
|
|
355
|
-
cookies: Arc::new(HashMap::new()),
|
|
356
|
-
method: "GET".to_string(),
|
|
357
|
-
path: "/".to_string(),
|
|
358
|
-
#[cfg(feature = "di")]
|
|
359
|
-
dependencies: None,
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
#[tokio::test]
|
|
364
|
-
async fn test_di_handler_resolves_dependencies() {
|
|
365
|
-
let mut container = DependencyContainer::new();
|
|
366
|
-
container
|
|
367
|
-
.register(
|
|
368
|
-
"config".to_string(),
|
|
369
|
-
Arc::new(ValueDependency::new("config", "test_value")),
|
|
370
|
-
)
|
|
371
|
-
.unwrap();
|
|
372
|
-
|
|
373
|
-
let handler = Arc::new(TestHandler);
|
|
374
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
375
|
-
|
|
376
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
377
|
-
let request_data = create_request_data();
|
|
378
|
-
|
|
379
|
-
let result = di_handler.call(request, request_data).await;
|
|
380
|
-
|
|
381
|
-
assert!(result.is_ok());
|
|
382
|
-
let response = result.unwrap();
|
|
383
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
#[tokio::test]
|
|
387
|
-
async fn test_di_handler_error_on_missing_dependency() {
|
|
388
|
-
let container = DependencyContainer::new();
|
|
389
|
-
let handler = Arc::new(TestHandler);
|
|
390
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["database".to_string()]);
|
|
391
|
-
|
|
392
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
393
|
-
let request_data = create_request_data();
|
|
394
|
-
|
|
395
|
-
let result = di_handler.call(request, request_data).await;
|
|
396
|
-
|
|
397
|
-
assert!(result.is_ok());
|
|
398
|
-
let response = result.unwrap();
|
|
399
|
-
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
#[tokio::test]
|
|
403
|
-
async fn test_di_handler_empty_dependencies() {
|
|
404
|
-
let container = DependencyContainer::new();
|
|
405
|
-
let handler = Arc::new(TestHandler);
|
|
406
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec![]);
|
|
407
|
-
|
|
408
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
409
|
-
let request_data = create_request_data();
|
|
410
|
-
|
|
411
|
-
let result = di_handler.call(request, request_data).await;
|
|
412
|
-
|
|
413
|
-
assert!(result.is_ok());
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
#[tokio::test]
|
|
417
|
-
async fn test_di_handler_multiple_dependencies() {
|
|
418
|
-
let mut container = DependencyContainer::new();
|
|
419
|
-
container
|
|
420
|
-
.register("db".to_string(), Arc::new(ValueDependency::new("db", "postgresql")))
|
|
421
|
-
.unwrap();
|
|
422
|
-
container
|
|
423
|
-
.register("cache".to_string(), Arc::new(ValueDependency::new("cache", "redis")))
|
|
424
|
-
.unwrap();
|
|
425
|
-
container
|
|
426
|
-
.register("logger".to_string(), Arc::new(ValueDependency::new("logger", "slog")))
|
|
427
|
-
.unwrap();
|
|
428
|
-
container
|
|
429
|
-
.register(
|
|
430
|
-
"config".to_string(),
|
|
431
|
-
Arc::new(ValueDependency::new("config", "config_data")),
|
|
432
|
-
)
|
|
433
|
-
.unwrap();
|
|
434
|
-
|
|
435
|
-
let handler = Arc::new(TestHandler);
|
|
436
|
-
let di_handler = DependencyInjectingHandler::new(
|
|
437
|
-
handler,
|
|
438
|
-
Arc::new(container),
|
|
439
|
-
vec![
|
|
440
|
-
"db".to_string(),
|
|
441
|
-
"cache".to_string(),
|
|
442
|
-
"logger".to_string(),
|
|
443
|
-
"config".to_string(),
|
|
444
|
-
],
|
|
445
|
-
);
|
|
446
|
-
|
|
447
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
448
|
-
let request_data = create_request_data();
|
|
449
|
-
let result = di_handler.call(request, request_data).await;
|
|
450
|
-
|
|
451
|
-
assert!(result.is_ok());
|
|
452
|
-
let response = result.unwrap();
|
|
453
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
#[tokio::test]
|
|
457
|
-
async fn test_di_handler_required_dependencies_getter() {
|
|
458
|
-
let container = DependencyContainer::new();
|
|
459
|
-
let handler = Arc::new(TestHandler);
|
|
460
|
-
let deps = vec!["db".to_string(), "cache".to_string(), "logger".to_string()];
|
|
461
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), deps.clone());
|
|
462
|
-
|
|
463
|
-
assert_eq!(di_handler.required_dependencies(), deps.as_slice());
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
#[tokio::test]
|
|
467
|
-
async fn test_di_handler_handler_error_propagation() {
|
|
468
|
-
let mut container = DependencyContainer::new();
|
|
469
|
-
container
|
|
470
|
-
.register(
|
|
471
|
-
"config".to_string(),
|
|
472
|
-
Arc::new(ValueDependency::new("config", "test_value")),
|
|
473
|
-
)
|
|
474
|
-
.unwrap();
|
|
475
|
-
|
|
476
|
-
let handler = Arc::new(ErrorHandler);
|
|
477
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
478
|
-
|
|
479
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
480
|
-
let request_data = create_request_data();
|
|
481
|
-
let result = di_handler.call(request, request_data).await;
|
|
482
|
-
|
|
483
|
-
assert!(result.is_err());
|
|
484
|
-
let (status, msg) = result.unwrap_err();
|
|
485
|
-
assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR);
|
|
486
|
-
assert!(msg.contains("inner handler error"));
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
#[tokio::test]
|
|
490
|
-
async fn test_di_handler_request_data_enrichment() {
|
|
491
|
-
let mut container = DependencyContainer::new();
|
|
492
|
-
container
|
|
493
|
-
.register(
|
|
494
|
-
"service".to_string(),
|
|
495
|
-
Arc::new(ValueDependency::new("service", "my_service")),
|
|
496
|
-
)
|
|
497
|
-
.unwrap();
|
|
498
|
-
|
|
499
|
-
let handler = Arc::new(ReadDependencyHandler);
|
|
500
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["service".to_string()]);
|
|
501
|
-
|
|
502
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
503
|
-
let request_data = create_request_data();
|
|
504
|
-
let result = di_handler.call(request, request_data).await;
|
|
505
|
-
|
|
506
|
-
assert!(result.is_ok());
|
|
507
|
-
let response = result.unwrap();
|
|
508
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
#[tokio::test]
|
|
512
|
-
async fn test_di_handler_missing_dependency_json_structure() {
|
|
513
|
-
let container = DependencyContainer::new();
|
|
514
|
-
let handler = Arc::new(TestHandler);
|
|
515
|
-
let di_handler =
|
|
516
|
-
DependencyInjectingHandler::new(handler, Arc::new(container), vec!["missing_service".to_string()]);
|
|
517
|
-
|
|
518
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
519
|
-
let request_data = create_request_data();
|
|
520
|
-
let result = di_handler.call(request, request_data).await;
|
|
521
|
-
|
|
522
|
-
assert!(result.is_ok());
|
|
523
|
-
let response = result.unwrap();
|
|
524
|
-
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
525
|
-
|
|
526
|
-
let content_type = response.headers().get("Content-Type").and_then(|v| v.to_str().ok());
|
|
527
|
-
assert_eq!(content_type, Some("application/json"));
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
#[tokio::test]
|
|
531
|
-
async fn test_di_handler_partial_dependencies_present() {
|
|
532
|
-
let mut container = DependencyContainer::new();
|
|
533
|
-
container
|
|
534
|
-
.register("db".to_string(), Arc::new(ValueDependency::new("db", "postgresql")))
|
|
535
|
-
.unwrap();
|
|
536
|
-
|
|
537
|
-
let handler = Arc::new(TestHandler);
|
|
538
|
-
let di_handler = DependencyInjectingHandler::new(
|
|
539
|
-
handler,
|
|
540
|
-
Arc::new(container),
|
|
541
|
-
vec!["db".to_string(), "cache".to_string()],
|
|
542
|
-
);
|
|
543
|
-
|
|
544
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
545
|
-
let request_data = create_request_data();
|
|
546
|
-
let result = di_handler.call(request, request_data).await;
|
|
547
|
-
|
|
548
|
-
assert!(result.is_ok());
|
|
549
|
-
let response = result.unwrap();
|
|
550
|
-
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
#[tokio::test]
|
|
554
|
-
async fn test_di_handler_cleanup_executed() {
|
|
555
|
-
let mut container = DependencyContainer::new();
|
|
556
|
-
|
|
557
|
-
container
|
|
558
|
-
.register("config".to_string(), Arc::new(ValueDependency::new("config", "test")))
|
|
559
|
-
.unwrap();
|
|
560
|
-
|
|
561
|
-
let handler = Arc::new(TestHandler);
|
|
562
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
563
|
-
|
|
564
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
565
|
-
let request_data = create_request_data();
|
|
566
|
-
|
|
567
|
-
let result = di_handler.call(request, request_data).await;
|
|
568
|
-
|
|
569
|
-
assert!(result.is_ok());
|
|
570
|
-
let response = result.unwrap();
|
|
571
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
#[tokio::test]
|
|
575
|
-
async fn test_di_handler_dependent_dependencies() {
|
|
576
|
-
let mut container = DependencyContainer::new();
|
|
577
|
-
|
|
578
|
-
container
|
|
579
|
-
.register(
|
|
580
|
-
"config".to_string(),
|
|
581
|
-
Arc::new(ValueDependency::new("config", "base_config")),
|
|
582
|
-
)
|
|
583
|
-
.unwrap();
|
|
584
|
-
|
|
585
|
-
container
|
|
586
|
-
.register(
|
|
587
|
-
"database".to_string(),
|
|
588
|
-
Arc::new(ValueDependency::new("database", "db_from_config")),
|
|
589
|
-
)
|
|
590
|
-
.unwrap();
|
|
591
|
-
|
|
592
|
-
let handler = Arc::new(TestHandler);
|
|
593
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["database".to_string()]);
|
|
594
|
-
|
|
595
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
596
|
-
let request_data = create_request_data();
|
|
597
|
-
let result = di_handler.call(request, request_data).await;
|
|
598
|
-
|
|
599
|
-
assert!(result.is_ok());
|
|
600
|
-
let response = result.unwrap();
|
|
601
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
#[tokio::test]
|
|
605
|
-
async fn test_di_handler_parallel_independent_dependencies() {
|
|
606
|
-
let mut container = DependencyContainer::new();
|
|
607
|
-
|
|
608
|
-
container
|
|
609
|
-
.register(
|
|
610
|
-
"service_a".to_string(),
|
|
611
|
-
Arc::new(ValueDependency::new("service_a", "svc_a")),
|
|
612
|
-
)
|
|
613
|
-
.unwrap();
|
|
614
|
-
container
|
|
615
|
-
.register(
|
|
616
|
-
"service_b".to_string(),
|
|
617
|
-
Arc::new(ValueDependency::new("service_b", "svc_b")),
|
|
618
|
-
)
|
|
619
|
-
.unwrap();
|
|
620
|
-
container
|
|
621
|
-
.register(
|
|
622
|
-
"service_c".to_string(),
|
|
623
|
-
Arc::new(ValueDependency::new("service_c", "svc_c")),
|
|
624
|
-
)
|
|
625
|
-
.unwrap();
|
|
626
|
-
|
|
627
|
-
let handler = Arc::new(TestHandler);
|
|
628
|
-
let di_handler = DependencyInjectingHandler::new(
|
|
629
|
-
handler,
|
|
630
|
-
Arc::new(container),
|
|
631
|
-
vec![
|
|
632
|
-
"service_a".to_string(),
|
|
633
|
-
"service_b".to_string(),
|
|
634
|
-
"service_c".to_string(),
|
|
635
|
-
],
|
|
636
|
-
);
|
|
637
|
-
|
|
638
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
639
|
-
let request_data = create_request_data();
|
|
640
|
-
let result = di_handler.call(request, request_data).await;
|
|
641
|
-
|
|
642
|
-
assert!(result.is_ok());
|
|
643
|
-
let response = result.unwrap();
|
|
644
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
#[tokio::test]
|
|
648
|
-
async fn test_di_handler_request_method_preserved() {
|
|
649
|
-
let mut container = DependencyContainer::new();
|
|
650
|
-
container
|
|
651
|
-
.register("config".to_string(), Arc::new(ValueDependency::new("config", "test")))
|
|
652
|
-
.unwrap();
|
|
653
|
-
|
|
654
|
-
let handler = Arc::new(TestHandler);
|
|
655
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
656
|
-
|
|
657
|
-
let request = Request::builder().method("POST").body(Body::empty()).unwrap();
|
|
658
|
-
let mut request_data = create_request_data();
|
|
659
|
-
request_data.method = "POST".to_string();
|
|
660
|
-
|
|
661
|
-
let result = di_handler.call(request, request_data).await;
|
|
662
|
-
|
|
663
|
-
assert!(result.is_ok());
|
|
664
|
-
let response = result.unwrap();
|
|
665
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
#[tokio::test]
|
|
669
|
-
async fn test_di_handler_complex_scenario_multiple_deps_with_error() {
|
|
670
|
-
let mut container = DependencyContainer::new();
|
|
671
|
-
|
|
672
|
-
for i in 1..=5 {
|
|
673
|
-
container
|
|
674
|
-
.register(
|
|
675
|
-
format!("service_{}", i),
|
|
676
|
-
Arc::new(ValueDependency::new(&format!("service_{}", i), format!("svc_{}", i))),
|
|
677
|
-
)
|
|
678
|
-
.unwrap();
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
let handler = Arc::new(ErrorHandler);
|
|
682
|
-
let di_handler = DependencyInjectingHandler::new(
|
|
683
|
-
handler,
|
|
684
|
-
Arc::new(container),
|
|
685
|
-
vec![
|
|
686
|
-
"service_1".to_string(),
|
|
687
|
-
"service_2".to_string(),
|
|
688
|
-
"service_3".to_string(),
|
|
689
|
-
"service_4".to_string(),
|
|
690
|
-
"service_5".to_string(),
|
|
691
|
-
],
|
|
692
|
-
);
|
|
693
|
-
|
|
694
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
695
|
-
let request_data = create_request_data();
|
|
696
|
-
let result = di_handler.call(request, request_data).await;
|
|
697
|
-
|
|
698
|
-
assert!(result.is_err());
|
|
699
|
-
let (status, _msg) = result.unwrap_err();
|
|
700
|
-
assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR);
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
#[tokio::test]
|
|
704
|
-
async fn test_di_handler_empty_request_body_with_deps() {
|
|
705
|
-
let mut container = DependencyContainer::new();
|
|
706
|
-
container
|
|
707
|
-
.register("config".to_string(), Arc::new(ValueDependency::new("config", "test")))
|
|
708
|
-
.unwrap();
|
|
709
|
-
|
|
710
|
-
let handler = Arc::new(TestHandler);
|
|
711
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
712
|
-
|
|
713
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
714
|
-
let request_data = create_request_data();
|
|
715
|
-
|
|
716
|
-
let result = di_handler.call(request, request_data).await;
|
|
717
|
-
|
|
718
|
-
assert!(result.is_ok());
|
|
719
|
-
let response = result.unwrap();
|
|
720
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
#[tokio::test]
|
|
724
|
-
async fn test_di_handler_shared_container_across_handlers() {
|
|
725
|
-
let mut container = DependencyContainer::new();
|
|
726
|
-
container
|
|
727
|
-
.register(
|
|
728
|
-
"shared_config".to_string(),
|
|
729
|
-
Arc::new(ValueDependency::new("shared_config", "shared_value")),
|
|
730
|
-
)
|
|
731
|
-
.unwrap();
|
|
732
|
-
|
|
733
|
-
let shared_container = Arc::new(container);
|
|
734
|
-
|
|
735
|
-
let handler1 = Arc::new(TestHandler);
|
|
736
|
-
let di_handler1 = DependencyInjectingHandler::new(
|
|
737
|
-
handler1,
|
|
738
|
-
Arc::clone(&shared_container),
|
|
739
|
-
vec!["shared_config".to_string()],
|
|
740
|
-
);
|
|
741
|
-
|
|
742
|
-
let handler2 = Arc::new(TestHandler);
|
|
743
|
-
let di_handler2 = DependencyInjectingHandler::new(
|
|
744
|
-
handler2,
|
|
745
|
-
Arc::clone(&shared_container),
|
|
746
|
-
vec!["shared_config".to_string()],
|
|
747
|
-
);
|
|
748
|
-
|
|
749
|
-
let request1 = Request::builder().body(Body::empty()).unwrap();
|
|
750
|
-
let request_data1 = create_request_data();
|
|
751
|
-
let result1 = di_handler1.call(request1, request_data1).await;
|
|
752
|
-
|
|
753
|
-
let request2 = Request::builder().body(Body::empty()).unwrap();
|
|
754
|
-
let request_data2 = create_request_data();
|
|
755
|
-
let result2 = di_handler2.call(request2, request_data2).await;
|
|
756
|
-
|
|
757
|
-
assert!(result1.is_ok());
|
|
758
|
-
assert!(result2.is_ok());
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
#[tokio::test]
|
|
762
|
-
async fn test_concurrent_requests_same_handler_no_race() {
|
|
763
|
-
let mut container = DependencyContainer::new();
|
|
764
|
-
container
|
|
765
|
-
.register(
|
|
766
|
-
"config".to_string(),
|
|
767
|
-
Arc::new(ValueDependency::new("config", "test_config")),
|
|
768
|
-
)
|
|
769
|
-
.unwrap();
|
|
770
|
-
|
|
771
|
-
let shared_container = Arc::new(container);
|
|
772
|
-
let handler = Arc::new(TestHandler);
|
|
773
|
-
let di_handler = Arc::new(DependencyInjectingHandler::new(
|
|
774
|
-
handler,
|
|
775
|
-
shared_container,
|
|
776
|
-
vec!["config".to_string()],
|
|
777
|
-
));
|
|
778
|
-
|
|
779
|
-
let handles: Vec<_> = (0..10)
|
|
780
|
-
.map(|_| {
|
|
781
|
-
let di_handler = Arc::clone(&di_handler);
|
|
782
|
-
tokio::spawn(async move {
|
|
783
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
784
|
-
let request_data = create_request_data();
|
|
785
|
-
di_handler.call(request, request_data).await
|
|
786
|
-
})
|
|
787
|
-
})
|
|
788
|
-
.collect();
|
|
789
|
-
|
|
790
|
-
for handle in handles {
|
|
791
|
-
let result = handle.await.unwrap();
|
|
792
|
-
assert!(result.is_ok());
|
|
793
|
-
let response = result.unwrap();
|
|
794
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
#[tokio::test]
|
|
799
|
-
async fn test_concurrent_different_handlers_shared_container() {
|
|
800
|
-
let mut container = DependencyContainer::new();
|
|
801
|
-
container
|
|
802
|
-
.register("db".to_string(), Arc::new(ValueDependency::new("db", "postgres")))
|
|
803
|
-
.unwrap();
|
|
804
|
-
container
|
|
805
|
-
.register("cache".to_string(), Arc::new(ValueDependency::new("cache", "redis")))
|
|
806
|
-
.unwrap();
|
|
807
|
-
|
|
808
|
-
let shared_container = Arc::new(container);
|
|
809
|
-
|
|
810
|
-
let mut handles = vec![];
|
|
811
|
-
for i in 0..5 {
|
|
812
|
-
let container = Arc::clone(&shared_container);
|
|
813
|
-
let handler = Arc::new(TestHandler);
|
|
814
|
-
let di_handler = DependencyInjectingHandler::new(
|
|
815
|
-
handler,
|
|
816
|
-
container,
|
|
817
|
-
if i % 2 == 0 {
|
|
818
|
-
vec!["db".to_string()]
|
|
819
|
-
} else {
|
|
820
|
-
vec!["cache".to_string()]
|
|
821
|
-
},
|
|
822
|
-
);
|
|
823
|
-
|
|
824
|
-
let handle = tokio::spawn(async move {
|
|
825
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
826
|
-
let request_data = create_request_data();
|
|
827
|
-
di_handler.call(request, request_data).await
|
|
828
|
-
});
|
|
829
|
-
handles.push(handle);
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
for handle in handles {
|
|
833
|
-
let result = handle.await.unwrap();
|
|
834
|
-
assert!(result.is_ok());
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
#[tokio::test]
|
|
839
|
-
async fn test_missing_dependency_multiple_concurrent_requests() {
|
|
840
|
-
let container = DependencyContainer::new();
|
|
841
|
-
let shared_container = Arc::new(container);
|
|
842
|
-
let handler = Arc::new(TestHandler);
|
|
843
|
-
let di_handler = Arc::new(DependencyInjectingHandler::new(
|
|
844
|
-
handler,
|
|
845
|
-
shared_container,
|
|
846
|
-
vec!["nonexistent".to_string()],
|
|
847
|
-
));
|
|
848
|
-
|
|
849
|
-
let handles: Vec<_> = (0..5)
|
|
850
|
-
.map(|_| {
|
|
851
|
-
let di_handler = Arc::clone(&di_handler);
|
|
852
|
-
tokio::spawn(async move {
|
|
853
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
854
|
-
let request_data = create_request_data();
|
|
855
|
-
di_handler.call(request, request_data).await
|
|
856
|
-
})
|
|
857
|
-
})
|
|
858
|
-
.collect();
|
|
859
|
-
|
|
860
|
-
for handle in handles {
|
|
861
|
-
let result = handle.await.unwrap();
|
|
862
|
-
assert!(result.is_ok());
|
|
863
|
-
let response = result.unwrap();
|
|
864
|
-
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
#[tokio::test]
|
|
869
|
-
async fn test_large_dependency_tree_resolution() {
|
|
870
|
-
let mut container = DependencyContainer::new();
|
|
871
|
-
for i in 0..20 {
|
|
872
|
-
container
|
|
873
|
-
.register(
|
|
874
|
-
format!("dep_{}", i),
|
|
875
|
-
Arc::new(ValueDependency::new(&format!("dep_{}", i), format!("value_{}", i))),
|
|
876
|
-
)
|
|
877
|
-
.unwrap();
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
let handler = Arc::new(TestHandler);
|
|
881
|
-
let mut required = vec![];
|
|
882
|
-
for i in 0..20 {
|
|
883
|
-
required.push(format!("dep_{}", i));
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), required);
|
|
887
|
-
|
|
888
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
889
|
-
let request_data = create_request_data();
|
|
890
|
-
let result = di_handler.call(request, request_data).await;
|
|
891
|
-
|
|
892
|
-
assert!(result.is_ok());
|
|
893
|
-
let response = result.unwrap();
|
|
894
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
#[tokio::test]
|
|
898
|
-
async fn test_handler_error_does_not_prevent_cleanup() {
|
|
899
|
-
let mut container = DependencyContainer::new();
|
|
900
|
-
container
|
|
901
|
-
.register("config".to_string(), Arc::new(ValueDependency::new("config", "test")))
|
|
902
|
-
.unwrap();
|
|
903
|
-
|
|
904
|
-
let handler = Arc::new(ErrorHandler);
|
|
905
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
906
|
-
|
|
907
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
908
|
-
let request_data = create_request_data();
|
|
909
|
-
let result = di_handler.call(request, request_data).await;
|
|
910
|
-
|
|
911
|
-
assert!(result.is_err());
|
|
912
|
-
let (status, msg) = result.unwrap_err();
|
|
913
|
-
assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR);
|
|
914
|
-
assert!(msg.contains("inner handler error"));
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
#[tokio::test]
|
|
918
|
-
async fn test_partial_dependency_resolution_failure() {
|
|
919
|
-
let mut container = DependencyContainer::new();
|
|
920
|
-
container
|
|
921
|
-
.register(
|
|
922
|
-
"service_a".to_string(),
|
|
923
|
-
Arc::new(ValueDependency::new("service_a", "svc_a")),
|
|
924
|
-
)
|
|
925
|
-
.unwrap();
|
|
926
|
-
|
|
927
|
-
let handler = Arc::new(TestHandler);
|
|
928
|
-
let di_handler = DependencyInjectingHandler::new(
|
|
929
|
-
handler,
|
|
930
|
-
Arc::new(container),
|
|
931
|
-
vec!["service_a".to_string(), "service_b".to_string()],
|
|
932
|
-
);
|
|
933
|
-
|
|
934
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
935
|
-
let request_data = create_request_data();
|
|
936
|
-
let result = di_handler.call(request, request_data).await;
|
|
937
|
-
|
|
938
|
-
assert!(result.is_ok());
|
|
939
|
-
let response = result.unwrap();
|
|
940
|
-
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
#[tokio::test]
|
|
944
|
-
async fn test_circular_dependency_detection() {
|
|
945
|
-
let mut container = DependencyContainer::new();
|
|
946
|
-
container
|
|
947
|
-
.register(
|
|
948
|
-
"service_a".to_string(),
|
|
949
|
-
Arc::new(ValueDependency::new("service_a", "svc_a")),
|
|
950
|
-
)
|
|
951
|
-
.unwrap();
|
|
952
|
-
|
|
953
|
-
let handler = Arc::new(TestHandler);
|
|
954
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["service_a".to_string()]);
|
|
955
|
-
|
|
956
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
957
|
-
let request_data = create_request_data();
|
|
958
|
-
let result = di_handler.call(request, request_data).await;
|
|
959
|
-
|
|
960
|
-
assert!(result.is_ok());
|
|
961
|
-
let response = result.unwrap();
|
|
962
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
#[tokio::test]
|
|
966
|
-
async fn test_empty_required_dependencies_with_multiple_registered() {
|
|
967
|
-
let mut container = DependencyContainer::new();
|
|
968
|
-
for i in 0..5 {
|
|
969
|
-
container
|
|
970
|
-
.register(
|
|
971
|
-
format!("unused_{}", i),
|
|
972
|
-
Arc::new(ValueDependency::new(&format!("unused_{}", i), format!("val_{}", i))),
|
|
973
|
-
)
|
|
974
|
-
.unwrap();
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
let handler = Arc::new(TestHandler);
|
|
978
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec![]);
|
|
979
|
-
|
|
980
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
981
|
-
let request_data = create_request_data();
|
|
982
|
-
let result = di_handler.call(request, request_data).await;
|
|
983
|
-
|
|
984
|
-
assert!(result.is_ok());
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
#[tokio::test]
|
|
988
|
-
async fn test_concurrent_resolution_with_varying_dependency_counts() {
|
|
989
|
-
let mut container = DependencyContainer::new();
|
|
990
|
-
for i in 0..10 {
|
|
991
|
-
container
|
|
992
|
-
.register(
|
|
993
|
-
format!("svc_{}", i),
|
|
994
|
-
Arc::new(ValueDependency::new(&format!("svc_{}", i), format!("s_{}", i))),
|
|
995
|
-
)
|
|
996
|
-
.unwrap();
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
let shared_container = Arc::new(container);
|
|
1000
|
-
|
|
1001
|
-
let mut handles = vec![];
|
|
1002
|
-
for i in 0..10 {
|
|
1003
|
-
let container = Arc::clone(&shared_container);
|
|
1004
|
-
let handler = Arc::new(TestHandler);
|
|
1005
|
-
|
|
1006
|
-
let required: Vec<String> = (0..=(i % 5)).map(|j| format!("svc_{}", j)).collect();
|
|
1007
|
-
|
|
1008
|
-
let di_handler = DependencyInjectingHandler::new(handler, container, required);
|
|
1009
|
-
|
|
1010
|
-
let handle = tokio::spawn(async move {
|
|
1011
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1012
|
-
let request_data = create_request_data();
|
|
1013
|
-
di_handler.call(request, request_data).await
|
|
1014
|
-
});
|
|
1015
|
-
handles.push(handle);
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
for handle in handles {
|
|
1019
|
-
let result = handle.await.unwrap();
|
|
1020
|
-
assert!(result.is_ok());
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
#[tokio::test]
|
|
1025
|
-
async fn test_request_data_isolation_across_concurrent_requests() {
|
|
1026
|
-
let mut container = DependencyContainer::new();
|
|
1027
|
-
container
|
|
1028
|
-
.register("config".to_string(), Arc::new(ValueDependency::new("config", "test")))
|
|
1029
|
-
.unwrap();
|
|
1030
|
-
|
|
1031
|
-
let shared_container = Arc::new(container);
|
|
1032
|
-
let handler = Arc::new(TestHandler);
|
|
1033
|
-
let di_handler = Arc::new(DependencyInjectingHandler::new(
|
|
1034
|
-
handler,
|
|
1035
|
-
shared_container,
|
|
1036
|
-
vec!["config".to_string()],
|
|
1037
|
-
));
|
|
1038
|
-
|
|
1039
|
-
let mut handles = vec![];
|
|
1040
|
-
for i in 0..10 {
|
|
1041
|
-
let di_handler = Arc::clone(&di_handler);
|
|
1042
|
-
let handle = tokio::spawn(async move {
|
|
1043
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1044
|
-
let mut request_data = create_request_data();
|
|
1045
|
-
request_data.path = format!("/path/{}", i);
|
|
1046
|
-
di_handler.call(request, request_data).await
|
|
1047
|
-
});
|
|
1048
|
-
handles.push(handle);
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
for handle in handles {
|
|
1052
|
-
let result = handle.await.unwrap();
|
|
1053
|
-
assert!(result.is_ok());
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
#[tokio::test]
|
|
1058
|
-
async fn test_missing_dependency_error_json_format() {
|
|
1059
|
-
let container = DependencyContainer::new();
|
|
1060
|
-
let handler = Arc::new(TestHandler);
|
|
1061
|
-
let di_handler =
|
|
1062
|
-
DependencyInjectingHandler::new(handler, Arc::new(container), vec!["missing_service".to_string()]);
|
|
1063
|
-
|
|
1064
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1065
|
-
let request_data = create_request_data();
|
|
1066
|
-
let result = di_handler.call(request, request_data).await;
|
|
1067
|
-
|
|
1068
|
-
assert!(result.is_ok());
|
|
1069
|
-
let response = result.unwrap();
|
|
1070
|
-
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
1071
|
-
assert_eq!(
|
|
1072
|
-
response.headers().get("Content-Type").and_then(|v| v.to_str().ok()),
|
|
1073
|
-
Some("application/json")
|
|
1074
|
-
);
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
#[tokio::test]
|
|
1078
|
-
async fn test_many_sequential_requests_same_handler_state() {
|
|
1079
|
-
let mut container = DependencyContainer::new();
|
|
1080
|
-
container
|
|
1081
|
-
.register("state".to_string(), Arc::new(ValueDependency::new("state", "initial")))
|
|
1082
|
-
.unwrap();
|
|
1083
|
-
|
|
1084
|
-
let handler = Arc::new(TestHandler);
|
|
1085
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["state".to_string()]);
|
|
1086
|
-
|
|
1087
|
-
for _ in 0..50 {
|
|
1088
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1089
|
-
let request_data = create_request_data();
|
|
1090
|
-
let result = di_handler.call(request, request_data).await;
|
|
1091
|
-
|
|
1092
|
-
assert!(result.is_ok());
|
|
1093
|
-
let response = result.unwrap();
|
|
1094
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
#[tokio::test]
|
|
1099
|
-
async fn test_dependency_availability_after_resolution() {
|
|
1100
|
-
let mut container = DependencyContainer::new();
|
|
1101
|
-
container
|
|
1102
|
-
.register(
|
|
1103
|
-
"service".to_string(),
|
|
1104
|
-
Arc::new(ValueDependency::new("service", "my_service")),
|
|
1105
|
-
)
|
|
1106
|
-
.unwrap();
|
|
1107
|
-
|
|
1108
|
-
let handler = Arc::new(ReadDependencyHandler);
|
|
1109
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["service".to_string()]);
|
|
1110
|
-
|
|
1111
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1112
|
-
let request_data = create_request_data();
|
|
1113
|
-
let result = di_handler.call(request, request_data).await;
|
|
1114
|
-
|
|
1115
|
-
assert!(result.is_ok());
|
|
1116
|
-
let response = result.unwrap();
|
|
1117
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
#[tokio::test]
|
|
1121
|
-
async fn test_container_keys_availability_during_resolution() {
|
|
1122
|
-
let mut container = DependencyContainer::new();
|
|
1123
|
-
container
|
|
1124
|
-
.register("key1".to_string(), Arc::new(ValueDependency::new("key1", "val1")))
|
|
1125
|
-
.unwrap();
|
|
1126
|
-
container
|
|
1127
|
-
.register("key2".to_string(), Arc::new(ValueDependency::new("key2", "val2")))
|
|
1128
|
-
.unwrap();
|
|
1129
|
-
|
|
1130
|
-
let handler = Arc::new(TestHandler);
|
|
1131
|
-
let di_handler = DependencyInjectingHandler::new(
|
|
1132
|
-
handler,
|
|
1133
|
-
Arc::new(container),
|
|
1134
|
-
vec!["key1".to_string(), "key2".to_string()],
|
|
1135
|
-
);
|
|
1136
|
-
|
|
1137
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1138
|
-
let request_data = create_request_data();
|
|
1139
|
-
let result = di_handler.call(request, request_data).await;
|
|
1140
|
-
|
|
1141
|
-
assert!(result.is_ok());
|
|
1142
|
-
let response = result.unwrap();
|
|
1143
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
#[tokio::test]
|
|
1147
|
-
async fn test_post_request_with_dependencies() {
|
|
1148
|
-
let mut container = DependencyContainer::new();
|
|
1149
|
-
container
|
|
1150
|
-
.register(
|
|
1151
|
-
"validator".to_string(),
|
|
1152
|
-
Arc::new(ValueDependency::new("validator", "strict_mode")),
|
|
1153
|
-
)
|
|
1154
|
-
.unwrap();
|
|
1155
|
-
|
|
1156
|
-
let handler = Arc::new(TestHandler);
|
|
1157
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["validator".to_string()]);
|
|
1158
|
-
|
|
1159
|
-
let request = Request::builder()
|
|
1160
|
-
.method("POST")
|
|
1161
|
-
.header("Content-Type", "application/json")
|
|
1162
|
-
.body(Body::from(r#"{"key":"value"}"#))
|
|
1163
|
-
.unwrap();
|
|
1164
|
-
let mut request_data = create_request_data();
|
|
1165
|
-
request_data.method = "POST".to_string();
|
|
1166
|
-
request_data.body = serde_json::json!({"key": "value"});
|
|
1167
|
-
|
|
1168
|
-
let result = di_handler.call(request, request_data).await;
|
|
1169
|
-
|
|
1170
|
-
assert!(result.is_ok());
|
|
1171
|
-
let response = result.unwrap();
|
|
1172
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
#[tokio::test]
|
|
1176
|
-
async fn test_delete_request_with_authorization_dependency() {
|
|
1177
|
-
let mut container = DependencyContainer::new();
|
|
1178
|
-
container
|
|
1179
|
-
.register(
|
|
1180
|
-
"auth".to_string(),
|
|
1181
|
-
Arc::new(ValueDependency::new("auth", "bearer_token")),
|
|
1182
|
-
)
|
|
1183
|
-
.unwrap();
|
|
1184
|
-
|
|
1185
|
-
let handler = Arc::new(TestHandler);
|
|
1186
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["auth".to_string()]);
|
|
1187
|
-
|
|
1188
|
-
let request = Request::builder().method("DELETE").body(Body::empty()).unwrap();
|
|
1189
|
-
let mut request_data = create_request_data();
|
|
1190
|
-
request_data.method = "DELETE".to_string();
|
|
1191
|
-
request_data.path = "/resource/123".to_string();
|
|
1192
|
-
|
|
1193
|
-
let result = di_handler.call(request, request_data).await;
|
|
1194
|
-
|
|
1195
|
-
assert!(result.is_ok());
|
|
1196
|
-
let response = result.unwrap();
|
|
1197
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
#[tokio::test]
|
|
1201
|
-
async fn test_very_large_number_of_dependencies_in_single_handler() {
|
|
1202
|
-
let mut container = DependencyContainer::new();
|
|
1203
|
-
let mut required_deps = vec![];
|
|
1204
|
-
for i in 0..50 {
|
|
1205
|
-
let key = format!("dep_{}", i);
|
|
1206
|
-
container
|
|
1207
|
-
.register(
|
|
1208
|
-
key.clone(),
|
|
1209
|
-
Arc::new(ValueDependency::new(&key, format!("value_{}", i))),
|
|
1210
|
-
)
|
|
1211
|
-
.unwrap();
|
|
1212
|
-
required_deps.push(key);
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
let handler = Arc::new(TestHandler);
|
|
1216
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), required_deps);
|
|
1217
|
-
|
|
1218
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1219
|
-
let request_data = create_request_data();
|
|
1220
|
-
let result = di_handler.call(request, request_data).await;
|
|
1221
|
-
|
|
1222
|
-
assert!(result.is_ok());
|
|
1223
|
-
let response = result.unwrap();
|
|
1224
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
#[tokio::test]
|
|
1228
|
-
async fn test_handler_cloning_with_same_container() {
|
|
1229
|
-
let mut container = DependencyContainer::new();
|
|
1230
|
-
container
|
|
1231
|
-
.register("svc".to_string(), Arc::new(ValueDependency::new("svc", "service")))
|
|
1232
|
-
.unwrap();
|
|
1233
|
-
|
|
1234
|
-
let shared_container = Arc::new(container);
|
|
1235
|
-
let base_handler: Arc<dyn Handler> = Arc::new(TestHandler);
|
|
1236
|
-
|
|
1237
|
-
let di_handler1 = Arc::new(DependencyInjectingHandler::new(
|
|
1238
|
-
base_handler.clone(),
|
|
1239
|
-
Arc::clone(&shared_container),
|
|
1240
|
-
vec!["svc".to_string()],
|
|
1241
|
-
));
|
|
1242
|
-
|
|
1243
|
-
let di_handler2 = Arc::new(DependencyInjectingHandler::new(
|
|
1244
|
-
base_handler.clone(),
|
|
1245
|
-
Arc::clone(&shared_container),
|
|
1246
|
-
vec!["svc".to_string()],
|
|
1247
|
-
));
|
|
1248
|
-
|
|
1249
|
-
let handle1 = tokio::spawn({
|
|
1250
|
-
let dih = Arc::clone(&di_handler1);
|
|
1251
|
-
async move {
|
|
1252
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1253
|
-
let request_data = create_request_data();
|
|
1254
|
-
dih.call(request, request_data).await
|
|
1255
|
-
}
|
|
1256
|
-
});
|
|
1257
|
-
|
|
1258
|
-
let handle2 = tokio::spawn({
|
|
1259
|
-
let dih = Arc::clone(&di_handler2);
|
|
1260
|
-
async move {
|
|
1261
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1262
|
-
let request_data = create_request_data();
|
|
1263
|
-
dih.call(request, request_data).await
|
|
1264
|
-
}
|
|
1265
|
-
});
|
|
1266
|
-
|
|
1267
|
-
assert!(handle1.await.unwrap().is_ok());
|
|
1268
|
-
assert!(handle2.await.unwrap().is_ok());
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
#[tokio::test]
|
|
1272
|
-
async fn test_request_parts_reconstruction_correctness() {
|
|
1273
|
-
let mut container = DependencyContainer::new();
|
|
1274
|
-
container
|
|
1275
|
-
.register("config".to_string(), Arc::new(ValueDependency::new("config", "test")))
|
|
1276
|
-
.unwrap();
|
|
1277
|
-
|
|
1278
|
-
let handler = Arc::new(TestHandler);
|
|
1279
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
1280
|
-
|
|
1281
|
-
let request = Request::builder()
|
|
1282
|
-
.method("GET")
|
|
1283
|
-
.header("User-Agent", "test-client")
|
|
1284
|
-
.header("Accept", "application/json")
|
|
1285
|
-
.body(Body::empty())
|
|
1286
|
-
.unwrap();
|
|
1287
|
-
let mut request_data = create_request_data();
|
|
1288
|
-
request_data.method = "GET".to_string();
|
|
1289
|
-
|
|
1290
|
-
let result = di_handler.call(request, request_data).await;
|
|
1291
|
-
|
|
1292
|
-
assert!(result.is_ok());
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
#[tokio::test]
|
|
1296
|
-
async fn test_resolution_failure_returns_service_unavailable() {
|
|
1297
|
-
let mut container = DependencyContainer::new();
|
|
1298
|
-
container
|
|
1299
|
-
.register(
|
|
1300
|
-
"external_api".to_string(),
|
|
1301
|
-
Arc::new(ValueDependency::new("external_api", "unavailable")),
|
|
1302
|
-
)
|
|
1303
|
-
.unwrap();
|
|
1304
|
-
|
|
1305
|
-
let handler = Arc::new(TestHandler);
|
|
1306
|
-
let di_handler =
|
|
1307
|
-
DependencyInjectingHandler::new(handler, Arc::new(container), vec!["external_api".to_string()]);
|
|
1308
|
-
|
|
1309
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1310
|
-
let request_data = create_request_data();
|
|
1311
|
-
let result = di_handler.call(request, request_data).await;
|
|
1312
|
-
|
|
1313
|
-
assert!(result.is_ok());
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
|
-
#[tokio::test]
|
|
1317
|
-
async fn test_multiple_missing_dependencies_reports_first() {
|
|
1318
|
-
let container = DependencyContainer::new();
|
|
1319
|
-
let handler = Arc::new(TestHandler);
|
|
1320
|
-
let di_handler = DependencyInjectingHandler::new(
|
|
1321
|
-
handler,
|
|
1322
|
-
Arc::new(container),
|
|
1323
|
-
vec![
|
|
1324
|
-
"missing_a".to_string(),
|
|
1325
|
-
"missing_b".to_string(),
|
|
1326
|
-
"missing_c".to_string(),
|
|
1327
|
-
],
|
|
1328
|
-
);
|
|
1329
|
-
|
|
1330
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1331
|
-
let request_data = create_request_data();
|
|
1332
|
-
let result = di_handler.call(request, request_data).await;
|
|
1333
|
-
|
|
1334
|
-
assert!(result.is_ok());
|
|
1335
|
-
let response = result.unwrap();
|
|
1336
|
-
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
#[tokio::test]
|
|
1340
|
-
async fn test_required_dependencies_getter_consistency() {
|
|
1341
|
-
let deps = vec![
|
|
1342
|
-
"dep_a".to_string(),
|
|
1343
|
-
"dep_b".to_string(),
|
|
1344
|
-
"dep_c".to_string(),
|
|
1345
|
-
"dep_d".to_string(),
|
|
1346
|
-
];
|
|
1347
|
-
let container = DependencyContainer::new();
|
|
1348
|
-
let handler = Arc::new(TestHandler);
|
|
1349
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), deps.clone());
|
|
1350
|
-
|
|
1351
|
-
let returned_deps = di_handler.required_dependencies();
|
|
1352
|
-
assert_eq!(returned_deps.len(), 4);
|
|
1353
|
-
assert_eq!(returned_deps, deps.as_slice());
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
|
-
#[tokio::test]
|
|
1357
|
-
async fn test_concurrent_error_handlers_isolation() {
|
|
1358
|
-
let container = DependencyContainer::new();
|
|
1359
|
-
let handler = Arc::new(ErrorHandler);
|
|
1360
|
-
let di_handler = Arc::new(DependencyInjectingHandler::new(handler, Arc::new(container), vec![]));
|
|
1361
|
-
|
|
1362
|
-
let handles: Vec<_> = (0..10)
|
|
1363
|
-
.map(|_| {
|
|
1364
|
-
let dih = Arc::clone(&di_handler);
|
|
1365
|
-
tokio::spawn(async move {
|
|
1366
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1367
|
-
let request_data = create_request_data();
|
|
1368
|
-
dih.call(request, request_data).await
|
|
1369
|
-
})
|
|
1370
|
-
})
|
|
1371
|
-
.collect();
|
|
1372
|
-
|
|
1373
|
-
for handle in handles {
|
|
1374
|
-
let result = handle.await.unwrap();
|
|
1375
|
-
assert!(result.is_err());
|
|
1376
|
-
let (status, msg) = result.unwrap_err();
|
|
1377
|
-
assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR);
|
|
1378
|
-
assert!(msg.contains("inner handler error"));
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
#[tokio::test]
|
|
1383
|
-
async fn test_patch_request_with_dependencies() {
|
|
1384
|
-
let mut container = DependencyContainer::new();
|
|
1385
|
-
container
|
|
1386
|
-
.register(
|
|
1387
|
-
"merger".to_string(),
|
|
1388
|
-
Arc::new(ValueDependency::new("merger", "strategic_merge")),
|
|
1389
|
-
)
|
|
1390
|
-
.unwrap();
|
|
1391
|
-
|
|
1392
|
-
let handler = Arc::new(TestHandler);
|
|
1393
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["merger".to_string()]);
|
|
1394
|
-
|
|
1395
|
-
let request = Request::builder().method("PATCH").body(Body::empty()).unwrap();
|
|
1396
|
-
let mut request_data = create_request_data();
|
|
1397
|
-
request_data.method = "PATCH".to_string();
|
|
1398
|
-
|
|
1399
|
-
let result = di_handler.call(request, request_data).await;
|
|
1400
|
-
|
|
1401
|
-
assert!(result.is_ok());
|
|
1402
|
-
let response = result.unwrap();
|
|
1403
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
#[tokio::test]
|
|
1407
|
-
async fn test_handler_receives_enriched_request_data_with_multiple_deps() {
|
|
1408
|
-
let mut container = DependencyContainer::new();
|
|
1409
|
-
for i in 0..5 {
|
|
1410
|
-
container
|
|
1411
|
-
.register(
|
|
1412
|
-
format!("svc_{}", i),
|
|
1413
|
-
Arc::new(ValueDependency::new(&format!("svc_{}", i), format!("s_{}", i))),
|
|
1414
|
-
)
|
|
1415
|
-
.unwrap();
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
let handler = Arc::new(ReadDependencyHandler);
|
|
1419
|
-
let required: Vec<String> = (0..5).map(|i| format!("svc_{}", i)).collect();
|
|
1420
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), required);
|
|
1421
|
-
|
|
1422
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1423
|
-
let request_data = create_request_data();
|
|
1424
|
-
let result = di_handler.call(request, request_data).await;
|
|
1425
|
-
|
|
1426
|
-
assert!(result.is_ok());
|
|
1427
|
-
let response = result.unwrap();
|
|
1428
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
#[tokio::test]
|
|
1432
|
-
async fn test_arc_try_unwrap_cleanup_branch() {
|
|
1433
|
-
let mut container = DependencyContainer::new();
|
|
1434
|
-
container
|
|
1435
|
-
.register(
|
|
1436
|
-
"resource".to_string(),
|
|
1437
|
-
Arc::new(ValueDependency::new("resource", "allocated")),
|
|
1438
|
-
)
|
|
1439
|
-
.unwrap();
|
|
1440
|
-
|
|
1441
|
-
let handler = Arc::new(TestHandler);
|
|
1442
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["resource".to_string()]);
|
|
1443
|
-
|
|
1444
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1445
|
-
let request_data = create_request_data();
|
|
1446
|
-
let result = di_handler.call(request, request_data).await;
|
|
1447
|
-
|
|
1448
|
-
assert!(result.is_ok());
|
|
1449
|
-
let response = result.unwrap();
|
|
1450
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
1451
|
-
}
|
|
1452
|
-
|
|
1453
|
-
#[tokio::test]
|
|
1454
|
-
async fn test_head_request_with_dependencies() {
|
|
1455
|
-
let mut container = DependencyContainer::new();
|
|
1456
|
-
container
|
|
1457
|
-
.register(
|
|
1458
|
-
"metadata".to_string(),
|
|
1459
|
-
Arc::new(ValueDependency::new("metadata", "headers_only")),
|
|
1460
|
-
)
|
|
1461
|
-
.unwrap();
|
|
1462
|
-
|
|
1463
|
-
let handler = Arc::new(TestHandler);
|
|
1464
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["metadata".to_string()]);
|
|
1465
|
-
|
|
1466
|
-
let request = Request::builder().method("HEAD").body(Body::empty()).unwrap();
|
|
1467
|
-
let mut request_data = create_request_data();
|
|
1468
|
-
request_data.method = "HEAD".to_string();
|
|
1469
|
-
|
|
1470
|
-
let result = di_handler.call(request, request_data).await;
|
|
1471
|
-
|
|
1472
|
-
assert!(result.is_ok());
|
|
1473
|
-
let response = result.unwrap();
|
|
1474
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
#[tokio::test]
|
|
1478
|
-
async fn test_options_request_with_dependencies() {
|
|
1479
|
-
let mut container = DependencyContainer::new();
|
|
1480
|
-
container
|
|
1481
|
-
.register("cors".to_string(), Arc::new(ValueDependency::new("cors", "permissive")))
|
|
1482
|
-
.unwrap();
|
|
1483
|
-
|
|
1484
|
-
let handler = Arc::new(TestHandler);
|
|
1485
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["cors".to_string()]);
|
|
1486
|
-
|
|
1487
|
-
let request = Request::builder().method("OPTIONS").body(Body::empty()).unwrap();
|
|
1488
|
-
let mut request_data = create_request_data();
|
|
1489
|
-
request_data.method = "OPTIONS".to_string();
|
|
1490
|
-
|
|
1491
|
-
let result = di_handler.call(request, request_data).await;
|
|
1492
|
-
|
|
1493
|
-
assert!(result.is_ok());
|
|
1494
|
-
let response = result.unwrap();
|
|
1495
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
#[tokio::test]
|
|
1499
|
-
async fn test_circular_dependency_error_json_structure() {
|
|
1500
|
-
let container = DependencyContainer::new();
|
|
1501
|
-
let handler = Arc::new(TestHandler);
|
|
1502
|
-
|
|
1503
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["missing".to_string()]);
|
|
1504
|
-
|
|
1505
|
-
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1506
|
-
let request_data = create_request_data();
|
|
1507
|
-
let result = di_handler.call(request, request_data).await;
|
|
1508
|
-
|
|
1509
|
-
assert!(result.is_ok());
|
|
1510
|
-
let response = result.unwrap();
|
|
1511
|
-
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
1512
|
-
|
|
1513
|
-
let content_type = response.headers().get("Content-Type").and_then(|v| v.to_str().ok());
|
|
1514
|
-
assert_eq!(content_type, Some("application/json"));
|
|
1515
|
-
|
|
1516
|
-
let body_bytes = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap();
|
|
1517
|
-
let json_body: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap();
|
|
1518
|
-
|
|
1519
|
-
assert!(json_body.get("type").is_some(), "type field must be present");
|
|
1520
|
-
assert!(json_body.get("title").is_some(), "title field must be present");
|
|
1521
|
-
assert!(json_body.get("detail").is_some(), "detail field must be present");
|
|
1522
|
-
assert!(json_body.get("status").is_some(), "status field must be present");
|
|
1523
|
-
|
|
1524
|
-
assert_eq!(json_body.get("status").and_then(|v| v.as_i64()), Some(500));
|
|
1525
|
-
assert_eq!(
|
|
1526
|
-
json_body.get("type").and_then(|v| v.as_str()),
|
|
1527
|
-
Some("https://spikard.dev/errors/dependency-error")
|
|
1528
|
-
);
|
|
1529
|
-
}
|
|
1530
|
-
|
|
1531
|
-
#[tokio::test]
|
|
1532
|
-
async fn test_request_data_is_cloned_not_moved_to_handler() {
|
|
1533
|
-
let mut container = DependencyContainer::new();
|
|
1534
|
-
container
|
|
1535
|
-
.register(
|
|
1536
|
-
"service".to_string(),
|
|
1537
|
-
Arc::new(ValueDependency::new("service", "test_service")),
|
|
1538
|
-
)
|
|
1539
|
-
.unwrap();
|
|
1540
|
-
|
|
1541
|
-
let handler = Arc::new(ReadDependencyHandler);
|
|
1542
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["service".to_string()]);
|
|
1543
|
-
|
|
1544
|
-
let mut original_request_data = create_request_data();
|
|
1545
|
-
original_request_data.path = "/api/test".to_string();
|
|
1546
|
-
original_request_data.method = "POST".to_string();
|
|
1547
|
-
|
|
1548
|
-
let mut headers = HashMap::new();
|
|
1549
|
-
headers.insert("X-Custom-Header".to_string(), "custom-value".to_string());
|
|
1550
|
-
original_request_data.headers = Arc::new(headers.clone());
|
|
1551
|
-
|
|
1552
|
-
let mut cookies = HashMap::new();
|
|
1553
|
-
cookies.insert("session_id".to_string(), "test-session".to_string());
|
|
1554
|
-
original_request_data.cookies = Arc::new(cookies.clone());
|
|
1555
|
-
|
|
1556
|
-
let original_path = original_request_data.path.clone();
|
|
1557
|
-
let original_method = original_request_data.method.clone();
|
|
1558
|
-
|
|
1559
|
-
let request = Request::builder().method("POST").body(Body::empty()).unwrap();
|
|
1560
|
-
let request_data_clone = original_request_data.clone();
|
|
1561
|
-
let result = di_handler.call(request, original_request_data).await;
|
|
1562
|
-
|
|
1563
|
-
assert!(result.is_ok());
|
|
1564
|
-
|
|
1565
|
-
assert_eq!(request_data_clone.path, original_path);
|
|
1566
|
-
assert_eq!(request_data_clone.method, original_method);
|
|
1567
|
-
|
|
1568
|
-
assert!(request_data_clone.dependencies.is_none());
|
|
1569
|
-
|
|
1570
|
-
assert_eq!(*request_data_clone.headers, headers);
|
|
1571
|
-
assert_eq!(*request_data_clone.cookies, cookies);
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
#[tokio::test]
|
|
1575
|
-
async fn test_core_request_data_conversion_preserves_all_fields() {
|
|
1576
|
-
let mut container = DependencyContainer::new();
|
|
1577
|
-
container
|
|
1578
|
-
.register(
|
|
1579
|
-
"config".to_string(),
|
|
1580
|
-
Arc::new(ValueDependency::new("config", "test_config")),
|
|
1581
|
-
)
|
|
1582
|
-
.unwrap();
|
|
1583
|
-
|
|
1584
|
-
let handler = Arc::new(TestHandler);
|
|
1585
|
-
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
1586
|
-
|
|
1587
|
-
let mut path_params = HashMap::new();
|
|
1588
|
-
path_params.insert("id".to_string(), "123".to_string());
|
|
1589
|
-
path_params.insert("resource".to_string(), "users".to_string());
|
|
1590
|
-
|
|
1591
|
-
let mut raw_query_params = HashMap::new();
|
|
1592
|
-
raw_query_params.insert("filter".to_string(), vec!["active".to_string()]);
|
|
1593
|
-
raw_query_params.insert("sort".to_string(), vec!["name".to_string(), "asc".to_string()]);
|
|
1594
|
-
|
|
1595
|
-
let mut headers = HashMap::new();
|
|
1596
|
-
headers.insert("Authorization".to_string(), "Bearer token123".to_string());
|
|
1597
|
-
headers.insert("Content-Type".to_string(), "application/json".to_string());
|
|
1598
|
-
|
|
1599
|
-
let mut cookies = HashMap::new();
|
|
1600
|
-
cookies.insert("session".to_string(), "abc123".to_string());
|
|
1601
|
-
cookies.insert("preferences".to_string(), "dark_mode".to_string());
|
|
1602
|
-
|
|
1603
|
-
let request_data = RequestData {
|
|
1604
|
-
path_params: Arc::new(path_params.clone()),
|
|
1605
|
-
query_params: serde_json::json!({"filter": "active", "sort": "name"}),
|
|
1606
|
-
validated_params: None,
|
|
1607
|
-
raw_query_params: Arc::new(raw_query_params.clone()),
|
|
1608
|
-
body: serde_json::json!({"name": "John", "email": "john@example.com"}),
|
|
1609
|
-
raw_body: Some(bytes::Bytes::from(r#"{"name":"John","email":"john@example.com"}"#)),
|
|
1610
|
-
headers: Arc::new(headers.clone()),
|
|
1611
|
-
cookies: Arc::new(cookies.clone()),
|
|
1612
|
-
method: "POST".to_string(),
|
|
1613
|
-
path: "/api/users/123".to_string(),
|
|
1614
|
-
#[cfg(feature = "di")]
|
|
1615
|
-
dependencies: None,
|
|
1616
|
-
};
|
|
1617
|
-
|
|
1618
|
-
let original_path = request_data.path.clone();
|
|
1619
|
-
let original_method = request_data.method.clone();
|
|
1620
|
-
let original_body = request_data.body.clone();
|
|
1621
|
-
let original_query_params = request_data.query_params.clone();
|
|
1622
|
-
|
|
1623
|
-
let request = Request::builder().method("POST").body(Body::empty()).unwrap();
|
|
1624
|
-
let result = di_handler.call(request, request_data.clone()).await;
|
|
1625
|
-
|
|
1626
|
-
assert!(result.is_ok());
|
|
1627
|
-
let response = result.unwrap();
|
|
1628
|
-
assert_eq!(response.status(), StatusCode::OK);
|
|
1629
|
-
|
|
1630
|
-
assert_eq!(request_data.path, original_path, "path field must be preserved");
|
|
1631
|
-
assert_eq!(request_data.method, original_method, "method field must be preserved");
|
|
1632
|
-
assert_eq!(request_data.body, original_body, "body field must be preserved");
|
|
1633
|
-
assert_eq!(
|
|
1634
|
-
request_data.query_params, original_query_params,
|
|
1635
|
-
"query_params must be preserved"
|
|
1636
|
-
);
|
|
1637
|
-
|
|
1638
|
-
assert_eq!(request_data.path_params.get("id"), Some(&"123".to_string()));
|
|
1639
|
-
assert_eq!(request_data.path_params.get("resource"), Some(&"users".to_string()));
|
|
1640
|
-
|
|
1641
|
-
assert_eq!(
|
|
1642
|
-
request_data.raw_query_params.get("filter"),
|
|
1643
|
-
Some(&vec!["active".to_string()])
|
|
1644
|
-
);
|
|
1645
|
-
assert_eq!(
|
|
1646
|
-
request_data.raw_query_params.get("sort"),
|
|
1647
|
-
Some(&vec!["name".to_string(), "asc".to_string()])
|
|
1648
|
-
);
|
|
1649
|
-
|
|
1650
|
-
assert_eq!(
|
|
1651
|
-
request_data.headers.get("Authorization"),
|
|
1652
|
-
Some(&"Bearer token123".to_string())
|
|
1653
|
-
);
|
|
1654
|
-
assert_eq!(
|
|
1655
|
-
request_data.headers.get("Content-Type"),
|
|
1656
|
-
Some(&"application/json".to_string())
|
|
1657
|
-
);
|
|
1658
|
-
|
|
1659
|
-
assert_eq!(request_data.cookies.get("session"), Some(&"abc123".to_string()));
|
|
1660
|
-
assert_eq!(request_data.cookies.get("preferences"), Some(&"dark_mode".to_string()));
|
|
1661
|
-
|
|
1662
|
-
assert!(request_data.raw_body.is_some());
|
|
1663
|
-
assert_eq!(
|
|
1664
|
-
request_data.raw_body.as_ref().unwrap().as_ref(),
|
|
1665
|
-
r#"{"name":"John","email":"john@example.com"}"#.as_bytes()
|
|
1666
|
-
);
|
|
1667
|
-
}
|
|
1668
|
-
}
|
|
1
|
+
//! Dependency Injection Handler Wrapper
|
|
2
|
+
//!
|
|
3
|
+
//! This module provides a handler wrapper that integrates the DI system with the HTTP
|
|
4
|
+
//! handler pipeline. It follows the same composition pattern as `ValidatingHandler`.
|
|
5
|
+
//!
|
|
6
|
+
//! # Architecture
|
|
7
|
+
//!
|
|
8
|
+
//! The `DependencyInjectingHandler` wraps any `Handler` and:
|
|
9
|
+
//! 1. Resolves required dependencies in parallel batches before calling the handler
|
|
10
|
+
//! 2. Attaches resolved dependencies to `RequestData`
|
|
11
|
+
//! 3. Calls the inner handler with the enriched request data
|
|
12
|
+
//! 4. Cleans up dependencies after the handler completes (async Drop pattern)
|
|
13
|
+
//!
|
|
14
|
+
//! # Performance
|
|
15
|
+
//!
|
|
16
|
+
//! - **Zero overhead when no DI**: If no container is provided, DI is skipped entirely
|
|
17
|
+
//! - **Parallel resolution**: Independent dependencies are resolved concurrently
|
|
18
|
+
//! - **Efficient caching**: Singleton and per-request caching minimize redundant work
|
|
19
|
+
//! - **Composable**: Works seamlessly with `ValidatingHandler` and lifecycle hooks
|
|
20
|
+
//!
|
|
21
|
+
//! # Examples
|
|
22
|
+
//!
|
|
23
|
+
//! ```ignore
|
|
24
|
+
//! use spikard_http::di_handler::DependencyInjectingHandler;
|
|
25
|
+
//! use spikard_core::di::DependencyContainer;
|
|
26
|
+
//! use std::sync::Arc;
|
|
27
|
+
//!
|
|
28
|
+
//! # tokio_test::block_on(async {
|
|
29
|
+
//! let container = Arc::new(DependencyContainer::new());
|
|
30
|
+
//! let handler = Arc::new(MyHandler::new());
|
|
31
|
+
//!
|
|
32
|
+
//! let di_handler = DependencyInjectingHandler::new(
|
|
33
|
+
//! handler,
|
|
34
|
+
//! container,
|
|
35
|
+
//! vec!["database".to_string(), "cache".to_string()],
|
|
36
|
+
//! );
|
|
37
|
+
//! # });
|
|
38
|
+
//! ```
|
|
39
|
+
|
|
40
|
+
use crate::handler_trait::{Handler, HandlerResult, RequestData};
|
|
41
|
+
use axum::body::Body;
|
|
42
|
+
use axum::http::{Request, StatusCode};
|
|
43
|
+
use spikard_core::di::{DependencyContainer, DependencyError};
|
|
44
|
+
use std::future::Future;
|
|
45
|
+
use std::pin::Pin;
|
|
46
|
+
use std::sync::Arc;
|
|
47
|
+
use tracing::{debug, info_span, instrument};
|
|
48
|
+
|
|
49
|
+
/// Handler wrapper that resolves dependencies before calling the inner handler
|
|
50
|
+
///
|
|
51
|
+
/// This wrapper follows the composition pattern used by `ValidatingHandler`:
|
|
52
|
+
/// it wraps an existing handler and enriches the request with resolved dependencies.
|
|
53
|
+
///
|
|
54
|
+
/// # Thread Safety
|
|
55
|
+
///
|
|
56
|
+
/// This struct is `Send + Sync` and can be safely shared across threads.
|
|
57
|
+
/// The container is shared via `Arc`, and all dependencies must be `Send + Sync`.
|
|
58
|
+
pub struct DependencyInjectingHandler {
|
|
59
|
+
/// The wrapped handler that will receive the enriched request
|
|
60
|
+
inner: Arc<dyn Handler>,
|
|
61
|
+
/// Shared dependency container for resolution
|
|
62
|
+
container: Arc<DependencyContainer>,
|
|
63
|
+
/// List of dependency names required by this handler
|
|
64
|
+
required_dependencies: Vec<String>,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
impl DependencyInjectingHandler {
|
|
68
|
+
/// Create a new dependency-injecting handler wrapper
|
|
69
|
+
///
|
|
70
|
+
/// # Arguments
|
|
71
|
+
///
|
|
72
|
+
/// * `handler` - The handler to wrap
|
|
73
|
+
/// * `container` - Shared dependency container
|
|
74
|
+
/// * `required_dependencies` - Names of dependencies to resolve for this handler
|
|
75
|
+
///
|
|
76
|
+
/// # Examples
|
|
77
|
+
///
|
|
78
|
+
/// ```ignore
|
|
79
|
+
/// use spikard_http::di_handler::DependencyInjectingHandler;
|
|
80
|
+
/// use spikard_core::di::DependencyContainer;
|
|
81
|
+
/// use std::sync::Arc;
|
|
82
|
+
///
|
|
83
|
+
/// # tokio_test::block_on(async {
|
|
84
|
+
/// let container = Arc::new(DependencyContainer::new());
|
|
85
|
+
/// let handler = Arc::new(MyHandler::new());
|
|
86
|
+
///
|
|
87
|
+
/// let di_handler = DependencyInjectingHandler::new(
|
|
88
|
+
/// handler,
|
|
89
|
+
/// container,
|
|
90
|
+
/// vec!["db".to_string()],
|
|
91
|
+
/// );
|
|
92
|
+
/// # });
|
|
93
|
+
/// ```
|
|
94
|
+
pub fn new(
|
|
95
|
+
handler: Arc<dyn Handler>,
|
|
96
|
+
container: Arc<DependencyContainer>,
|
|
97
|
+
required_dependencies: Vec<String>,
|
|
98
|
+
) -> Self {
|
|
99
|
+
Self {
|
|
100
|
+
inner: handler,
|
|
101
|
+
container,
|
|
102
|
+
required_dependencies,
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// Get the list of required dependencies
|
|
107
|
+
pub fn required_dependencies(&self) -> &[String] {
|
|
108
|
+
&self.required_dependencies
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
impl Handler for DependencyInjectingHandler {
|
|
113
|
+
#[instrument(
|
|
114
|
+
skip(self, request, request_data),
|
|
115
|
+
fields(
|
|
116
|
+
required_deps = %self.required_dependencies.len(),
|
|
117
|
+
deps = ?self.required_dependencies
|
|
118
|
+
)
|
|
119
|
+
)]
|
|
120
|
+
fn call(
|
|
121
|
+
&self,
|
|
122
|
+
request: Request<Body>,
|
|
123
|
+
mut request_data: RequestData,
|
|
124
|
+
) -> Pin<Box<dyn Future<Output = HandlerResult> + Send + '_>> {
|
|
125
|
+
tracing::debug!(
|
|
126
|
+
target = "spikard::di",
|
|
127
|
+
required_deps = ?self.required_dependencies,
|
|
128
|
+
"entering DI handler"
|
|
129
|
+
);
|
|
130
|
+
let inner = self.inner.clone();
|
|
131
|
+
let container = self.container.clone();
|
|
132
|
+
let required_dependencies = self.required_dependencies.clone();
|
|
133
|
+
|
|
134
|
+
Box::pin(async move {
|
|
135
|
+
debug!(
|
|
136
|
+
"DI handler invoked for {} deps; container keys: {:?}",
|
|
137
|
+
required_dependencies.len(),
|
|
138
|
+
container.keys()
|
|
139
|
+
);
|
|
140
|
+
let resolution_span = info_span!(
|
|
141
|
+
"resolve_dependencies",
|
|
142
|
+
count = %required_dependencies.len()
|
|
143
|
+
);
|
|
144
|
+
let _enter = resolution_span.enter();
|
|
145
|
+
|
|
146
|
+
debug!(
|
|
147
|
+
"Resolving {} dependencies: {:?}",
|
|
148
|
+
required_dependencies.len(),
|
|
149
|
+
required_dependencies
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
let start = std::time::Instant::now();
|
|
153
|
+
|
|
154
|
+
let core_request_data = spikard_core::RequestData {
|
|
155
|
+
path_params: Arc::clone(&request_data.path_params),
|
|
156
|
+
query_params: request_data.query_params.clone(),
|
|
157
|
+
validated_params: request_data.validated_params.clone(),
|
|
158
|
+
raw_query_params: Arc::clone(&request_data.raw_query_params),
|
|
159
|
+
body: request_data.body.clone(),
|
|
160
|
+
raw_body: request_data.raw_body.clone(),
|
|
161
|
+
headers: Arc::clone(&request_data.headers),
|
|
162
|
+
cookies: Arc::clone(&request_data.cookies),
|
|
163
|
+
method: request_data.method.clone(),
|
|
164
|
+
path: request_data.path.clone(),
|
|
165
|
+
#[cfg(feature = "di")]
|
|
166
|
+
dependencies: None,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
let (parts, _body) = request.into_parts();
|
|
170
|
+
let core_request = Request::from_parts(parts.clone(), ());
|
|
171
|
+
|
|
172
|
+
let request = Request::from_parts(parts, axum::body::Body::default());
|
|
173
|
+
|
|
174
|
+
let resolved = match container
|
|
175
|
+
.resolve_for_handler(&required_dependencies, &core_request, &core_request_data)
|
|
176
|
+
.await
|
|
177
|
+
{
|
|
178
|
+
Ok(resolved) => resolved,
|
|
179
|
+
Err(e) => {
|
|
180
|
+
debug!("DI error: {}", e);
|
|
181
|
+
|
|
182
|
+
let (status, json_body) = match e {
|
|
183
|
+
DependencyError::NotFound { ref key } => {
|
|
184
|
+
let body = serde_json::json!({
|
|
185
|
+
"detail": "Required dependency not found",
|
|
186
|
+
"errors": [{
|
|
187
|
+
"dependency_key": key,
|
|
188
|
+
"msg": format!("Dependency '{}' is not registered", key),
|
|
189
|
+
"type": "missing_dependency"
|
|
190
|
+
}],
|
|
191
|
+
"status": 500,
|
|
192
|
+
"title": "Dependency Resolution Failed",
|
|
193
|
+
"type": "https://spikard.dev/errors/dependency-error"
|
|
194
|
+
});
|
|
195
|
+
(StatusCode::INTERNAL_SERVER_ERROR, body)
|
|
196
|
+
}
|
|
197
|
+
DependencyError::CircularDependency { ref cycle } => {
|
|
198
|
+
let body = serde_json::json!({
|
|
199
|
+
"detail": "Circular dependency detected",
|
|
200
|
+
"errors": [{
|
|
201
|
+
"cycle": cycle,
|
|
202
|
+
"msg": "Circular dependency detected in dependency graph",
|
|
203
|
+
"type": "circular_dependency"
|
|
204
|
+
}],
|
|
205
|
+
"status": 500,
|
|
206
|
+
"title": "Dependency Resolution Failed",
|
|
207
|
+
"type": "https://spikard.dev/errors/dependency-error"
|
|
208
|
+
});
|
|
209
|
+
(StatusCode::INTERNAL_SERVER_ERROR, body)
|
|
210
|
+
}
|
|
211
|
+
DependencyError::ResolutionFailed { ref message } => {
|
|
212
|
+
let body = serde_json::json!({
|
|
213
|
+
"detail": "Dependency resolution failed",
|
|
214
|
+
"errors": [{
|
|
215
|
+
"msg": message,
|
|
216
|
+
"type": "resolution_failed"
|
|
217
|
+
}],
|
|
218
|
+
"status": 503,
|
|
219
|
+
"title": "Service Unavailable",
|
|
220
|
+
"type": "https://spikard.dev/errors/dependency-error"
|
|
221
|
+
});
|
|
222
|
+
(StatusCode::SERVICE_UNAVAILABLE, body)
|
|
223
|
+
}
|
|
224
|
+
_ => {
|
|
225
|
+
let body = serde_json::json!({
|
|
226
|
+
"detail": "Dependency resolution failed",
|
|
227
|
+
"errors": [{
|
|
228
|
+
"msg": e.to_string(),
|
|
229
|
+
"type": "unknown"
|
|
230
|
+
}],
|
|
231
|
+
"status": 500,
|
|
232
|
+
"title": "Dependency Resolution Failed",
|
|
233
|
+
"type": "https://spikard.dev/errors/dependency-error"
|
|
234
|
+
});
|
|
235
|
+
(StatusCode::INTERNAL_SERVER_ERROR, body)
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
let response = axum::http::Response::builder()
|
|
240
|
+
.status(status)
|
|
241
|
+
.header("Content-Type", "application/json")
|
|
242
|
+
.body(Body::from(json_body.to_string()))
|
|
243
|
+
.unwrap();
|
|
244
|
+
|
|
245
|
+
return Ok(response);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
let duration = start.elapsed();
|
|
250
|
+
debug!(
|
|
251
|
+
"Dependencies resolved in {:?} ({} dependencies)",
|
|
252
|
+
duration,
|
|
253
|
+
required_dependencies.len()
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
drop(_enter);
|
|
257
|
+
|
|
258
|
+
let deps = Arc::new(resolved);
|
|
259
|
+
request_data.dependencies = Some(Arc::clone(&deps));
|
|
260
|
+
|
|
261
|
+
let result = inner.call(request, request_data).await;
|
|
262
|
+
|
|
263
|
+
if let Ok(deps) = Arc::try_unwrap(deps) {
|
|
264
|
+
let cleanup_span = info_span!("cleanup_dependencies");
|
|
265
|
+
let _enter = cleanup_span.enter();
|
|
266
|
+
|
|
267
|
+
debug!("Running dependency cleanup tasks");
|
|
268
|
+
deps.cleanup().await;
|
|
269
|
+
} else {
|
|
270
|
+
debug!("Skipping cleanup: dependencies still shared");
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
result
|
|
274
|
+
})
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
#[cfg(test)]
|
|
279
|
+
mod tests {
|
|
280
|
+
use super::*;
|
|
281
|
+
use crate::handler_trait::RequestData;
|
|
282
|
+
use axum::http::Response;
|
|
283
|
+
use spikard_core::di::ValueDependency;
|
|
284
|
+
use std::collections::HashMap;
|
|
285
|
+
|
|
286
|
+
/// Test handler that checks for dependency presence
|
|
287
|
+
struct TestHandler;
|
|
288
|
+
|
|
289
|
+
impl Handler for TestHandler {
|
|
290
|
+
fn call(
|
|
291
|
+
&self,
|
|
292
|
+
_request: Request<Body>,
|
|
293
|
+
request_data: RequestData,
|
|
294
|
+
) -> Pin<Box<dyn Future<Output = HandlerResult> + Send + '_>> {
|
|
295
|
+
Box::pin(async move {
|
|
296
|
+
if request_data.dependencies.is_some() {
|
|
297
|
+
let response = Response::builder()
|
|
298
|
+
.status(StatusCode::OK)
|
|
299
|
+
.body(Body::from("dependencies present"))
|
|
300
|
+
.unwrap();
|
|
301
|
+
Ok(response)
|
|
302
|
+
} else {
|
|
303
|
+
Err((StatusCode::INTERNAL_SERVER_ERROR, "no dependencies".to_string()))
|
|
304
|
+
}
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/// Handler that returns error to test error propagation
|
|
310
|
+
struct ErrorHandler;
|
|
311
|
+
|
|
312
|
+
impl Handler for ErrorHandler {
|
|
313
|
+
fn call(
|
|
314
|
+
&self,
|
|
315
|
+
_request: Request<Body>,
|
|
316
|
+
_request_data: RequestData,
|
|
317
|
+
) -> Pin<Box<dyn Future<Output = HandlerResult> + Send + '_>> {
|
|
318
|
+
Box::pin(async move { Err((StatusCode::INTERNAL_SERVER_ERROR, "inner handler error".to_string())) })
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/// Handler that reads and validates dependency values
|
|
323
|
+
struct ReadDependencyHandler;
|
|
324
|
+
|
|
325
|
+
impl Handler for ReadDependencyHandler {
|
|
326
|
+
fn call(
|
|
327
|
+
&self,
|
|
328
|
+
_request: Request<Body>,
|
|
329
|
+
request_data: RequestData,
|
|
330
|
+
) -> Pin<Box<dyn Future<Output = HandlerResult> + Send + '_>> {
|
|
331
|
+
Box::pin(async move {
|
|
332
|
+
if request_data.dependencies.is_some() {
|
|
333
|
+
let response = Response::builder()
|
|
334
|
+
.status(StatusCode::OK)
|
|
335
|
+
.body(Body::from("dependencies resolved and accessible"))
|
|
336
|
+
.unwrap();
|
|
337
|
+
Ok(response)
|
|
338
|
+
} else {
|
|
339
|
+
Err((StatusCode::INTERNAL_SERVER_ERROR, "no dependencies".to_string()))
|
|
340
|
+
}
|
|
341
|
+
})
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/// Helper function to create a basic RequestData
|
|
346
|
+
fn create_request_data() -> RequestData {
|
|
347
|
+
RequestData {
|
|
348
|
+
path_params: Arc::new(HashMap::new()),
|
|
349
|
+
query_params: serde_json::Value::Null,
|
|
350
|
+
validated_params: None,
|
|
351
|
+
raw_query_params: Arc::new(HashMap::new()),
|
|
352
|
+
body: serde_json::Value::Null,
|
|
353
|
+
raw_body: None,
|
|
354
|
+
headers: Arc::new(HashMap::new()),
|
|
355
|
+
cookies: Arc::new(HashMap::new()),
|
|
356
|
+
method: "GET".to_string(),
|
|
357
|
+
path: "/".to_string(),
|
|
358
|
+
#[cfg(feature = "di")]
|
|
359
|
+
dependencies: None,
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
#[tokio::test]
|
|
364
|
+
async fn test_di_handler_resolves_dependencies() {
|
|
365
|
+
let mut container = DependencyContainer::new();
|
|
366
|
+
container
|
|
367
|
+
.register(
|
|
368
|
+
"config".to_string(),
|
|
369
|
+
Arc::new(ValueDependency::new("config", "test_value")),
|
|
370
|
+
)
|
|
371
|
+
.unwrap();
|
|
372
|
+
|
|
373
|
+
let handler = Arc::new(TestHandler);
|
|
374
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
375
|
+
|
|
376
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
377
|
+
let request_data = create_request_data();
|
|
378
|
+
|
|
379
|
+
let result = di_handler.call(request, request_data).await;
|
|
380
|
+
|
|
381
|
+
assert!(result.is_ok());
|
|
382
|
+
let response = result.unwrap();
|
|
383
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
#[tokio::test]
|
|
387
|
+
async fn test_di_handler_error_on_missing_dependency() {
|
|
388
|
+
let container = DependencyContainer::new();
|
|
389
|
+
let handler = Arc::new(TestHandler);
|
|
390
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["database".to_string()]);
|
|
391
|
+
|
|
392
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
393
|
+
let request_data = create_request_data();
|
|
394
|
+
|
|
395
|
+
let result = di_handler.call(request, request_data).await;
|
|
396
|
+
|
|
397
|
+
assert!(result.is_ok());
|
|
398
|
+
let response = result.unwrap();
|
|
399
|
+
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
#[tokio::test]
|
|
403
|
+
async fn test_di_handler_empty_dependencies() {
|
|
404
|
+
let container = DependencyContainer::new();
|
|
405
|
+
let handler = Arc::new(TestHandler);
|
|
406
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec![]);
|
|
407
|
+
|
|
408
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
409
|
+
let request_data = create_request_data();
|
|
410
|
+
|
|
411
|
+
let result = di_handler.call(request, request_data).await;
|
|
412
|
+
|
|
413
|
+
assert!(result.is_ok());
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
#[tokio::test]
|
|
417
|
+
async fn test_di_handler_multiple_dependencies() {
|
|
418
|
+
let mut container = DependencyContainer::new();
|
|
419
|
+
container
|
|
420
|
+
.register("db".to_string(), Arc::new(ValueDependency::new("db", "postgresql")))
|
|
421
|
+
.unwrap();
|
|
422
|
+
container
|
|
423
|
+
.register("cache".to_string(), Arc::new(ValueDependency::new("cache", "redis")))
|
|
424
|
+
.unwrap();
|
|
425
|
+
container
|
|
426
|
+
.register("logger".to_string(), Arc::new(ValueDependency::new("logger", "slog")))
|
|
427
|
+
.unwrap();
|
|
428
|
+
container
|
|
429
|
+
.register(
|
|
430
|
+
"config".to_string(),
|
|
431
|
+
Arc::new(ValueDependency::new("config", "config_data")),
|
|
432
|
+
)
|
|
433
|
+
.unwrap();
|
|
434
|
+
|
|
435
|
+
let handler = Arc::new(TestHandler);
|
|
436
|
+
let di_handler = DependencyInjectingHandler::new(
|
|
437
|
+
handler,
|
|
438
|
+
Arc::new(container),
|
|
439
|
+
vec![
|
|
440
|
+
"db".to_string(),
|
|
441
|
+
"cache".to_string(),
|
|
442
|
+
"logger".to_string(),
|
|
443
|
+
"config".to_string(),
|
|
444
|
+
],
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
448
|
+
let request_data = create_request_data();
|
|
449
|
+
let result = di_handler.call(request, request_data).await;
|
|
450
|
+
|
|
451
|
+
assert!(result.is_ok());
|
|
452
|
+
let response = result.unwrap();
|
|
453
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
#[tokio::test]
|
|
457
|
+
async fn test_di_handler_required_dependencies_getter() {
|
|
458
|
+
let container = DependencyContainer::new();
|
|
459
|
+
let handler = Arc::new(TestHandler);
|
|
460
|
+
let deps = vec!["db".to_string(), "cache".to_string(), "logger".to_string()];
|
|
461
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), deps.clone());
|
|
462
|
+
|
|
463
|
+
assert_eq!(di_handler.required_dependencies(), deps.as_slice());
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
#[tokio::test]
|
|
467
|
+
async fn test_di_handler_handler_error_propagation() {
|
|
468
|
+
let mut container = DependencyContainer::new();
|
|
469
|
+
container
|
|
470
|
+
.register(
|
|
471
|
+
"config".to_string(),
|
|
472
|
+
Arc::new(ValueDependency::new("config", "test_value")),
|
|
473
|
+
)
|
|
474
|
+
.unwrap();
|
|
475
|
+
|
|
476
|
+
let handler = Arc::new(ErrorHandler);
|
|
477
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
478
|
+
|
|
479
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
480
|
+
let request_data = create_request_data();
|
|
481
|
+
let result = di_handler.call(request, request_data).await;
|
|
482
|
+
|
|
483
|
+
assert!(result.is_err());
|
|
484
|
+
let (status, msg) = result.unwrap_err();
|
|
485
|
+
assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR);
|
|
486
|
+
assert!(msg.contains("inner handler error"));
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
#[tokio::test]
|
|
490
|
+
async fn test_di_handler_request_data_enrichment() {
|
|
491
|
+
let mut container = DependencyContainer::new();
|
|
492
|
+
container
|
|
493
|
+
.register(
|
|
494
|
+
"service".to_string(),
|
|
495
|
+
Arc::new(ValueDependency::new("service", "my_service")),
|
|
496
|
+
)
|
|
497
|
+
.unwrap();
|
|
498
|
+
|
|
499
|
+
let handler = Arc::new(ReadDependencyHandler);
|
|
500
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["service".to_string()]);
|
|
501
|
+
|
|
502
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
503
|
+
let request_data = create_request_data();
|
|
504
|
+
let result = di_handler.call(request, request_data).await;
|
|
505
|
+
|
|
506
|
+
assert!(result.is_ok());
|
|
507
|
+
let response = result.unwrap();
|
|
508
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
#[tokio::test]
|
|
512
|
+
async fn test_di_handler_missing_dependency_json_structure() {
|
|
513
|
+
let container = DependencyContainer::new();
|
|
514
|
+
let handler = Arc::new(TestHandler);
|
|
515
|
+
let di_handler =
|
|
516
|
+
DependencyInjectingHandler::new(handler, Arc::new(container), vec!["missing_service".to_string()]);
|
|
517
|
+
|
|
518
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
519
|
+
let request_data = create_request_data();
|
|
520
|
+
let result = di_handler.call(request, request_data).await;
|
|
521
|
+
|
|
522
|
+
assert!(result.is_ok());
|
|
523
|
+
let response = result.unwrap();
|
|
524
|
+
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
525
|
+
|
|
526
|
+
let content_type = response.headers().get("Content-Type").and_then(|v| v.to_str().ok());
|
|
527
|
+
assert_eq!(content_type, Some("application/json"));
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
#[tokio::test]
|
|
531
|
+
async fn test_di_handler_partial_dependencies_present() {
|
|
532
|
+
let mut container = DependencyContainer::new();
|
|
533
|
+
container
|
|
534
|
+
.register("db".to_string(), Arc::new(ValueDependency::new("db", "postgresql")))
|
|
535
|
+
.unwrap();
|
|
536
|
+
|
|
537
|
+
let handler = Arc::new(TestHandler);
|
|
538
|
+
let di_handler = DependencyInjectingHandler::new(
|
|
539
|
+
handler,
|
|
540
|
+
Arc::new(container),
|
|
541
|
+
vec!["db".to_string(), "cache".to_string()],
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
545
|
+
let request_data = create_request_data();
|
|
546
|
+
let result = di_handler.call(request, request_data).await;
|
|
547
|
+
|
|
548
|
+
assert!(result.is_ok());
|
|
549
|
+
let response = result.unwrap();
|
|
550
|
+
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
#[tokio::test]
|
|
554
|
+
async fn test_di_handler_cleanup_executed() {
|
|
555
|
+
let mut container = DependencyContainer::new();
|
|
556
|
+
|
|
557
|
+
container
|
|
558
|
+
.register("config".to_string(), Arc::new(ValueDependency::new("config", "test")))
|
|
559
|
+
.unwrap();
|
|
560
|
+
|
|
561
|
+
let handler = Arc::new(TestHandler);
|
|
562
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
563
|
+
|
|
564
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
565
|
+
let request_data = create_request_data();
|
|
566
|
+
|
|
567
|
+
let result = di_handler.call(request, request_data).await;
|
|
568
|
+
|
|
569
|
+
assert!(result.is_ok());
|
|
570
|
+
let response = result.unwrap();
|
|
571
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
#[tokio::test]
|
|
575
|
+
async fn test_di_handler_dependent_dependencies() {
|
|
576
|
+
let mut container = DependencyContainer::new();
|
|
577
|
+
|
|
578
|
+
container
|
|
579
|
+
.register(
|
|
580
|
+
"config".to_string(),
|
|
581
|
+
Arc::new(ValueDependency::new("config", "base_config")),
|
|
582
|
+
)
|
|
583
|
+
.unwrap();
|
|
584
|
+
|
|
585
|
+
container
|
|
586
|
+
.register(
|
|
587
|
+
"database".to_string(),
|
|
588
|
+
Arc::new(ValueDependency::new("database", "db_from_config")),
|
|
589
|
+
)
|
|
590
|
+
.unwrap();
|
|
591
|
+
|
|
592
|
+
let handler = Arc::new(TestHandler);
|
|
593
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["database".to_string()]);
|
|
594
|
+
|
|
595
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
596
|
+
let request_data = create_request_data();
|
|
597
|
+
let result = di_handler.call(request, request_data).await;
|
|
598
|
+
|
|
599
|
+
assert!(result.is_ok());
|
|
600
|
+
let response = result.unwrap();
|
|
601
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
#[tokio::test]
|
|
605
|
+
async fn test_di_handler_parallel_independent_dependencies() {
|
|
606
|
+
let mut container = DependencyContainer::new();
|
|
607
|
+
|
|
608
|
+
container
|
|
609
|
+
.register(
|
|
610
|
+
"service_a".to_string(),
|
|
611
|
+
Arc::new(ValueDependency::new("service_a", "svc_a")),
|
|
612
|
+
)
|
|
613
|
+
.unwrap();
|
|
614
|
+
container
|
|
615
|
+
.register(
|
|
616
|
+
"service_b".to_string(),
|
|
617
|
+
Arc::new(ValueDependency::new("service_b", "svc_b")),
|
|
618
|
+
)
|
|
619
|
+
.unwrap();
|
|
620
|
+
container
|
|
621
|
+
.register(
|
|
622
|
+
"service_c".to_string(),
|
|
623
|
+
Arc::new(ValueDependency::new("service_c", "svc_c")),
|
|
624
|
+
)
|
|
625
|
+
.unwrap();
|
|
626
|
+
|
|
627
|
+
let handler = Arc::new(TestHandler);
|
|
628
|
+
let di_handler = DependencyInjectingHandler::new(
|
|
629
|
+
handler,
|
|
630
|
+
Arc::new(container),
|
|
631
|
+
vec![
|
|
632
|
+
"service_a".to_string(),
|
|
633
|
+
"service_b".to_string(),
|
|
634
|
+
"service_c".to_string(),
|
|
635
|
+
],
|
|
636
|
+
);
|
|
637
|
+
|
|
638
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
639
|
+
let request_data = create_request_data();
|
|
640
|
+
let result = di_handler.call(request, request_data).await;
|
|
641
|
+
|
|
642
|
+
assert!(result.is_ok());
|
|
643
|
+
let response = result.unwrap();
|
|
644
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
#[tokio::test]
|
|
648
|
+
async fn test_di_handler_request_method_preserved() {
|
|
649
|
+
let mut container = DependencyContainer::new();
|
|
650
|
+
container
|
|
651
|
+
.register("config".to_string(), Arc::new(ValueDependency::new("config", "test")))
|
|
652
|
+
.unwrap();
|
|
653
|
+
|
|
654
|
+
let handler = Arc::new(TestHandler);
|
|
655
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
656
|
+
|
|
657
|
+
let request = Request::builder().method("POST").body(Body::empty()).unwrap();
|
|
658
|
+
let mut request_data = create_request_data();
|
|
659
|
+
request_data.method = "POST".to_string();
|
|
660
|
+
|
|
661
|
+
let result = di_handler.call(request, request_data).await;
|
|
662
|
+
|
|
663
|
+
assert!(result.is_ok());
|
|
664
|
+
let response = result.unwrap();
|
|
665
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
#[tokio::test]
|
|
669
|
+
async fn test_di_handler_complex_scenario_multiple_deps_with_error() {
|
|
670
|
+
let mut container = DependencyContainer::new();
|
|
671
|
+
|
|
672
|
+
for i in 1..=5 {
|
|
673
|
+
container
|
|
674
|
+
.register(
|
|
675
|
+
format!("service_{}", i),
|
|
676
|
+
Arc::new(ValueDependency::new(&format!("service_{}", i), format!("svc_{}", i))),
|
|
677
|
+
)
|
|
678
|
+
.unwrap();
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
let handler = Arc::new(ErrorHandler);
|
|
682
|
+
let di_handler = DependencyInjectingHandler::new(
|
|
683
|
+
handler,
|
|
684
|
+
Arc::new(container),
|
|
685
|
+
vec![
|
|
686
|
+
"service_1".to_string(),
|
|
687
|
+
"service_2".to_string(),
|
|
688
|
+
"service_3".to_string(),
|
|
689
|
+
"service_4".to_string(),
|
|
690
|
+
"service_5".to_string(),
|
|
691
|
+
],
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
695
|
+
let request_data = create_request_data();
|
|
696
|
+
let result = di_handler.call(request, request_data).await;
|
|
697
|
+
|
|
698
|
+
assert!(result.is_err());
|
|
699
|
+
let (status, _msg) = result.unwrap_err();
|
|
700
|
+
assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
#[tokio::test]
|
|
704
|
+
async fn test_di_handler_empty_request_body_with_deps() {
|
|
705
|
+
let mut container = DependencyContainer::new();
|
|
706
|
+
container
|
|
707
|
+
.register("config".to_string(), Arc::new(ValueDependency::new("config", "test")))
|
|
708
|
+
.unwrap();
|
|
709
|
+
|
|
710
|
+
let handler = Arc::new(TestHandler);
|
|
711
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
712
|
+
|
|
713
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
714
|
+
let request_data = create_request_data();
|
|
715
|
+
|
|
716
|
+
let result = di_handler.call(request, request_data).await;
|
|
717
|
+
|
|
718
|
+
assert!(result.is_ok());
|
|
719
|
+
let response = result.unwrap();
|
|
720
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
#[tokio::test]
|
|
724
|
+
async fn test_di_handler_shared_container_across_handlers() {
|
|
725
|
+
let mut container = DependencyContainer::new();
|
|
726
|
+
container
|
|
727
|
+
.register(
|
|
728
|
+
"shared_config".to_string(),
|
|
729
|
+
Arc::new(ValueDependency::new("shared_config", "shared_value")),
|
|
730
|
+
)
|
|
731
|
+
.unwrap();
|
|
732
|
+
|
|
733
|
+
let shared_container = Arc::new(container);
|
|
734
|
+
|
|
735
|
+
let handler1 = Arc::new(TestHandler);
|
|
736
|
+
let di_handler1 = DependencyInjectingHandler::new(
|
|
737
|
+
handler1,
|
|
738
|
+
Arc::clone(&shared_container),
|
|
739
|
+
vec!["shared_config".to_string()],
|
|
740
|
+
);
|
|
741
|
+
|
|
742
|
+
let handler2 = Arc::new(TestHandler);
|
|
743
|
+
let di_handler2 = DependencyInjectingHandler::new(
|
|
744
|
+
handler2,
|
|
745
|
+
Arc::clone(&shared_container),
|
|
746
|
+
vec!["shared_config".to_string()],
|
|
747
|
+
);
|
|
748
|
+
|
|
749
|
+
let request1 = Request::builder().body(Body::empty()).unwrap();
|
|
750
|
+
let request_data1 = create_request_data();
|
|
751
|
+
let result1 = di_handler1.call(request1, request_data1).await;
|
|
752
|
+
|
|
753
|
+
let request2 = Request::builder().body(Body::empty()).unwrap();
|
|
754
|
+
let request_data2 = create_request_data();
|
|
755
|
+
let result2 = di_handler2.call(request2, request_data2).await;
|
|
756
|
+
|
|
757
|
+
assert!(result1.is_ok());
|
|
758
|
+
assert!(result2.is_ok());
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
#[tokio::test]
|
|
762
|
+
async fn test_concurrent_requests_same_handler_no_race() {
|
|
763
|
+
let mut container = DependencyContainer::new();
|
|
764
|
+
container
|
|
765
|
+
.register(
|
|
766
|
+
"config".to_string(),
|
|
767
|
+
Arc::new(ValueDependency::new("config", "test_config")),
|
|
768
|
+
)
|
|
769
|
+
.unwrap();
|
|
770
|
+
|
|
771
|
+
let shared_container = Arc::new(container);
|
|
772
|
+
let handler = Arc::new(TestHandler);
|
|
773
|
+
let di_handler = Arc::new(DependencyInjectingHandler::new(
|
|
774
|
+
handler,
|
|
775
|
+
shared_container,
|
|
776
|
+
vec!["config".to_string()],
|
|
777
|
+
));
|
|
778
|
+
|
|
779
|
+
let handles: Vec<_> = (0..10)
|
|
780
|
+
.map(|_| {
|
|
781
|
+
let di_handler = Arc::clone(&di_handler);
|
|
782
|
+
tokio::spawn(async move {
|
|
783
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
784
|
+
let request_data = create_request_data();
|
|
785
|
+
di_handler.call(request, request_data).await
|
|
786
|
+
})
|
|
787
|
+
})
|
|
788
|
+
.collect();
|
|
789
|
+
|
|
790
|
+
for handle in handles {
|
|
791
|
+
let result = handle.await.unwrap();
|
|
792
|
+
assert!(result.is_ok());
|
|
793
|
+
let response = result.unwrap();
|
|
794
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
#[tokio::test]
|
|
799
|
+
async fn test_concurrent_different_handlers_shared_container() {
|
|
800
|
+
let mut container = DependencyContainer::new();
|
|
801
|
+
container
|
|
802
|
+
.register("db".to_string(), Arc::new(ValueDependency::new("db", "postgres")))
|
|
803
|
+
.unwrap();
|
|
804
|
+
container
|
|
805
|
+
.register("cache".to_string(), Arc::new(ValueDependency::new("cache", "redis")))
|
|
806
|
+
.unwrap();
|
|
807
|
+
|
|
808
|
+
let shared_container = Arc::new(container);
|
|
809
|
+
|
|
810
|
+
let mut handles = vec![];
|
|
811
|
+
for i in 0..5 {
|
|
812
|
+
let container = Arc::clone(&shared_container);
|
|
813
|
+
let handler = Arc::new(TestHandler);
|
|
814
|
+
let di_handler = DependencyInjectingHandler::new(
|
|
815
|
+
handler,
|
|
816
|
+
container,
|
|
817
|
+
if i % 2 == 0 {
|
|
818
|
+
vec!["db".to_string()]
|
|
819
|
+
} else {
|
|
820
|
+
vec!["cache".to_string()]
|
|
821
|
+
},
|
|
822
|
+
);
|
|
823
|
+
|
|
824
|
+
let handle = tokio::spawn(async move {
|
|
825
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
826
|
+
let request_data = create_request_data();
|
|
827
|
+
di_handler.call(request, request_data).await
|
|
828
|
+
});
|
|
829
|
+
handles.push(handle);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
for handle in handles {
|
|
833
|
+
let result = handle.await.unwrap();
|
|
834
|
+
assert!(result.is_ok());
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
#[tokio::test]
|
|
839
|
+
async fn test_missing_dependency_multiple_concurrent_requests() {
|
|
840
|
+
let container = DependencyContainer::new();
|
|
841
|
+
let shared_container = Arc::new(container);
|
|
842
|
+
let handler = Arc::new(TestHandler);
|
|
843
|
+
let di_handler = Arc::new(DependencyInjectingHandler::new(
|
|
844
|
+
handler,
|
|
845
|
+
shared_container,
|
|
846
|
+
vec!["nonexistent".to_string()],
|
|
847
|
+
));
|
|
848
|
+
|
|
849
|
+
let handles: Vec<_> = (0..5)
|
|
850
|
+
.map(|_| {
|
|
851
|
+
let di_handler = Arc::clone(&di_handler);
|
|
852
|
+
tokio::spawn(async move {
|
|
853
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
854
|
+
let request_data = create_request_data();
|
|
855
|
+
di_handler.call(request, request_data).await
|
|
856
|
+
})
|
|
857
|
+
})
|
|
858
|
+
.collect();
|
|
859
|
+
|
|
860
|
+
for handle in handles {
|
|
861
|
+
let result = handle.await.unwrap();
|
|
862
|
+
assert!(result.is_ok());
|
|
863
|
+
let response = result.unwrap();
|
|
864
|
+
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
#[tokio::test]
|
|
869
|
+
async fn test_large_dependency_tree_resolution() {
|
|
870
|
+
let mut container = DependencyContainer::new();
|
|
871
|
+
for i in 0..20 {
|
|
872
|
+
container
|
|
873
|
+
.register(
|
|
874
|
+
format!("dep_{}", i),
|
|
875
|
+
Arc::new(ValueDependency::new(&format!("dep_{}", i), format!("value_{}", i))),
|
|
876
|
+
)
|
|
877
|
+
.unwrap();
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
let handler = Arc::new(TestHandler);
|
|
881
|
+
let mut required = vec![];
|
|
882
|
+
for i in 0..20 {
|
|
883
|
+
required.push(format!("dep_{}", i));
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), required);
|
|
887
|
+
|
|
888
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
889
|
+
let request_data = create_request_data();
|
|
890
|
+
let result = di_handler.call(request, request_data).await;
|
|
891
|
+
|
|
892
|
+
assert!(result.is_ok());
|
|
893
|
+
let response = result.unwrap();
|
|
894
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
#[tokio::test]
|
|
898
|
+
async fn test_handler_error_does_not_prevent_cleanup() {
|
|
899
|
+
let mut container = DependencyContainer::new();
|
|
900
|
+
container
|
|
901
|
+
.register("config".to_string(), Arc::new(ValueDependency::new("config", "test")))
|
|
902
|
+
.unwrap();
|
|
903
|
+
|
|
904
|
+
let handler = Arc::new(ErrorHandler);
|
|
905
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
906
|
+
|
|
907
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
908
|
+
let request_data = create_request_data();
|
|
909
|
+
let result = di_handler.call(request, request_data).await;
|
|
910
|
+
|
|
911
|
+
assert!(result.is_err());
|
|
912
|
+
let (status, msg) = result.unwrap_err();
|
|
913
|
+
assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR);
|
|
914
|
+
assert!(msg.contains("inner handler error"));
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
#[tokio::test]
|
|
918
|
+
async fn test_partial_dependency_resolution_failure() {
|
|
919
|
+
let mut container = DependencyContainer::new();
|
|
920
|
+
container
|
|
921
|
+
.register(
|
|
922
|
+
"service_a".to_string(),
|
|
923
|
+
Arc::new(ValueDependency::new("service_a", "svc_a")),
|
|
924
|
+
)
|
|
925
|
+
.unwrap();
|
|
926
|
+
|
|
927
|
+
let handler = Arc::new(TestHandler);
|
|
928
|
+
let di_handler = DependencyInjectingHandler::new(
|
|
929
|
+
handler,
|
|
930
|
+
Arc::new(container),
|
|
931
|
+
vec!["service_a".to_string(), "service_b".to_string()],
|
|
932
|
+
);
|
|
933
|
+
|
|
934
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
935
|
+
let request_data = create_request_data();
|
|
936
|
+
let result = di_handler.call(request, request_data).await;
|
|
937
|
+
|
|
938
|
+
assert!(result.is_ok());
|
|
939
|
+
let response = result.unwrap();
|
|
940
|
+
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
#[tokio::test]
|
|
944
|
+
async fn test_circular_dependency_detection() {
|
|
945
|
+
let mut container = DependencyContainer::new();
|
|
946
|
+
container
|
|
947
|
+
.register(
|
|
948
|
+
"service_a".to_string(),
|
|
949
|
+
Arc::new(ValueDependency::new("service_a", "svc_a")),
|
|
950
|
+
)
|
|
951
|
+
.unwrap();
|
|
952
|
+
|
|
953
|
+
let handler = Arc::new(TestHandler);
|
|
954
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["service_a".to_string()]);
|
|
955
|
+
|
|
956
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
957
|
+
let request_data = create_request_data();
|
|
958
|
+
let result = di_handler.call(request, request_data).await;
|
|
959
|
+
|
|
960
|
+
assert!(result.is_ok());
|
|
961
|
+
let response = result.unwrap();
|
|
962
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
#[tokio::test]
|
|
966
|
+
async fn test_empty_required_dependencies_with_multiple_registered() {
|
|
967
|
+
let mut container = DependencyContainer::new();
|
|
968
|
+
for i in 0..5 {
|
|
969
|
+
container
|
|
970
|
+
.register(
|
|
971
|
+
format!("unused_{}", i),
|
|
972
|
+
Arc::new(ValueDependency::new(&format!("unused_{}", i), format!("val_{}", i))),
|
|
973
|
+
)
|
|
974
|
+
.unwrap();
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
let handler = Arc::new(TestHandler);
|
|
978
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec![]);
|
|
979
|
+
|
|
980
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
981
|
+
let request_data = create_request_data();
|
|
982
|
+
let result = di_handler.call(request, request_data).await;
|
|
983
|
+
|
|
984
|
+
assert!(result.is_ok());
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
#[tokio::test]
|
|
988
|
+
async fn test_concurrent_resolution_with_varying_dependency_counts() {
|
|
989
|
+
let mut container = DependencyContainer::new();
|
|
990
|
+
for i in 0..10 {
|
|
991
|
+
container
|
|
992
|
+
.register(
|
|
993
|
+
format!("svc_{}", i),
|
|
994
|
+
Arc::new(ValueDependency::new(&format!("svc_{}", i), format!("s_{}", i))),
|
|
995
|
+
)
|
|
996
|
+
.unwrap();
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
let shared_container = Arc::new(container);
|
|
1000
|
+
|
|
1001
|
+
let mut handles = vec![];
|
|
1002
|
+
for i in 0..10 {
|
|
1003
|
+
let container = Arc::clone(&shared_container);
|
|
1004
|
+
let handler = Arc::new(TestHandler);
|
|
1005
|
+
|
|
1006
|
+
let required: Vec<String> = (0..=(i % 5)).map(|j| format!("svc_{}", j)).collect();
|
|
1007
|
+
|
|
1008
|
+
let di_handler = DependencyInjectingHandler::new(handler, container, required);
|
|
1009
|
+
|
|
1010
|
+
let handle = tokio::spawn(async move {
|
|
1011
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1012
|
+
let request_data = create_request_data();
|
|
1013
|
+
di_handler.call(request, request_data).await
|
|
1014
|
+
});
|
|
1015
|
+
handles.push(handle);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
for handle in handles {
|
|
1019
|
+
let result = handle.await.unwrap();
|
|
1020
|
+
assert!(result.is_ok());
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
#[tokio::test]
|
|
1025
|
+
async fn test_request_data_isolation_across_concurrent_requests() {
|
|
1026
|
+
let mut container = DependencyContainer::new();
|
|
1027
|
+
container
|
|
1028
|
+
.register("config".to_string(), Arc::new(ValueDependency::new("config", "test")))
|
|
1029
|
+
.unwrap();
|
|
1030
|
+
|
|
1031
|
+
let shared_container = Arc::new(container);
|
|
1032
|
+
let handler = Arc::new(TestHandler);
|
|
1033
|
+
let di_handler = Arc::new(DependencyInjectingHandler::new(
|
|
1034
|
+
handler,
|
|
1035
|
+
shared_container,
|
|
1036
|
+
vec!["config".to_string()],
|
|
1037
|
+
));
|
|
1038
|
+
|
|
1039
|
+
let mut handles = vec![];
|
|
1040
|
+
for i in 0..10 {
|
|
1041
|
+
let di_handler = Arc::clone(&di_handler);
|
|
1042
|
+
let handle = tokio::spawn(async move {
|
|
1043
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1044
|
+
let mut request_data = create_request_data();
|
|
1045
|
+
request_data.path = format!("/path/{}", i);
|
|
1046
|
+
di_handler.call(request, request_data).await
|
|
1047
|
+
});
|
|
1048
|
+
handles.push(handle);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
for handle in handles {
|
|
1052
|
+
let result = handle.await.unwrap();
|
|
1053
|
+
assert!(result.is_ok());
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
#[tokio::test]
|
|
1058
|
+
async fn test_missing_dependency_error_json_format() {
|
|
1059
|
+
let container = DependencyContainer::new();
|
|
1060
|
+
let handler = Arc::new(TestHandler);
|
|
1061
|
+
let di_handler =
|
|
1062
|
+
DependencyInjectingHandler::new(handler, Arc::new(container), vec!["missing_service".to_string()]);
|
|
1063
|
+
|
|
1064
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1065
|
+
let request_data = create_request_data();
|
|
1066
|
+
let result = di_handler.call(request, request_data).await;
|
|
1067
|
+
|
|
1068
|
+
assert!(result.is_ok());
|
|
1069
|
+
let response = result.unwrap();
|
|
1070
|
+
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
1071
|
+
assert_eq!(
|
|
1072
|
+
response.headers().get("Content-Type").and_then(|v| v.to_str().ok()),
|
|
1073
|
+
Some("application/json")
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
#[tokio::test]
|
|
1078
|
+
async fn test_many_sequential_requests_same_handler_state() {
|
|
1079
|
+
let mut container = DependencyContainer::new();
|
|
1080
|
+
container
|
|
1081
|
+
.register("state".to_string(), Arc::new(ValueDependency::new("state", "initial")))
|
|
1082
|
+
.unwrap();
|
|
1083
|
+
|
|
1084
|
+
let handler = Arc::new(TestHandler);
|
|
1085
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["state".to_string()]);
|
|
1086
|
+
|
|
1087
|
+
for _ in 0..50 {
|
|
1088
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1089
|
+
let request_data = create_request_data();
|
|
1090
|
+
let result = di_handler.call(request, request_data).await;
|
|
1091
|
+
|
|
1092
|
+
assert!(result.is_ok());
|
|
1093
|
+
let response = result.unwrap();
|
|
1094
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
#[tokio::test]
|
|
1099
|
+
async fn test_dependency_availability_after_resolution() {
|
|
1100
|
+
let mut container = DependencyContainer::new();
|
|
1101
|
+
container
|
|
1102
|
+
.register(
|
|
1103
|
+
"service".to_string(),
|
|
1104
|
+
Arc::new(ValueDependency::new("service", "my_service")),
|
|
1105
|
+
)
|
|
1106
|
+
.unwrap();
|
|
1107
|
+
|
|
1108
|
+
let handler = Arc::new(ReadDependencyHandler);
|
|
1109
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["service".to_string()]);
|
|
1110
|
+
|
|
1111
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1112
|
+
let request_data = create_request_data();
|
|
1113
|
+
let result = di_handler.call(request, request_data).await;
|
|
1114
|
+
|
|
1115
|
+
assert!(result.is_ok());
|
|
1116
|
+
let response = result.unwrap();
|
|
1117
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
#[tokio::test]
|
|
1121
|
+
async fn test_container_keys_availability_during_resolution() {
|
|
1122
|
+
let mut container = DependencyContainer::new();
|
|
1123
|
+
container
|
|
1124
|
+
.register("key1".to_string(), Arc::new(ValueDependency::new("key1", "val1")))
|
|
1125
|
+
.unwrap();
|
|
1126
|
+
container
|
|
1127
|
+
.register("key2".to_string(), Arc::new(ValueDependency::new("key2", "val2")))
|
|
1128
|
+
.unwrap();
|
|
1129
|
+
|
|
1130
|
+
let handler = Arc::new(TestHandler);
|
|
1131
|
+
let di_handler = DependencyInjectingHandler::new(
|
|
1132
|
+
handler,
|
|
1133
|
+
Arc::new(container),
|
|
1134
|
+
vec!["key1".to_string(), "key2".to_string()],
|
|
1135
|
+
);
|
|
1136
|
+
|
|
1137
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1138
|
+
let request_data = create_request_data();
|
|
1139
|
+
let result = di_handler.call(request, request_data).await;
|
|
1140
|
+
|
|
1141
|
+
assert!(result.is_ok());
|
|
1142
|
+
let response = result.unwrap();
|
|
1143
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
#[tokio::test]
|
|
1147
|
+
async fn test_post_request_with_dependencies() {
|
|
1148
|
+
let mut container = DependencyContainer::new();
|
|
1149
|
+
container
|
|
1150
|
+
.register(
|
|
1151
|
+
"validator".to_string(),
|
|
1152
|
+
Arc::new(ValueDependency::new("validator", "strict_mode")),
|
|
1153
|
+
)
|
|
1154
|
+
.unwrap();
|
|
1155
|
+
|
|
1156
|
+
let handler = Arc::new(TestHandler);
|
|
1157
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["validator".to_string()]);
|
|
1158
|
+
|
|
1159
|
+
let request = Request::builder()
|
|
1160
|
+
.method("POST")
|
|
1161
|
+
.header("Content-Type", "application/json")
|
|
1162
|
+
.body(Body::from(r#"{"key":"value"}"#))
|
|
1163
|
+
.unwrap();
|
|
1164
|
+
let mut request_data = create_request_data();
|
|
1165
|
+
request_data.method = "POST".to_string();
|
|
1166
|
+
request_data.body = serde_json::json!({"key": "value"});
|
|
1167
|
+
|
|
1168
|
+
let result = di_handler.call(request, request_data).await;
|
|
1169
|
+
|
|
1170
|
+
assert!(result.is_ok());
|
|
1171
|
+
let response = result.unwrap();
|
|
1172
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
#[tokio::test]
|
|
1176
|
+
async fn test_delete_request_with_authorization_dependency() {
|
|
1177
|
+
let mut container = DependencyContainer::new();
|
|
1178
|
+
container
|
|
1179
|
+
.register(
|
|
1180
|
+
"auth".to_string(),
|
|
1181
|
+
Arc::new(ValueDependency::new("auth", "bearer_token")),
|
|
1182
|
+
)
|
|
1183
|
+
.unwrap();
|
|
1184
|
+
|
|
1185
|
+
let handler = Arc::new(TestHandler);
|
|
1186
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["auth".to_string()]);
|
|
1187
|
+
|
|
1188
|
+
let request = Request::builder().method("DELETE").body(Body::empty()).unwrap();
|
|
1189
|
+
let mut request_data = create_request_data();
|
|
1190
|
+
request_data.method = "DELETE".to_string();
|
|
1191
|
+
request_data.path = "/resource/123".to_string();
|
|
1192
|
+
|
|
1193
|
+
let result = di_handler.call(request, request_data).await;
|
|
1194
|
+
|
|
1195
|
+
assert!(result.is_ok());
|
|
1196
|
+
let response = result.unwrap();
|
|
1197
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
#[tokio::test]
|
|
1201
|
+
async fn test_very_large_number_of_dependencies_in_single_handler() {
|
|
1202
|
+
let mut container = DependencyContainer::new();
|
|
1203
|
+
let mut required_deps = vec![];
|
|
1204
|
+
for i in 0..50 {
|
|
1205
|
+
let key = format!("dep_{}", i);
|
|
1206
|
+
container
|
|
1207
|
+
.register(
|
|
1208
|
+
key.clone(),
|
|
1209
|
+
Arc::new(ValueDependency::new(&key, format!("value_{}", i))),
|
|
1210
|
+
)
|
|
1211
|
+
.unwrap();
|
|
1212
|
+
required_deps.push(key);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
let handler = Arc::new(TestHandler);
|
|
1216
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), required_deps);
|
|
1217
|
+
|
|
1218
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1219
|
+
let request_data = create_request_data();
|
|
1220
|
+
let result = di_handler.call(request, request_data).await;
|
|
1221
|
+
|
|
1222
|
+
assert!(result.is_ok());
|
|
1223
|
+
let response = result.unwrap();
|
|
1224
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
#[tokio::test]
|
|
1228
|
+
async fn test_handler_cloning_with_same_container() {
|
|
1229
|
+
let mut container = DependencyContainer::new();
|
|
1230
|
+
container
|
|
1231
|
+
.register("svc".to_string(), Arc::new(ValueDependency::new("svc", "service")))
|
|
1232
|
+
.unwrap();
|
|
1233
|
+
|
|
1234
|
+
let shared_container = Arc::new(container);
|
|
1235
|
+
let base_handler: Arc<dyn Handler> = Arc::new(TestHandler);
|
|
1236
|
+
|
|
1237
|
+
let di_handler1 = Arc::new(DependencyInjectingHandler::new(
|
|
1238
|
+
base_handler.clone(),
|
|
1239
|
+
Arc::clone(&shared_container),
|
|
1240
|
+
vec!["svc".to_string()],
|
|
1241
|
+
));
|
|
1242
|
+
|
|
1243
|
+
let di_handler2 = Arc::new(DependencyInjectingHandler::new(
|
|
1244
|
+
base_handler.clone(),
|
|
1245
|
+
Arc::clone(&shared_container),
|
|
1246
|
+
vec!["svc".to_string()],
|
|
1247
|
+
));
|
|
1248
|
+
|
|
1249
|
+
let handle1 = tokio::spawn({
|
|
1250
|
+
let dih = Arc::clone(&di_handler1);
|
|
1251
|
+
async move {
|
|
1252
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1253
|
+
let request_data = create_request_data();
|
|
1254
|
+
dih.call(request, request_data).await
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
let handle2 = tokio::spawn({
|
|
1259
|
+
let dih = Arc::clone(&di_handler2);
|
|
1260
|
+
async move {
|
|
1261
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1262
|
+
let request_data = create_request_data();
|
|
1263
|
+
dih.call(request, request_data).await
|
|
1264
|
+
}
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1267
|
+
assert!(handle1.await.unwrap().is_ok());
|
|
1268
|
+
assert!(handle2.await.unwrap().is_ok());
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
#[tokio::test]
|
|
1272
|
+
async fn test_request_parts_reconstruction_correctness() {
|
|
1273
|
+
let mut container = DependencyContainer::new();
|
|
1274
|
+
container
|
|
1275
|
+
.register("config".to_string(), Arc::new(ValueDependency::new("config", "test")))
|
|
1276
|
+
.unwrap();
|
|
1277
|
+
|
|
1278
|
+
let handler = Arc::new(TestHandler);
|
|
1279
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
1280
|
+
|
|
1281
|
+
let request = Request::builder()
|
|
1282
|
+
.method("GET")
|
|
1283
|
+
.header("User-Agent", "test-client")
|
|
1284
|
+
.header("Accept", "application/json")
|
|
1285
|
+
.body(Body::empty())
|
|
1286
|
+
.unwrap();
|
|
1287
|
+
let mut request_data = create_request_data();
|
|
1288
|
+
request_data.method = "GET".to_string();
|
|
1289
|
+
|
|
1290
|
+
let result = di_handler.call(request, request_data).await;
|
|
1291
|
+
|
|
1292
|
+
assert!(result.is_ok());
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
#[tokio::test]
|
|
1296
|
+
async fn test_resolution_failure_returns_service_unavailable() {
|
|
1297
|
+
let mut container = DependencyContainer::new();
|
|
1298
|
+
container
|
|
1299
|
+
.register(
|
|
1300
|
+
"external_api".to_string(),
|
|
1301
|
+
Arc::new(ValueDependency::new("external_api", "unavailable")),
|
|
1302
|
+
)
|
|
1303
|
+
.unwrap();
|
|
1304
|
+
|
|
1305
|
+
let handler = Arc::new(TestHandler);
|
|
1306
|
+
let di_handler =
|
|
1307
|
+
DependencyInjectingHandler::new(handler, Arc::new(container), vec!["external_api".to_string()]);
|
|
1308
|
+
|
|
1309
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1310
|
+
let request_data = create_request_data();
|
|
1311
|
+
let result = di_handler.call(request, request_data).await;
|
|
1312
|
+
|
|
1313
|
+
assert!(result.is_ok());
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
#[tokio::test]
|
|
1317
|
+
async fn test_multiple_missing_dependencies_reports_first() {
|
|
1318
|
+
let container = DependencyContainer::new();
|
|
1319
|
+
let handler = Arc::new(TestHandler);
|
|
1320
|
+
let di_handler = DependencyInjectingHandler::new(
|
|
1321
|
+
handler,
|
|
1322
|
+
Arc::new(container),
|
|
1323
|
+
vec![
|
|
1324
|
+
"missing_a".to_string(),
|
|
1325
|
+
"missing_b".to_string(),
|
|
1326
|
+
"missing_c".to_string(),
|
|
1327
|
+
],
|
|
1328
|
+
);
|
|
1329
|
+
|
|
1330
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1331
|
+
let request_data = create_request_data();
|
|
1332
|
+
let result = di_handler.call(request, request_data).await;
|
|
1333
|
+
|
|
1334
|
+
assert!(result.is_ok());
|
|
1335
|
+
let response = result.unwrap();
|
|
1336
|
+
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
#[tokio::test]
|
|
1340
|
+
async fn test_required_dependencies_getter_consistency() {
|
|
1341
|
+
let deps = vec![
|
|
1342
|
+
"dep_a".to_string(),
|
|
1343
|
+
"dep_b".to_string(),
|
|
1344
|
+
"dep_c".to_string(),
|
|
1345
|
+
"dep_d".to_string(),
|
|
1346
|
+
];
|
|
1347
|
+
let container = DependencyContainer::new();
|
|
1348
|
+
let handler = Arc::new(TestHandler);
|
|
1349
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), deps.clone());
|
|
1350
|
+
|
|
1351
|
+
let returned_deps = di_handler.required_dependencies();
|
|
1352
|
+
assert_eq!(returned_deps.len(), 4);
|
|
1353
|
+
assert_eq!(returned_deps, deps.as_slice());
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
#[tokio::test]
|
|
1357
|
+
async fn test_concurrent_error_handlers_isolation() {
|
|
1358
|
+
let container = DependencyContainer::new();
|
|
1359
|
+
let handler = Arc::new(ErrorHandler);
|
|
1360
|
+
let di_handler = Arc::new(DependencyInjectingHandler::new(handler, Arc::new(container), vec![]));
|
|
1361
|
+
|
|
1362
|
+
let handles: Vec<_> = (0..10)
|
|
1363
|
+
.map(|_| {
|
|
1364
|
+
let dih = Arc::clone(&di_handler);
|
|
1365
|
+
tokio::spawn(async move {
|
|
1366
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1367
|
+
let request_data = create_request_data();
|
|
1368
|
+
dih.call(request, request_data).await
|
|
1369
|
+
})
|
|
1370
|
+
})
|
|
1371
|
+
.collect();
|
|
1372
|
+
|
|
1373
|
+
for handle in handles {
|
|
1374
|
+
let result = handle.await.unwrap();
|
|
1375
|
+
assert!(result.is_err());
|
|
1376
|
+
let (status, msg) = result.unwrap_err();
|
|
1377
|
+
assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR);
|
|
1378
|
+
assert!(msg.contains("inner handler error"));
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
#[tokio::test]
|
|
1383
|
+
async fn test_patch_request_with_dependencies() {
|
|
1384
|
+
let mut container = DependencyContainer::new();
|
|
1385
|
+
container
|
|
1386
|
+
.register(
|
|
1387
|
+
"merger".to_string(),
|
|
1388
|
+
Arc::new(ValueDependency::new("merger", "strategic_merge")),
|
|
1389
|
+
)
|
|
1390
|
+
.unwrap();
|
|
1391
|
+
|
|
1392
|
+
let handler = Arc::new(TestHandler);
|
|
1393
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["merger".to_string()]);
|
|
1394
|
+
|
|
1395
|
+
let request = Request::builder().method("PATCH").body(Body::empty()).unwrap();
|
|
1396
|
+
let mut request_data = create_request_data();
|
|
1397
|
+
request_data.method = "PATCH".to_string();
|
|
1398
|
+
|
|
1399
|
+
let result = di_handler.call(request, request_data).await;
|
|
1400
|
+
|
|
1401
|
+
assert!(result.is_ok());
|
|
1402
|
+
let response = result.unwrap();
|
|
1403
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
#[tokio::test]
|
|
1407
|
+
async fn test_handler_receives_enriched_request_data_with_multiple_deps() {
|
|
1408
|
+
let mut container = DependencyContainer::new();
|
|
1409
|
+
for i in 0..5 {
|
|
1410
|
+
container
|
|
1411
|
+
.register(
|
|
1412
|
+
format!("svc_{}", i),
|
|
1413
|
+
Arc::new(ValueDependency::new(&format!("svc_{}", i), format!("s_{}", i))),
|
|
1414
|
+
)
|
|
1415
|
+
.unwrap();
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
let handler = Arc::new(ReadDependencyHandler);
|
|
1419
|
+
let required: Vec<String> = (0..5).map(|i| format!("svc_{}", i)).collect();
|
|
1420
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), required);
|
|
1421
|
+
|
|
1422
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1423
|
+
let request_data = create_request_data();
|
|
1424
|
+
let result = di_handler.call(request, request_data).await;
|
|
1425
|
+
|
|
1426
|
+
assert!(result.is_ok());
|
|
1427
|
+
let response = result.unwrap();
|
|
1428
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
#[tokio::test]
|
|
1432
|
+
async fn test_arc_try_unwrap_cleanup_branch() {
|
|
1433
|
+
let mut container = DependencyContainer::new();
|
|
1434
|
+
container
|
|
1435
|
+
.register(
|
|
1436
|
+
"resource".to_string(),
|
|
1437
|
+
Arc::new(ValueDependency::new("resource", "allocated")),
|
|
1438
|
+
)
|
|
1439
|
+
.unwrap();
|
|
1440
|
+
|
|
1441
|
+
let handler = Arc::new(TestHandler);
|
|
1442
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["resource".to_string()]);
|
|
1443
|
+
|
|
1444
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1445
|
+
let request_data = create_request_data();
|
|
1446
|
+
let result = di_handler.call(request, request_data).await;
|
|
1447
|
+
|
|
1448
|
+
assert!(result.is_ok());
|
|
1449
|
+
let response = result.unwrap();
|
|
1450
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
#[tokio::test]
|
|
1454
|
+
async fn test_head_request_with_dependencies() {
|
|
1455
|
+
let mut container = DependencyContainer::new();
|
|
1456
|
+
container
|
|
1457
|
+
.register(
|
|
1458
|
+
"metadata".to_string(),
|
|
1459
|
+
Arc::new(ValueDependency::new("metadata", "headers_only")),
|
|
1460
|
+
)
|
|
1461
|
+
.unwrap();
|
|
1462
|
+
|
|
1463
|
+
let handler = Arc::new(TestHandler);
|
|
1464
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["metadata".to_string()]);
|
|
1465
|
+
|
|
1466
|
+
let request = Request::builder().method("HEAD").body(Body::empty()).unwrap();
|
|
1467
|
+
let mut request_data = create_request_data();
|
|
1468
|
+
request_data.method = "HEAD".to_string();
|
|
1469
|
+
|
|
1470
|
+
let result = di_handler.call(request, request_data).await;
|
|
1471
|
+
|
|
1472
|
+
assert!(result.is_ok());
|
|
1473
|
+
let response = result.unwrap();
|
|
1474
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
#[tokio::test]
|
|
1478
|
+
async fn test_options_request_with_dependencies() {
|
|
1479
|
+
let mut container = DependencyContainer::new();
|
|
1480
|
+
container
|
|
1481
|
+
.register("cors".to_string(), Arc::new(ValueDependency::new("cors", "permissive")))
|
|
1482
|
+
.unwrap();
|
|
1483
|
+
|
|
1484
|
+
let handler = Arc::new(TestHandler);
|
|
1485
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["cors".to_string()]);
|
|
1486
|
+
|
|
1487
|
+
let request = Request::builder().method("OPTIONS").body(Body::empty()).unwrap();
|
|
1488
|
+
let mut request_data = create_request_data();
|
|
1489
|
+
request_data.method = "OPTIONS".to_string();
|
|
1490
|
+
|
|
1491
|
+
let result = di_handler.call(request, request_data).await;
|
|
1492
|
+
|
|
1493
|
+
assert!(result.is_ok());
|
|
1494
|
+
let response = result.unwrap();
|
|
1495
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
#[tokio::test]
|
|
1499
|
+
async fn test_circular_dependency_error_json_structure() {
|
|
1500
|
+
let container = DependencyContainer::new();
|
|
1501
|
+
let handler = Arc::new(TestHandler);
|
|
1502
|
+
|
|
1503
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["missing".to_string()]);
|
|
1504
|
+
|
|
1505
|
+
let request = Request::builder().body(Body::empty()).unwrap();
|
|
1506
|
+
let request_data = create_request_data();
|
|
1507
|
+
let result = di_handler.call(request, request_data).await;
|
|
1508
|
+
|
|
1509
|
+
assert!(result.is_ok());
|
|
1510
|
+
let response = result.unwrap();
|
|
1511
|
+
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
1512
|
+
|
|
1513
|
+
let content_type = response.headers().get("Content-Type").and_then(|v| v.to_str().ok());
|
|
1514
|
+
assert_eq!(content_type, Some("application/json"));
|
|
1515
|
+
|
|
1516
|
+
let body_bytes = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap();
|
|
1517
|
+
let json_body: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap();
|
|
1518
|
+
|
|
1519
|
+
assert!(json_body.get("type").is_some(), "type field must be present");
|
|
1520
|
+
assert!(json_body.get("title").is_some(), "title field must be present");
|
|
1521
|
+
assert!(json_body.get("detail").is_some(), "detail field must be present");
|
|
1522
|
+
assert!(json_body.get("status").is_some(), "status field must be present");
|
|
1523
|
+
|
|
1524
|
+
assert_eq!(json_body.get("status").and_then(|v| v.as_i64()), Some(500));
|
|
1525
|
+
assert_eq!(
|
|
1526
|
+
json_body.get("type").and_then(|v| v.as_str()),
|
|
1527
|
+
Some("https://spikard.dev/errors/dependency-error")
|
|
1528
|
+
);
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
#[tokio::test]
|
|
1532
|
+
async fn test_request_data_is_cloned_not_moved_to_handler() {
|
|
1533
|
+
let mut container = DependencyContainer::new();
|
|
1534
|
+
container
|
|
1535
|
+
.register(
|
|
1536
|
+
"service".to_string(),
|
|
1537
|
+
Arc::new(ValueDependency::new("service", "test_service")),
|
|
1538
|
+
)
|
|
1539
|
+
.unwrap();
|
|
1540
|
+
|
|
1541
|
+
let handler = Arc::new(ReadDependencyHandler);
|
|
1542
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["service".to_string()]);
|
|
1543
|
+
|
|
1544
|
+
let mut original_request_data = create_request_data();
|
|
1545
|
+
original_request_data.path = "/api/test".to_string();
|
|
1546
|
+
original_request_data.method = "POST".to_string();
|
|
1547
|
+
|
|
1548
|
+
let mut headers = HashMap::new();
|
|
1549
|
+
headers.insert("X-Custom-Header".to_string(), "custom-value".to_string());
|
|
1550
|
+
original_request_data.headers = Arc::new(headers.clone());
|
|
1551
|
+
|
|
1552
|
+
let mut cookies = HashMap::new();
|
|
1553
|
+
cookies.insert("session_id".to_string(), "test-session".to_string());
|
|
1554
|
+
original_request_data.cookies = Arc::new(cookies.clone());
|
|
1555
|
+
|
|
1556
|
+
let original_path = original_request_data.path.clone();
|
|
1557
|
+
let original_method = original_request_data.method.clone();
|
|
1558
|
+
|
|
1559
|
+
let request = Request::builder().method("POST").body(Body::empty()).unwrap();
|
|
1560
|
+
let request_data_clone = original_request_data.clone();
|
|
1561
|
+
let result = di_handler.call(request, original_request_data).await;
|
|
1562
|
+
|
|
1563
|
+
assert!(result.is_ok());
|
|
1564
|
+
|
|
1565
|
+
assert_eq!(request_data_clone.path, original_path);
|
|
1566
|
+
assert_eq!(request_data_clone.method, original_method);
|
|
1567
|
+
|
|
1568
|
+
assert!(request_data_clone.dependencies.is_none());
|
|
1569
|
+
|
|
1570
|
+
assert_eq!(*request_data_clone.headers, headers);
|
|
1571
|
+
assert_eq!(*request_data_clone.cookies, cookies);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
#[tokio::test]
|
|
1575
|
+
async fn test_core_request_data_conversion_preserves_all_fields() {
|
|
1576
|
+
let mut container = DependencyContainer::new();
|
|
1577
|
+
container
|
|
1578
|
+
.register(
|
|
1579
|
+
"config".to_string(),
|
|
1580
|
+
Arc::new(ValueDependency::new("config", "test_config")),
|
|
1581
|
+
)
|
|
1582
|
+
.unwrap();
|
|
1583
|
+
|
|
1584
|
+
let handler = Arc::new(TestHandler);
|
|
1585
|
+
let di_handler = DependencyInjectingHandler::new(handler, Arc::new(container), vec!["config".to_string()]);
|
|
1586
|
+
|
|
1587
|
+
let mut path_params = HashMap::new();
|
|
1588
|
+
path_params.insert("id".to_string(), "123".to_string());
|
|
1589
|
+
path_params.insert("resource".to_string(), "users".to_string());
|
|
1590
|
+
|
|
1591
|
+
let mut raw_query_params = HashMap::new();
|
|
1592
|
+
raw_query_params.insert("filter".to_string(), vec!["active".to_string()]);
|
|
1593
|
+
raw_query_params.insert("sort".to_string(), vec!["name".to_string(), "asc".to_string()]);
|
|
1594
|
+
|
|
1595
|
+
let mut headers = HashMap::new();
|
|
1596
|
+
headers.insert("Authorization".to_string(), "Bearer token123".to_string());
|
|
1597
|
+
headers.insert("Content-Type".to_string(), "application/json".to_string());
|
|
1598
|
+
|
|
1599
|
+
let mut cookies = HashMap::new();
|
|
1600
|
+
cookies.insert("session".to_string(), "abc123".to_string());
|
|
1601
|
+
cookies.insert("preferences".to_string(), "dark_mode".to_string());
|
|
1602
|
+
|
|
1603
|
+
let request_data = RequestData {
|
|
1604
|
+
path_params: Arc::new(path_params.clone()),
|
|
1605
|
+
query_params: serde_json::json!({"filter": "active", "sort": "name"}),
|
|
1606
|
+
validated_params: None,
|
|
1607
|
+
raw_query_params: Arc::new(raw_query_params.clone()),
|
|
1608
|
+
body: serde_json::json!({"name": "John", "email": "john@example.com"}),
|
|
1609
|
+
raw_body: Some(bytes::Bytes::from(r#"{"name":"John","email":"john@example.com"}"#)),
|
|
1610
|
+
headers: Arc::new(headers.clone()),
|
|
1611
|
+
cookies: Arc::new(cookies.clone()),
|
|
1612
|
+
method: "POST".to_string(),
|
|
1613
|
+
path: "/api/users/123".to_string(),
|
|
1614
|
+
#[cfg(feature = "di")]
|
|
1615
|
+
dependencies: None,
|
|
1616
|
+
};
|
|
1617
|
+
|
|
1618
|
+
let original_path = request_data.path.clone();
|
|
1619
|
+
let original_method = request_data.method.clone();
|
|
1620
|
+
let original_body = request_data.body.clone();
|
|
1621
|
+
let original_query_params = request_data.query_params.clone();
|
|
1622
|
+
|
|
1623
|
+
let request = Request::builder().method("POST").body(Body::empty()).unwrap();
|
|
1624
|
+
let result = di_handler.call(request, request_data.clone()).await;
|
|
1625
|
+
|
|
1626
|
+
assert!(result.is_ok());
|
|
1627
|
+
let response = result.unwrap();
|
|
1628
|
+
assert_eq!(response.status(), StatusCode::OK);
|
|
1629
|
+
|
|
1630
|
+
assert_eq!(request_data.path, original_path, "path field must be preserved");
|
|
1631
|
+
assert_eq!(request_data.method, original_method, "method field must be preserved");
|
|
1632
|
+
assert_eq!(request_data.body, original_body, "body field must be preserved");
|
|
1633
|
+
assert_eq!(
|
|
1634
|
+
request_data.query_params, original_query_params,
|
|
1635
|
+
"query_params must be preserved"
|
|
1636
|
+
);
|
|
1637
|
+
|
|
1638
|
+
assert_eq!(request_data.path_params.get("id"), Some(&"123".to_string()));
|
|
1639
|
+
assert_eq!(request_data.path_params.get("resource"), Some(&"users".to_string()));
|
|
1640
|
+
|
|
1641
|
+
assert_eq!(
|
|
1642
|
+
request_data.raw_query_params.get("filter"),
|
|
1643
|
+
Some(&vec!["active".to_string()])
|
|
1644
|
+
);
|
|
1645
|
+
assert_eq!(
|
|
1646
|
+
request_data.raw_query_params.get("sort"),
|
|
1647
|
+
Some(&vec!["name".to_string(), "asc".to_string()])
|
|
1648
|
+
);
|
|
1649
|
+
|
|
1650
|
+
assert_eq!(
|
|
1651
|
+
request_data.headers.get("Authorization"),
|
|
1652
|
+
Some(&"Bearer token123".to_string())
|
|
1653
|
+
);
|
|
1654
|
+
assert_eq!(
|
|
1655
|
+
request_data.headers.get("Content-Type"),
|
|
1656
|
+
Some(&"application/json".to_string())
|
|
1657
|
+
);
|
|
1658
|
+
|
|
1659
|
+
assert_eq!(request_data.cookies.get("session"), Some(&"abc123".to_string()));
|
|
1660
|
+
assert_eq!(request_data.cookies.get("preferences"), Some(&"dark_mode".to_string()));
|
|
1661
|
+
|
|
1662
|
+
assert!(request_data.raw_body.is_some());
|
|
1663
|
+
assert_eq!(
|
|
1664
|
+
request_data.raw_body.as_ref().unwrap().as_ref(),
|
|
1665
|
+
r#"{"name":"John","email":"john@example.com"}"#.as_bytes()
|
|
1666
|
+
);
|
|
1667
|
+
}
|
|
1668
|
+
}
|