#[cfg(test)]
mod tests {
use crate::container::{IocContainerBuilder, ServiceBinder};
use crate::errors::CoreError;
use std::sync::Arc;
trait UserRepository: Send + Sync {
fn find(&self, id: u32) -> Option<String>;
}
trait EmailService: Send + Sync {
fn send(&self, to: &str, message: &str) -> Result<(), String>;
}
trait Logger: Send + Sync {
fn log(&self, level: &str, message: &str);
}
#[derive(Default)]
struct PostgresUserRepository;
unsafe impl Send for PostgresUserRepository {}
unsafe impl Sync for PostgresUserRepository {}
impl UserRepository for PostgresUserRepository {
fn find(&self, id: u32) -> Option<String> {
Some(format!("User {}", id))
}
}
#[derive(Default)]
struct SmtpEmailService;
unsafe impl Send for SmtpEmailService {}
unsafe impl Sync for SmtpEmailService {}
impl EmailService for SmtpEmailService {
fn send(&self, _to: &str, _message: &str) -> Result<(), String> {
Ok(())
}
}
#[derive(Default)]
struct FileLogger;
unsafe impl Send for FileLogger {}
unsafe impl Sync for FileLogger {}
impl Logger for FileLogger {
fn log(&self, _level: &str, _message: &str) {
}
}
#[derive(Default)]
struct UserService;
unsafe impl Send for UserService {}
unsafe impl Sync for UserService {}
impl UserService {
#[allow(dead_code)]
pub fn create_user(&self, _name: &str) -> Result<u32, String> {
Ok(42)
}
}
#[test]
fn test_target_ioc_api() {
let mut builder = IocContainerBuilder::new();
builder
.bind::<PostgresUserRepository, PostgresUserRepository>() .bind_singleton::<SmtpEmailService, SmtpEmailService>()
.bind_factory::<FileLogger, _, _>(|| Ok(FileLogger::default()));
let container = builder.build().unwrap();
let repo = container.resolve::<PostgresUserRepository>().unwrap();
let email = container.resolve::<SmtpEmailService>().unwrap();
let logger = container.resolve::<FileLogger>().unwrap();
assert!(repo.find(1).is_some());
assert!(email.send("test@example.com", "Hello").is_ok());
logger.log("info", "Test message");
let email2 = container.resolve::<SmtpEmailService>().unwrap();
assert!(Arc::ptr_eq(&email, &email2));
}
#[test]
fn test_named_services_api() {
let mut builder = IocContainerBuilder::new();
builder
.bind_named::<PostgresUserRepository, PostgresUserRepository>("primary")
.bind_named::<PostgresUserRepository, PostgresUserRepository>("backup");
let container = builder.build().unwrap();
let primary = container
.resolve_named::<PostgresUserRepository>("primary")
.unwrap();
let backup = container
.resolve_named::<PostgresUserRepository>("backup")
.unwrap();
assert!(primary.find(1).is_some());
assert!(backup.find(1).is_some());
assert!(!Arc::ptr_eq(&primary, &backup));
}
#[test]
fn test_error_handling_api() {
let container = IocContainerBuilder::new().build().unwrap();
let result = container.resolve::<UserService>();
assert!(result.is_err());
match result {
Err(CoreError::ServiceNotFound { service_type }) => {
assert!(service_type.contains("UserService"));
assert!(!service_type.contains("unknown"));
}
_ => panic!("Expected ServiceNotFound error"),
}
let result = container.resolve_named::<UserService>("nonexistent");
assert!(result.is_err());
match result {
Err(CoreError::ServiceNotFound { service_type }) => {
assert!(service_type.contains("UserService"));
assert!(service_type.contains("nonexistent"));
assert!(!service_type.contains("unknown"));
}
_ => panic!("Expected ServiceNotFound error"),
}
}
#[test]
fn test_container_validation() {
let mut builder = IocContainerBuilder::new();
builder
.bind::<UserService, UserService>()
.bind::<SmtpEmailService, SmtpEmailService>()
.bind::<FileLogger, FileLogger>();
let container = builder.build().unwrap();
assert!(container.validate().is_ok());
assert_eq!(container.service_count(), 3);
assert!(container.contains::<UserService>());
assert!(container.contains::<SmtpEmailService>());
assert!(container.contains::<FileLogger>());
assert!(!container.contains::<String>());
}
#[test]
fn test_performance_characteristics() {
let mut builder = IocContainerBuilder::new();
for i in 0..100 {
builder.bind_named::<FileLogger, FileLogger>(&format!("logger_{}", i));
}
let container = builder.build().unwrap();
let start = std::time::Instant::now();
for i in 0..100 {
let _logger = container
.resolve_named::<FileLogger>(&format!("logger_{}", i))
.unwrap();
}
let duration = start.elapsed();
assert!(
duration.as_millis() < 10,
"Resolution took too long: {:?}",
duration
);
assert_eq!(container.service_count(), 100);
}
}