1use crate::context::RequestContext;
37use crate::extract::FromRequest;
38use crate::request::Request;
39use crate::response::{IntoResponse, Response, ResponseBody, StatusCode};
40use parking_lot::Mutex;
41use parking_lot::RwLock;
42use std::any::{Any, TypeId};
43use std::collections::HashMap;
44use std::future::Future;
45use std::marker::PhantomData;
46use std::ops::{Deref, DerefMut};
47use std::pin::Pin;
48use std::sync::Arc;
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum DependencyScope {
53 Function,
55 Request,
57}
58
59pub type CleanupFn = Box<dyn FnOnce() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send>;
69
70pub struct CleanupStack {
114 cleanups: Mutex<Vec<CleanupFn>>,
116}
117
118impl CleanupStack {
119 #[must_use]
121 pub fn new() -> Self {
122 Self {
123 cleanups: Mutex::new(Vec::new()),
124 }
125 }
126
127 pub fn push(&self, cleanup: CleanupFn) {
131 let mut guard = self.cleanups.lock();
132 guard.push(cleanup);
133 }
134
135 pub fn take_cleanups(&self) -> Vec<CleanupFn> {
140 let mut guard = self.cleanups.lock();
141 let mut cleanups = std::mem::take(&mut *guard);
142 cleanups.reverse(); cleanups
144 }
145
146 #[must_use]
148 pub fn len(&self) -> usize {
149 let guard = self.cleanups.lock();
150 guard.len()
151 }
152
153 #[must_use]
155 pub fn is_empty(&self) -> bool {
156 self.len() == 0
157 }
158
159 pub async fn run_cleanups(&self) -> usize {
169 let cleanups = self.take_cleanups();
170 let mut completed = 0;
171
172 for cleanup in cleanups {
173 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| (cleanup)()));
175 match result {
176 Ok(future) => {
177 if Self::run_cleanup_future(future).await {
179 completed += 1;
180 }
181 }
183 Err(_) => {
184 }
186 }
187 }
188
189 completed
190 }
191
192 async fn run_cleanup_future(mut future: Pin<Box<dyn Future<Output = ()> + Send>>) -> bool {
196 use std::panic::{AssertUnwindSafe, catch_unwind};
197 use std::task::Poll;
198
199 std::future::poll_fn(move |cx| {
200 match catch_unwind(AssertUnwindSafe(|| future.as_mut().poll(cx))) {
201 Ok(Poll::Ready(())) => Poll::Ready(true),
202 Ok(Poll::Pending) => Poll::Pending,
203 Err(_) => Poll::Ready(false), }
205 })
206 .await
207 }
208}
209
210impl Default for CleanupStack {
211 fn default() -> Self {
212 Self::new()
213 }
214}
215
216impl std::fmt::Debug for CleanupStack {
217 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218 f.debug_struct("CleanupStack")
219 .field("count", &self.len())
220 .finish()
221 }
222}
223
224pub trait FromDependencyWithCleanup: Clone + Send + Sync + 'static {
260 type Value: Clone + Send + Sync + 'static;
262 type Error: IntoResponse + Send + Sync + 'static;
264
265 fn setup(
270 ctx: &RequestContext,
271 req: &mut Request,
272 ) -> impl Future<Output = Result<(Self::Value, Option<CleanupFn>), Self::Error>> + Send;
273}
274
275#[derive(Debug, Clone)]
281pub struct DependsCleanup<T, C = DefaultDependencyConfig>(pub T, PhantomData<C>);
282
283impl<T, C> DependsCleanup<T, C> {
284 #[must_use]
286 pub fn new(value: T) -> Self {
287 Self(value, PhantomData)
288 }
289
290 #[must_use]
292 pub fn into_inner(self) -> T {
293 self.0
294 }
295}
296
297impl<T, C> Deref for DependsCleanup<T, C> {
298 type Target = T;
299
300 fn deref(&self) -> &Self::Target {
301 &self.0
302 }
303}
304
305impl<T, C> DerefMut for DependsCleanup<T, C> {
306 fn deref_mut(&mut self) -> &mut Self::Target {
307 &mut self.0
308 }
309}
310
311impl<T, C> FromRequest for DependsCleanup<T, C>
312where
313 T: FromDependencyWithCleanup<Value = T>,
314 C: DependsConfig,
315{
316 type Error = T::Error;
317
318 async fn from_request(ctx: &RequestContext, req: &mut Request) -> Result<Self, Self::Error> {
319 let _ = ctx.checkpoint();
321
322 let scope = C::SCOPE.unwrap_or(DependencyScope::Request);
323 let use_cache = C::USE_CACHE && scope == DependencyScope::Request;
324
325 if use_cache {
327 if let Some(cached) = ctx.dependency_cache().get::<T::Value>() {
328 return Ok(DependsCleanup::new(cached));
329 }
330 }
331
332 let type_name = std::any::type_name::<T>();
334 if let Some(cycle) = ctx.resolution_stack().check_cycle::<T>(type_name) {
335 handle_circular_dependency(cycle);
336 }
337
338 if let Some(scope_err) = ctx
340 .resolution_stack()
341 .check_scope_violation(type_name, scope)
342 {
343 handle_scope_violation(scope_err);
344 }
345
346 ctx.resolution_stack().push::<T>(type_name, scope);
348 let _guard = ResolutionGuard::new(ctx.resolution_stack());
349
350 let _ = ctx.checkpoint();
352 let (value, cleanup) = T::setup(ctx, req).await?;
353
354 if let Some(cleanup_fn) = cleanup {
356 ctx.cleanup_stack().push(cleanup_fn);
357 }
358
359 if use_cache {
361 ctx.dependency_cache().insert::<T::Value>(value.clone());
362 }
363
364 Ok(DependsCleanup::new(value))
365 }
366}
367
368pub trait DependsConfig {
370 const USE_CACHE: bool;
372 const SCOPE: Option<DependencyScope>;
374}
375
376#[derive(Debug, Clone, Copy)]
378pub struct DefaultDependencyConfig;
379
380pub type DefaultConfig = DefaultDependencyConfig;
382
383impl DependsConfig for DefaultDependencyConfig {
384 const USE_CACHE: bool = true;
385 const SCOPE: Option<DependencyScope> = None;
386}
387
388#[derive(Debug, Clone, Copy)]
390pub struct NoCache;
391
392impl DependsConfig for NoCache {
393 const USE_CACHE: bool = false;
394 const SCOPE: Option<DependencyScope> = Some(DependencyScope::Function);
395}
396
397#[derive(Debug, Clone)]
413pub struct CircularDependencyError {
414 pub cycle: Vec<String>,
417}
418
419#[derive(Debug, Clone)]
448pub struct DependencyScopeError {
449 pub request_scoped_type: String,
451 pub function_scoped_type: String,
453}
454
455impl CircularDependencyError {
456 #[must_use]
458 pub fn new(cycle: Vec<String>) -> Self {
459 Self { cycle }
460 }
461
462 #[must_use]
464 pub fn cycle_path(&self) -> String {
465 self.cycle.join(" -> ")
466 }
467}
468
469impl std::fmt::Display for CircularDependencyError {
470 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
471 write!(f, "Circular dependency detected: {}", self.cycle_path())
472 }
473}
474
475impl std::error::Error for CircularDependencyError {}
476
477impl IntoResponse for CircularDependencyError {
478 fn into_response(self) -> Response {
479 let body = format!(
480 r#"{{"detail":"Circular dependency detected: {}"}}"#,
481 self.cycle_path()
482 );
483 Response::with_status(StatusCode::INTERNAL_SERVER_ERROR)
484 .header("content-type", b"application/json".to_vec())
485 .body(ResponseBody::Bytes(body.into_bytes()))
486 }
487}
488
489impl DependencyScopeError {
490 #[must_use]
492 pub fn new(request_scoped_type: String, function_scoped_type: String) -> Self {
493 Self {
494 request_scoped_type,
495 function_scoped_type,
496 }
497 }
498}
499
500impl std::fmt::Display for DependencyScopeError {
501 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
502 write!(
503 f,
504 "Dependency scope violation: request-scoped '{}' depends on function-scoped '{}'. \
505 Request-scoped dependencies cannot depend on function-scoped dependencies \
506 because the cached value would become stale.",
507 self.request_scoped_type, self.function_scoped_type
508 )
509 }
510}
511
512impl std::error::Error for DependencyScopeError {}
513
514impl IntoResponse for DependencyScopeError {
515 fn into_response(self) -> Response {
516 let body = format!(
517 r#"{{"detail":"Dependency scope violation: request-scoped '{}' depends on function-scoped '{}'. Request-scoped dependencies cannot depend on function-scoped dependencies."}}"#,
518 self.request_scoped_type, self.function_scoped_type
519 );
520 Response::with_status(StatusCode::INTERNAL_SERVER_ERROR)
521 .header("content-type", b"application/json".to_vec())
522 .body(ResponseBody::Bytes(body.into_bytes()))
523 }
524}
525
526#[cold]
535#[inline(never)]
536fn handle_circular_dependency(cycle: Vec<String>) -> ! {
537 let err = CircularDependencyError::new(cycle);
538 panic!(
539 "\n\n\
540 ╔══════════════════════════════════════════════════════════════════════╗\n\
541 ║ CIRCULAR DEPENDENCY DETECTED ║\n\
542 ╠══════════════════════════════════════════════════════════════════════╣\n\
543 ║ {}\n\
544 ╠══════════════════════════════════════════════════════════════════════╣\n\
545 ║ This is a configuration error in your dependency graph. ║\n\
546 ║ ║\n\
547 ║ To fix: ║\n\
548 ║ 1. Review the cycle path above ║\n\
549 ║ 2. Break the cycle by removing or refactoring one dependency ║\n\
550 ║ 3. Consider using lazy initialization or scoped dependencies ║\n\
551 ╚══════════════════════════════════════════════════════════════════════╝\n",
552 err
553 );
554}
555
556#[cold]
565#[inline(never)]
566fn handle_scope_violation(scope_err: DependencyScopeError) -> ! {
567 panic!(
568 "\n\n\
569 ╔══════════════════════════════════════════════════════════════════════╗\n\
570 ║ DEPENDENCY SCOPE VIOLATION ║\n\
571 ╠══════════════════════════════════════════════════════════════════════╣\n\
572 ║ {}\n\
573 ╠══════════════════════════════════════════════════════════════════════╣\n\
574 ║ Request-scoped dependencies are cached per-request and must not ║\n\
575 ║ depend on function-scoped dependencies (which are created fresh ║\n\
576 ║ each time). ║\n\
577 ║ ║\n\
578 ║ To fix: ║\n\
579 ║ 1. Change the inner dependency to request scope ║\n\
580 ║ 2. Or change the outer dependency to function scope ║\n\
581 ║ 3. Or restructure to avoid the dependency ║\n\
582 ╚══════════════════════════════════════════════════════════════════════╝\n",
583 scope_err
584 );
585}
586
587pub struct ResolutionStack {
594 stack: RwLock<Vec<(TypeId, String, DependencyScope)>>,
596}
597
598impl ResolutionStack {
599 #[must_use]
601 pub fn new() -> Self {
602 Self {
603 stack: RwLock::new(Vec::new()),
604 }
605 }
606
607 pub fn check_cycle<T: 'static>(&self, type_name: &str) -> Option<Vec<String>> {
612 let type_id = TypeId::of::<T>();
613 let guard = self.stack.read();
614
615 if let Some(pos) = guard.iter().position(|(id, _, _)| *id == type_id) {
617 let mut cycle: Vec<String> = guard[pos..]
619 .iter()
620 .map(|(_, name, _)| name.clone())
621 .collect();
622 cycle.push(type_name.to_owned());
624 return Some(cycle);
625 }
626
627 None
628 }
629
630 pub fn check_scope_violation(
642 &self,
643 type_name: &str,
644 scope: DependencyScope,
645 ) -> Option<DependencyScopeError> {
646 if scope != DependencyScope::Function {
648 return None;
649 }
650
651 let guard = self.stack.read();
652
653 for (_, name, dep_scope) in guard.iter().rev() {
656 if *dep_scope == DependencyScope::Request {
657 return Some(DependencyScopeError::new(
658 name.clone(),
659 type_name.to_owned(),
660 ));
661 }
662 }
663
664 None
665 }
666
667 pub fn push<T: 'static>(&self, type_name: &str, scope: DependencyScope) {
671 let mut guard = self.stack.write();
672 guard.push((TypeId::of::<T>(), type_name.to_owned(), scope));
673 }
674
675 pub fn pop(&self) {
679 let mut guard = self.stack.write();
680 guard.pop();
681 }
682
683 #[must_use]
685 pub fn depth(&self) -> usize {
686 let guard = self.stack.read();
687 guard.len()
688 }
689
690 #[must_use]
692 pub fn is_empty(&self) -> bool {
693 self.depth() == 0
694 }
695}
696
697impl Default for ResolutionStack {
698 fn default() -> Self {
699 Self::new()
700 }
701}
702
703impl std::fmt::Debug for ResolutionStack {
704 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
705 let guard = self.stack.read();
706 f.debug_struct("ResolutionStack")
707 .field("depth", &guard.len())
708 .field(
709 "types",
710 &guard
711 .iter()
712 .map(|(_, name, scope)| format!("{}({:?})", name, scope))
713 .collect::<Vec<_>>(),
714 )
715 .finish()
716 }
717}
718
719pub struct ResolutionGuard<'a> {
724 stack: &'a ResolutionStack,
725}
726
727impl<'a> ResolutionGuard<'a> {
728 fn new(stack: &'a ResolutionStack) -> Self {
730 Self { stack }
731 }
732}
733
734impl Drop for ResolutionGuard<'_> {
735 fn drop(&mut self) {
736 self.stack.pop();
737 }
738}
739
740#[derive(Debug, Clone)]
742pub struct Depends<T, C = DefaultDependencyConfig>(pub T, PhantomData<C>);
743
744impl<T, C> Depends<T, C> {
745 #[must_use]
747 pub fn new(value: T) -> Self {
748 Self(value, PhantomData)
749 }
750
751 #[must_use]
753 pub fn into_inner(self) -> T {
754 self.0
755 }
756}
757
758impl<T, C> Deref for Depends<T, C> {
759 type Target = T;
760
761 fn deref(&self) -> &Self::Target {
762 &self.0
763 }
764}
765
766impl<T, C> DerefMut for Depends<T, C> {
767 fn deref_mut(&mut self) -> &mut Self::Target {
768 &mut self.0
769 }
770}
771
772pub trait FromDependency: Clone + Send + Sync + 'static {
774 type Error: IntoResponse + Send + Sync + 'static;
776
777 fn from_dependency(
779 ctx: &RequestContext,
780 req: &mut Request,
781 ) -> impl Future<Output = Result<Self, Self::Error>> + Send;
782}
783
784impl<T, C> FromRequest for Depends<T, C>
785where
786 T: FromDependency,
787 C: DependsConfig,
788{
789 type Error = T::Error;
790
791 async fn from_request(ctx: &RequestContext, req: &mut Request) -> Result<Self, Self::Error> {
792 let _ = ctx.checkpoint();
794
795 if let Some(result) = ctx.dependency_overrides().resolve::<T>(ctx, req).await {
797 return result.map(Depends::new);
798 }
799
800 let scope = C::SCOPE.unwrap_or(DependencyScope::Request);
801 let use_cache = C::USE_CACHE && scope == DependencyScope::Request;
802
803 if use_cache {
805 if let Some(cached) = ctx.dependency_cache().get::<T>() {
806 return Ok(Depends::new(cached));
807 }
808 }
809
810 let type_name = std::any::type_name::<T>();
813 if let Some(cycle) = ctx.resolution_stack().check_cycle::<T>(type_name) {
814 handle_circular_dependency(cycle);
815 }
816
817 if let Some(scope_err) = ctx
819 .resolution_stack()
820 .check_scope_violation(type_name, scope)
821 {
822 handle_scope_violation(scope_err);
823 }
824
825 ctx.resolution_stack().push::<T>(type_name, scope);
827 let _guard = ResolutionGuard::new(ctx.resolution_stack());
828
829 let _ = ctx.checkpoint();
831 let value = T::from_dependency(ctx, req).await?;
832
833 if use_cache {
835 ctx.dependency_cache().insert::<T>(value.clone());
836 }
837
838 Ok(Depends::new(value))
839 }
840}
841
842pub struct DependencyCache {
844 inner: RwLock<HashMap<TypeId, Box<dyn Any + Send + Sync>>>,
845}
846
847impl DependencyCache {
848 #[must_use]
850 pub fn new() -> Self {
851 Self {
852 inner: RwLock::new(HashMap::new()),
853 }
854 }
855
856 #[must_use]
858 pub fn get<T: Clone + Send + Sync + 'static>(&self) -> Option<T> {
859 let guard = self.inner.read();
860 guard
861 .get(&TypeId::of::<T>())
862 .and_then(|boxed| boxed.downcast_ref::<T>())
863 .cloned()
864 }
865
866 pub fn insert<T: Clone + Send + Sync + 'static>(&self, value: T) {
868 let mut guard = self.inner.write();
869 guard.insert(TypeId::of::<T>(), Box::new(value));
870 }
871
872 pub fn clear(&self) {
874 let mut guard = self.inner.write();
875 guard.clear();
876 }
877
878 #[must_use]
880 pub fn len(&self) -> usize {
881 let guard = self.inner.read();
882 guard.len()
883 }
884
885 #[must_use]
887 pub fn is_empty(&self) -> bool {
888 self.len() == 0
889 }
890}
891
892impl Default for DependencyCache {
893 fn default() -> Self {
894 Self::new()
895 }
896}
897
898impl std::fmt::Debug for DependencyCache {
899 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
900 f.debug_struct("DependencyCache")
901 .field("size", &self.len())
902 .finish()
903 }
904}
905
906type OverrideBox = Box<dyn Any + Send + Sync>;
907type OverrideFuture = Pin<Box<dyn Future<Output = Result<OverrideBox, OverrideBox>> + Send>>;
908type OverrideFn = Arc<dyn Fn(&RequestContext, &mut Request) -> OverrideFuture + Send + Sync>;
909
910pub struct DependencyOverrides {
912 inner: RwLock<HashMap<TypeId, OverrideFn>>,
913}
914
915impl DependencyOverrides {
916 #[must_use]
918 pub fn new() -> Self {
919 Self {
920 inner: RwLock::new(HashMap::new()),
921 }
922 }
923
924 pub fn insert<T, F, Fut>(&self, f: F)
926 where
927 T: FromDependency,
928 F: Fn(&RequestContext, &mut Request) -> Fut + Send + Sync + 'static,
929 Fut: Future<Output = Result<T, T::Error>> + Send + 'static,
930 {
931 let wrapper: OverrideFn = Arc::new(move |ctx, req| {
932 let fut = f(ctx, req);
933 Box::pin(async move {
934 match fut.await {
935 Ok(value) => Ok(Box::new(value) as OverrideBox),
936 Err(err) => Err(Box::new(err) as OverrideBox),
937 }
938 })
939 });
940
941 let mut guard = self.inner.write();
942 guard.insert(TypeId::of::<T>(), wrapper);
943 }
944
945 pub fn insert_value<T>(&self, value: T)
947 where
948 T: FromDependency,
949 {
950 self.insert::<T, _, _>(move |_ctx, _req| {
951 let value = value.clone();
952 async move { Ok(value) }
953 });
954 }
955
956 pub fn clear(&self) {
958 let mut guard = self.inner.write();
959 guard.clear();
960 }
961
962 pub async fn resolve<T>(
972 &self,
973 ctx: &RequestContext,
974 req: &mut Request,
975 ) -> Option<Result<T, T::Error>>
976 where
977 T: FromDependency,
978 {
979 let override_fn = {
980 let guard = self.inner.read();
981 guard.get(&TypeId::of::<T>()).cloned()
982 };
983
984 let override_fn = override_fn?;
985 match override_fn(ctx, req).await {
986 Ok(value) => match value.downcast::<T>() {
987 Ok(value) => Some(Ok(*value)),
988 Err(_) => {
989 debug_assert!(
992 false,
993 "dependency override type mismatch: expected {}, stored override returned wrong type",
994 std::any::type_name::<T>()
995 );
996 None
998 }
999 },
1000 Err(err) => match err.downcast::<T::Error>() {
1001 Ok(err) => Some(Err(*err)),
1002 Err(_) => {
1003 debug_assert!(
1006 false,
1007 "dependency override error type mismatch: expected {}, stored override returned wrong error type",
1008 std::any::type_name::<T::Error>()
1009 );
1010 None
1012 }
1013 },
1014 }
1015 }
1016
1017 #[must_use]
1019 pub fn len(&self) -> usize {
1020 let guard = self.inner.read();
1021 guard.len()
1022 }
1023
1024 #[must_use]
1026 pub fn is_empty(&self) -> bool {
1027 self.len() == 0
1028 }
1029}
1030
1031impl Default for DependencyOverrides {
1032 fn default() -> Self {
1033 Self::new()
1034 }
1035}
1036
1037impl std::fmt::Debug for DependencyOverrides {
1038 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1039 f.debug_struct("DependencyOverrides")
1040 .field("size", &self.len())
1041 .finish()
1042 }
1043}
1044
1045#[cfg(test)]
1046mod tests {
1047 use super::*;
1048 use crate::error::HttpError;
1049 use crate::request::Method;
1050 use asupersync::Cx;
1051 use std::sync::Arc;
1052 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
1053
1054 fn test_context(overrides: Option<Arc<DependencyOverrides>>) -> RequestContext {
1055 let cx = Cx::for_testing();
1056 let request_id = 1;
1057 if let Some(overrides) = overrides {
1058 RequestContext::with_overrides(cx, request_id, overrides)
1059 } else {
1060 RequestContext::new(cx, request_id)
1061 }
1062 }
1063
1064 fn empty_request() -> Request {
1065 Request::new(Method::Get, "/")
1066 }
1067
1068 #[derive(Clone)]
1069 struct CounterDep {
1070 value: usize,
1071 }
1072
1073 impl FromDependency for CounterDep {
1074 type Error = HttpError;
1075
1076 async fn from_dependency(
1077 _ctx: &RequestContext,
1078 _req: &mut Request,
1079 ) -> Result<Self, Self::Error> {
1080 Ok(CounterDep { value: 1 })
1081 }
1082 }
1083
1084 #[test]
1085 fn depends_basic_resolution() {
1086 let ctx = test_context(None);
1087 let mut req = empty_request();
1088 let dep = futures_executor::block_on(Depends::<CounterDep>::from_request(&ctx, &mut req))
1089 .expect("dependency resolution failed");
1090 assert_eq!(dep.value, 1);
1091 }
1092
1093 #[derive(Clone)]
1094 struct CountingDep;
1095
1096 impl FromDependency for CountingDep {
1097 type Error = HttpError;
1098
1099 async fn from_dependency(
1100 ctx: &RequestContext,
1101 _req: &mut Request,
1102 ) -> Result<Self, Self::Error> {
1103 let count = ctx
1104 .dependency_cache()
1105 .get::<Arc<AtomicUsize>>()
1106 .unwrap_or_else(|| Arc::new(AtomicUsize::new(0)));
1107 count.fetch_add(1, Ordering::SeqCst);
1108 ctx.dependency_cache().insert(Arc::clone(&count));
1109 Ok(CountingDep)
1110 }
1111 }
1112
1113 #[test]
1114 fn depends_caches_per_request() {
1115 let ctx = test_context(None);
1116 let mut req = empty_request();
1117
1118 let _ = futures_executor::block_on(Depends::<CountingDep>::from_request(&ctx, &mut req))
1119 .expect("first resolution failed");
1120 let _ = futures_executor::block_on(Depends::<CountingDep>::from_request(&ctx, &mut req))
1121 .expect("second resolution failed");
1122
1123 let counter = ctx
1124 .dependency_cache()
1125 .get::<Arc<AtomicUsize>>()
1126 .expect("missing counter");
1127 assert_eq!(counter.load(Ordering::SeqCst), 1);
1128 }
1129
1130 #[test]
1131 fn depends_no_cache_config() {
1132 let ctx = test_context(None);
1133 let mut req = empty_request();
1134
1135 let _ = futures_executor::block_on(Depends::<CountingDep, NoCache>::from_request(
1136 &ctx, &mut req,
1137 ))
1138 .expect("first resolution failed");
1139 let _ = futures_executor::block_on(Depends::<CountingDep, NoCache>::from_request(
1140 &ctx, &mut req,
1141 ))
1142 .expect("second resolution failed");
1143
1144 let counter = ctx
1145 .dependency_cache()
1146 .get::<Arc<AtomicUsize>>()
1147 .expect("missing counter");
1148 assert_eq!(counter.load(Ordering::SeqCst), 2);
1149 }
1150
1151 #[derive(Clone)]
1152 struct DepB;
1153
1154 impl FromDependency for DepB {
1155 type Error = HttpError;
1156
1157 async fn from_dependency(
1158 _ctx: &RequestContext,
1159 _req: &mut Request,
1160 ) -> Result<Self, Self::Error> {
1161 Ok(DepB)
1162 }
1163 }
1164
1165 #[derive(Clone)]
1166 struct DepA;
1167
1168 impl FromDependency for DepA {
1169 type Error = HttpError;
1170
1171 async fn from_dependency(
1172 ctx: &RequestContext,
1173 req: &mut Request,
1174 ) -> Result<Self, Self::Error> {
1175 let _ = Depends::<DepB>::from_request(ctx, req).await?;
1176 Ok(DepA)
1177 }
1178 }
1179
1180 #[test]
1181 fn depends_nested_resolution() {
1182 let ctx = test_context(None);
1183 let mut req = empty_request();
1184 let _ = futures_executor::block_on(Depends::<DepA>::from_request(&ctx, &mut req))
1185 .expect("nested resolution failed");
1186 }
1187
1188 #[derive(Clone, Debug)]
1189 struct OverrideDep {
1190 value: usize,
1191 }
1192
1193 impl FromDependency for OverrideDep {
1194 type Error = HttpError;
1195
1196 async fn from_dependency(
1197 _ctx: &RequestContext,
1198 _req: &mut Request,
1199 ) -> Result<Self, Self::Error> {
1200 Ok(OverrideDep { value: 1 })
1201 }
1202 }
1203
1204 #[test]
1205 fn depends_override_substitution() {
1206 let overrides = Arc::new(DependencyOverrides::new());
1207 overrides.insert_value(OverrideDep { value: 42 });
1208 let ctx = test_context(Some(overrides));
1209 let mut req = empty_request();
1210
1211 let dep = futures_executor::block_on(Depends::<OverrideDep>::from_request(&ctx, &mut req))
1212 .expect("override resolution failed");
1213 assert_eq!(dep.value, 42);
1214 }
1215
1216 #[derive(Clone, Debug)]
1217 struct ErrorDep;
1218
1219 impl FromDependency for ErrorDep {
1220 type Error = HttpError;
1221
1222 async fn from_dependency(
1223 _ctx: &RequestContext,
1224 _req: &mut Request,
1225 ) -> Result<Self, Self::Error> {
1226 Err(HttpError::bad_request().with_detail("boom"))
1227 }
1228 }
1229
1230 #[test]
1231 fn depends_error_propagation() {
1232 let ctx = test_context(None);
1233 let mut req = empty_request();
1234 let err = futures_executor::block_on(Depends::<ErrorDep>::from_request(&ctx, &mut req))
1235 .expect_err("expected dependency error");
1236 assert_eq!(err.status.as_u16(), 400);
1237 }
1238
1239 #[derive(Clone)]
1240 struct DepC;
1241
1242 impl FromDependency for DepC {
1243 type Error = HttpError;
1244
1245 async fn from_dependency(
1246 ctx: &RequestContext,
1247 req: &mut Request,
1248 ) -> Result<Self, Self::Error> {
1249 let _ = Depends::<DepA>::from_request(ctx, req).await?;
1250 let _ = Depends::<DepB>::from_request(ctx, req).await?;
1251 Ok(DepC)
1252 }
1253 }
1254
1255 #[test]
1256 fn depends_complex_graph() {
1257 let ctx = test_context(None);
1258 let mut req = empty_request();
1259 let _ = futures_executor::block_on(Depends::<DepC>::from_request(&ctx, &mut req))
1260 .expect("complex graph resolution failed");
1261 }
1262
1263 #[test]
1277 fn resolution_stack_detects_simple_cycle() {
1278 struct TypeA;
1280 struct TypeB;
1281
1282 let stack = ResolutionStack::new();
1283
1284 assert!(stack.check_cycle::<TypeA>("TypeA").is_none());
1287 stack.push::<TypeA>("TypeA", DependencyScope::Request);
1288
1289 assert!(stack.check_cycle::<TypeB>("TypeB").is_none());
1291 stack.push::<TypeB>("TypeB", DependencyScope::Request);
1292
1293 let cycle = stack.check_cycle::<TypeA>("TypeA");
1295 assert!(cycle.is_some(), "Should detect A -> B -> A cycle");
1296 let cycle_path = cycle.unwrap();
1297 assert_eq!(cycle_path.len(), 3); assert_eq!(cycle_path[0], "TypeA");
1299 assert_eq!(cycle_path[1], "TypeB");
1300 assert_eq!(cycle_path[2], "TypeA");
1301 }
1302
1303 #[test]
1305 fn resolution_stack_detects_long_cycle() {
1306 struct TypeA;
1308 struct TypeB;
1309 struct TypeC;
1310 struct TypeD;
1311
1312 let stack = ResolutionStack::new();
1313
1314 stack.push::<TypeA>("TypeA", DependencyScope::Request);
1316 stack.push::<TypeB>("TypeB", DependencyScope::Request);
1317 stack.push::<TypeC>("TypeC", DependencyScope::Request);
1318 stack.push::<TypeD>("TypeD", DependencyScope::Request);
1319
1320 let cycle = stack.check_cycle::<TypeA>("TypeA");
1322 assert!(cycle.is_some(), "Should detect A -> B -> C -> D -> A cycle");
1323 let cycle_path = cycle.unwrap();
1324 assert_eq!(cycle_path.len(), 5); assert_eq!(cycle_path[0], "TypeA");
1326 assert_eq!(cycle_path[4], "TypeA");
1327 }
1328
1329 #[test]
1331 fn resolution_stack_allows_diamond() {
1332 struct Top;
1333 struct Left;
1334 struct Right;
1335 struct Bottom;
1336
1337 let stack = ResolutionStack::new();
1338
1339 stack.push::<Top>("Top", DependencyScope::Request);
1341 stack.push::<Left>("Left", DependencyScope::Request);
1342 stack.push::<Bottom>("Bottom", DependencyScope::Request);
1343 stack.pop(); stack.pop(); stack.push::<Right>("Right", DependencyScope::Request);
1348 assert!(
1350 stack.check_cycle::<Bottom>("Bottom").is_none(),
1351 "Diamond pattern should not be detected as a cycle"
1352 );
1353 stack.push::<Bottom>("Bottom", DependencyScope::Request);
1354 stack.pop(); stack.pop(); stack.pop(); assert!(stack.is_empty());
1359 }
1360
1361 #[test]
1363 fn resolution_stack_detects_self_dependency() {
1364 struct SelfRef;
1365
1366 let stack = ResolutionStack::new();
1367 stack.push::<SelfRef>("SelfRef", DependencyScope::Request);
1368
1369 let cycle = stack.check_cycle::<SelfRef>("SelfRef");
1371 assert!(cycle.is_some(), "Should detect self-dependency");
1372 let cycle_path = cycle.unwrap();
1373 assert_eq!(cycle_path.len(), 2); assert_eq!(cycle_path[0], "SelfRef");
1375 assert_eq!(cycle_path[1], "SelfRef");
1376 }
1377
1378 #[test]
1380 fn resolution_stack_basic() {
1381 let stack = ResolutionStack::new();
1382 assert!(stack.is_empty());
1383 assert_eq!(stack.depth(), 0);
1384
1385 stack.push::<CounterDep>("CounterDep", DependencyScope::Request);
1386 assert!(!stack.is_empty());
1387 assert_eq!(stack.depth(), 1);
1388
1389 assert!(stack.check_cycle::<ErrorDep>("ErrorDep").is_none());
1391
1392 stack.push::<ErrorDep>("ErrorDep", DependencyScope::Request);
1394 assert_eq!(stack.depth(), 2);
1395
1396 let cycle = stack.check_cycle::<CounterDep>("CounterDep");
1398 assert!(cycle.is_some());
1399 let cycle_path = cycle.unwrap();
1400 assert!(cycle_path.contains(&"CounterDep".to_string()));
1401
1402 stack.pop();
1404 assert_eq!(stack.depth(), 1);
1405 stack.pop();
1406 assert!(stack.is_empty());
1407 }
1408
1409 #[test]
1411 fn circular_dependency_error_formatting() {
1412 let err = CircularDependencyError::new(vec![
1413 "DbPool".to_string(),
1414 "UserService".to_string(),
1415 "AuthService".to_string(),
1416 "DbPool".to_string(),
1417 ]);
1418 let msg = err.to_string();
1419 assert!(msg.contains("Circular dependency detected"));
1420 assert!(msg.contains("DbPool -> UserService -> AuthService -> DbPool"));
1421 assert_eq!(
1422 err.cycle_path(),
1423 "DbPool -> UserService -> AuthService -> DbPool"
1424 );
1425 }
1426
1427 #[test]
1429 fn circular_dependency_error_into_response() {
1430 let err =
1431 CircularDependencyError::new(vec!["A".to_string(), "B".to_string(), "A".to_string()]);
1432 let response = err.into_response();
1433 assert_eq!(response.status().as_u16(), 500);
1434 }
1435
1436 #[derive(Clone)]
1438 struct DiamondBottom {
1439 id: u32,
1440 }
1441 #[derive(Clone)]
1442 struct DiamondLeft {
1443 bottom_id: u32,
1444 }
1445 #[derive(Clone)]
1446 struct DiamondRight {
1447 bottom_id: u32,
1448 }
1449 #[derive(Clone)]
1450 struct DiamondTop {
1451 left_id: u32,
1452 right_id: u32,
1453 }
1454
1455 impl FromDependency for DiamondBottom {
1456 type Error = HttpError;
1457 async fn from_dependency(
1458 _ctx: &RequestContext,
1459 _req: &mut Request,
1460 ) -> Result<Self, Self::Error> {
1461 Ok(DiamondBottom { id: 42 })
1462 }
1463 }
1464
1465 impl FromDependency for DiamondLeft {
1466 type Error = HttpError;
1467 async fn from_dependency(
1468 ctx: &RequestContext,
1469 req: &mut Request,
1470 ) -> Result<Self, Self::Error> {
1471 let bottom = Depends::<DiamondBottom>::from_request(ctx, req).await?;
1472 Ok(DiamondLeft {
1473 bottom_id: bottom.id,
1474 })
1475 }
1476 }
1477
1478 impl FromDependency for DiamondRight {
1479 type Error = HttpError;
1480 async fn from_dependency(
1481 ctx: &RequestContext,
1482 req: &mut Request,
1483 ) -> Result<Self, Self::Error> {
1484 let bottom = Depends::<DiamondBottom>::from_request(ctx, req).await?;
1485 Ok(DiamondRight {
1486 bottom_id: bottom.id,
1487 })
1488 }
1489 }
1490
1491 impl FromDependency for DiamondTop {
1492 type Error = HttpError;
1493 async fn from_dependency(
1494 ctx: &RequestContext,
1495 req: &mut Request,
1496 ) -> Result<Self, Self::Error> {
1497 let left = Depends::<DiamondLeft>::from_request(ctx, req).await?;
1498 let right = Depends::<DiamondRight>::from_request(ctx, req).await?;
1499 Ok(DiamondTop {
1500 left_id: left.bottom_id,
1501 right_id: right.bottom_id,
1502 })
1503 }
1504 }
1505
1506 #[test]
1507 fn diamond_pattern_resolves_correctly() {
1508 let ctx = test_context(None);
1509 let mut req = empty_request();
1510 let result =
1512 futures_executor::block_on(Depends::<DiamondTop>::from_request(&ctx, &mut req));
1513 assert!(result.is_ok(), "Diamond pattern should not be a cycle");
1514 let top = result.unwrap();
1515 assert_eq!(top.left_id, 42);
1516 assert_eq!(top.right_id, 42);
1517 }
1518
1519 #[derive(Clone)]
1525 struct NestedInnerDep {
1526 value: String,
1527 }
1528
1529 impl FromDependency for NestedInnerDep {
1530 type Error = HttpError;
1531 async fn from_dependency(
1532 _ctx: &RequestContext,
1533 _req: &mut Request,
1534 ) -> Result<Self, Self::Error> {
1535 Ok(NestedInnerDep {
1536 value: "original".to_string(),
1537 })
1538 }
1539 }
1540
1541 #[derive(Clone)]
1542 struct NestedOuterDep {
1543 inner_value: String,
1544 }
1545
1546 impl FromDependency for NestedOuterDep {
1547 type Error = HttpError;
1548 async fn from_dependency(
1549 ctx: &RequestContext,
1550 req: &mut Request,
1551 ) -> Result<Self, Self::Error> {
1552 let inner = Depends::<NestedInnerDep>::from_request(ctx, req).await?;
1553 Ok(NestedOuterDep {
1554 inner_value: inner.value.clone(),
1555 })
1556 }
1557 }
1558
1559 #[test]
1560 fn override_affects_nested_dependencies() {
1561 let overrides = Arc::new(DependencyOverrides::new());
1562 overrides.insert_value(NestedInnerDep {
1563 value: "overridden".to_string(),
1564 });
1565 let ctx = test_context(Some(overrides));
1566 let mut req = empty_request();
1567
1568 let result =
1569 futures_executor::block_on(Depends::<NestedOuterDep>::from_request(&ctx, &mut req))
1570 .expect("nested override resolution failed");
1571
1572 assert_eq!(
1573 result.inner_value, "overridden",
1574 "Override should propagate to nested dependencies"
1575 );
1576 }
1577
1578 #[test]
1580 fn clear_overrides_restores_original() {
1581 let overrides = Arc::new(DependencyOverrides::new());
1582 overrides.insert_value(OverrideDep { value: 99 });
1583 assert_eq!(overrides.len(), 1);
1584
1585 overrides.clear();
1586 assert_eq!(overrides.len(), 0);
1587 assert!(overrides.is_empty());
1588
1589 let ctx = test_context(Some(overrides));
1591 let mut req = empty_request();
1592 let result =
1593 futures_executor::block_on(Depends::<OverrideDep>::from_request(&ctx, &mut req))
1594 .expect("resolution after clear failed");
1595
1596 assert_eq!(result.value, 1, "After clear, should resolve to original");
1598 }
1599
1600 #[derive(Clone)]
1602 struct DepX {
1603 x: i32,
1604 }
1605 #[derive(Clone)]
1606 struct DepY {
1607 y: i32,
1608 }
1609 #[derive(Clone)]
1610 struct DepZ {
1611 z: i32,
1612 }
1613
1614 impl FromDependency for DepX {
1615 type Error = HttpError;
1616 async fn from_dependency(
1617 _ctx: &RequestContext,
1618 _req: &mut Request,
1619 ) -> Result<Self, Self::Error> {
1620 Ok(DepX { x: 10 })
1621 }
1622 }
1623
1624 impl FromDependency for DepY {
1625 type Error = HttpError;
1626 async fn from_dependency(
1627 _ctx: &RequestContext,
1628 _req: &mut Request,
1629 ) -> Result<Self, Self::Error> {
1630 Ok(DepY { y: 20 })
1631 }
1632 }
1633
1634 impl FromDependency for DepZ {
1635 type Error = HttpError;
1636 async fn from_dependency(
1637 _ctx: &RequestContext,
1638 _req: &mut Request,
1639 ) -> Result<Self, Self::Error> {
1640 Ok(DepZ { z: 30 })
1641 }
1642 }
1643
1644 #[test]
1645 fn multiple_independent_dependencies() {
1646 let ctx = test_context(None);
1647 let mut req = empty_request();
1648
1649 let dep_x =
1651 futures_executor::block_on(Depends::<DepX>::from_request(&ctx, &mut req)).unwrap();
1652 let dep_y =
1653 futures_executor::block_on(Depends::<DepY>::from_request(&ctx, &mut req)).unwrap();
1654 let dep_z =
1655 futures_executor::block_on(Depends::<DepZ>::from_request(&ctx, &mut req)).unwrap();
1656
1657 assert_eq!(dep_x.x, 10);
1658 assert_eq!(dep_y.y, 20);
1659 assert_eq!(dep_z.z, 30);
1660 }
1661
1662 #[test]
1664 fn request_scope_isolation() {
1665 let ctx1 = test_context(None);
1667 let mut req1 = empty_request();
1668
1669 let _ = futures_executor::block_on(Depends::<CountingDep>::from_request(&ctx1, &mut req1))
1670 .unwrap();
1671 let counter1 = ctx1.dependency_cache().get::<Arc<AtomicUsize>>().unwrap();
1672
1673 let ctx2 = test_context(None);
1675 let mut req2 = empty_request();
1676
1677 let _ = futures_executor::block_on(Depends::<CountingDep>::from_request(&ctx2, &mut req2))
1678 .unwrap();
1679 let counter2 = ctx2.dependency_cache().get::<Arc<AtomicUsize>>().unwrap();
1680
1681 assert_eq!(counter1.load(Ordering::SeqCst), 1);
1683 assert_eq!(counter2.load(Ordering::SeqCst), 1);
1684
1685 assert!(!Arc::ptr_eq(&counter1, &counter2));
1687 }
1688
1689 #[test]
1691 fn function_scope_no_caching() {
1692 let ctx = test_context(None);
1693 let mut req = empty_request();
1694
1695 let _ = futures_executor::block_on(Depends::<CountingDep, NoCache>::from_request(
1697 &ctx, &mut req,
1698 ))
1699 .unwrap();
1700
1701 let _ = futures_executor::block_on(Depends::<CountingDep, NoCache>::from_request(
1703 &ctx, &mut req,
1704 ))
1705 .unwrap();
1706
1707 let _ = futures_executor::block_on(Depends::<CountingDep, NoCache>::from_request(
1709 &ctx, &mut req,
1710 ))
1711 .unwrap();
1712
1713 let counter = ctx.dependency_cache().get::<Arc<AtomicUsize>>().unwrap();
1714 assert_eq!(
1715 counter.load(Ordering::SeqCst),
1716 3,
1717 "NoCache should resolve 3 times"
1718 );
1719 }
1720
1721 #[derive(Clone)]
1723 struct AsyncDep {
1724 computed: u64,
1725 }
1726
1727 impl FromDependency for AsyncDep {
1728 type Error = HttpError;
1729 async fn from_dependency(
1730 _ctx: &RequestContext,
1731 _req: &mut Request,
1732 ) -> Result<Self, Self::Error> {
1733 let result = async {
1735 let a = 21u64;
1736 let b = 21u64;
1737 a + b
1738 }
1739 .await;
1740 Ok(AsyncDep { computed: result })
1741 }
1742 }
1743
1744 #[test]
1745 fn async_dependency_resolution() {
1746 let ctx = test_context(None);
1747 let mut req = empty_request();
1748
1749 let dep = futures_executor::block_on(Depends::<AsyncDep>::from_request(&ctx, &mut req))
1750 .expect("async dependency resolution failed");
1751
1752 assert_eq!(dep.computed, 42);
1753 }
1754
1755 #[derive(Clone, Debug)]
1757 struct DepThatDependsOnError;
1758
1759 impl FromDependency for DepThatDependsOnError {
1760 type Error = HttpError;
1761 async fn from_dependency(
1762 ctx: &RequestContext,
1763 req: &mut Request,
1764 ) -> Result<Self, Self::Error> {
1765 let _ = Depends::<ErrorDep>::from_request(ctx, req).await?;
1767 Ok(DepThatDependsOnError)
1768 }
1769 }
1770
1771 #[test]
1772 fn nested_error_propagation() {
1773 let ctx = test_context(None);
1774 let mut req = empty_request();
1775
1776 let result = futures_executor::block_on(Depends::<DepThatDependsOnError>::from_request(
1777 &ctx, &mut req,
1778 ));
1779
1780 assert!(result.is_err(), "Nested error should propagate");
1781 let err = result.unwrap_err();
1782 assert_eq!(err.status.as_u16(), 400);
1783 }
1784
1785 #[test]
1787 fn dependency_cache_operations() {
1788 let cache = DependencyCache::new();
1789 assert!(cache.is_empty());
1790 assert_eq!(cache.len(), 0);
1791
1792 cache.insert::<String>("test".to_string());
1793 assert!(!cache.is_empty());
1794 assert_eq!(cache.len(), 1);
1795
1796 cache.insert::<i32>(42);
1797 assert_eq!(cache.len(), 2);
1798
1799 let retrieved = cache.get::<String>().unwrap();
1800 assert_eq!(retrieved, "test");
1801
1802 cache.clear();
1803 assert!(cache.is_empty());
1804 assert!(cache.get::<String>().is_none());
1805 }
1806
1807 #[test]
1809 fn dynamic_override_resolver() {
1810 let overrides = Arc::new(DependencyOverrides::new());
1811
1812 overrides.insert::<OverrideDep, _, _>(|_ctx, _req| async move {
1814 Ok(OverrideDep { value: 100 })
1816 });
1817
1818 let ctx = test_context(Some(overrides));
1819 let mut req = empty_request();
1820
1821 let dep = futures_executor::block_on(Depends::<OverrideDep>::from_request(&ctx, &mut req))
1822 .expect("dynamic override failed");
1823
1824 assert_eq!(dep.value, 100);
1825 }
1826
1827 #[test]
1830 fn overrides_new_is_empty() {
1831 let overrides = DependencyOverrides::new();
1832 assert!(overrides.is_empty());
1833 assert_eq!(overrides.len(), 0);
1834 }
1835
1836 #[test]
1837 fn overrides_default_is_empty() {
1838 let overrides = DependencyOverrides::default();
1839 assert!(overrides.is_empty());
1840 assert_eq!(overrides.len(), 0);
1841 }
1842
1843 #[test]
1844 fn overrides_debug_format() {
1845 let overrides = DependencyOverrides::new();
1846 let debug = format!("{:?}", overrides);
1847 assert!(debug.contains("DependencyOverrides"));
1848 assert!(debug.contains("size"));
1849 }
1850
1851 #[test]
1852 fn overrides_insert_value_increments_len() {
1853 let overrides = DependencyOverrides::new();
1854 assert_eq!(overrides.len(), 0);
1855
1856 overrides.insert_value(OverrideDep { value: 42 });
1857 assert_eq!(overrides.len(), 1);
1858 assert!(!overrides.is_empty());
1859 }
1860
1861 #[test]
1862 fn overrides_multiple_types_registered() {
1863 let overrides = Arc::new(DependencyOverrides::new());
1864 overrides.insert_value(OverrideDep { value: 10 });
1865 overrides.insert_value(NestedInnerDep {
1866 value: "mocked".to_string(),
1867 });
1868 assert_eq!(overrides.len(), 2);
1869
1870 let ctx = test_context(Some(overrides.clone()));
1872 let mut req = empty_request();
1873
1874 let dep1 = futures_executor::block_on(Depends::<OverrideDep>::from_request(&ctx, &mut req))
1875 .expect("OverrideDep override failed");
1876 assert_eq!(dep1.value, 10);
1877
1878 let mut req2 = empty_request();
1879 let dep2 =
1880 futures_executor::block_on(Depends::<NestedInnerDep>::from_request(&ctx, &mut req2))
1881 .expect("NestedInnerDep override failed");
1882 assert_eq!(dep2.value, "mocked");
1883 }
1884
1885 #[test]
1886 fn overrides_replace_same_type() {
1887 let overrides = Arc::new(DependencyOverrides::new());
1888 overrides.insert_value(OverrideDep { value: 1 });
1889 assert_eq!(overrides.len(), 1);
1890
1891 overrides.insert_value(OverrideDep { value: 999 });
1893 assert_eq!(overrides.len(), 1); let ctx = test_context(Some(overrides));
1896 let mut req = empty_request();
1897
1898 let dep = futures_executor::block_on(Depends::<OverrideDep>::from_request(&ctx, &mut req))
1899 .expect("override resolution failed");
1900 assert_eq!(dep.value, 999);
1901 }
1902
1903 #[test]
1904 fn overrides_clear_removes_all() {
1905 let overrides = DependencyOverrides::new();
1906 overrides.insert_value(OverrideDep { value: 42 });
1907 overrides.insert_value(NestedInnerDep {
1908 value: "mock".to_string(),
1909 });
1910 assert_eq!(overrides.len(), 2);
1911
1912 overrides.clear();
1913 assert!(overrides.is_empty());
1914 assert_eq!(overrides.len(), 0);
1915 }
1916
1917 #[test]
1918 fn overrides_resolve_returns_none_for_unregistered_type() {
1919 let overrides = Arc::new(DependencyOverrides::new());
1920 overrides.insert_value(OverrideDep { value: 42 });
1922
1923 let ctx = test_context(Some(overrides.clone()));
1924 let mut req = empty_request();
1925
1926 let result =
1928 futures_executor::block_on(overrides.resolve::<NestedInnerDep>(&ctx, &mut req));
1929 assert!(result.is_none(), "Unregistered type should resolve to None");
1930 }
1931
1932 #[test]
1933 fn overrides_resolve_some_for_registered_type() {
1934 let overrides = Arc::new(DependencyOverrides::new());
1935 overrides.insert_value(OverrideDep { value: 77 });
1936
1937 let ctx = test_context(Some(overrides.clone()));
1938 let mut req = empty_request();
1939
1940 let result = futures_executor::block_on(overrides.resolve::<OverrideDep>(&ctx, &mut req));
1941 assert!(result.is_some());
1942 let dep = result.unwrap().expect("resolve should succeed");
1943 assert_eq!(dep.value, 77);
1944 }
1945
1946 #[test]
1947 fn overrides_not_affect_unrelated_dependency() {
1948 let overrides = Arc::new(DependencyOverrides::new());
1949 overrides.insert_value(NestedInnerDep {
1951 value: "overridden".to_string(),
1952 });
1953
1954 let ctx = test_context(Some(overrides));
1955 let mut req = empty_request();
1956
1957 let dep = futures_executor::block_on(Depends::<OverrideDep>::from_request(&ctx, &mut req))
1959 .expect("should resolve from real implementation");
1960 assert_eq!(
1961 dep.value, 1,
1962 "Unoverridden dep should use real implementation"
1963 );
1964 }
1965
1966 #[test]
1967 fn overrides_take_precedence_over_cache() {
1968 let overrides = Arc::new(DependencyOverrides::new());
1969 overrides.insert_value(OverrideDep { value: 42 });
1970 let ctx = test_context(Some(overrides));
1971
1972 ctx.dependency_cache().insert(OverrideDep { value: 999 });
1974
1975 let mut req = empty_request();
1976 let dep = futures_executor::block_on(Depends::<OverrideDep>::from_request(&ctx, &mut req))
1977 .expect("override should take precedence");
1978
1979 assert_eq!(dep.value, 42, "Override should take precedence over cache");
1981 }
1982
1983 #[test]
1984 fn overrides_dynamic_resolver_can_return_error() {
1985 let overrides = Arc::new(DependencyOverrides::new());
1986
1987 overrides.insert::<OverrideDep, _, _>(|_ctx, _req| async move {
1988 Err(
1989 HttpError::new(crate::response::StatusCode::INTERNAL_SERVER_ERROR)
1990 .with_detail("override error"),
1991 )
1992 });
1993
1994 let ctx = test_context(Some(overrides));
1995 let mut req = empty_request();
1996
1997 let err = futures_executor::block_on(Depends::<OverrideDep>::from_request(&ctx, &mut req))
1998 .expect_err("override should return error");
1999 assert_eq!(err.status.as_u16(), 500);
2000 }
2001
2002 #[test]
2003 fn overrides_insert_value_works_for_multiple_resolves() {
2004 let overrides = Arc::new(DependencyOverrides::new());
2006 overrides.insert_value(OverrideDep { value: 7 });
2007
2008 let ctx = test_context(Some(overrides));
2009
2010 for _ in 0..5 {
2011 let mut req = empty_request();
2012 let dep =
2013 futures_executor::block_on(Depends::<OverrideDep>::from_request(&ctx, &mut req))
2014 .expect("repeated resolve should work");
2015 assert_eq!(dep.value, 7);
2016 }
2017 }
2018
2019 #[test]
2020 fn overrides_dynamic_resolver_accesses_request() {
2021 let overrides = Arc::new(DependencyOverrides::new());
2022
2023 overrides.insert::<OverrideDep, _, _>(|_ctx, req| {
2025 let value = req.get_extension::<usize>().copied().unwrap_or(0);
2026 async move { Ok(OverrideDep { value }) }
2027 });
2028
2029 let ctx = test_context(Some(overrides));
2030 let mut req = empty_request();
2031 req.insert_extension(42usize);
2032
2033 let dep = futures_executor::block_on(Depends::<OverrideDep>::from_request(&ctx, &mut req))
2034 .expect("dynamic resolver with request access failed");
2035 assert_eq!(dep.value, 42, "Dynamic resolver should read from request");
2036 }
2037
2038 #[test]
2039 fn overrides_after_clear_fall_back_to_real_dependency() {
2040 let overrides = Arc::new(DependencyOverrides::new());
2041 overrides.insert_value(OverrideDep { value: 999 });
2042
2043 let ctx = test_context(Some(overrides.clone()));
2045 let mut req = empty_request();
2046 let dep = futures_executor::block_on(Depends::<OverrideDep>::from_request(&ctx, &mut req))
2047 .unwrap();
2048 assert_eq!(dep.value, 999);
2049
2050 overrides.clear();
2052 let ctx2 = test_context(Some(overrides));
2053 let mut req2 = empty_request();
2054 let dep2 =
2055 futures_executor::block_on(Depends::<OverrideDep>::from_request(&ctx2, &mut req2))
2056 .unwrap();
2057 assert_eq!(dep2.value, 1, "After clear, real dependency should be used");
2058 }
2059
2060 #[test]
2061 fn overrides_without_overrides_use_real_dependency() {
2062 let ctx = test_context(None);
2064 let mut req = empty_request();
2065
2066 let dep = futures_executor::block_on(Depends::<OverrideDep>::from_request(&ctx, &mut req))
2067 .unwrap();
2068 assert_eq!(
2069 dep.value, 1,
2070 "Without overrides, real dependency should be used"
2071 );
2072 }
2073
2074 #[test]
2076 fn resolution_guard_cleanup() {
2077 let stack = ResolutionStack::new();
2078 stack.push::<CounterDep>("CounterDep", DependencyScope::Request);
2079 assert_eq!(stack.depth(), 1);
2080
2081 {
2082 let _guard = ResolutionGuard::new(&stack);
2084 stack.push::<ErrorDep>("ErrorDep", DependencyScope::Request);
2085 assert_eq!(stack.depth(), 2);
2086 }
2088
2089 assert_eq!(stack.depth(), 1);
2092 }
2093
2094 #[test]
2099 fn cleanup_stack_basic() {
2100 let stack = CleanupStack::new();
2101 assert!(stack.is_empty());
2102 assert_eq!(stack.len(), 0);
2103
2104 let counter = Arc::new(AtomicUsize::new(0));
2106 let counter_clone = Arc::clone(&counter);
2107 stack.push(Box::new(move || {
2108 Box::pin(async move {
2109 counter_clone.fetch_add(1, Ordering::SeqCst);
2110 }) as Pin<Box<dyn Future<Output = ()> + Send>>
2111 }));
2112
2113 assert!(!stack.is_empty());
2114 assert_eq!(stack.len(), 1);
2115
2116 let completed = futures_executor::block_on(stack.run_cleanups());
2118 assert_eq!(completed, 1);
2119 assert_eq!(counter.load(Ordering::SeqCst), 1);
2120
2121 assert!(stack.is_empty());
2123 }
2124
2125 #[test]
2126 fn cleanup_stack_lifo_order() {
2127 let order = Arc::new(parking_lot::Mutex::new(Vec::<i32>::new()));
2129
2130 let stack = CleanupStack::new();
2131
2132 for i in 1..=3 {
2134 let order_clone = Arc::clone(&order);
2135 stack.push(Box::new(move || {
2136 Box::pin(async move {
2137 order_clone.lock().push(i);
2138 }) as Pin<Box<dyn Future<Output = ()> + Send>>
2139 }));
2140 }
2141
2142 futures_executor::block_on(stack.run_cleanups());
2144
2145 let executed_order = order.lock().clone();
2146 assert_eq!(
2147 executed_order,
2148 vec![3, 2, 1],
2149 "Cleanups should run in LIFO order"
2150 );
2151 }
2152
2153 #[test]
2154 fn cleanup_stack_take_cleanups() {
2155 let stack = CleanupStack::new();
2156
2157 for _ in 0..3 {
2159 stack.push(Box::new(|| {
2160 Box::pin(async {}) as Pin<Box<dyn Future<Output = ()> + Send>>
2161 }));
2162 }
2163
2164 assert_eq!(stack.len(), 3);
2165
2166 let cleanups = stack.take_cleanups();
2168 assert_eq!(cleanups.len(), 3);
2169
2170 assert!(stack.is_empty());
2172 }
2173
2174 #[test]
2175 fn cleanup_stack_multiple_runs() {
2176 let counter = Arc::new(AtomicUsize::new(0));
2177 let stack = CleanupStack::new();
2178
2179 let counter_clone = Arc::clone(&counter);
2181 stack.push(Box::new(move || {
2182 Box::pin(async move {
2183 counter_clone.fetch_add(1, Ordering::SeqCst);
2184 }) as Pin<Box<dyn Future<Output = ()> + Send>>
2185 }));
2186 futures_executor::block_on(stack.run_cleanups());
2187
2188 let counter_clone = Arc::clone(&counter);
2190 stack.push(Box::new(move || {
2191 Box::pin(async move {
2192 counter_clone.fetch_add(10, Ordering::SeqCst);
2193 }) as Pin<Box<dyn Future<Output = ()> + Send>>
2194 }));
2195 futures_executor::block_on(stack.run_cleanups());
2196
2197 assert_eq!(counter.load(Ordering::SeqCst), 11);
2198 }
2199
2200 #[test]
2201 fn cleanup_stack_panic_continues() {
2202 let order = Arc::new(parking_lot::Mutex::new(Vec::<i32>::new()));
2204
2205 let stack = CleanupStack::new();
2206
2207 let order_clone = Arc::clone(&order);
2209 stack.push(Box::new(move || {
2210 Box::pin(async move {
2211 order_clone.lock().push(1);
2212 }) as Pin<Box<dyn Future<Output = ()> + Send>>
2213 }));
2214
2215 stack.push(Box::new(|| -> Pin<Box<dyn Future<Output = ()> + Send>> {
2217 panic!("cleanup 2 panics");
2218 }));
2219
2220 let order_clone = Arc::clone(&order);
2222 stack.push(Box::new(move || {
2223 Box::pin(async move {
2224 order_clone.lock().push(3);
2225 }) as Pin<Box<dyn Future<Output = ()> + Send>>
2226 }));
2227
2228 let completed = futures_executor::block_on(stack.run_cleanups());
2230
2231 assert_eq!(completed, 2, "Should report 2 successful cleanups");
2233
2234 let executed_order = order.lock().clone();
2235 assert_eq!(
2237 executed_order,
2238 vec![3, 1],
2239 "Cleanups should continue after panic"
2240 );
2241 }
2242
2243 #[test]
2244 fn cleanup_runs_after_handler_error() {
2245 let cleanup_ran = Arc::new(AtomicBool::new(false));
2249 let cleanup_ran_clone = Arc::clone(&cleanup_ran);
2250
2251 let ctx = test_context(None);
2252
2253 ctx.cleanup_stack().push(Box::new(move || {
2255 Box::pin(async move {
2256 cleanup_ran_clone.store(true, Ordering::SeqCst);
2257 }) as Pin<Box<dyn Future<Output = ()> + Send>>
2258 }));
2259
2260 let handler_result: Result<(), HttpError> =
2262 Err(HttpError::new(StatusCode::INTERNAL_SERVER_ERROR).with_detail("handler failed"));
2263
2264 futures_executor::block_on(ctx.cleanup_stack().run_cleanups());
2266
2267 assert!(
2268 cleanup_ran.load(Ordering::SeqCst),
2269 "Cleanup should run even after handler error"
2270 );
2271
2272 assert!(handler_result.is_err());
2274 }
2275
2276 #[derive(Clone)]
2282 struct TrackedResource {
2283 id: u32,
2284 }
2285
2286 impl FromDependencyWithCleanup for TrackedResource {
2287 type Value = TrackedResource;
2288 type Error = HttpError;
2289
2290 async fn setup(
2291 ctx: &RequestContext,
2292 _req: &mut Request,
2293 ) -> Result<(Self::Value, Option<CleanupFn>), Self::Error> {
2294 let tracker = ctx
2296 .dependency_cache()
2297 .get::<Arc<parking_lot::Mutex<Vec<String>>>>()
2298 .unwrap_or_else(|| {
2299 let t = Arc::new(parking_lot::Mutex::new(Vec::new()));
2300 ctx.dependency_cache().insert(Arc::clone(&t));
2301 t
2302 });
2303
2304 tracker.lock().push("setup:resource".to_string());
2305
2306 let cleanup_tracker = Arc::clone(&tracker);
2307 let cleanup = Box::new(move || {
2308 Box::pin(async move {
2309 cleanup_tracker.lock().push("cleanup:resource".to_string());
2310 }) as Pin<Box<dyn Future<Output = ()> + Send>>
2311 }) as CleanupFn;
2312
2313 Ok((TrackedResource { id: 42 }, Some(cleanup)))
2314 }
2315 }
2316
2317 #[test]
2318 fn depends_cleanup_registers_cleanup() {
2319 let ctx = test_context(None);
2320 let mut req = empty_request();
2321
2322 let dep = futures_executor::block_on(DependsCleanup::<TrackedResource>::from_request(
2324 &ctx, &mut req,
2325 ))
2326 .expect("cleanup dependency resolution failed");
2327
2328 assert_eq!(dep.id, 42);
2329
2330 assert_eq!(ctx.cleanup_stack().len(), 1);
2332
2333 let tracker = ctx
2335 .dependency_cache()
2336 .get::<Arc<parking_lot::Mutex<Vec<String>>>>()
2337 .unwrap();
2338
2339 let events = tracker.lock().clone();
2341 assert_eq!(events, vec!["setup:resource"]);
2342
2343 futures_executor::block_on(ctx.cleanup_stack().run_cleanups());
2345
2346 let events = tracker.lock().clone();
2348 assert_eq!(events, vec!["setup:resource", "cleanup:resource"]);
2349 }
2350
2351 #[derive(Clone)]
2353 struct NoCleanupResource {
2354 value: String,
2355 }
2356
2357 impl FromDependencyWithCleanup for NoCleanupResource {
2358 type Value = NoCleanupResource;
2359 type Error = HttpError;
2360
2361 async fn setup(
2362 _ctx: &RequestContext,
2363 _req: &mut Request,
2364 ) -> Result<(Self::Value, Option<CleanupFn>), Self::Error> {
2365 Ok((
2366 NoCleanupResource {
2367 value: "no cleanup".to_string(),
2368 },
2369 None, ))
2371 }
2372 }
2373
2374 #[test]
2375 fn depends_cleanup_no_cleanup_fn() {
2376 let ctx = test_context(None);
2377 let mut req = empty_request();
2378
2379 let dep = futures_executor::block_on(DependsCleanup::<NoCleanupResource>::from_request(
2380 &ctx, &mut req,
2381 ))
2382 .expect("no cleanup dependency resolution failed");
2383
2384 assert_eq!(dep.value, "no cleanup");
2385
2386 assert!(ctx.cleanup_stack().is_empty());
2388 }
2389
2390 #[derive(Clone)]
2392 struct OuterWithCleanup {
2393 inner_id: u32,
2394 }
2395
2396 impl FromDependencyWithCleanup for OuterWithCleanup {
2397 type Value = OuterWithCleanup;
2398 type Error = HttpError;
2399
2400 async fn setup(
2401 ctx: &RequestContext,
2402 req: &mut Request,
2403 ) -> Result<(Self::Value, Option<CleanupFn>), Self::Error> {
2404 let inner = DependsCleanup::<TrackedResource>::from_request(ctx, req).await?;
2406
2407 let tracker = ctx
2409 .dependency_cache()
2410 .get::<Arc<parking_lot::Mutex<Vec<String>>>>()
2411 .unwrap();
2412
2413 tracker.lock().push("setup:outer".to_string());
2414
2415 let cleanup_tracker = Arc::clone(&tracker);
2416 let cleanup = Box::new(move || {
2417 Box::pin(async move {
2418 cleanup_tracker.lock().push("cleanup:outer".to_string());
2419 }) as Pin<Box<dyn Future<Output = ()> + Send>>
2420 }) as CleanupFn;
2421
2422 Ok((OuterWithCleanup { inner_id: inner.id }, Some(cleanup)))
2423 }
2424 }
2425
2426 #[test]
2427 fn depends_cleanup_nested_lifo() {
2428 let ctx = test_context(None);
2429 let mut req = empty_request();
2430
2431 let dep = futures_executor::block_on(DependsCleanup::<OuterWithCleanup>::from_request(
2433 &ctx, &mut req,
2434 ))
2435 .expect("nested cleanup dependency resolution failed");
2436
2437 assert_eq!(dep.inner_id, 42);
2438
2439 assert_eq!(ctx.cleanup_stack().len(), 2);
2441
2442 let tracker = ctx
2444 .dependency_cache()
2445 .get::<Arc<parking_lot::Mutex<Vec<String>>>>()
2446 .unwrap();
2447
2448 let events_before = tracker.lock().clone();
2450 assert_eq!(events_before, vec!["setup:resource", "setup:outer"]);
2451
2452 futures_executor::block_on(ctx.cleanup_stack().run_cleanups());
2454
2455 let events_after = tracker.lock().clone();
2456 assert_eq!(
2457 events_after,
2458 vec![
2459 "setup:resource",
2460 "setup:outer",
2461 "cleanup:outer",
2462 "cleanup:resource"
2463 ],
2464 "Cleanups should run in LIFO order"
2465 );
2466 }
2467
2468 #[test]
2469 fn depends_cleanup_caching() {
2470 let ctx = test_context(None);
2471 let mut req = empty_request();
2472
2473 let _dep1 = futures_executor::block_on(DependsCleanup::<TrackedResource>::from_request(
2475 &ctx, &mut req,
2476 ))
2477 .unwrap();
2478
2479 let _dep2 = futures_executor::block_on(DependsCleanup::<TrackedResource>::from_request(
2481 &ctx, &mut req,
2482 ))
2483 .unwrap();
2484
2485 assert_eq!(ctx.cleanup_stack().len(), 1);
2487
2488 let tracker = ctx
2490 .dependency_cache()
2491 .get::<Arc<parking_lot::Mutex<Vec<String>>>>()
2492 .unwrap();
2493
2494 let events = tracker.lock().clone();
2496 assert_eq!(events, vec!["setup:resource"]);
2497 }
2498
2499 #[test]
2505 fn scope_error_formatting() {
2506 let err = DependencyScopeError::new("CachedUser".to_string(), "DbConnection".to_string());
2507 let msg = err.to_string();
2508 assert!(msg.contains("Dependency scope violation"));
2509 assert!(msg.contains("request-scoped 'CachedUser'"));
2510 assert!(msg.contains("function-scoped 'DbConnection'"));
2511 assert!(msg.contains("cached value would become stale"));
2512 }
2513
2514 #[test]
2516 fn scope_error_into_response() {
2517 let err = DependencyScopeError::new("A".to_string(), "B".to_string());
2518 let response = err.into_response();
2519 assert_eq!(response.status().as_u16(), 500);
2520 }
2521
2522 #[test]
2524 fn resolution_stack_detects_scope_violation() {
2525 #[allow(dead_code)]
2526 struct RequestScoped;
2527 #[allow(dead_code)]
2528 struct FunctionScoped;
2529
2530 let stack = ResolutionStack::new();
2531
2532 stack.push::<RequestScoped>("RequestScoped", DependencyScope::Request);
2534
2535 let violation = stack.check_scope_violation("FunctionScoped", DependencyScope::Function);
2537 assert!(
2538 violation.is_some(),
2539 "Should detect request -> function scope violation"
2540 );
2541 let err = violation.unwrap();
2542 assert_eq!(err.request_scoped_type, "RequestScoped");
2543 assert_eq!(err.function_scoped_type, "FunctionScoped");
2544 }
2545
2546 #[test]
2548 fn resolution_stack_allows_request_to_request() {
2549 #[allow(dead_code)]
2550 struct RequestA;
2551 #[allow(dead_code)]
2552 struct RequestB;
2553
2554 let stack = ResolutionStack::new();
2555
2556 stack.push::<RequestA>("RequestA", DependencyScope::Request);
2558
2559 let violation = stack.check_scope_violation("RequestB", DependencyScope::Request);
2561 assert!(violation.is_none(), "Request -> Request should be allowed");
2562 }
2563
2564 #[test]
2566 fn resolution_stack_allows_function_to_function() {
2567 #[allow(dead_code)]
2568 struct FunctionA;
2569 #[allow(dead_code)]
2570 struct FunctionB;
2571
2572 let stack = ResolutionStack::new();
2573
2574 stack.push::<FunctionA>("FunctionA", DependencyScope::Function);
2576
2577 let violation = stack.check_scope_violation("FunctionB", DependencyScope::Function);
2579 assert!(
2580 violation.is_none(),
2581 "Function -> Function should be allowed"
2582 );
2583 }
2584
2585 #[test]
2587 fn resolution_stack_allows_function_to_request() {
2588 #[allow(dead_code)]
2589 struct FunctionScoped;
2590 #[allow(dead_code)]
2591 struct RequestScoped;
2592
2593 let stack = ResolutionStack::new();
2594
2595 stack.push::<FunctionScoped>("FunctionScoped", DependencyScope::Function);
2597
2598 let violation = stack.check_scope_violation("RequestScoped", DependencyScope::Request);
2600 assert!(violation.is_none(), "Function -> Request should be allowed");
2601 }
2602
2603 #[test]
2605 fn resolution_stack_nested_scope_violation() {
2606 #[allow(dead_code)]
2607 struct OuterRequest;
2608 #[allow(dead_code)]
2609 struct MiddleRequest;
2610 #[allow(dead_code)]
2611 struct InnerFunction;
2612
2613 let stack = ResolutionStack::new();
2614
2615 stack.push::<OuterRequest>("OuterRequest", DependencyScope::Request);
2617 stack.push::<MiddleRequest>("MiddleRequest", DependencyScope::Request);
2618
2619 let violation = stack.check_scope_violation("InnerFunction", DependencyScope::Function);
2621 assert!(violation.is_some(), "Should detect nested scope violation");
2622 let err = violation.unwrap();
2623 assert_eq!(err.request_scoped_type, "MiddleRequest");
2625 assert_eq!(err.function_scoped_type, "InnerFunction");
2626 }
2627
2628 #[test]
2630 fn resolution_stack_empty_no_scope_violation() {
2631 let stack = ResolutionStack::new();
2632
2633 let violation_fn = stack.check_scope_violation("SomeDep", DependencyScope::Function);
2635 let violation_req = stack.check_scope_violation("SomeDep", DependencyScope::Request);
2636
2637 assert!(
2638 violation_fn.is_none(),
2639 "Empty stack should allow function scope"
2640 );
2641 assert!(
2642 violation_req.is_none(),
2643 "Empty stack should allow request scope"
2644 );
2645 }
2646
2647 #[test]
2657 fn function_scoped_resolves_fresh_each_time() {
2658 let ctx = test_context(None);
2659 let mut req = empty_request();
2660
2661 let _ = futures_executor::block_on(Depends::<CountingDep, NoCache>::from_request(
2663 &ctx, &mut req,
2664 ))
2665 .unwrap();
2666
2667 let _ = futures_executor::block_on(Depends::<CountingDep, NoCache>::from_request(
2668 &ctx, &mut req,
2669 ))
2670 .unwrap();
2671
2672 let counter = ctx.dependency_cache().get::<Arc<AtomicUsize>>().unwrap();
2673 assert_eq!(
2674 counter.load(Ordering::SeqCst),
2675 2,
2676 "Function-scoped should resolve 2 times (not cached)"
2677 );
2678 }
2679
2680 #[test]
2682 fn request_scoped_cached_within_request() {
2683 let ctx = test_context(None);
2684 let mut req = empty_request();
2685
2686 let _ = futures_executor::block_on(Depends::<CountingDep>::from_request(&ctx, &mut req))
2688 .unwrap();
2689
2690 let _ = futures_executor::block_on(Depends::<CountingDep>::from_request(&ctx, &mut req))
2691 .unwrap();
2692
2693 let counter = ctx.dependency_cache().get::<Arc<AtomicUsize>>().unwrap();
2694 assert_eq!(
2695 counter.load(Ordering::SeqCst),
2696 1,
2697 "Request-scoped should resolve only once (cached)"
2698 );
2699 }
2700
2701 #[test]
2708 fn request_scope_cleanup_only_once() {
2709 let ctx = test_context(None);
2710 let mut req = empty_request();
2711
2712 let _ = futures_executor::block_on(DependsCleanup::<TrackedResource>::from_request(
2714 &ctx, &mut req,
2715 ))
2716 .unwrap();
2717
2718 let _ = futures_executor::block_on(DependsCleanup::<TrackedResource>::from_request(
2719 &ctx, &mut req,
2720 ))
2721 .unwrap();
2722
2723 assert_eq!(
2725 ctx.cleanup_stack().len(),
2726 1,
2727 "Request-scoped should only register cleanup once"
2728 );
2729
2730 let tracker = ctx
2731 .dependency_cache()
2732 .get::<Arc<parking_lot::Mutex<Vec<String>>>>()
2733 .unwrap();
2734
2735 assert_eq!(tracker.lock().len(), 1);
2737 assert_eq!(tracker.lock()[0], "setup:resource");
2738
2739 futures_executor::block_on(ctx.cleanup_stack().run_cleanups());
2741
2742 let events = tracker.lock().clone();
2744 assert_eq!(
2745 events,
2746 vec!["setup:resource", "cleanup:resource"],
2747 "Cleanup should run exactly once for cached dependency"
2748 );
2749 }
2750
2751 #[test]
2753 fn function_scope_cleanup_each_time() {
2754 #[derive(Clone)]
2756 #[allow(dead_code)]
2757 struct FunctionScopedWithCleanup {
2758 id: u32,
2759 }
2760
2761 impl FromDependencyWithCleanup for FunctionScopedWithCleanup {
2762 type Value = FunctionScopedWithCleanup;
2763 type Error = HttpError;
2764
2765 async fn setup(
2766 ctx: &RequestContext,
2767 _req: &mut Request,
2768 ) -> Result<(Self::Value, Option<CleanupFn>), Self::Error> {
2769 let counter = ctx
2771 .dependency_cache()
2772 .get::<Arc<AtomicUsize>>()
2773 .unwrap_or_else(|| {
2774 let c = Arc::new(AtomicUsize::new(0));
2775 ctx.dependency_cache().insert(Arc::clone(&c));
2776 c
2777 });
2778
2779 let cleanup_counter = Arc::clone(&counter);
2780 let cleanup = Box::new(move || {
2781 Box::pin(async move {
2782 cleanup_counter.fetch_add(1, Ordering::SeqCst);
2783 }) as Pin<Box<dyn Future<Output = ()> + Send>>
2784 }) as CleanupFn;
2785
2786 Ok((FunctionScopedWithCleanup { id: 42 }, Some(cleanup)))
2787 }
2788 }
2789
2790 let ctx = test_context(None);
2791 let mut req = empty_request();
2792
2793 let _ = futures_executor::block_on(
2795 DependsCleanup::<FunctionScopedWithCleanup, NoCache>::from_request(&ctx, &mut req),
2796 )
2797 .unwrap();
2798
2799 let _ = futures_executor::block_on(
2800 DependsCleanup::<FunctionScopedWithCleanup, NoCache>::from_request(&ctx, &mut req),
2801 )
2802 .unwrap();
2803
2804 let _ = futures_executor::block_on(
2805 DependsCleanup::<FunctionScopedWithCleanup, NoCache>::from_request(&ctx, &mut req),
2806 )
2807 .unwrap();
2808
2809 assert_eq!(
2811 ctx.cleanup_stack().len(),
2812 3,
2813 "Function-scoped should register cleanup each time"
2814 );
2815
2816 futures_executor::block_on(ctx.cleanup_stack().run_cleanups());
2818
2819 let counter = ctx.dependency_cache().get::<Arc<AtomicUsize>>().unwrap();
2821 assert_eq!(
2822 counter.load(Ordering::SeqCst),
2823 3,
2824 "All 3 cleanups should have run"
2825 );
2826 }
2827}