elif_core/
module.rs

1use crate::container::{Container, ContainerBuilder};
2use crate::provider::{ProviderRegistry, ServiceProvider};
3use std::collections::HashMap;
4use std::sync::atomic::{AtomicBool, Ordering};
5use std::sync::Arc;
6use std::time::{Duration, Instant};
7use thiserror::Error;
8use tokio::signal;
9use tokio::sync::mpsc;
10
11/// HTTP method enumeration for route definitions
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub enum HttpMethod {
14    GET,
15    POST,
16    PUT,
17    PATCH,
18    DELETE,
19    OPTIONS,
20    HEAD,
21}
22
23/// Route definition for module routing
24#[derive(Debug, Clone)]
25pub struct RouteDefinition {
26    pub method: HttpMethod,
27    pub path: String,
28    pub handler: String, // Handler function name or identifier
29    pub middleware: Vec<String>, // Middleware names to apply
30    pub description: Option<String>,
31}
32
33impl RouteDefinition {
34    pub fn new(method: HttpMethod, path: impl Into<String>, handler: impl Into<String>) -> Self {
35        Self {
36            method,
37            path: path.into(),
38            handler: handler.into(),
39            middleware: Vec::new(),
40            description: None,
41        }
42    }
43    
44    pub fn with_middleware(mut self, middleware: Vec<String>) -> Self {
45        self.middleware = middleware;
46        self
47    }
48    
49    pub fn with_description(mut self, description: impl Into<String>) -> Self {
50        self.description = Some(description.into());
51        self
52    }
53}
54
55/// Middleware definition for module middleware
56#[derive(Debug, Clone)]
57pub struct MiddlewareDefinition {
58    pub name: String,
59    pub priority: i32, // Lower numbers = higher priority (executed first)
60    pub description: Option<String>,
61}
62
63impl MiddlewareDefinition {
64    pub fn new(name: impl Into<String>, priority: i32) -> Self {
65        Self {
66            name: name.into(),
67            priority,
68            description: None,
69        }
70    }
71    
72    pub fn with_description(mut self, description: impl Into<String>) -> Self {
73        self.description = Some(description.into());
74        self
75    }
76}
77
78/// Application module trait that integrates with service-builder
79pub trait Module: Send + Sync {
80    /// Module name for identification
81    fn name(&self) -> &'static str;
82    
83    /// Configure services in the container builder
84    fn configure(&self, builder: ContainerBuilder) -> Result<ContainerBuilder, ModuleError>;
85    
86    /// Define routes for this module
87    fn routes(&self) -> Vec<RouteDefinition> {
88        vec![]
89    }
90    
91    /// Define middleware for this module
92    fn middleware(&self) -> Vec<MiddlewareDefinition> {
93        vec![]
94    }
95    
96    /// Boot the module after container is built
97    fn boot(&self, _container: &Container) -> Result<(), ModuleError> {
98        // Default implementation does nothing
99        Ok(())
100    }
101    
102    /// Module dependencies (other modules that must be loaded first)
103    fn dependencies(&self) -> Vec<&'static str> {
104        vec![]
105    }
106}
107
108/// Module registry for managing module lifecycle and dependencies
109pub struct ModuleRegistry {
110    modules: Vec<Box<dyn Module>>,
111    loading_order: Vec<usize>,
112    routes: Vec<RouteDefinition>,
113    middleware: Vec<MiddlewareDefinition>,
114}
115
116impl ModuleRegistry {
117    pub fn new() -> Self {
118        Self {
119            modules: Vec::new(),
120            loading_order: Vec::new(),
121            routes: Vec::new(),
122            middleware: Vec::new(),
123        }
124    }
125    
126    /// Register a module
127    pub fn register<M: Module + 'static>(&mut self, module: M) {
128        self.modules.push(Box::new(module));
129    }
130    
131    /// Resolve module dependencies and determine loading order
132    pub fn resolve_dependencies(&mut self) -> Result<(), ModuleError> {
133        let module_count = self.modules.len();
134        
135        // Create name to index mapping
136        let name_to_index: HashMap<String, usize> = self.modules
137            .iter()
138            .enumerate()
139            .map(|(i, m)| (m.name().to_string(), i))
140            .collect();
141        
142        // Perform topological sort
143        let mut visited = vec![false; module_count];
144        let mut temp_mark = vec![false; module_count];
145        let mut result = Vec::new();
146        
147        for i in 0..module_count {
148            if !visited[i] {
149                self.visit_module(i, &name_to_index, &mut visited, &mut temp_mark, &mut result)?;
150            }
151        }
152        
153        self.loading_order = result;
154        Ok(())
155    }
156    
157    /// Visit module for dependency resolution (topological sort)
158    fn visit_module(
159        &self,
160        index: usize,
161        name_to_index: &HashMap<String, usize>,
162        visited: &mut Vec<bool>,
163        temp_mark: &mut Vec<bool>,
164        result: &mut Vec<usize>,
165    ) -> Result<(), ModuleError> {
166        if temp_mark[index] {
167            return Err(ModuleError::CircularDependency {
168                module: self.modules[index].name().to_string(),
169            });
170        }
171        
172        if visited[index] {
173            return Ok(());
174        }
175        
176        temp_mark[index] = true;
177        
178        // Visit all dependencies first
179        let dependencies = self.modules[index].dependencies();
180        for dep_name in dependencies {
181            if let Some(&dep_index) = name_to_index.get(dep_name) {
182                self.visit_module(dep_index, name_to_index, visited, temp_mark, result)?;
183            } else {
184                return Err(ModuleError::MissingDependency {
185                    module: self.modules[index].name().to_string(),
186                    dependency: dep_name.to_string(),
187                });
188            }
189        }
190        
191        temp_mark[index] = false;
192        visited[index] = true;
193        result.push(index);
194        
195        Ok(())
196    }
197    
198    /// Configure all modules with the container builder
199    pub fn configure_all(&self, mut builder: ContainerBuilder) -> Result<ContainerBuilder, ModuleError> {
200        for &index in &self.loading_order {
201            let module = &self.modules[index];
202            builder = module.configure(builder)
203                .map_err(|e| ModuleError::ConfigurationFailed {
204                    module: module.name().to_string(),
205                    error: e.to_string(),
206                })?;
207        }
208        Ok(builder)
209    }
210    
211    /// Boot all modules
212    pub fn boot_all(&self, container: &Container) -> Result<(), ModuleError> {
213        for &index in &self.loading_order {
214            let module = &self.modules[index];
215            module.boot(container)
216                .map_err(|e| ModuleError::BootFailed {
217                    module: module.name().to_string(),
218                    error: e.to_string(),
219                })?;
220        }
221        Ok(())
222    }
223    
224    /// Collect all routes from modules
225    pub fn collect_routes(&mut self) -> &[RouteDefinition] {
226        self.routes.clear();
227        for module in &self.modules {
228            self.routes.extend(module.routes());
229        }
230        &self.routes
231    }
232    
233    /// Collect all middleware from modules (sorted by priority)
234    pub fn collect_middleware(&mut self) -> &[MiddlewareDefinition] {
235        self.middleware.clear();
236        for module in &self.modules {
237            self.middleware.extend(module.middleware());
238        }
239        // Sort by priority (lower numbers first)
240        self.middleware.sort_by_key(|m| m.priority);
241        &self.middleware
242    }
243    
244    /// Get all registered module names
245    pub fn module_names(&self) -> Vec<&str> {
246        self.modules.iter().map(|m| m.name()).collect()
247    }
248    
249    /// Get loading order
250    pub fn loading_order(&self) -> &[usize] {
251        &self.loading_order
252    }
253}
254
255/// Application state enumeration
256#[derive(Debug, Clone, PartialEq)]
257pub enum ApplicationState {
258    Created,
259    Starting,
260    Running,
261    Stopping,
262    Stopped,
263    Failed(String),
264}
265
266/// Lifecycle hook trait for custom startup/shutdown behavior
267/// Uses boxed futures to maintain trait object compatibility
268pub trait LifecycleHook: Send + Sync {
269    /// Hook name for identification
270    fn name(&self) -> &'static str;
271    
272    /// Called before application startup
273    fn before_start<'life0, 'async_trait>(
274        &'life0 self,
275        container: &'life0 Container,
276    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send + 'async_trait>>
277    where
278        'life0: 'async_trait,
279        Self: 'async_trait,
280    {
281        Box::pin(async move { Ok(()) })
282    }
283    
284    /// Called after successful startup
285    fn after_start<'life0, 'async_trait>(
286        &'life0 self,
287        container: &'life0 Container,
288    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send + 'async_trait>>
289    where
290        'life0: 'async_trait,
291        Self: 'async_trait,
292    {
293        Box::pin(async move { Ok(()) })
294    }
295    
296    /// Called before application shutdown
297    fn before_stop<'life0, 'async_trait>(
298        &'life0 self,
299        container: &'life0 Container,
300    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send + 'async_trait>>
301    where
302        'life0: 'async_trait,
303        Self: 'async_trait,
304    {
305        Box::pin(async move { Ok(()) })
306    }
307    
308    /// Called after application shutdown
309    fn after_stop<'life0, 'async_trait>(
310        &'life0 self,
311        container: &'life0 Container,
312    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send + 'async_trait>>
313    where
314        'life0: 'async_trait,
315        Self: 'async_trait,
316    {
317        Box::pin(async move { Ok(()) })
318    }
319}
320
321/// Application that manages modules, providers, and container with lifecycle management
322pub struct Application {
323    container: Container,
324    modules: ModuleRegistry,
325    providers: ProviderRegistry,
326    state: ApplicationState,
327    shutdown_signal: Arc<AtomicBool>,
328    lifecycle_hooks: Vec<Box<dyn LifecycleHook>>,
329    startup_time: Option<Instant>,
330    shutdown_timeout: Duration,
331}
332
333impl Application {
334    /// Create a new application builder
335    pub fn builder() -> ApplicationBuilder {
336        ApplicationBuilder::new()
337    }
338    
339    /// Get the service container
340    pub fn container(&self) -> &Container {
341        &self.container
342    }
343    
344    /// Get application state
345    pub fn state(&self) -> &ApplicationState {
346        &self.state
347    }
348    
349    /// Get module registry
350    pub fn modules(&mut self) -> &mut ModuleRegistry {
351        &mut self.modules
352    }
353    
354    /// Get all routes from modules
355    pub fn routes(&mut self) -> &[RouteDefinition] {
356        self.modules.collect_routes()
357    }
358    
359    /// Get all middleware from modules (sorted by priority)
360    pub fn middleware(&mut self) -> &[MiddlewareDefinition] {
361        self.modules.collect_middleware()
362    }
363    
364    /// Get startup time if application has started
365    pub fn uptime(&self) -> Option<Duration> {
366        self.startup_time.map(|start| start.elapsed())
367    }
368    
369    /// Check if application is running
370    pub fn is_running(&self) -> bool {
371        self.state == ApplicationState::Running
372    }
373    
374    /// Start the application with full lifecycle management
375    pub async fn start(&mut self) -> Result<(), ApplicationError> {
376        if self.state != ApplicationState::Created {
377            return Err(ApplicationError::InvalidState {
378                current: format!("{:?}", self.state),
379                expected: "Created".to_string(),
380            });
381        }
382        
383        self.state = ApplicationState::Starting;
384        let start_time = Instant::now();
385        
386        // Execute before_start hooks
387        for hook in &self.lifecycle_hooks {
388            if let Err(e) = hook.before_start(&self.container).await {
389                self.state = ApplicationState::Failed(e.to_string());
390                return Err(ApplicationError::LifecycleHookFailed {
391                    hook: hook.name().to_string(),
392                    phase: "before_start".to_string(),
393                    error: e.to_string(),
394                });
395            }
396        }
397        
398        // Boot all providers first
399        if let Err(e) = self.providers.boot_all(&self.container) {
400            self.state = ApplicationState::Failed(e.to_string());
401            return Err(ApplicationError::ProviderBoot(e));
402        }
403        
404        // Boot all modules
405        if let Err(e) = self.modules.boot_all(&self.container) {
406            self.state = ApplicationState::Failed(e.to_string());
407            return Err(ApplicationError::ModuleBoot(e));
408        }
409        
410        self.state = ApplicationState::Running;
411        self.startup_time = Some(start_time);
412        
413        // Execute after_start hooks
414        for hook in &self.lifecycle_hooks {
415            if let Err(e) = hook.after_start(&self.container).await {
416                tracing::warn!("After start hook '{}' failed: {}", hook.name(), e);
417                // Don't fail startup for after_start hook failures
418            }
419        }
420        
421        let startup_duration = start_time.elapsed();
422        tracing::info!("Application started successfully in {:?}", startup_duration);
423        
424        Ok(())
425    }
426    
427    /// Run the application with signal handling
428    pub async fn run(&mut self) -> Result<(), ApplicationError> {
429        self.start().await?;
430        
431        // Setup signal handling for graceful shutdown
432        let shutdown_signal = self.shutdown_signal.clone();
433        let (tx, mut rx) = mpsc::channel::<()>(1);
434        
435        // Spawn signal handler
436        tokio::spawn(async move {
437            let mut sigterm = signal::unix::signal(signal::unix::SignalKind::terminate())
438                .expect("Failed to install SIGTERM handler");
439            let mut sigint = signal::unix::signal(signal::unix::SignalKind::interrupt())
440                .expect("Failed to install SIGINT handler");
441            
442            tokio::select! {
443                _ = sigterm.recv() => {
444                    tracing::info!("Received SIGTERM, initiating graceful shutdown");
445                }
446                _ = sigint.recv() => {
447                    tracing::info!("Received SIGINT, initiating graceful shutdown");
448                }
449            }
450            
451            shutdown_signal.store(true, Ordering::SeqCst);
452            let _ = tx.send(()).await;
453        });
454        
455        // Wait for shutdown signal
456        rx.recv().await;
457        
458        // Perform graceful shutdown
459        self.shutdown().await?;
460        
461        Ok(())
462    }
463    
464    /// Gracefully shutdown the application
465    pub async fn shutdown(&mut self) -> Result<(), ApplicationError> {
466        if self.state != ApplicationState::Running {
467            return Err(ApplicationError::InvalidState {
468                current: format!("{:?}", self.state),
469                expected: "Running".to_string(),
470            });
471        }
472        
473        self.state = ApplicationState::Stopping;
474        let shutdown_start = Instant::now();
475        
476        tracing::info!("Beginning graceful shutdown...");
477        
478        // Execute before_stop hooks
479        for hook in &self.lifecycle_hooks {
480            if let Err(e) = hook.before_stop(&self.container).await {
481                tracing::warn!("Before stop hook '{}' failed: {}", hook.name(), e);
482                // Continue with shutdown even if hooks fail
483            }
484        }
485        
486        // Perform graceful shutdown with timeout
487        let shutdown_result = tokio::time::timeout(
488            self.shutdown_timeout,
489            self.perform_shutdown()
490        ).await;
491        
492        match shutdown_result {
493            Ok(Ok(())) => {
494                // Execute after_stop hooks
495                for hook in &self.lifecycle_hooks {
496                    if let Err(e) = hook.after_stop(&self.container).await {
497                        tracing::warn!("After stop hook '{}' failed: {}", hook.name(), e);
498                    }
499                }
500                
501                self.state = ApplicationState::Stopped;
502                let shutdown_duration = shutdown_start.elapsed();
503                tracing::info!("Application stopped gracefully in {:?}", shutdown_duration);
504                Ok(())
505            }
506            Ok(Err(e)) => {
507                self.state = ApplicationState::Failed(e.to_string());
508                Err(e)
509            }
510            Err(_) => {
511                self.state = ApplicationState::Failed("Shutdown timeout".to_string());
512                Err(ApplicationError::ShutdownTimeout {
513                    timeout: self.shutdown_timeout,
514                })
515            }
516        }
517    }
518    
519    /// Internal shutdown procedure
520    async fn perform_shutdown(&self) -> Result<(), ApplicationError> {
521        // In a real implementation, this would:
522        // 1. Stop accepting new connections
523        // 2. Drain existing connections
524        // 3. Stop background tasks
525        // 4. Cleanup resources
526        
527        // Simulate graceful shutdown work
528        tokio::time::sleep(Duration::from_millis(100)).await;
529        
530        Ok(())
531    }
532    
533    /// Request shutdown (can be called from signal handlers)
534    pub fn request_shutdown(&self) {
535        self.shutdown_signal.store(true, Ordering::SeqCst);
536    }
537    
538    /// Check if shutdown has been requested
539    pub fn shutdown_requested(&self) -> bool {
540        self.shutdown_signal.load(Ordering::SeqCst)
541    }
542}
543
544/// Builder for constructing applications
545pub struct ApplicationBuilder {
546    modules: ModuleRegistry,
547    providers: ProviderRegistry,
548    lifecycle_hooks: Vec<Box<dyn LifecycleHook>>,
549    shutdown_timeout: Duration,
550}
551
552impl ApplicationBuilder {
553    pub fn new() -> Self {
554        Self {
555            modules: ModuleRegistry::new(),
556            providers: ProviderRegistry::new(),
557            lifecycle_hooks: Vec::new(),
558            shutdown_timeout: Duration::from_secs(30), // Default 30 second timeout
559        }
560    }
561    
562    /// Add a module to the application
563    pub fn module<M: Module + 'static>(mut self, module: M) -> Self {
564        self.modules.register(module);
565        self
566    }
567    
568    /// Add a service provider to the application
569    pub fn provider<P: ServiceProvider + 'static>(mut self, provider: P) -> Self {
570        self.providers.register(provider);
571        self
572    }
573    
574    /// Add a lifecycle hook to the application
575    pub fn lifecycle_hook<H: LifecycleHook + 'static>(mut self, hook: H) -> Self {
576        self.lifecycle_hooks.push(Box::new(hook));
577        self
578    }
579    
580    /// Set shutdown timeout (default: 30 seconds)
581    pub fn shutdown_timeout(mut self, timeout: Duration) -> Self {
582        self.shutdown_timeout = timeout;
583        self
584    }
585    
586    /// Build the application by resolving dependencies and creating container
587    pub fn build(mut self) -> Result<Application, ApplicationError> {
588        // Resolve provider dependencies first
589        self.providers.resolve_dependencies()
590            .map_err(ApplicationError::ProviderDependency)?;
591            
592        // Resolve module dependencies
593        self.modules.resolve_dependencies()
594            .map_err(ApplicationError::ModuleDependency)?;
595        
596        // Create container with providers first
597        let mut builder = Container::builder();
598        
599        // Register all providers
600        builder = self.providers.register_all(builder)
601            .map_err(ApplicationError::ProviderRegistration)?;
602            
603        // Configure all modules in dependency order
604        builder = self.modules.configure_all(builder)
605            .map_err(ApplicationError::ModuleConfiguration)?;
606        
607        let container = builder.build()
608            .map_err(|e| ApplicationError::ContainerBuild {
609                error: e.to_string(),
610            })?;
611        
612        Ok(Application {
613            container,
614            modules: self.modules,
615            providers: self.providers,
616            state: ApplicationState::Created,
617            shutdown_signal: Arc::new(AtomicBool::new(false)),
618            lifecycle_hooks: self.lifecycle_hooks,
619            startup_time: None,
620            shutdown_timeout: self.shutdown_timeout,
621        })
622    }
623    
624}
625
626#[derive(Error, Debug)]
627pub enum ModuleError {
628    #[error("Module configuration failed for '{module}': {error}")]
629    ConfigurationFailed { module: String, error: String },
630    
631    #[error("Module boot failed for '{module}': {error}")]
632    BootFailed { module: String, error: String },
633    
634    #[error("Circular dependency detected for module '{module}'")]
635    CircularDependency { module: String },
636    
637    #[error("Missing dependency '{dependency}' for module '{module}'")]
638    MissingDependency { module: String, dependency: String },
639    
640    #[error("Service registration failed: {service}")]
641    ServiceRegistrationFailed { service: String },
642}
643
644#[derive(Error, Debug)]
645pub enum ApplicationError {
646    #[error("Module configuration error: {0}")]
647    ModuleConfiguration(#[from] ModuleError),
648    
649    #[error("Module boot error: {0}")]
650    ModuleBoot(ModuleError),
651    
652    #[error("Module dependency error: {0}")]
653    ModuleDependency(ModuleError),
654    
655    #[error("Container build failed: {error}")]
656    ContainerBuild { error: String },
657    
658    #[error("Circular dependency detected for module '{module}'")]
659    CircularDependency { module: String },
660    
661    #[error("Missing dependency '{dependency}' for module '{module}'")]
662    MissingDependency { module: String, dependency: String },
663    
664    #[error("Provider dependency error: {0}")]
665    ProviderDependency(#[from] crate::provider::ProviderError),
666    
667    #[error("Provider registration error: {0}")]
668    ProviderRegistration(crate::provider::ProviderError),
669    
670    #[error("Provider boot error: {0}")]
671    ProviderBoot(crate::provider::ProviderError),
672    
673    #[error("Invalid application state: expected {expected}, found {current}")]
674    InvalidState { current: String, expected: String },
675    
676    #[error("Lifecycle hook '{hook}' failed during {phase}: {error}")]
677    LifecycleHookFailed { hook: String, phase: String, error: String },
678    
679    #[error("Application shutdown timeout after {timeout:?}")]
680    ShutdownTimeout { timeout: Duration },
681}
682
683#[cfg(test)]
684mod tests {
685    use super::*;
686    use crate::app_config::{AppConfig, Environment};
687    use crate::container::DatabaseConnection;
688    use std::sync::Arc;
689    
690    // Test implementations - use the shared function from container tests
691    fn create_test_config() -> AppConfig {
692        AppConfig {
693            name: "test-app".to_string(),
694            environment: Environment::Testing,
695            database_url: "sqlite::memory:".to_string(),
696            jwt_secret: Some("test-secret".to_string()),
697            server: crate::app_config::ServerConfig {
698                host: "127.0.0.1".to_string(),
699                port: 8080,
700                workers: 4,
701            },
702            logging: crate::app_config::LoggingConfig {
703                level: "info".to_string(),
704                format: "compact".to_string(),
705            },
706        }
707    }
708    
709    struct TestDatabase;
710    impl DatabaseConnection for TestDatabase {
711        fn is_connected(&self) -> bool { true }
712        fn execute(&self, _query: &str) -> Result<(), crate::container::DatabaseError> { Ok(()) }
713    }
714    
715    // Test modules
716    struct CoreModule;
717    impl Module for CoreModule {
718        fn name(&self) -> &'static str { "core" }
719        
720        fn configure(&self, builder: ContainerBuilder) -> Result<ContainerBuilder, ModuleError> {
721            // Core module doesn't configure services directly
722            Ok(builder)
723        }
724        
725        fn routes(&self) -> Vec<RouteDefinition> {
726            vec![
727                RouteDefinition::new(HttpMethod::GET, "/", "CoreController::index")
728                    .with_description("Core module home route"),
729                RouteDefinition::new(HttpMethod::GET, "/health", "CoreController::health")
730                    .with_middleware(vec!["logging".to_string()])
731                    .with_description("Health check endpoint"),
732            ]
733        }
734        
735        fn middleware(&self) -> Vec<MiddlewareDefinition> {
736            vec![
737                MiddlewareDefinition::new("logging", 100)
738                    .with_description("Request logging middleware"),
739                MiddlewareDefinition::new("cors", 200)
740                    .with_description("CORS handling middleware"),
741            ]
742        }
743    }
744    
745    struct AuthModule;
746    impl Module for AuthModule {
747        fn name(&self) -> &'static str { "auth" }
748        
749        fn dependencies(&self) -> Vec<&'static str> {
750            vec!["core"]
751        }
752        
753        fn configure(&self, builder: ContainerBuilder) -> Result<ContainerBuilder, ModuleError> {
754            // Auth module doesn't add new services, just uses existing ones
755            Ok(builder)
756        }
757        
758        fn routes(&self) -> Vec<RouteDefinition> {
759            vec![
760                RouteDefinition::new(HttpMethod::POST, "/auth/login", "AuthController::login")
761                    .with_middleware(vec!["rate_limit".to_string()])
762                    .with_description("User login endpoint"),
763                RouteDefinition::new(HttpMethod::POST, "/auth/logout", "AuthController::logout")
764                    .with_middleware(vec!["auth".to_string()])
765                    .with_description("User logout endpoint"),
766            ]
767        }
768        
769        fn middleware(&self) -> Vec<MiddlewareDefinition> {
770            vec![
771                MiddlewareDefinition::new("auth", 50)
772                    .with_description("Authentication middleware"),
773                MiddlewareDefinition::new("rate_limit", 150)
774                    .with_description("Rate limiting middleware"),
775            ]
776        }
777        
778        fn boot(&self, container: &Container) -> Result<(), ModuleError> {
779            let _config = container.config();
780            // Perform auth module initialization
781            Ok(())
782        }
783    }
784    
785    #[tokio::test]
786    async fn test_application_with_modules_and_providers() {
787        use crate::provider::ServiceProvider;
788        
789        // Create a provider that handles service registration
790        struct TestProvider;
791        impl ServiceProvider for TestProvider {
792            fn name(&self) -> &'static str { "test" }
793            
794            fn register(&self, builder: crate::container::ContainerBuilder) -> Result<crate::container::ContainerBuilder, crate::provider::ProviderError> {
795                let config = Arc::new(create_test_config());
796                let database = Arc::new(TestDatabase) as Arc<dyn DatabaseConnection>;
797                Ok(builder.config(config).database(database))
798            }
799        }
800        
801        let mut app = Application::builder()
802            .provider(TestProvider)
803            .module(CoreModule)
804            .module(AuthModule)
805            .build()
806            .unwrap();
807        
808        // Verify container is properly configured
809        let config = app.container().config();
810        assert_eq!(config.environment, Environment::Testing);
811        
812        // Start the application
813        app.start().await.unwrap();
814    }
815    
816    #[test]
817    fn test_module_registry_dependency_resolution() {
818        let mut registry = ModuleRegistry::new();
819        registry.register(AuthModule); // Depends on core
820        registry.register(CoreModule); // No dependencies
821        
822        registry.resolve_dependencies().unwrap();
823        
824        let loading_order = registry.loading_order();
825        
826        // Core should be loaded before auth
827        let core_pos = loading_order.iter().position(|&i| registry.modules[i].name() == "core").unwrap();
828        let auth_pos = loading_order.iter().position(|&i| registry.modules[i].name() == "auth").unwrap();
829        
830        assert!(core_pos < auth_pos);
831    }
832    
833    #[test]
834    fn test_module_routes_collection() {
835        let mut registry = ModuleRegistry::new();
836        registry.register(CoreModule);
837        registry.register(AuthModule);
838        
839        let routes = registry.collect_routes();
840        
841        // Should have routes from both modules
842        assert_eq!(routes.len(), 4);
843        
844        // Check specific routes exist
845        assert!(routes.iter().any(|r| r.path == "/" && r.method == HttpMethod::GET));
846        assert!(routes.iter().any(|r| r.path == "/health" && r.method == HttpMethod::GET));
847        assert!(routes.iter().any(|r| r.path == "/auth/login" && r.method == HttpMethod::POST));
848        assert!(routes.iter().any(|r| r.path == "/auth/logout" && r.method == HttpMethod::POST));
849    }
850    
851    #[test]
852    fn test_module_middleware_collection() {
853        let mut registry = ModuleRegistry::new();
854        registry.register(CoreModule);
855        registry.register(AuthModule);
856        
857        let middleware = registry.collect_middleware();
858        
859        // Should have middleware from both modules, sorted by priority
860        assert_eq!(middleware.len(), 4);
861        
862        // Check priority ordering (lower numbers first)
863        assert_eq!(middleware[0].name, "auth"); // priority 50
864        assert_eq!(middleware[1].name, "logging"); // priority 100
865        assert_eq!(middleware[2].name, "rate_limit"); // priority 150
866        assert_eq!(middleware[3].name, "cors"); // priority 200
867    }
868    
869    #[test]
870    fn test_module_missing_dependency() {
871        let mut registry = ModuleRegistry::new();
872        registry.register(AuthModule); // Depends on core, but core is not added
873        
874        let result = registry.resolve_dependencies();
875        assert!(result.is_err());
876        
877        if let Err(ModuleError::MissingDependency { module, dependency }) = result {
878            assert_eq!(module, "auth");
879            assert_eq!(dependency, "core");
880        } else {
881            panic!("Expected MissingDependency error");
882        }
883    }
884    
885    #[test]
886    fn test_module_circular_dependency() {
887        struct ModuleA;
888        impl Module for ModuleA {
889            fn name(&self) -> &'static str { "a" }
890            fn dependencies(&self) -> Vec<&'static str> { vec!["b"] }
891            fn configure(&self, builder: ContainerBuilder) -> Result<ContainerBuilder, ModuleError> { Ok(builder) }
892        }
893        
894        struct ModuleB;
895        impl Module for ModuleB {
896            fn name(&self) -> &'static str { "b" }
897            fn dependencies(&self) -> Vec<&'static str> { vec!["a"] }
898            fn configure(&self, builder: ContainerBuilder) -> Result<ContainerBuilder, ModuleError> { Ok(builder) }
899        }
900        
901        let mut registry = ModuleRegistry::new();
902        registry.register(ModuleA);
903        registry.register(ModuleB);
904        
905        let result = registry.resolve_dependencies();
906        assert!(result.is_err());
907        assert!(matches!(result, Err(ModuleError::CircularDependency { .. })));
908    }
909    
910    // Test lifecycle hooks
911    struct TestLifecycleHook {
912        name: &'static str,
913        executed_phases: Arc<std::sync::Mutex<Vec<String>>>,
914    }
915    
916    impl TestLifecycleHook {
917        fn new(name: &'static str) -> (Self, Arc<std::sync::Mutex<Vec<String>>>) {
918            let executed_phases = Arc::new(std::sync::Mutex::new(Vec::new()));
919            let hook = Self {
920                name,
921                executed_phases: executed_phases.clone(),
922            };
923            (hook, executed_phases)
924        }
925    }
926    
927    impl LifecycleHook for TestLifecycleHook {
928        fn name(&self) -> &'static str {
929            self.name
930        }
931        
932        fn before_start<'life0, 'async_trait>(
933            &'life0 self,
934            _container: &'life0 Container,
935        ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send + 'async_trait>>
936        where
937            'life0: 'async_trait,
938            Self: 'async_trait,
939        {
940            let phases = self.executed_phases.clone();
941            Box::pin(async move {
942                phases.lock().unwrap().push("before_start".to_string());
943                Ok(())
944            })
945        }
946        
947        fn after_start<'life0, 'async_trait>(
948            &'life0 self,
949            _container: &'life0 Container,
950        ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send + 'async_trait>>
951        where
952            'life0: 'async_trait,
953            Self: 'async_trait,
954        {
955            let phases = self.executed_phases.clone();
956            Box::pin(async move {
957                phases.lock().unwrap().push("after_start".to_string());
958                Ok(())
959            })
960        }
961        
962        fn before_stop<'life0, 'async_trait>(
963            &'life0 self,
964            _container: &'life0 Container,
965        ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send + 'async_trait>>
966        where
967            'life0: 'async_trait,
968            Self: 'async_trait,
969        {
970            let phases = self.executed_phases.clone();
971            Box::pin(async move {
972                phases.lock().unwrap().push("before_stop".to_string());
973                Ok(())
974            })
975        }
976        
977        fn after_stop<'life0, 'async_trait>(
978            &'life0 self,
979            _container: &'life0 Container,
980        ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send + 'async_trait>>
981        where
982            'life0: 'async_trait,
983            Self: 'async_trait,
984        {
985            let phases = self.executed_phases.clone();
986            Box::pin(async move {
987                phases.lock().unwrap().push("after_stop".to_string());
988                Ok(())
989            })
990        }
991    }
992    
993    #[tokio::test]
994    async fn test_application_lifecycle() {
995        use crate::provider::ServiceProvider;
996        
997        struct TestProvider;
998        impl ServiceProvider for TestProvider {
999            fn name(&self) -> &'static str { "test" }
1000            
1001            fn register(&self, builder: crate::container::ContainerBuilder) -> Result<crate::container::ContainerBuilder, crate::provider::ProviderError> {
1002                let config = Arc::new(create_test_config());
1003                let database = Arc::new(TestDatabase) as Arc<dyn DatabaseConnection>;
1004                Ok(builder.config(config).database(database))
1005            }
1006        }
1007        
1008        let (hook, phases) = TestLifecycleHook::new("test_hook");
1009        
1010        let mut app = Application::builder()
1011            .provider(TestProvider)
1012            .module(CoreModule)
1013            .lifecycle_hook(hook)
1014            .shutdown_timeout(Duration::from_secs(1)) // Shorter timeout for tests
1015            .build()
1016            .unwrap();
1017        
1018        // Test initial state
1019        assert_eq!(app.state(), &ApplicationState::Created);
1020        assert!(!app.is_running());
1021        assert!(app.uptime().is_none());
1022        
1023        // Start the application
1024        app.start().await.unwrap();
1025        
1026        // Test running state
1027        assert_eq!(app.state(), &ApplicationState::Running);
1028        assert!(app.is_running());
1029        assert!(app.uptime().is_some());
1030        
1031        // Verify startup lifecycle hooks were executed
1032        {
1033            let executed = phases.lock().unwrap();
1034            assert!(executed.contains(&"before_start".to_string()));
1035            assert!(executed.contains(&"after_start".to_string()));
1036        } // Drop the lock before shutdown
1037        
1038        // Shutdown the application
1039        app.shutdown().await.unwrap();
1040        
1041        // Test stopped state
1042        assert_eq!(app.state(), &ApplicationState::Stopped);
1043        assert!(!app.is_running());
1044        
1045        // Verify all lifecycle hooks were executed
1046        let executed = phases.lock().unwrap();
1047        assert!(executed.contains(&"before_start".to_string()));
1048        assert!(executed.contains(&"after_start".to_string()));
1049        assert!(executed.contains(&"before_stop".to_string()));
1050        assert!(executed.contains(&"after_stop".to_string()));
1051    }
1052    
1053    #[tokio::test]
1054    async fn test_application_state_validation() {
1055        use crate::provider::ServiceProvider;
1056        
1057        struct TestProvider;
1058        impl ServiceProvider for TestProvider {
1059            fn name(&self) -> &'static str { "test" }
1060            fn register(&self, builder: crate::container::ContainerBuilder) -> Result<crate::container::ContainerBuilder, crate::provider::ProviderError> {
1061                let config = Arc::new(create_test_config());
1062                let database = Arc::new(TestDatabase) as Arc<dyn DatabaseConnection>;
1063                Ok(builder.config(config).database(database))
1064            }
1065        }
1066        
1067        let mut app = Application::builder()
1068            .provider(TestProvider)
1069            .build()
1070            .unwrap();
1071        
1072        // Cannot shutdown before starting
1073        let result = app.shutdown().await;
1074        assert!(result.is_err());
1075        assert!(matches!(result, Err(ApplicationError::InvalidState { .. })));
1076        
1077        // Start the application
1078        app.start().await.unwrap();
1079        
1080        // Cannot start twice
1081        let result = app.start().await;
1082        assert!(result.is_err());
1083        assert!(matches!(result, Err(ApplicationError::InvalidState { .. })));
1084    }
1085    
1086    #[tokio::test] 
1087    async fn test_failed_lifecycle_hook() {
1088        use crate::provider::ServiceProvider;
1089        
1090        struct FailingHook;
1091        impl LifecycleHook for FailingHook {
1092            fn name(&self) -> &'static str { "failing_hook" }
1093            
1094            fn before_start<'life0, 'async_trait>(
1095                &'life0 self,
1096                _container: &'life0 Container,
1097            ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send + 'async_trait>>
1098            where
1099                'life0: 'async_trait,
1100                Self: 'async_trait,
1101            {
1102                Box::pin(async move {
1103                    Err("Hook failed".into())
1104                })
1105            }
1106        }
1107        
1108        struct TestProvider;
1109        impl ServiceProvider for TestProvider {
1110            fn name(&self) -> &'static str { "test" }
1111            fn register(&self, builder: crate::container::ContainerBuilder) -> Result<crate::container::ContainerBuilder, crate::provider::ProviderError> {
1112                let config = Arc::new(create_test_config());
1113                let database = Arc::new(TestDatabase) as Arc<dyn DatabaseConnection>;
1114                Ok(builder.config(config).database(database))
1115            }
1116        }
1117        
1118        let mut app = Application::builder()
1119            .provider(TestProvider)
1120            .lifecycle_hook(FailingHook)
1121            .build()
1122            .unwrap();
1123        
1124        let result = app.start().await;
1125        assert!(result.is_err());
1126        assert!(matches!(result, Err(ApplicationError::LifecycleHookFailed { .. })));
1127        assert!(matches!(app.state(), ApplicationState::Failed(_)));
1128    }
1129    
1130    #[tokio::test]
1131    async fn test_full_application_with_modules() {
1132        use crate::provider::ServiceProvider;
1133        
1134        // Create a provider that handles service registration
1135        struct TestProvider;
1136        impl ServiceProvider for TestProvider {
1137            fn name(&self) -> &'static str { "test" }
1138            
1139            fn register(&self, builder: crate::container::ContainerBuilder) -> Result<crate::container::ContainerBuilder, crate::provider::ProviderError> {
1140                let config = Arc::new(create_test_config());
1141                let database = Arc::new(TestDatabase) as Arc<dyn DatabaseConnection>;
1142                Ok(builder.config(config).database(database))
1143            }
1144        }
1145        
1146        let mut app = Application::builder()
1147            .provider(TestProvider)
1148            .module(CoreModule)
1149            .module(AuthModule)
1150            .build()
1151            .unwrap();
1152        
1153        // Verify container is properly configured
1154        let config = app.container().config();
1155        assert_eq!(config.environment, Environment::Testing);
1156        
1157        // Check routes are collected
1158        let routes = app.routes();
1159        assert_eq!(routes.len(), 4);
1160        
1161        // Check middleware is collected and sorted
1162        let middleware = app.middleware();
1163        assert_eq!(middleware.len(), 4);
1164        assert_eq!(middleware[0].name, "auth"); // Lowest priority first
1165        
1166        // Start the application
1167        app.start().await.unwrap();
1168        
1169        // Verify it's running
1170        assert!(app.is_running());
1171        assert!(app.uptime().is_some());
1172        
1173        // Shutdown the application
1174        app.shutdown().await.unwrap();
1175        
1176        // Verify it's stopped
1177        assert!(!app.is_running());
1178        assert_eq!(app.state(), &ApplicationState::Stopped);
1179    }
1180}