spikard 0.13.0 → 0.15.2

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