qorzen_oxide/app/
native.rs

1// src/app.rs - Enhanced application core with all systems integrated
2
3use std::collections::HashMap;
4use std::path::Path;
5use std::sync::Arc;
6use std::time::Duration;
7
8use async_trait::async_trait;
9use chrono::{DateTime, Utc};
10use serde::{Deserialize, Serialize};
11use tokio::sync::{broadcast, Mutex, RwLock};
12use tokio::time::{interval, timeout};
13use uuid::Uuid;
14
15use crate::auth::{
16    AccountManager, MemorySessionStore, MemoryUserStore, SecurityPolicy, User, UserSession,
17};
18#[cfg(not(target_arch = "wasm32"))]
19use crate::concurrency::ConcurrencyManager;
20use crate::config::{ConfigurationTier, MemoryConfigStore, TieredConfigManager};
21use crate::error::{Error, ErrorKind, Result}; // Removed unused imports
22use crate::event::EventBusManager;
23#[cfg(not(target_arch = "wasm32"))]
24use crate::file::FileManager;
25#[cfg(not(target_arch = "wasm32"))]
26use crate::logging::LoggingManager;
27use crate::manager::{HealthStatus, ManagedState, Manager, ManagerState, ManagerStatus};
28use crate::platform::PlatformManager;
29use crate::plugin::PluginManager;
30#[cfg(not(target_arch = "wasm32"))]
31use crate::task::TaskManager;
32use crate::ui::UILayoutManager;
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
35pub enum ApplicationState {
36    Created,
37    Initializing,
38    Running,
39    ShuttingDown,
40    Shutdown,
41    Error,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct ApplicationHealth {
46    pub status: HealthStatus,
47    pub uptime: Duration,
48    pub managers: HashMap<String, HealthStatus>,
49    pub last_check: DateTime<Utc>,
50    pub details: HashMap<String, serde_json::Value>,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct ApplicationStats {
55    pub version: String,
56    pub started_at: DateTime<Utc>,
57    pub uptime: Duration,
58    pub state: ApplicationState,
59    pub manager_count: usize,
60    pub initialized_managers: usize,
61    pub failed_managers: usize,
62    pub memory_usage_bytes: u64,
63    pub cpu_usage_percent: f64,
64    pub system_info: SystemInfo,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct SystemInfo {
69    pub os_name: String,
70    pub os_version: String,
71    pub arch: String,
72    pub cpu_cores: usize,
73    pub total_memory_bytes: u64,
74    pub available_memory_bytes: u64,
75    pub hostname: String,
76}
77
78impl SystemInfo {
79    pub fn collect() -> Self {
80        Self {
81            os_name: std::env::consts::OS.to_string(),
82            os_version: "1.0".to_string(),
83            arch: std::env::consts::ARCH.to_string(),
84            cpu_cores: num_cpus::get(),
85            total_memory_bytes: 0,
86            available_memory_bytes: 0,
87            hostname: hostname::get()
88                .unwrap_or_default()
89                .to_string_lossy()
90                .to_string(),
91        }
92    }
93}
94
95/// Enhanced Application Core with all systems integrated
96pub struct ApplicationCore {
97    state: ManagedState,
98    app_state: Arc<RwLock<ApplicationState>>,
99    started_at: DateTime<Utc>,
100
101    // Platform abstraction (must be first)
102    platform_manager: Option<PlatformManager>,
103
104    // Core configuration and settings
105    config_manager: Option<Arc<Mutex<TieredConfigManager>>>,
106
107    // Enhanced core managers
108    logging_manager: Option<LoggingManager>,
109    account_manager: Option<AccountManager>,
110
111    // Existing managers (enhanced)
112    event_bus_manager: Option<Arc<EventBusManager>>,
113    file_manager: Option<FileManager>,
114    concurrency_manager: Option<ConcurrencyManager>,
115    task_manager: Option<TaskManager>,
116
117    // New systems
118    plugin_manager: Option<PluginManager>,
119    ui_layout_manager: Option<UILayoutManager>,
120
121    // Application lifecycle
122    shutdown_signal: broadcast::Sender<()>,
123    health_check_interval: Duration,
124
125    // Current user context
126    current_user: Arc<RwLock<Option<User>>>,
127    current_session: Arc<RwLock<Option<UserSession>>>,
128
129    // System monitoring
130    system_info: SystemInfo,
131    manager_registry: HashMap<String, Box<dyn Manager>>,
132}
133
134impl std::fmt::Debug for ApplicationCore {
135    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136        f.debug_struct("ApplicationCore")
137            .field("started_at", &self.started_at)
138            .field("system_info", &self.system_info)
139            .finish()
140    }
141}
142
143impl ApplicationCore {
144    /// Creates a new application core
145    pub fn new() -> Self {
146        let (shutdown_signal, _) = broadcast::channel(1);
147
148        Self {
149            state: ManagedState::new(Uuid::new_v4(), "application_core"),
150            app_state: Arc::new(RwLock::new(ApplicationState::Created)),
151            started_at: Utc::now(),
152            platform_manager: None,
153            config_manager: None,
154            logging_manager: None,
155            account_manager: None,
156            event_bus_manager: None,
157            file_manager: None,
158            concurrency_manager: None,
159            task_manager: None,
160            plugin_manager: None,
161            ui_layout_manager: None,
162            shutdown_signal,
163            health_check_interval: Duration::from_secs(30),
164            current_user: Arc::new(RwLock::new(None)),
165            current_session: Arc::new(RwLock::new(None)),
166            system_info: SystemInfo::collect(),
167            manager_registry: HashMap::new(),
168        }
169    }
170
171    /// Creates application with custom config file
172    pub fn with_config_file(_config_path: impl AsRef<Path>) -> Self {
173        // let app = Self::new();
174        // Config file handling would be implemented here
175        // app
176        Self::new()
177    }
178
179    /// Enhanced initialization with complete system setup
180    pub async fn initialize(&mut self) -> Result<()> {
181        *self.app_state.write().await = ApplicationState::Initializing;
182        self.state.set_state(ManagerState::Initializing).await;
183
184        tracing::info!("Starting Qorzen application initialization");
185
186        // 1. Initialize platform manager first (critical dependency)
187        self.init_platform_manager().await?;
188
189        // 2. Initialize configuration system
190        self.init_config_manager().await?;
191
192        // 3. Initialize logging with configuration
193        self.init_logging_manager().await?;
194
195        // 4. Initialize core application managers
196        self.init_concurrency_manager().await?;
197        self.init_event_bus_manager().await?;
198        self.init_file_manager().await?;
199        self.init_task_manager().await?;
200
201        // 5. Initialize authentication and authorization
202        self.init_account_manager().await?;
203
204        // 6. Initialize UI and plugin systems
205        self.init_ui_layout_manager().await?;
206        self.init_plugin_manager().await?;
207
208        // 7. Start background services
209        self.start_background_services().await?;
210
211        // 8. Setup signal handling
212        self.setup_signal_handlers().await?;
213
214        *self.app_state.write().await = ApplicationState::Running;
215        self.state.set_state(ManagerState::Running).await;
216
217        tracing::info!("Qorzen application initialization complete");
218        Ok(())
219    }
220
221    async fn init_platform_manager(&mut self) -> Result<()> {
222        tracing::info!("Initializing platform manager");
223        let mut platform_manager = PlatformManager::new()?;
224        platform_manager.initialize().await?;
225        self.platform_manager = Some(platform_manager);
226        Ok(())
227    }
228
229    async fn init_config_manager(&mut self) -> Result<()> {
230        tracing::info!("Initializing configuration manager");
231        let mut config_manager = TieredConfigManager::new();
232
233        // Add configuration stores for different tiers
234        config_manager.add_store(
235            ConfigurationTier::System,
236            Box::new(MemoryConfigStore::new(ConfigurationTier::System)),
237        );
238        config_manager.add_store(
239            ConfigurationTier::Runtime,
240            Box::new(MemoryConfigStore::new(ConfigurationTier::Runtime)),
241        );
242
243        config_manager.initialize().await?;
244        self.config_manager = Some(Arc::new(Mutex::new(config_manager)));
245        Ok(())
246    }
247
248    async fn init_logging_manager(&mut self) -> Result<()> {
249        tracing::info!("Initializing logging manager");
250        let config = if let Some(config_manager) = &self.config_manager {
251            // Get logging config from configuration system
252            let manager = config_manager.lock().await;
253            manager
254                .get("logging")
255                .await
256                .unwrap_or(None)
257                .unwrap_or_else(crate::config::LoggingConfig::default)
258        } else {
259            crate::config::LoggingConfig::default()
260        };
261
262        let mut logging_manager = LoggingManager::new(config);
263        logging_manager.initialize().await?;
264        self.logging_manager = Some(logging_manager);
265        Ok(())
266    }
267
268    async fn init_concurrency_manager(&mut self) -> Result<()> {
269        tracing::info!("Initializing concurrency manager");
270        let config = if let Some(config_manager) = &self.config_manager {
271            let manager = config_manager.lock().await;
272            manager
273                .get("concurrency")
274                .await
275                .unwrap_or(None)
276                .unwrap_or_else(crate::config::ConcurrencyConfig::default)
277        } else {
278            crate::config::ConcurrencyConfig::default()
279        };
280
281        let mut concurrency_manager = ConcurrencyManager::new(config)?;
282        concurrency_manager.initialize().await?;
283        self.concurrency_manager = Some(concurrency_manager);
284        Ok(())
285    }
286
287    async fn init_event_bus_manager(&mut self) -> Result<()> {
288        tracing::info!("Initializing event bus manager");
289        let config = if let Some(config_manager) = &self.config_manager {
290            let manager = config_manager.lock().await;
291            manager
292                .get("event_bus")
293                .await
294                .unwrap_or(None)
295                .unwrap_or_default()
296        } else {
297            crate::config::EventBusConfig::default()
298        };
299
300        let event_config = crate::event::EventBusConfig {
301            worker_count: config.worker_count,
302            queue_capacity: config.queue_size,
303            default_timeout: Duration::from_millis(config.publish_timeout_ms),
304            enable_persistence: config.enable_persistence,
305            enable_metrics: config.enable_metrics,
306            batch_size: 100,
307            max_retry_delay: Duration::from_secs(60),
308        };
309
310        let mut event_bus_manager = EventBusManager::new(event_config);
311        event_bus_manager.initialize().await?;
312        self.event_bus_manager = Some(Arc::new(event_bus_manager));
313        Ok(())
314    }
315
316    async fn init_file_manager(&mut self) -> Result<()> {
317        tracing::info!("Initializing file manager");
318        let config = if let Some(config_manager) = &self.config_manager {
319            let manager = config_manager.lock().await;
320            manager
321                .get("files")
322                .await
323                .unwrap_or(None)
324                .unwrap_or_else(crate::config::FileConfig::default)
325        } else {
326            crate::config::FileConfig::default()
327        };
328
329        let mut file_manager = FileManager::new(config);
330
331        // Set event bus for file events
332        if let Some(event_bus) = &self.event_bus_manager {
333            file_manager.set_event_bus(Arc::clone(event_bus));
334        }
335
336        file_manager.initialize().await?;
337        self.file_manager = Some(file_manager);
338        Ok(())
339    }
340
341    async fn init_task_manager(&mut self) -> Result<()> {
342        tracing::info!("Initializing task manager");
343        let config = if let Some(config_manager) = &self.config_manager {
344            let manager = config_manager.lock().await;
345            manager
346                .get("tasks")
347                .await
348                .unwrap_or(None)
349                .unwrap_or_default()
350        } else {
351            crate::config::TaskConfig::default()
352        };
353
354        let mut task_manager = TaskManager::new(config);
355
356        // Set event bus for task events
357        if let Some(event_bus) = &self.event_bus_manager {
358            task_manager.set_event_bus(Arc::clone(event_bus));
359        }
360
361        task_manager.initialize().await?;
362        self.task_manager = Some(task_manager);
363        Ok(())
364    }
365
366    async fn init_account_manager(&mut self) -> Result<()> {
367        tracing::info!("Initializing account manager");
368        let security_policy = if let Some(config_manager) = &self.config_manager {
369            let manager = config_manager.lock().await;
370            manager
371                .get("security")
372                .await
373                .unwrap_or(None)
374                .unwrap_or_else(SecurityPolicy::default)
375        } else {
376            SecurityPolicy::default()
377        };
378
379        let session_store = Box::new(MemorySessionStore::new());
380        let user_store = Box::new(MemoryUserStore::new());
381
382        let mut account_manager = AccountManager::new(session_store, user_store, security_policy);
383        account_manager.initialize().await?;
384        self.account_manager = Some(account_manager);
385        Ok(())
386    }
387
388    async fn init_ui_layout_manager(&mut self) -> Result<()> {
389        tracing::info!("Initializing UI layout manager");
390        let mut ui_layout_manager = UILayoutManager::new();
391        ui_layout_manager.initialize().await?;
392        self.ui_layout_manager = Some(ui_layout_manager);
393        Ok(())
394    }
395
396    async fn init_plugin_manager(&mut self) -> Result<()> {
397        tracing::info!("Initializing plugin manager");
398
399        // Create a simple plugin loader for this example
400        let loader = Box::new(SimplePluginLoader::new());
401        let mut plugin_manager = PluginManager::new(loader);
402        plugin_manager.initialize().await?;
403        self.plugin_manager = Some(plugin_manager);
404        Ok(())
405    }
406
407    async fn start_background_services(&self) -> Result<()> {
408        tracing::info!("Starting background services");
409
410        // Start health monitoring
411        self.start_health_monitoring().await?;
412
413        // Start configuration sync (if enabled)
414        if let Some(config_manager) = self.config_manager.as_ref().cloned() {
415            tokio::spawn(async move {
416                let mut interval = interval(Duration::from_secs(300));
417                loop {
418                    interval.tick().await;
419                    let result = {
420                        let manager = config_manager.lock().await;
421                        manager.sync().await
422                    };
423
424                    if let Err(e) = result {
425                        tracing::error!("Configuration sync failed: {}", e);
426                    }
427                }
428            });
429        }
430
431        Ok(())
432    }
433
434    async fn start_health_monitoring(&self) -> Result<()> {
435        let health_interval = self.health_check_interval;
436        let app_state = Arc::clone(&self.app_state);
437
438        tokio::spawn(async move {
439            let mut interval = interval(health_interval);
440
441            loop {
442                interval.tick().await;
443
444                let state = *app_state.read().await;
445                if state != ApplicationState::Running {
446                    break;
447                }
448
449                // Perform health checks
450                // In a real implementation, this would check all managers
451                tracing::debug!("Performing health check");
452            }
453        });
454
455        Ok(())
456    }
457
458    async fn setup_signal_handlers(&self) -> Result<()> {
459        let shutdown_sender = self.shutdown_signal.clone();
460        let app_state = Arc::clone(&self.app_state);
461
462        tokio::spawn(async move {
463            #[cfg(unix)]
464            {
465                use tokio::signal::unix::{signal, SignalKind};
466
467                let mut sigterm =
468                    signal(SignalKind::terminate()).expect("Failed to register SIGTERM handler");
469                let mut sigint =
470                    signal(SignalKind::interrupt()).expect("Failed to register SIGINT handler");
471
472                tokio::select! {
473                    _ = sigterm.recv() => {
474                        tracing::info!("Received SIGTERM, initiating graceful shutdown");
475                    }
476                    _ = sigint.recv() => {
477                        tracing::info!("Received SIGINT, initiating graceful shutdown");
478                    }
479                }
480            }
481
482            #[cfg(windows)]
483            {
484                use tokio::signal;
485
486                signal::ctrl_c().await.expect("Failed to listen for ctrl+c");
487                tracing::info!("Received Ctrl+C, initiating graceful shutdown");
488            }
489
490            #[cfg(target_arch = "wasm32")]
491            {
492                // WASM doesn't support signal handling
493                // Could implement custom shutdown mechanism here
494            }
495
496            *app_state.write().await = ApplicationState::ShuttingDown;
497            let _ = shutdown_sender.send(());
498        });
499
500        Ok(())
501    }
502
503    /// Graceful shutdown of all systems
504    pub async fn shutdown(&mut self) -> Result<()> {
505        *self.app_state.write().await = ApplicationState::ShuttingDown;
506        self.state.set_state(ManagerState::ShuttingDown).await;
507
508        tracing::info!("Shutting down Qorzen application");
509
510        // Shutdown in reverse dependency order
511        if let Some(mut plugin_manager) = self.plugin_manager.take() {
512            let _ = timeout(Duration::from_secs(10), plugin_manager.shutdown()).await;
513        }
514
515        if let Some(mut ui_layout_manager) = self.ui_layout_manager.take() {
516            let _ = timeout(Duration::from_secs(5), ui_layout_manager.shutdown()).await;
517        }
518
519        if let Some(mut account_manager) = self.account_manager.take() {
520            let _ = timeout(Duration::from_secs(5), account_manager.shutdown()).await;
521        }
522
523        if let Some(mut task_manager) = self.task_manager.take() {
524            let _ = timeout(Duration::from_secs(10), task_manager.shutdown()).await;
525        }
526
527        if let Some(mut file_manager) = self.file_manager.take() {
528            let _ = timeout(Duration::from_secs(5), file_manager.shutdown()).await;
529        }
530
531        if let Some(event_bus_manager) = self.event_bus_manager.take() {
532            if let Ok(mut manager) = Arc::try_unwrap(event_bus_manager) {
533                let _ = timeout(Duration::from_secs(5), manager.shutdown()).await;
534            }
535        }
536
537        if let Some(mut concurrency_manager) = self.concurrency_manager.take() {
538            let _ = timeout(Duration::from_secs(10), concurrency_manager.shutdown()).await;
539        }
540
541        if let Some(mut logging_manager) = self.logging_manager.take() {
542            let _ = timeout(Duration::from_secs(5), logging_manager.shutdown()).await;
543        }
544
545        if let Some(config_manager) = self.config_manager.take() {
546            let mut manager = config_manager.lock().await;
547            let _ = timeout(Duration::from_secs(2), manager.shutdown()).await;
548        }
549
550        if let Some(mut platform_manager) = self.platform_manager.take() {
551            let _ = timeout(Duration::from_secs(5), platform_manager.shutdown()).await;
552        }
553
554        *self.app_state.write().await = ApplicationState::Shutdown;
555        self.state.set_state(ManagerState::Shutdown).await;
556
557        tracing::info!("Qorzen application shutdown complete");
558        Ok(())
559    }
560
561    /// Waits for shutdown signal
562    pub async fn wait_for_shutdown(&self) -> Result<()> {
563        let mut receiver = self.shutdown_signal.subscribe();
564        receiver.recv().await.map_err(|_| {
565            Error::new(
566                ErrorKind::Application,
567                "Shutdown signal channel closed unexpectedly",
568            )
569        })?;
570        Ok(())
571    }
572
573    /// Gets current application health
574    pub async fn get_health(&self) -> ApplicationHealth {
575        let mut manager_health = HashMap::new();
576        let mut overall_healthy = true;
577
578        // Check each manager's health
579        if let Some(platform_manager) = &self.platform_manager {
580            let health = platform_manager.health_check().await;
581            if health != HealthStatus::Healthy {
582                overall_healthy = false;
583            }
584            manager_health.insert("platform_manager".to_string(), health);
585        }
586
587        if let Some(config_manager) = &self.config_manager {
588            let manager = config_manager.lock().await;
589            let health = manager.health_check().await;
590            if health != HealthStatus::Healthy {
591                overall_healthy = false;
592            }
593            manager_health.insert("config_manager".to_string(), health);
594        }
595
596        // Add other managers...
597
598        let overall_status = if overall_healthy {
599            HealthStatus::Healthy
600        } else {
601            HealthStatus::Degraded
602        };
603
604        ApplicationHealth {
605            status: overall_status,
606            uptime: Utc::now()
607                .signed_duration_since(self.started_at)
608                .to_std()
609                .unwrap_or_default(),
610            managers: manager_health,
611            last_check: Utc::now(),
612            details: HashMap::new(),
613        }
614    }
615
616    /// Gets application statistics
617    pub async fn get_stats(&self) -> ApplicationStats {
618        ApplicationStats {
619            version: crate::VERSION.to_string(),
620            started_at: self.started_at,
621            uptime: Utc::now()
622                .signed_duration_since(self.started_at)
623                .to_std()
624                .unwrap_or_default(),
625            state: *self.app_state.read().await,
626            manager_count: self.manager_registry.len(),
627            initialized_managers: self.manager_registry.len(), // Simplified
628            failed_managers: 0,                                // Simplified
629            memory_usage_bytes: 0,                             // Would use platform-specific APIs
630            cpu_usage_percent: 0.0,                            // Would use platform-specific APIs
631            system_info: self.system_info.clone(),
632        }
633    }
634
635    /// Gets current user
636    pub async fn current_user(&self) -> Option<User> {
637        self.current_user.read().await.clone()
638    }
639
640    /// Gets current session
641    pub async fn current_session(&self) -> Option<UserSession> {
642        self.current_session.read().await.clone()
643    }
644
645    /// Gets application state
646    pub async fn get_state(&self) -> ApplicationState {
647        *self.app_state.read().await
648    }
649}
650
651impl Default for ApplicationCore {
652    fn default() -> Self {
653        Self::new()
654    }
655}
656
657#[async_trait]
658impl Manager for ApplicationCore {
659    fn name(&self) -> &str {
660        "application_core"
661    }
662
663    fn id(&self) -> Uuid {
664        self.state.id()
665    }
666
667    async fn initialize(&mut self) -> Result<()> {
668        // Main initialization is handled by the public initialize method
669        Ok(())
670    }
671
672    async fn shutdown(&mut self) -> Result<()> {
673        // Main shutdown is handled by the public shutdown method
674        Ok(())
675    }
676
677    async fn status(&self) -> ManagerStatus {
678        let mut status = self.state.status().await;
679        let app_stats = self.get_stats().await;
680
681        status.add_metadata(
682            "app_state",
683            serde_json::Value::String(format!("{:?}", app_stats.state)),
684        );
685        status.add_metadata(
686            "uptime_seconds",
687            serde_json::Value::from(app_stats.uptime.as_secs()),
688        );
689        status.add_metadata(
690            "manager_count",
691            serde_json::Value::from(app_stats.manager_count),
692        );
693        status.add_metadata("version", serde_json::Value::String(app_stats.version));
694
695        status
696    }
697
698    async fn health_check(&self) -> HealthStatus {
699        let health = self.get_health().await;
700        health.status
701    }
702}
703
704/// Simple plugin loader for demonstration
705struct SimplePluginLoader {
706    // Plugin loading implementation
707}
708
709impl SimplePluginLoader {
710    fn new() -> Self {
711        Self {}
712    }
713}
714
715#[async_trait]
716impl crate::plugin::PluginLoader for SimplePluginLoader {
717    async fn load_plugin(&self, _path: &str) -> Result<Box<dyn crate::plugin::Plugin>> {
718        Err(Error::plugin(
719            "loader",
720            "Plugin loading not implemented in example",
721        ))
722    }
723
724    async fn validate_plugin(
725        &self,
726        _plugin: &dyn crate::plugin::Plugin,
727    ) -> Result<crate::plugin::ValidationResult> {
728        Ok(crate::plugin::ValidationResult {
729            is_valid: true,
730            errors: Vec::new(),
731            warnings: Vec::new(),
732        })
733    }
734
735    async fn unload_plugin(&self, _plugin_id: &str) -> Result<()> {
736        Ok(())
737    }
738}
739
740#[cfg(test)]
741mod tests {
742    use super::*;
743
744    #[tokio::test]
745    async fn test_application_lifecycle() {
746        let mut app = ApplicationCore::new();
747
748        assert_eq!(app.get_state().await, ApplicationState::Created);
749
750        app.initialize().await.unwrap();
751        assert_eq!(app.get_state().await, ApplicationState::Running);
752
753        app.shutdown().await.unwrap();
754        assert_eq!(app.get_state().await, ApplicationState::Shutdown);
755    }
756
757    #[tokio::test]
758    async fn test_application_health() {
759        let mut app = ApplicationCore::new();
760        app.initialize().await.unwrap();
761
762        let health = app.get_health().await;
763        assert!(matches!(
764            health.status,
765            HealthStatus::Healthy | HealthStatus::Degraded
766        ));
767
768        app.shutdown().await.unwrap();
769    }
770
771    #[tokio::test]
772    async fn test_application_stats() {
773        let mut app = ApplicationCore::new();
774        app.initialize().await.unwrap();
775
776        let stats = app.get_stats().await;
777        assert_eq!(stats.version, crate::VERSION);
778        assert_eq!(stats.state, ApplicationState::Running);
779
780        app.shutdown().await.unwrap();
781    }
782}