#![allow(non_snake_case)]
#![allow(dead_code)]
mod common;
use rust_dix::*;
use rust_dix_macros::Inject;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
#[test]
fn transient_get_owned_returns_fresh_owned() {
let p = ServiceCollection::new()
.transient(|_| Arc::new(common::MyService { value: 0 }))
.build()
.unwrap();
let mut a = p.get_owned::<common::MyService>().unwrap();
let b = p.get_owned::<common::MyService>().unwrap();
a.value = 42;
assert_eq!(b.value, 0, "second owned instance must be unaffected");
assert_eq!(a.value, 42);
}
#[test]
fn scoped_get_owned_bypasses_cache() {
let p = ServiceCollection::new()
.scoped(|_| Arc::new(common::MyService { value: 0 }))
.build()
.unwrap();
let a = p.get::<common::MyService>().unwrap();
let b = p.get::<common::MyService>().unwrap();
assert!(Arc::ptr_eq(&a, &b), "get() must cache scoped in root");
let mut o1 = p.get_owned::<common::MyService>().unwrap();
let o2 = p.get_owned::<common::MyService>().unwrap();
o1.value = 99;
assert_eq!(o1.value, 99);
assert_eq!(o2.value, 0, "get_owned must bypass cache (fresh instances)");
let c = p.get::<common::MyService>().unwrap();
assert!(
Arc::ptr_eq(&a, &c),
"get_owned must not pollute root_scoped_cache"
);
assert_eq!(
c.value, 0,
"owned mutation must not leak into cached instance"
);
}
#[test]
fn singleton_get_owned_panics() {
let p = ServiceCollection::new()
.singleton(|_| Arc::new(common::MyService { value: 1 }))
.build()
.unwrap();
let result = p.get_owned::<common::MyService>();
assert!(result.is_err());
}
#[test]
fn singleton_try_get_owned_returns_none() {
let p = ServiceCollection::new()
.singleton(|_| Arc::new(common::MyService { value: 1 }))
.build()
.unwrap();
assert!(p.try_get_owned::<common::MyService>().is_none());
}
#[test]
fn try_get_owned_none_when_unregistered() {
let p = ServiceCollection::new().build().unwrap();
assert!(p.try_get_owned::<common::MyService>().is_none());
}
#[derive(Debug)]
struct ScopedCore(u64);
#[derive(Inject)]
struct OwnedHandler {
#[inject]
core: Arc<ScopedCore>,
}
#[test]
fn scope_owned_transient_arc_scoped_dep_binds_to_child() {
static CORE_CALLS: AtomicU64 = AtomicU64::new(0);
let provider = ServiceCollection::new()
.scoped(|_| {
let n = CORE_CALLS.fetch_add(1, Ordering::SeqCst);
Arc::new(ScopedCore(n))
})
.transient(|r| __rdi_construct_OwnedHandler(r))
.build()
.unwrap();
let scope1 = provider.scope();
let h1 = scope1.get_owned::<OwnedHandler>().unwrap();
let h2 = scope1.get_owned::<OwnedHandler>().unwrap();
assert!(
Arc::ptr_eq(&h1.core, &h2.core),
"Arc<ScopedCore> must be cached in child scope"
);
let scope2 = provider.scope();
let h3 = scope2.get_owned::<OwnedHandler>().unwrap();
assert!(
!Arc::ptr_eq(&h1.core, &h3.core),
"different scopes must have independent ScopedCore"
);
assert_eq!(
CORE_CALLS.load(Ordering::SeqCst),
2,
"ScopedCore factory should run once per scope"
);
}
#[test]
fn keyed_transient_get_owned() {
let p = ServiceCollection::new()
.keyed_transient("primary", |_| Arc::new(common::MyService { value: 7 }))
.build()
.unwrap();
let svc = p.get_keyed_owned::<common::MyService>("primary").unwrap();
assert_eq!(svc.value, 7);
}
#[test]
fn keyed_singleton_get_owned_panics() {
let p = ServiceCollection::new()
.keyed_singleton("primary", |_| Arc::new(common::MyService { value: 7 }))
.build()
.unwrap();
let result = p.get_keyed_owned::<common::MyService>("primary");
assert!(result.is_err());
}
#[derive(Inject)]
struct MixedHandler {
#[inject(owned)]
svc: common::MyService,
#[inject]
logger: Arc<common::Logger>,
}
#[test]
fn derive_inject_owned_field() {
let p = ServiceCollection::new()
.transient(|_| Arc::new(common::MyService { value: 0 }))
.singleton(|_| {
Arc::new(common::Logger {
prefix: "log".into(),
})
})
.transient(|r| __rdi_construct_MixedHandler(r))
.build()
.unwrap();
let mut h = p.get_owned::<MixedHandler>().unwrap();
h.svc.value = 123;
assert_eq!(h.svc.value, 123, "owned field must be mutable");
assert_eq!(h.logger.prefix, "log");
}
#[derive(Inject)]
struct OptHandler {
#[inject(owned)]
maybe: Option<common::MyService>,
}
#[test]
fn derive_inject_option_owned_field_present() {
let p = ServiceCollection::new()
.transient(|_| Arc::new(common::MyService { value: 5 }))
.transient(|r| __rdi_construct_OptHandler(r))
.build()
.unwrap();
let h = p.get_owned::<OptHandler>().unwrap();
assert!(h.maybe.is_some(), "registered dep → Some(owned)");
assert_eq!(h.maybe.as_ref().unwrap().value, 5);
}
#[test]
fn derive_inject_option_owned_field_absent() {
let p = ServiceCollection::new()
.transient(|r| __rdi_construct_OptHandler(r))
.build()
.unwrap();
let h = p.get_owned::<OptHandler>().unwrap();
assert!(h.maybe.is_none(), "unregistered dep → None (no panic)");
}
#[derive(Inject)]
struct KeyedOwnedHandler {
#[inject(owned, key = "primary")]
svc: common::MyService,
}
#[test]
fn derive_inject_keyed_owned_field() {
let p = ServiceCollection::new()
.keyed_transient("primary", |_| Arc::new(common::MyService { value: 9 }))
.transient(|r| __rdi_construct_KeyedOwnedHandler(r))
.build()
.unwrap();
let h = p.get_owned::<KeyedOwnedHandler>().unwrap();
assert_eq!(h.svc.value, 9, "keyed owned field must resolve by key");
}
#[derive(Default)]
struct DbContext {
count: u32,
}
impl DbContext {
fn add(&mut self) {
self.count += 1;
}
fn total(&self) -> u32 {
self.count
}
}
#[derive(Inject)]
struct BlogHandler {
#[inject(owned)]
ctx: DbContext,
#[inject]
logger: Arc<common::Logger>,
}
#[test]
fn mixed_handler_mutable_self_e2e() {
let p = ServiceCollection::new()
.transient(|_| Arc::new(DbContext::default()))
.singleton(|_| {
Arc::new(common::Logger {
prefix: "ef".into(),
})
})
.transient(|r| __rdi_construct_BlogHandler(r))
.build()
.unwrap();
let mut h = p.get_owned::<BlogHandler>().unwrap();
assert_eq!(h.ctx.total(), 0, "fresh DbContext starts at 0");
h.ctx.add();
h.ctx.add();
assert_eq!(
h.ctx.total(),
2,
"&mut self mutation succeeds on owned DbContext"
);
assert_eq!(h.logger.prefix, "ef");
}
#[derive(Default)]
struct CaptiveScoped;
#[derive(Inject)]
struct CaptiveSingleton {
#[inject(owned)]
ctx: CaptiveScoped,
}
#[test]
fn captive_singleton_depends_on_scoped_owned_rejected() {
let result = ServiceCollection::new()
.scoped(|_| Arc::new(CaptiveScoped))
.singleton(|r| __rdi_construct_CaptiveSingleton(r))
.build();
assert!(
matches!(result, Err(RdiError::SingletonDependsOnScoped { .. })),
"Singleton with owned Scoped field must be rejected at build"
);
}