agentd 0.1.2

Agent daemon for secure capability execution with pluggable isolation backends
Documentation
/*!
 * Self-test command implementation
 *
 * Extracted from main.rs to reduce complexity and improve maintainability.
 * Handles comprehensive system and security validation tests.
 */

use anyhow::Result;
use std::path::PathBuf;

use crate::bootstrap::validate_security_capabilities;
use crate::commands::check_config::{check_directories, check_nats_connectivity};
use crate::config::{self, Config};
use crate::{health, isolation_tests};

/// Handles the self-test command
pub struct SelfTestCommand;

impl SelfTestCommand {
    pub async fn execute(config_path: PathBuf, comprehensive: bool) -> Result<()> {
        println!("๐Ÿงช Smith Executor Self-Test");
        println!("===========================");

        // Load configuration
        let config = match config::load_config(&config_path) {
            Ok(config) => {
                println!("โœ… Configuration loaded successfully");
                config
            }
            Err(e) => {
                println!("โŒ Configuration failed to load: {}", e);
                std::process::exit(1);
            }
        };

        let mut all_tests_passed = true;

        // Platform check
        all_tests_passed &= Self::run_platform_check().await;

        // Security features check
        all_tests_passed &= Self::run_security_check().await;

        // Isolation tests
        all_tests_passed &= Self::run_isolation_tests(comprehensive).await;

        // Directory validation
        all_tests_passed &= Self::run_directory_validation(&config).await;

        // Configuration validation
        all_tests_passed &= Self::run_configuration_validation(&config).await;

        // NATS connectivity test
        Self::run_nats_connectivity_test(&config).await;

        // Final result
        Self::print_final_results(all_tests_passed).await;

        if !all_tests_passed {
            Self::print_troubleshooting_tips();
            std::process::exit(1);
        }

        println!("\nโœจ Self-test completed successfully!");

        if !comprehensive {
            println!("๐Ÿ’ก For comprehensive isolation testing, run:");
            println!("   smith-executor self-test --comprehensive");
        }

        Ok(())
    }

    async fn run_platform_check() -> bool {
        let platform = health::PlatformInfo::detect();
        println!("\n๐Ÿ–ฅ๏ธ  Platform Information:");
        println!("โ”œโ”€ OS: {} {}", platform.os, platform.arch);
        println!(
            "โ”œโ”€ Linux: {}",
            if platform.is_linux {
                "โœ… Yes"
            } else {
                "โŒ No"
            }
        );
        println!(
            "โ””โ”€ Root: {}",
            if platform.is_root {
                "โš ๏ธ  Yes"
            } else {
                "โœ… No"
            }
        );

        true // Platform check is always informational
    }

    async fn run_security_check() -> bool {
        let security = health::SecurityStatus::detect();
        println!("\n๐Ÿ”’ Security Features:");
        println!(
            "โ”œโ”€ Landlock: {}",
            if security.landlock_available {
                "โœ… Available"
            } else {
                "โŒ Not Available"
            }
        );
        println!(
            "โ”œโ”€ Seccomp: {}",
            if security.seccomp_available {
                "โœ… Available"
            } else {
                "โŒ Not Available"
            }
        );
        println!(
            "โ”œโ”€ Cgroups: {}",
            if security.cgroups_available {
                "โœ… Available"
            } else {
                "โŒ Not Available"
            }
        );
        println!(
            "โ””โ”€ Namespaces: {}",
            if security.namespaces_available {
                "โœ… Available"
            } else {
                "โŒ Not Available"
            }
        );

        true // Security check is always informational
    }

    async fn run_isolation_tests(comprehensive: bool) -> bool {
        let mut tests_passed = true;

        // Quick isolation check (always run)
        println!("\n๐Ÿ›ก๏ธ  Quick Isolation Check:");
        match isolation_tests::quick_isolation_check().await {
            Ok(isolation_ok) => {
                if isolation_ok {
                    println!("โ”œโ”€ Result: โœ… Isolation mechanisms appear functional");
                } else {
                    println!("โ”œโ”€ Result: โš ๏ธ  Some isolation mechanisms may not be working");
                    tests_passed = false;
                }
            }
            Err(e) => {
                println!("โ”œโ”€ Result: โŒ Isolation check failed: {}", e);
                tests_passed = false;
            }
        }

        // Comprehensive isolation tests (if requested)
        if comprehensive {
            println!("\n๐Ÿงช Comprehensive Isolation Tests:");
            match isolation_tests::run_isolation_tests().await {
                Ok(results) => {
                    isolation_tests::print_isolation_report(&results);
                    if !results.overall_passed() {
                        tests_passed = false;
                    }
                }
                Err(e) => {
                    println!("โŒ Comprehensive isolation tests failed: {}", e);
                    tests_passed = false;
                }
            }
        } else {
            println!("โ””โ”€ Tip: Use --comprehensive for detailed isolation testing");
        }

        tests_passed
    }

    async fn run_directory_validation(config: &Config) -> bool {
        println!("\n๐Ÿ“ Directory Validation:");
        let dir_status = check_directories(config);
        println!(
            "โ”œโ”€ Work Root: {}",
            if dir_status.work_root {
                "โœ… OK"
            } else {
                "โŒ Inaccessible"
            }
        );
        println!(
            "โ”œโ”€ State Dir: {}",
            if dir_status.state_dir {
                "โœ… OK"
            } else {
                "โŒ Inaccessible"
            }
        );
        println!(
            "โ””โ”€ Audit Dir: {}",
            if dir_status.audit_dir {
                "โœ… OK"
            } else {
                "โŒ Inaccessible"
            }
        );

        dir_status.all_valid
    }

    async fn run_configuration_validation(config: &Config) -> bool {
        println!("\nโš™๏ธ  Configuration Validation:");
        let default_backend = if cfg!(target_os = "linux") {
            "landlock"
        } else {
            "gondolin"
        };
        match validate_security_capabilities(config, false, default_backend) {
            Ok(_) => {
                println!("โ””โ”€ Security Configuration: โœ… Valid");
                true
            }
            Err(e) => {
                println!("โ””โ”€ Security Configuration: โŒ Invalid: {}", e);
                false
            }
        }
    }

    async fn run_nats_connectivity_test(config: &Config) {
        println!("\n๐Ÿ”Œ NATS Connectivity Test:");
        let nats_status = check_nats_connectivity(config).await;
        if nats_status.connected {
            println!("โ”œโ”€ Connection: โœ… Connected");
            println!(
                "โ””โ”€ JetStream: {}",
                if nats_status.jetstream_available {
                    "โœ… Available"
                } else {
                    "โŒ Not Available"
                }
            );
        } else {
            println!("โ”œโ”€ Connection: โš ๏ธ  Failed (not critical for self-test)");
            if let Some(ref error) = nats_status.error {
                println!("โ””โ”€ Error: {}", error);
            }
            // NATS failure is not critical for self-test
        }
    }

    async fn print_final_results(all_tests_passed: bool) {
        let platform = health::PlatformInfo::detect();
        let security = health::SecurityStatus::detect();

        println!("\n๐Ÿ“Š Self-Test Summary:");
        println!(
            "โ”œโ”€ Platform: {}",
            if platform.is_linux {
                "โœ… Supported"
            } else {
                "โš ๏ธ  Demo Only"
            }
        );
        println!(
            "โ”œโ”€ Security: {}",
            if security.overall_secure {
                "โœ… Full"
            } else {
                "โš ๏ธ  Partial"
            }
        );
        println!(
            "โ”œโ”€ Isolation: {}",
            if all_tests_passed {
                "โœ… Working"
            } else {
                "โŒ Issues Detected"
            }
        );
        println!(
            "โ””โ”€ Configuration: {}",
            if all_tests_passed {
                "โœ… Valid"
            } else {
                "โŒ Issues Detected"
            }
        );

        println!(
            "\n๐Ÿš€ Final Status: {}",
            if all_tests_passed {
                if platform.is_linux && security.overall_secure {
                    "โœ… READY FOR PRODUCTION"
                } else {
                    "โš ๏ธ  READY FOR DEVELOPMENT"
                }
            } else {
                "โŒ CONFIGURATION ISSUES DETECTED"
            }
        );
    }

    fn print_troubleshooting_tips() {
        println!("\nโš ๏ธ  Issues detected during self-test:");
        println!("   - Review the failed checks above");
        println!("   - Fix configuration or system setup");
        println!("   - Re-run self-test to verify fixes");
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_self_test_command_struct() {
        let _cmd = SelfTestCommand;
        assert!(std::mem::size_of::<SelfTestCommand>() == 0); // Zero-sized type
    }

    #[test]
    fn test_print_troubleshooting_tips() {
        // Just verify it doesn't panic
        SelfTestCommand::print_troubleshooting_tips();
    }

    #[tokio::test]
    async fn test_run_platform_check() {
        // Should always return true (informational only)
        let result = SelfTestCommand::run_platform_check().await;
        assert!(result);
    }

    #[tokio::test]
    async fn test_run_security_check() {
        // Should always return true (informational only)
        let result = SelfTestCommand::run_security_check().await;
        assert!(result);
    }

    #[tokio::test]
    async fn test_print_final_results_passed() {
        // Just verify it doesn't panic
        SelfTestCommand::print_final_results(true).await;
    }

    #[tokio::test]
    async fn test_print_final_results_failed() {
        // Just verify it doesn't panic
        SelfTestCommand::print_final_results(false).await;
    }

    #[tokio::test]
    async fn test_run_isolation_tests_quick() {
        // Quick isolation tests should return a boolean
        let _result = SelfTestCommand::run_isolation_tests(false).await;
        // Result depends on platform - just verify it completes
    }
}