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.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +674 -674
  4. data/ext/spikard_rb/Cargo.toml +17 -17
  5. data/ext/spikard_rb/extconf.rb +13 -10
  6. data/ext/spikard_rb/src/lib.rs +6 -6
  7. data/lib/spikard/app.rb +405 -405
  8. data/lib/spikard/background.rb +27 -27
  9. data/lib/spikard/config.rb +396 -396
  10. data/lib/spikard/converters.rb +13 -13
  11. data/lib/spikard/handler_wrapper.rb +113 -113
  12. data/lib/spikard/provide.rb +214 -214
  13. data/lib/spikard/response.rb +173 -173
  14. data/lib/spikard/schema.rb +243 -243
  15. data/lib/spikard/sse.rb +111 -111
  16. data/lib/spikard/streaming_response.rb +44 -44
  17. data/lib/spikard/testing.rb +256 -256
  18. data/lib/spikard/upload_file.rb +131 -131
  19. data/lib/spikard/version.rb +5 -5
  20. data/lib/spikard/websocket.rb +59 -59
  21. data/lib/spikard.rb +43 -43
  22. data/sig/spikard.rbs +366 -366
  23. data/vendor/crates/spikard-bindings-shared/Cargo.toml +63 -63
  24. data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +132 -132
  25. data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +752 -752
  26. data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +194 -194
  27. data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +246 -246
  28. data/vendor/crates/spikard-bindings-shared/src/error_response.rs +401 -401
  29. data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +238 -238
  30. data/vendor/crates/spikard-bindings-shared/src/lib.rs +24 -24
  31. data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +292 -292
  32. data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +616 -616
  33. data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +305 -305
  34. data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +248 -248
  35. data/vendor/crates/spikard-bindings-shared/src/validation_helpers.rs +351 -351
  36. data/vendor/crates/spikard-bindings-shared/tests/comprehensive_coverage.rs +454 -454
  37. data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +383 -383
  38. data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +280 -280
  39. data/vendor/crates/spikard-core/Cargo.toml +40 -40
  40. data/vendor/crates/spikard-core/src/bindings/mod.rs +3 -3
  41. data/vendor/crates/spikard-core/src/bindings/response.rs +133 -133
  42. data/vendor/crates/spikard-core/src/debug.rs +127 -127
  43. data/vendor/crates/spikard-core/src/di/container.rs +702 -702
  44. data/vendor/crates/spikard-core/src/di/dependency.rs +273 -273
  45. data/vendor/crates/spikard-core/src/di/error.rs +118 -118
  46. data/vendor/crates/spikard-core/src/di/factory.rs +534 -534
  47. data/vendor/crates/spikard-core/src/di/graph.rs +506 -506
  48. data/vendor/crates/spikard-core/src/di/mod.rs +192 -192
  49. data/vendor/crates/spikard-core/src/di/resolved.rs +405 -405
  50. data/vendor/crates/spikard-core/src/di/value.rs +281 -281
  51. data/vendor/crates/spikard-core/src/errors.rs +69 -69
  52. data/vendor/crates/spikard-core/src/http.rs +415 -415
  53. data/vendor/crates/spikard-core/src/lib.rs +29 -29
  54. data/vendor/crates/spikard-core/src/lifecycle.rs +1186 -1186
  55. data/vendor/crates/spikard-core/src/metadata.rs +389 -389
  56. data/vendor/crates/spikard-core/src/parameters.rs +2525 -2525
  57. data/vendor/crates/spikard-core/src/problem.rs +344 -344
  58. data/vendor/crates/spikard-core/src/request_data.rs +1154 -1154
  59. data/vendor/crates/spikard-core/src/router.rs +510 -510
  60. data/vendor/crates/spikard-core/src/schema_registry.rs +183 -183
  61. data/vendor/crates/spikard-core/src/type_hints.rs +304 -304
  62. data/vendor/crates/spikard-core/src/validation/error_mapper.rs +696 -688
  63. data/vendor/crates/spikard-core/src/validation/mod.rs +457 -457
  64. data/vendor/crates/spikard-http/Cargo.toml +62 -64
  65. data/vendor/crates/spikard-http/examples/sse-notifications.rs +148 -148
  66. data/vendor/crates/spikard-http/examples/websocket-chat.rs +92 -92
  67. data/vendor/crates/spikard-http/src/auth.rs +296 -296
  68. data/vendor/crates/spikard-http/src/background.rs +1860 -1860
  69. data/vendor/crates/spikard-http/src/bindings/mod.rs +3 -3
  70. data/vendor/crates/spikard-http/src/bindings/response.rs +1 -1
  71. data/vendor/crates/spikard-http/src/body_metadata.rs +8 -8
  72. data/vendor/crates/spikard-http/src/cors.rs +1005 -1005
  73. data/vendor/crates/spikard-http/src/debug.rs +128 -128
  74. data/vendor/crates/spikard-http/src/di_handler.rs +1668 -1668
  75. data/vendor/crates/spikard-http/src/handler_response.rs +901 -901
  76. data/vendor/crates/spikard-http/src/handler_trait.rs +838 -830
  77. data/vendor/crates/spikard-http/src/handler_trait_tests.rs +290 -290
  78. data/vendor/crates/spikard-http/src/lib.rs +534 -534
  79. data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +230 -230
  80. data/vendor/crates/spikard-http/src/lifecycle.rs +1193 -1193
  81. data/vendor/crates/spikard-http/src/middleware/mod.rs +560 -540
  82. data/vendor/crates/spikard-http/src/middleware/multipart.rs +912 -912
  83. data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +513 -513
  84. data/vendor/crates/spikard-http/src/middleware/validation.rs +768 -735
  85. data/vendor/crates/spikard-http/src/openapi/mod.rs +309 -309
  86. data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +535 -535
  87. data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +1363 -1363
  88. data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +665 -665
  89. data/vendor/crates/spikard-http/src/query_parser.rs +793 -793
  90. data/vendor/crates/spikard-http/src/response.rs +720 -720
  91. data/vendor/crates/spikard-http/src/server/handler.rs +1650 -1650
  92. data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +234 -234
  93. data/vendor/crates/spikard-http/src/server/mod.rs +1593 -1502
  94. data/vendor/crates/spikard-http/src/server/request_extraction.rs +789 -770
  95. data/vendor/crates/spikard-http/src/server/routing_factory.rs +629 -599
  96. data/vendor/crates/spikard-http/src/sse.rs +1409 -1409
  97. data/vendor/crates/spikard-http/src/testing/form.rs +52 -52
  98. data/vendor/crates/spikard-http/src/testing/multipart.rs +64 -60
  99. data/vendor/crates/spikard-http/src/testing/test_client.rs +311 -283
  100. data/vendor/crates/spikard-http/src/testing.rs +406 -377
  101. data/vendor/crates/spikard-http/src/websocket.rs +1404 -1375
  102. data/vendor/crates/spikard-http/tests/background_behavior.rs +832 -832
  103. data/vendor/crates/spikard-http/tests/common/handlers.rs +309 -309
  104. data/vendor/crates/spikard-http/tests/common/mod.rs +26 -26
  105. data/vendor/crates/spikard-http/tests/di_integration.rs +192 -192
  106. data/vendor/crates/spikard-http/tests/doc_snippets.rs +5 -5
  107. data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +1093 -1093
  108. data/vendor/crates/spikard-http/tests/multipart_behavior.rs +656 -656
  109. data/vendor/crates/spikard-http/tests/server_config_builder.rs +314 -314
  110. data/vendor/crates/spikard-http/tests/sse_behavior.rs +620 -620
  111. data/vendor/crates/spikard-http/tests/websocket_behavior.rs +663 -663
  112. data/vendor/crates/spikard-rb/Cargo.toml +48 -48
  113. data/vendor/crates/spikard-rb/build.rs +199 -199
  114. data/vendor/crates/spikard-rb/src/background.rs +63 -63
  115. data/vendor/crates/spikard-rb/src/config/mod.rs +5 -5
  116. data/vendor/crates/spikard-rb/src/config/server_config.rs +285 -285
  117. data/vendor/crates/spikard-rb/src/conversion.rs +554 -554
  118. data/vendor/crates/spikard-rb/src/di/builder.rs +100 -100
  119. data/vendor/crates/spikard-rb/src/di/mod.rs +375 -375
  120. data/vendor/crates/spikard-rb/src/handler.rs +618 -618
  121. data/vendor/crates/spikard-rb/src/integration/mod.rs +3 -3
  122. data/vendor/crates/spikard-rb/src/lib.rs +1806 -1810
  123. data/vendor/crates/spikard-rb/src/lifecycle.rs +275 -275
  124. data/vendor/crates/spikard-rb/src/metadata/mod.rs +5 -5
  125. data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +442 -447
  126. data/vendor/crates/spikard-rb/src/runtime/mod.rs +5 -5
  127. data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +324 -324
  128. data/vendor/crates/spikard-rb/src/server.rs +305 -308
  129. data/vendor/crates/spikard-rb/src/sse.rs +231 -231
  130. data/vendor/crates/spikard-rb/src/testing/client.rs +538 -551
  131. data/vendor/crates/spikard-rb/src/testing/mod.rs +7 -7
  132. data/vendor/crates/spikard-rb/src/testing/sse.rs +143 -143
  133. data/vendor/crates/spikard-rb/src/testing/websocket.rs +608 -635
  134. data/vendor/crates/spikard-rb/src/websocket.rs +377 -374
  135. 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
+ }