spikard 0.12.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 (206) 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} +897 -451
  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 -45
  10. data/lib/spikard_rb.so +0 -0
  11. data/sig/types.rbs +427 -0
  12. metadata +14 -242
  13. data/LICENSE +0 -1
  14. data/README.md +0 -267
  15. data/ext/spikard_rb/Cargo.toml +0 -17
  16. data/lib/spikard/app.rb +0 -428
  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 -182
  21. data/lib/spikard/handler_wrapper.rb +0 -113
  22. data/lib/spikard/provide.rb +0 -214
  23. data/lib/spikard/response.rb +0 -173
  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 -432
  28. data/lib/spikard/upload_file.rb +0 -131
  29. data/lib/spikard/websocket.rb +0 -59
  30. data/sig/spikard.rbs +0 -719
  31. data/vendor/crates/spikard-bindings-shared/Cargo.toml +0 -80
  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 -60
  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 -702
  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 -538
  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 -87
  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 -1860
  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 -469
  99. data/vendor/crates/spikard-http/src/grpc/handler.rs +0 -1122
  100. data/vendor/crates/spikard-http/src/grpc/mod.rs +0 -434
  101. data/vendor/crates/spikard-http/src/grpc/service.rs +0 -622
  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 -58
  109. data/vendor/crates/spikard-http/src/jsonrpc/protocol.rs +0 -1207
  110. data/vendor/crates/spikard-http/src/jsonrpc/router.rs +0 -2262
  111. data/vendor/crates/spikard-http/src/lib.rs +0 -548
  112. data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +0 -230
  113. data/vendor/crates/spikard-http/src/lifecycle.rs +0 -1193
  114. data/vendor/crates/spikard-http/src/middleware/mod.rs +0 -560
  115. data/vendor/crates/spikard-http/src/middleware/multipart.rs +0 -912
  116. data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +0 -513
  117. data/vendor/crates/spikard-http/src/middleware/validation.rs +0 -768
  118. data/vendor/crates/spikard-http/src/openapi/mod.rs +0 -309
  119. data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +0 -535
  120. data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +0 -1363
  121. data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +0 -667
  122. data/vendor/crates/spikard-http/src/query_parser.rs +0 -793
  123. data/vendor/crates/spikard-http/src/response.rs +0 -720
  124. data/vendor/crates/spikard-http/src/server/fast_router.rs +0 -186
  125. data/vendor/crates/spikard-http/src/server/grpc_routing.rs +0 -858
  126. data/vendor/crates/spikard-http/src/server/handler.rs +0 -1661
  127. data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +0 -253
  128. data/vendor/crates/spikard-http/src/server/mod.rs +0 -1649
  129. data/vendor/crates/spikard-http/src/server/request_extraction.rs +0 -871
  130. data/vendor/crates/spikard-http/src/server/routing_factory.rs +0 -618
  131. data/vendor/crates/spikard-http/src/sse.rs +0 -1409
  132. data/vendor/crates/spikard-http/src/testing/form.rs +0 -52
  133. data/vendor/crates/spikard-http/src/testing/multipart.rs +0 -64
  134. data/vendor/crates/spikard-http/src/testing/test_client.rs +0 -787
  135. data/vendor/crates/spikard-http/src/testing.rs +0 -617
  136. data/vendor/crates/spikard-http/src/websocket.rs +0 -1477
  137. data/vendor/crates/spikard-http/tests/auth_integration.rs +0 -645
  138. data/vendor/crates/spikard-http/tests/background_behavior.rs +0 -832
  139. data/vendor/crates/spikard-http/tests/common/grpc_helpers.rs +0 -1012
  140. data/vendor/crates/spikard-http/tests/common/handlers.rs +0 -309
  141. data/vendor/crates/spikard-http/tests/common/mod.rs +0 -33
  142. data/vendor/crates/spikard-http/tests/common/test_builders.rs +0 -628
  143. data/vendor/crates/spikard-http/tests/di_handler_error_responses.rs +0 -162
  144. data/vendor/crates/spikard-http/tests/di_integration.rs +0 -192
  145. data/vendor/crates/spikard-http/tests/doc_snippets.rs +0 -5
  146. data/vendor/crates/spikard-http/tests/grpc_bidirectional_streaming.rs +0 -430
  147. data/vendor/crates/spikard-http/tests/grpc_client_streaming.rs +0 -738
  148. data/vendor/crates/spikard-http/tests/grpc_error_handling_test.rs +0 -652
  149. data/vendor/crates/spikard-http/tests/grpc_integration_test.rs +0 -334
  150. data/vendor/crates/spikard-http/tests/grpc_metadata_test.rs +0 -532
  151. data/vendor/crates/spikard-http/tests/grpc_server_integration.rs +0 -495
  152. data/vendor/crates/spikard-http/tests/grpc_server_streaming.rs +0 -974
  153. data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +0 -1093
  154. data/vendor/crates/spikard-http/tests/middleware_stack_integration.rs +0 -389
  155. data/vendor/crates/spikard-http/tests/multipart_behavior.rs +0 -656
  156. data/vendor/crates/spikard-http/tests/request_extraction_full.rs +0 -513
  157. data/vendor/crates/spikard-http/tests/server_auth_middleware_behavior.rs +0 -328
  158. data/vendor/crates/spikard-http/tests/server_config_builder.rs +0 -314
  159. data/vendor/crates/spikard-http/tests/server_configured_router_behavior.rs +0 -200
  160. data/vendor/crates/spikard-http/tests/server_cors_preflight.rs +0 -83
  161. data/vendor/crates/spikard-http/tests/server_handler_wrappers.rs +0 -464
  162. data/vendor/crates/spikard-http/tests/server_method_router_additional_behavior.rs +0 -286
  163. data/vendor/crates/spikard-http/tests/server_method_router_coverage.rs +0 -118
  164. data/vendor/crates/spikard-http/tests/server_middleware_behavior.rs +0 -99
  165. data/vendor/crates/spikard-http/tests/server_middleware_branches.rs +0 -204
  166. data/vendor/crates/spikard-http/tests/server_openapi_jsonrpc_static.rs +0 -421
  167. data/vendor/crates/spikard-http/tests/server_router_behavior.rs +0 -121
  168. data/vendor/crates/spikard-http/tests/sse_behavior.rs +0 -620
  169. data/vendor/crates/spikard-http/tests/sse_full_behavior.rs +0 -584
  170. data/vendor/crates/spikard-http/tests/sse_handler_behavior.rs +0 -130
  171. data/vendor/crates/spikard-http/tests/test_client_requests.rs +0 -167
  172. data/vendor/crates/spikard-http/tests/testing_helpers.rs +0 -87
  173. data/vendor/crates/spikard-http/tests/testing_module_coverage.rs +0 -155
  174. data/vendor/crates/spikard-http/tests/urlencoded_content_type.rs +0 -82
  175. data/vendor/crates/spikard-http/tests/websocket_behavior.rs +0 -663
  176. data/vendor/crates/spikard-http/tests/websocket_full_behavior.rs +0 -440
  177. data/vendor/crates/spikard-http/tests/websocket_integration.rs +0 -150
  178. data/vendor/crates/spikard-rb/Cargo.toml +0 -68
  179. data/vendor/crates/spikard-rb/build.rs +0 -200
  180. data/vendor/crates/spikard-rb/src/background.rs +0 -63
  181. data/vendor/crates/spikard-rb/src/config/mod.rs +0 -5
  182. data/vendor/crates/spikard-rb/src/config/server_config.rs +0 -401
  183. data/vendor/crates/spikard-rb/src/conversion.rs +0 -688
  184. data/vendor/crates/spikard-rb/src/di/builder.rs +0 -100
  185. data/vendor/crates/spikard-rb/src/di/mod.rs +0 -375
  186. data/vendor/crates/spikard-rb/src/grpc/handler.rs +0 -834
  187. data/vendor/crates/spikard-rb/src/grpc/mod.rs +0 -13
  188. data/vendor/crates/spikard-rb/src/gvl.rs +0 -80
  189. data/vendor/crates/spikard-rb/src/handler.rs +0 -699
  190. data/vendor/crates/spikard-rb/src/integration/mod.rs +0 -3
  191. data/vendor/crates/spikard-rb/src/lib.rs +0 -2264
  192. data/vendor/crates/spikard-rb/src/lifecycle.rs +0 -303
  193. data/vendor/crates/spikard-rb/src/metadata/mod.rs +0 -5
  194. data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +0 -507
  195. data/vendor/crates/spikard-rb/src/request.rs +0 -439
  196. data/vendor/crates/spikard-rb/src/runtime/mod.rs +0 -5
  197. data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +0 -344
  198. data/vendor/crates/spikard-rb/src/server.rs +0 -307
  199. data/vendor/crates/spikard-rb/src/sse.rs +0 -231
  200. data/vendor/crates/spikard-rb/src/testing/client.rs +0 -698
  201. data/vendor/crates/spikard-rb/src/testing/mod.rs +0 -7
  202. data/vendor/crates/spikard-rb/src/testing/sse.rs +0 -108
  203. data/vendor/crates/spikard-rb/src/testing/websocket.rs +0 -573
  204. data/vendor/crates/spikard-rb/src/websocket.rs +0 -475
  205. data/vendor/crates/spikard-rb-macros/Cargo.toml +0 -25
  206. 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
- }