spikard 0.3.6 → 0.5.0

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