use super::*;
use crate::config::AppConfig;
fn make_mysql_conn() -> DatabaseConnection {
DatabaseConnection {
r#type: "mysql".to_string(),
hostname: "172.17.16.14".to_string(),
database: "shop".to_string(),
username: "shop".to_string(),
password: String::new(),
hostport: 8802,
charset: "utf8mb4".to_string(),
prefix: "sz_".to_string(),
deploy: 0,
rw_separate: false,
fields_strict: true,
break_reconnect: true,
}
}
#[test]
fn test_app_init_and_global() {
let config = AppConfig::default();
let app = App::init(config);
let app2 = App::global();
assert!(app2.is_some());
assert!(std::ptr::eq(app, app2.unwrap()));
let config2 = AppConfig::default();
let app3 = App::init(config2);
assert!(std::ptr::eq(app, app3));
}
#[test]
fn test_db_connection() {
let mut config = AppConfig::default();
config
.database
.connections
.insert("mysql".to_string(), make_mysql_conn());
let app = App::new(config);
let conn = app.db_connection("mysql");
assert!(conn.is_some());
let conn = conn.unwrap();
assert_eq!(conn.hostname, "172.17.16.14");
assert_eq!(conn.hostport, 8802);
assert_eq!(conn.prefix, "sz_");
}
#[test]
fn test_db_connection_not_found() {
let config = AppConfig::default();
let app = App::new(config);
assert!(app.db_connection("nonexistent").is_none());
}
#[test]
fn test_default_db_connection() {
let mut config = AppConfig::default();
config.database.default = "mysql".to_string();
config
.database
.connections
.insert("mysql".to_string(), make_mysql_conn());
let app = App::new(config);
let conn = app.default_db_connection();
assert!(conn.is_some());
assert_eq!(conn.unwrap().database, "shop");
}
#[test]
fn test_cache() {
let config = AppConfig::default();
let app = App::new(config);
assert!(app.cache().is_none());
app.set_cache("memory_cache");
assert_eq!(app.cache(), Some("memory_cache".to_string()));
}
#[test]
fn test_log() {
let config = AppConfig::default();
let app = App::new(config);
assert!(app.log().is_none());
app.set_log("file_logger");
assert_eq!(app.log(), Some("file_logger".to_string()));
}
#[test]
fn test_db_connection_names() {
let mut config = AppConfig::default();
config
.database
.connections
.insert("mysql".to_string(), make_mysql_conn());
config.database.connections.insert(
"njszjt".to_string(),
DatabaseConnection {
r#type: "mysql".to_string(),
hostname: "172.17.16.14".to_string(),
database: "njszjt".to_string(),
username: "njszjt".to_string(),
password: String::new(),
hostport: 8802,
charset: "utf8mb4".to_string(),
prefix: "soci_".to_string(),
deploy: 0,
rw_separate: false,
fields_strict: true,
break_reconnect: true,
},
);
let app = App::new(config);
let mut names = app.db_connection_names();
names.sort();
assert_eq!(names, vec!["mysql", "njszjt"]);
}
#[tokio::test]
async fn test_load_5_db_connections() {
let config_dir = std::env::current_dir().ok().and_then(|d| {
let mut current = d.clone();
for _ in 0..5 {
if current.join("config").exists() {
return Some(current.join("config"));
}
if let Some(parent) = current.parent() {
current = parent.to_path_buf();
} else {
break;
}
}
None
});
let Some(config_dir) = config_dir else {
eprintln!("跳过:未找到 config 目录");
return;
};
let config = AppConfig::load_from_dir(&config_dir).await.unwrap();
let names: Vec<&str> = config
.database
.connections
.keys()
.map(|s| s.as_str())
.collect();
assert!(
names.len() >= 5,
"应有 5 个数据库连接,实际 {}: {:?}",
names.len(),
names
);
assert!(config.database.connections.contains_key("mysql"));
assert!(config.database.connections.contains_key("njszjt"));
assert!(config.database.connections.contains_key("ljclz"));
assert!(config.database.connections.contains_key("food"));
assert!(config.database.connections.contains_key("oceanbase"));
assert_eq!(config.database.default, "mysql");
let default_conn = config.database.connections.get("mysql").unwrap();
assert_eq!(default_conn.hostname, "localhost");
assert_eq!(default_conn.hostport, 8802);
assert_eq!(default_conn.prefix, "sz_");
}
#[derive(Debug, PartialEq)]
struct TestService {
value: i32,
}
impl TestService {
fn new() -> Self {
Self { value: 42 }
}
}
#[test]
fn test_container_bind_transient() {
let container = Container::new();
container.bind(TestService::new);
let s1 = container.make::<TestService>().expect("应能解析服务");
let s2 = container.make::<TestService>().expect("应能解析服务");
assert!(!Arc::ptr_eq(&s1, &s2));
assert_eq!(s1.value, 42);
assert_eq!(s2.value, 42);
}
#[test]
fn test_container_singleton() {
let container = Container::new();
container.singleton(TestService::new);
let s1 = container.make::<TestService>().expect("应能解析服务");
let s2 = container.make::<TestService>().expect("应能解析服务");
assert!(Arc::ptr_eq(&s1, &s2));
assert_eq!(s1.value, 42);
}
#[test]
fn test_container_make_unregistered() {
let container = Container::new();
assert!(container.make::<TestService>().is_none());
}
#[test]
fn test_container_has() {
let container = Container::new();
assert!(!container.has::<TestService>());
container.singleton(TestService::new);
assert!(container.has::<TestService>());
}
#[test]
fn test_container_forget() {
let container = Container::new();
container.singleton(TestService::new);
let _ = container.make::<TestService>();
assert!(container.has::<TestService>());
container.forget::<TestService>();
assert!(!container.has::<TestService>());
assert!(container.make::<TestService>().is_none());
}
#[test]
fn test_container_clear() {
let container = Container::new();
container.singleton(TestService::new);
container.bind(|| 99i32);
assert_eq!(container.count(), 2);
container.clear();
assert_eq!(container.count(), 0);
}
#[test]
fn test_app_di_proxy() {
let config = AppConfig::default();
let app = App::new(config);
app.singleton(TestService::new);
assert!(app.has_service::<TestService>());
let s1 = app.make::<TestService>().expect("应能解析");
let s2 = app.make::<TestService>().expect("应能解析");
assert!(Arc::ptr_eq(&s1, &s2));
}
#[test]
fn test_container_multiple_types() {
let container = Container::new();
container.singleton(TestService::new);
container.singleton(|| String::from("logger"));
container.bind(|| vec![1, 2, 3]);
assert_eq!(container.make::<TestService>().unwrap().value, 42);
assert_eq!(&*container.make::<String>().unwrap(), "logger");
assert_eq!(*container.make::<Vec<i32>>().unwrap(), vec![1, 2, 3]);
}
#[test]
fn test_container_count() {
let container = Container::new();
assert_eq!(container.count(), 0);
container.bind(TestService::new);
assert_eq!(container.count(), 1);
container.singleton(|| String::from("x"));
assert_eq!(container.count(), 2);
}
struct ScopedCounter {
value: i64,
}
impl ScopedCounter {
fn new(value: i64) -> Self {
Self { value }
}
}
#[test]
fn test_container_scoped_same_scope() {
let container = Container::new();
container.scoped(|| ScopedCounter::new(100));
let s1 = container
.make_with_scope::<ScopedCounter>(1)
.expect("应能解析 scoped 服务");
let s2 = container
.make_with_scope::<ScopedCounter>(1)
.expect("应能解析 scoped 服务");
assert!(Arc::ptr_eq(&s1, &s2), "同一作用域内必须返回同一实例");
assert_eq!(s1.value, 100);
}
#[test]
fn test_container_scoped_different_scope() {
let container = Container::new();
container.scoped(|| ScopedCounter::new(200));
let s1 = container
.make_with_scope::<ScopedCounter>(1)
.expect("scope 1 应能解析");
let s2 = container
.make_with_scope::<ScopedCounter>(2)
.expect("scope 2 应能解析");
assert!(!Arc::ptr_eq(&s1, &s2), "不同作用域必须返回不同实例");
assert_eq!(s1.value, 200);
assert_eq!(s2.value, 200);
}
#[test]
fn test_container_clear_scope() {
let container = Container::new();
container.scoped(|| ScopedCounter::new(300));
let s1 = container
.make_with_scope::<ScopedCounter>(1)
.expect("scope 1 应能解析");
assert_eq!(container.active_scope_count(), 1);
container.clear_scope(1);
assert_eq!(container.active_scope_count(), 0);
let s2 = container
.make_with_scope::<ScopedCounter>(1)
.expect("scope 1 清理后应能再次解析");
assert!(!Arc::ptr_eq(&s1, &s2), "清理后再次解析必须返回新实例");
}
#[test]
fn test_container_scoped_with_singleton() {
let container = Container::new();
container.singleton(|| ScopedCounter::new(1000));
container.scoped(|| ScopedCounter::new(2000));
let single_a = container.make_with_scope::<ScopedCounter>(1);
let single_b = container.make_with_scope::<ScopedCounter>(2);
let scoped_a = container
.make_with_scope::<ScopedCounter>(1)
.expect("应能解析");
let scoped_b = container
.make_with_scope::<ScopedCounter>(2)
.expect("应能解析");
assert!(!Arc::ptr_eq(&scoped_a, &scoped_b));
let _ = (single_a, single_b);
}
#[test]
fn test_container_forget_clears_scoped() {
let container = Container::new();
container.scoped(|| ScopedCounter::new(400));
let _ = container.make_with_scope::<ScopedCounter>(1);
let _ = container.make_with_scope::<ScopedCounter>(2);
let _ = container.make_with_scope::<ScopedCounter>(3);
assert_eq!(container.active_scope_count(), 3);
container.forget::<ScopedCounter>();
assert!(!container.has::<ScopedCounter>());
}
#[test]
fn test_container_clear_all() {
let container = Container::new();
container.singleton(TestService::new);
container.scoped(|| ScopedCounter::new(500));
container.alias::<TestService>("svc");
let _ = container.make_with_scope::<ScopedCounter>(1);
assert_eq!(container.count(), 2);
assert_eq!(container.alias_count(), 1);
assert_eq!(container.active_scope_count(), 1);
container.clear();
assert_eq!(container.count(), 0);
assert_eq!(container.alias_count(), 0);
assert_eq!(container.active_scope_count(), 0);
}
#[test]
fn test_container_instance_direct_binding() {
let container = Container::new();
let original = Arc::new(TestService { value: 999 });
container.instance(TestService { value: 999 });
let resolved = container
.make::<TestService>()
.expect("instance() 注册的服务应能解析");
assert_eq!(resolved.value, 999);
let resolved2 = container.make::<TestService>().expect("应能再次解析");
assert!(Arc::ptr_eq(&resolved, &resolved2));
let _ = original;
}
#[test]
fn test_container_instance_has() {
let container = Container::new();
assert!(!container.has::<TestService>());
container.instance(TestService::new());
assert!(container.has::<TestService>());
}
#[test]
fn test_container_instance_forget() {
let container = Container::new();
container.instance(TestService::new());
assert!(container.has::<TestService>());
container.forget::<TestService>();
assert!(!container.has::<TestService>());
assert!(container.make::<TestService>().is_none());
}
#[test]
fn test_app_instance_scoped_alias_proxy() {
let config = AppConfig::default();
let app = App::new(config);
app.instance(TestService { value: 777 });
let s = app.make::<TestService>().expect("App::instance 后应能解析");
assert_eq!(s.value, 777);
app.scoped(|| ScopedCounter::new(888));
let s1 = app
.make_with_scope::<ScopedCounter>(42)
.expect("App::make_with_scope 应能解析");
let s2 = app
.make_with_scope::<ScopedCounter>(42)
.expect("同 scope 应能再次解析");
assert!(Arc::ptr_eq(&s1, &s2));
app.alias::<TestService>("test_svc");
assert!(app.container().is_alias("test_svc"));
let type_id = app
.container()
.resolve_alias("test_svc")
.expect("别名应能解析");
assert_eq!(type_id, std::any::TypeId::of::<TestService>());
app.clear_scope(42);
assert_eq!(app.container().active_scope_count(), 0);
}
#[test]
fn test_container_alias_register_and_resolve() {
let container = Container::new();
container.singleton(TestService::new);
container.alias::<TestService>("test_service");
assert!(container.is_alias("test_service"));
assert!(!container.is_alias("nonexistent"));
let type_id = container
.resolve_alias("test_service")
.expect("别名应能解析");
assert_eq!(type_id, std::any::TypeId::of::<TestService>());
}
#[test]
fn test_container_alias_resolve_unregistered() {
let container = Container::new();
assert!(container.resolve_alias("not_registered").is_none());
}
#[test]
fn test_container_multiple_aliases() {
let container = Container::new();
container.singleton(TestService::new);
container.singleton(|| String::from("logger"));
container.alias::<TestService>("svc1");
container.alias::<TestService>("svc2");
container.alias::<String>("logger");
assert_eq!(container.alias_count(), 3);
let mut aliases = container.debug_aliases();
aliases.sort();
assert_eq!(
aliases,
vec!["logger".to_string(), "svc1".to_string(), "svc2".to_string()]
);
let tid1 = container.resolve_alias("svc1").unwrap();
let tid2 = container.resolve_alias("svc2").unwrap();
assert_eq!(tid1, tid2);
}
#[test]
fn test_container_alias_does_not_affect_make() {
let container = Container::new();
container.singleton(TestService::new);
container.alias::<TestService>("my_svc");
let svc = container.make::<TestService>().expect("make 应正常工作");
assert_eq!(svc.value, 42);
container.alias::<String>("my_str");
assert!(container.is_alias("my_str"));
assert!(container.make::<String>().is_none());
}
#[derive(Debug, PartialEq)]
struct FileLogger {
level: u8,
}
impl FileLogger {
fn new() -> Self {
Self { level: 1 }
}
}
#[derive(Debug, PartialEq)]
struct MailLogger {
level: u8,
}
impl MailLogger {
fn new() -> Self {
Self { level: 2 }
}
}
#[test]
fn test_container_tag_and_tagged() {
let container = Container::new();
container.singleton(FileLogger::new);
container.singleton(MailLogger::new);
container.tag::<FileLogger>("reporters");
let reporters = container.tagged::<FileLogger>("reporters");
assert_eq!(reporters.len(), 1);
assert_eq!(reporters[0].level, 1);
}
#[test]
fn test_container_tag_multiple_same_type() {
let container = Container::new();
container.singleton(FileLogger::new);
container.tag::<FileLogger>("reporters");
container.tag::<FileLogger>("reporters");
container.tag::<FileLogger>("reporters");
let reporters = container.tagged::<FileLogger>("reporters");
assert_eq!(reporters.len(), 3);
assert!(Arc::ptr_eq(&reporters[0], &reporters[1]));
assert!(Arc::ptr_eq(&reporters[1], &reporters[2]));
}
#[test]
fn test_container_tag_different_tags() {
let container = Container::new();
container.singleton(FileLogger::new);
container.singleton(MailLogger::new);
container.tag::<FileLogger>("file_reporters");
container.tag::<MailLogger>("mail_reporters");
assert_eq!(container.tagged::<FileLogger>("file_reporters").len(), 1);
assert_eq!(container.tagged::<MailLogger>("mail_reporters").len(), 1);
assert_eq!(container.tagged::<FileLogger>("mail_reporters").len(), 0);
assert_eq!(container.tagged::<MailLogger>("file_reporters").len(), 0);
}
#[test]
fn test_container_tagged_nonexistent_tag() {
let container = Container::new();
container.singleton(FileLogger::new);
let reporters = container.tagged::<FileLogger>("nonexistent");
assert!(reporters.is_empty());
}
#[test]
fn test_container_tagged_unregistered_type() {
let container = Container::new();
container.tag::<FileLogger>("reporters");
let reporters = container.tagged::<FileLogger>("reporters");
assert!(reporters.is_empty());
}
#[test]
fn test_container_tag_count() {
let container = Container::new();
assert_eq!(container.tag_count("nonexistent"), 0);
container.tag::<FileLogger>("reporters");
container.tag::<MailLogger>("reporters");
assert_eq!(container.tag_count("reporters"), 2);
}
#[test]
fn test_container_tag_names() {
let container = Container::new();
assert!(container.tag_names().is_empty());
container.tag::<FileLogger>("reporters");
container.tag::<MailLogger>("notifiers");
let mut names = container.tag_names();
names.sort();
assert_eq!(
names,
vec!["notifiers".to_string(), "reporters".to_string()]
);
}
#[test]
fn test_container_tagged_type_ids() {
let container = Container::new();
container.tag::<FileLogger>("reporters");
container.tag::<MailLogger>("reporters");
let type_ids = container.tagged_type_ids("reporters");
assert_eq!(type_ids.len(), 2);
assert!(type_ids.contains(&TypeId::of::<FileLogger>()));
assert!(type_ids.contains(&TypeId::of::<MailLogger>()));
assert!(container.tagged_type_ids("nonexistent").is_empty());
}
#[test]
fn test_container_forget_tag() {
let container = Container::new();
container.singleton(FileLogger::new);
container.tag::<FileLogger>("reporters");
assert_eq!(container.tag_count("reporters"), 1);
container.forget_tag("reporters");
assert_eq!(container.tag_count("reporters"), 0);
assert!(container.tagged::<FileLogger>("reporters").is_empty());
assert!(!container.tag_names().contains(&"reporters".to_string()));
}
#[test]
fn test_container_clear_clears_tags() {
let container = Container::new();
container.singleton(FileLogger::new);
container.tag::<FileLogger>("reporters");
container.tag::<FileLogger>("notifiers");
assert!(!container.tag_names().is_empty());
container.clear();
assert!(container.tag_names().is_empty());
assert_eq!(container.tag_count("reporters"), 0);
}
struct PhotoController;
struct VideoController;
#[test]
fn test_container_bind_contextual() {
let container = Container::new();
container.bind_contextual::<PhotoController, String, _>(|| "s3".to_string());
assert!(container.has_contextual::<PhotoController, String>());
assert!(!container.has_contextual::<VideoController, String>());
let fs = container
.make_for::<String, PhotoController>()
.expect("应为 PhotoController 解析上下文绑定");
assert_eq!(&*fs, "s3");
}
#[test]
fn test_container_make_for_fallback() {
let container = Container::new();
container.singleton(|| "default".to_string());
let result = container
.make_for::<String, PhotoController>()
.expect("应回退到普通 make");
assert_eq!(&*result, "default");
}
#[test]
fn test_container_make_for_no_binding() {
let container = Container::new();
let result = container.make_for::<String, PhotoController>();
assert!(result.is_none());
}
#[test]
fn test_container_contextual_different_consumers() {
let container = Container::new();
container.bind_contextual::<PhotoController, String, _>(|| "s3".to_string());
container.bind_contextual::<VideoController, String, _>(|| "local".to_string());
let photo_fs = container
.make_for::<String, PhotoController>()
.expect("PhotoController 应解析");
let video_fs = container
.make_for::<String, VideoController>()
.expect("VideoController 应解析");
assert_eq!(&*photo_fs, "s3");
assert_eq!(&*video_fs, "local");
}
#[test]
fn test_container_contextual_count() {
let container = Container::new();
assert_eq!(container.contextual_count(), 0);
container.bind_contextual::<PhotoController, String, _>(|| "s3".to_string());
assert_eq!(container.contextual_count(), 1);
container.bind_contextual::<VideoController, String, _>(|| "local".to_string());
assert_eq!(container.contextual_count(), 2);
container.bind_contextual::<PhotoController, String, _>(|| "azure".to_string());
assert_eq!(container.contextual_count(), 2);
}
#[test]
fn test_container_forget_contextual() {
let container = Container::new();
container.bind_contextual::<PhotoController, String, _>(|| "s3".to_string());
assert!(container.has_contextual::<PhotoController, String>());
container.forget_contextual::<PhotoController, String>();
assert!(!container.has_contextual::<PhotoController, String>());
assert!(container.make_for::<String, PhotoController>().is_none());
}
#[test]
fn test_container_clear_clears_contextual() {
let container = Container::new();
container.bind_contextual::<PhotoController, String, _>(|| "s3".to_string());
container.bind_contextual::<VideoController, String, _>(|| "local".to_string());
assert_eq!(container.contextual_count(), 2);
container.clear();
assert_eq!(container.contextual_count(), 0);
assert!(!container.has_contextual::<PhotoController, String>());
assert!(!container.has_contextual::<VideoController, String>());
}
#[test]
fn test_container_tag_and_contextual_coexist() {
let container = Container::new();
container.singleton(|| "default".to_string());
container.tag::<String>("text_services");
container.bind_contextual::<PhotoController, String, _>(|| "s3".to_string());
let tagged = container.tagged::<String>("text_services");
assert_eq!(tagged.len(), 1);
assert_eq!(&*tagged[0], "default");
let contextual = container
.make_for::<String, PhotoController>()
.expect("上下文绑定应正常工作");
assert_eq!(&*contextual, "s3");
}
#[test]
fn test_container_call_method_basic() {
let container = Container::new();
container.singleton(FileLogger::new);
let result: u8 =
container.call_method(|c| c.make::<FileLogger>().unwrap(), |logger| logger.level);
assert_eq!(result, 1);
}
#[test]
fn test_container_call_method_with_dependencies() {
let container = Container::new();
container.singleton(FileLogger::new);
container.singleton(MailLogger::new);
let result: String = container.call_method(
|c| {
(
c.make::<FileLogger>().unwrap(),
c.make::<MailLogger>().unwrap(),
)
},
|(file_logger, mail_logger)| {
format!("file={}, mail={}", file_logger.level, mail_logger.level)
},
);
assert_eq!(result, "file=1, mail=2");
}
#[test]
fn test_container_call_method_return_value() {
let container = Container::new();
container.singleton(TestService::new);
let result: i32 = container.call_method(
|c| c.make::<TestService>().unwrap(),
|service| service.value * 2,
);
assert_eq!(result, 84);
}
#[test]
fn test_container_invoke_basic() {
let container = Container::new();
container.singleton(FileLogger::new);
let result: u8 = container.invoke(|c| c.make::<FileLogger>().unwrap().level);
assert_eq!(result, 1);
}
#[test]
fn test_container_invoke_with_multiple_deps() {
let container = Container::new();
container.singleton(FileLogger::new);
container.singleton(MailLogger::new);
container.singleton(TestService::new);
let result: String = container.invoke(|c| {
let file_logger = c.make::<FileLogger>().unwrap();
let mail_logger = c.make::<MailLogger>().unwrap();
let service = c.make::<TestService>().unwrap();
format!(
"file={}, mail={}, service={}",
file_logger.level, mail_logger.level, service.value
)
});
assert_eq!(result, "file=1, mail=2, service=42");
}
#[test]
fn test_container_make_or_panic_success() {
let container = Container::new();
container.singleton(TestService::new);
let service = container.make_or_panic::<TestService>();
assert_eq!(service.value, 42);
let service2 = container.make_or_panic::<TestService>();
assert!(Arc::ptr_eq(&service, &service2));
}
#[test]
#[should_panic(expected = "无法解析服务")]
fn test_container_make_or_panic_panic() {
let container = Container::new();
let _ = container.make_or_panic::<TestService>();
}
#[test]
fn test_container_call_method_no_dependencies() {
let container = Container::new();
let result: String = container.call_method(|_| (), |_| String::from("no dependencies needed"));
assert_eq!(result, "no dependencies needed");
}
#[test]
fn test_container_constructing_stack_cleared_after_make() {
let container = Container::new();
container.singleton(TestService::new);
assert_eq!(container.constructing_depth(), 0);
let _svc = container.make::<TestService>();
assert_eq!(container.constructing_depth(), 0);
}
#[test]
fn test_container_non_circular_dependency_works() {
use std::sync::Arc;
#[derive(Debug, PartialEq)]
struct Inner {
value: i32,
}
#[derive(Debug, PartialEq)]
struct Outer {
inner: Arc<Inner>,
}
let container = Container::new();
container.singleton(|| Inner { value: 42 });
let c = Arc::new(container);
let c_outer = c.clone();
c.singleton(move || {
let inner = c_outer.make::<Inner>().expect("Inner 应能解析");
Outer { inner }
});
let outer = c.make::<Outer>().expect("Outer 应能解析");
assert_eq!(outer.inner.value, 42);
assert_eq!(c.constructing_depth(), 0);
}
#[test]
#[should_panic(expected = "DI 容器检测到循环依赖")]
fn test_container_circular_indirect_a_b_a() {
use std::sync::Arc;
#[derive(Debug)]
#[allow(dead_code)]
struct ServiceA(Arc<ServiceB>);
#[derive(Debug)]
#[allow(dead_code)]
struct ServiceB(Arc<ServiceA>);
let container = Container::new();
let c = Arc::new(container);
let c_b = c.clone();
c.singleton(move || ServiceA(c_b.make::<ServiceB>().expect("ServiceB 应能解析")));
let c_a = c.clone();
c.singleton(move || ServiceB(c_a.make::<ServiceA>().expect("ServiceA 应能解析")));
let _ = c.make::<ServiceA>();
}
#[test]
#[should_panic(expected = "DI 容器检测到循环依赖")]
fn test_container_circular_long_chain() {
use std::sync::Arc;
#[derive(Debug)]
#[allow(dead_code)]
struct NodeA(Arc<NodeB>);
#[derive(Debug)]
#[allow(dead_code)]
struct NodeB(Arc<NodeC>);
#[derive(Debug)]
#[allow(dead_code)]
struct NodeC(Arc<NodeA>);
let container = Container::new();
let c = Arc::new(container);
let c_b = c.clone();
c.singleton(move || NodeA(c_b.make::<NodeB>().expect("NodeB")));
let c_c = c.clone();
c.singleton(move || NodeB(c_c.make::<NodeC>().expect("NodeC")));
let c_a = c.clone();
c.singleton(move || NodeC(c_a.make::<NodeA>().expect("NodeA")));
let _ = c.make::<NodeA>();
}
#[test]
#[should_panic(
expected = "DI 容器检测到循环依赖: sz_rust_core::container::tests::test_container_circular_error_message_contains_chain::NodeX"
)]
fn test_container_circular_error_message_contains_chain() {
use std::sync::Arc;
#[derive(Debug)]
#[allow(dead_code)]
struct NodeX(Arc<NodeY>);
#[derive(Debug)]
#[allow(dead_code)]
struct NodeY(Arc<NodeX>);
let container = Container::new();
let c = Arc::new(container);
let c_y = c.clone();
c.singleton(move || NodeX(c_y.make::<NodeY>().expect("NodeY")));
let c_x = c.clone();
c.singleton(move || NodeY(c_x.make::<NodeX>().expect("NodeX")));
let _ = c.make::<NodeX>();
}
#[test]
fn test_app_has_service_registered() {
let app = App::new(AppConfig::default());
app.singleton(|| "hello".to_string());
assert!(app.has_service::<String>());
}
#[test]
fn test_app_has_service_not_registered() {
let app = App::new(AppConfig::default());
assert!(!app.has_service::<String>());
}
#[test]
fn test_app_make_with_scope_existing() {
#[derive(Debug)]
struct ScopedVal(i32);
let app = App::new(AppConfig::default());
app.scoped(|| ScopedVal(42));
let scope_id = 1;
let instance = app.make_with_scope::<ScopedVal>(scope_id);
assert!(instance.is_some());
assert_eq!(instance.unwrap().0, 42);
}
#[test]
fn test_app_make_with_scope_unregistered() {
let app = App::new(AppConfig::default());
let result = app.make_with_scope::<String>(1);
assert!(result.is_none());
}
#[test]
fn test_app_make_with_scope_returns_cached() {
use std::sync::Arc;
#[derive(Debug)]
struct Counter(Arc<std::sync::atomic::AtomicUsize>);
let app = App::new(AppConfig::default());
let counter = Arc::new(std::sync::atomic::AtomicUsize::new(0));
let c2 = counter.clone();
app.scoped(move || Counter(c2.clone()));
let scope_id = 7;
let first = app.make_with_scope::<Counter>(scope_id).unwrap();
let second = app.make_with_scope::<Counter>(scope_id).unwrap();
assert!(Arc::ptr_eq(&first.0, &second.0));
}