coderlib 0.1.0

A Rust library for AI-powered code assistance and agentic system
Documentation
//! Integration tests for LSP functionality

use coderlib::core::{CoderLib, CoderLibConfig};
use coderlib::lsp::{LspConfig, LspServerConfig};
use std::collections::HashMap;
use std::path::PathBuf;
use tempfile::TempDir;
use tokio;

#[tokio::test]
async fn test_lsp_integration_basic() {
    // Create a temporary directory for testing
    let temp_dir = TempDir::new().unwrap();
    let test_file = temp_dir.path().join("test.rs");
    
    // Write a simple Rust file with a potential issue
    std::fs::write(&test_file, r#"
fn main() {
    let unused_variable = 42;
    println!("Hello, world!");
}
"#).unwrap();
    
    // Create LSP config with rust-analyzer disabled for testing
    // (since we can't guarantee rust-analyzer is installed in CI)
    let mut lsp_config = LspConfig::default();
    lsp_config.enabled = false; // Disable for this test
    
    // Create CoderLib config
    let mut config = CoderLibConfig::default();
    config.lsp = lsp_config;
    
    // Create CoderLib instance
    let coderlib = CoderLib::new(config).await.unwrap();
    
    // Test LSP methods (should work gracefully even when LSP is disabled)
    let supports_rust = coderlib.lsp_supports_file(&test_file);
    println!("LSP supports Rust file: {}", supports_rust);
    
    // Test diagnostics (should return empty when LSP is disabled)
    let diagnostics = coderlib.get_diagnostics(&test_file).await.unwrap();
    assert!(diagnostics.is_empty(), "Should have no diagnostics when LSP is disabled");
    
    // Test file operations
    let content = std::fs::read_to_string(&test_file).unwrap();
    assert!(coderlib.lsp_did_open(&test_file, &content).await.is_ok());
    assert!(coderlib.lsp_did_change(&test_file, &content).await.is_ok());
    assert!(coderlib.lsp_did_close(&test_file).await.is_ok());
    
    // Test active servers
    let active_servers = coderlib.lsp_active_servers().await;
    assert!(active_servers.is_empty(), "Should have no active servers when LSP is disabled");
}

#[tokio::test]
async fn test_lsp_config_creation() {
    let config = LspConfig::default();
    
    // Test that default config has expected servers
    assert!(config.servers.contains_key("rust"));
    assert!(config.servers.contains_key("go"));
    
    let rust_config = &config.servers["rust"];
    assert_eq!(rust_config.command, "rust-analyzer");
    assert!(rust_config.enabled);
    assert!(rust_config.auto_start);
    
    // Test file pattern matching
    assert!(rust_config.handles_file(&PathBuf::from("main.rs")));
    assert!(rust_config.handles_file(&PathBuf::from("lib.rs")));
    assert!(!rust_config.handles_file(&PathBuf::from("main.go")));
}

#[tokio::test]
async fn test_lsp_server_config() {
    let mut servers = HashMap::new();
    
    // Create a custom server config
    servers.insert("test".to_string(), LspServerConfig {
        name: "Test Server".to_string(),
        command: "test-lsp".to_string(),
        args: vec!["--stdio".to_string()],
        env: HashMap::new(),
        file_patterns: vec!["*.test".to_string()],
        enabled: true,
        auto_start: false,
        settings: serde_json::json!({"test": true}),
    });
    
    let lsp_config = LspConfig {
        enabled: true,
        timeout: std::time::Duration::from_secs(10),
        max_servers: 3,
        servers,
    };
    
    let mut config = CoderLibConfig::default();
    config.lsp = lsp_config;
    
    // Test that custom config is used
    let coderlib = CoderLib::new(config).await.unwrap();
    
    // Test file support detection
    assert!(coderlib.lsp_supports_file(&PathBuf::from("example.test")));
    assert!(!coderlib.lsp_supports_file(&PathBuf::from("example.rs")));
}

#[tokio::test]
async fn test_diagnostics_tool() {
    use coderlib::tools::{Tool, DiagnosticsTool};
    use coderlib::integration::HostIntegration;
    use serde_json::json;
    
    // Create a mock host integration
    struct MockHost;
    
    #[async_trait::async_trait]
    impl HostIntegration for MockHost {
        async fn send_message(&self, _message: String) -> Result<(), coderlib::integration::IntegrationError> {
            Ok(())
        }
        
        async fn get_current_file(&self) -> Result<Option<std::path::PathBuf>, coderlib::integration::IntegrationError> {
            Ok(None)
        }
        
        async fn get_file_content(&self, _path: &std::path::Path) -> Result<String, coderlib::integration::IntegrationError> {
            Ok(String::new())
        }
        
        async fn set_file_content(&self, _path: &std::path::Path, _content: &str) -> Result<(), coderlib::integration::IntegrationError> {
            Ok(())
        }
        
        async fn show_notification(&self, _message: String, _level: coderlib::integration::MessageLevel) -> Result<(), coderlib::integration::IntegrationError> {
            Ok(())
        }
        
        async fn get_workspace_root(&self) -> Result<Option<std::path::PathBuf>, coderlib::integration::IntegrationError> {
            Ok(None)
        }
        
        async fn list_files(&self, _pattern: Option<String>) -> Result<Vec<std::path::PathBuf>, coderlib::integration::IntegrationError> {
            Ok(Vec::new())
        }
        
        fn get_capabilities(&self) -> coderlib::integration::HostCapabilities {
            coderlib::integration::HostCapabilities {
                can_edit_files: true,
                can_execute_commands: false,
                can_show_notifications: true,
                supports_streaming: false,
            }
        }
    }
    
    let tool = DiagnosticsTool::new();
    let host = MockHost;
    
    // Test tool metadata
    assert_eq!(tool.name(), "diagnostics");
    assert!(!tool.description().is_empty());
    
    // Test parameter schema
    let schema = tool.parameter_schema();
    assert!(schema.get("properties").is_some());
    assert!(schema.get("required").is_some());
    
    // Test execution with invalid parameters
    let invalid_params = json!({});
    let result = tool.execute(invalid_params, &host).await;
    assert!(result.is_err());
    
    // Test execution with valid parameters (should fail gracefully without LSP)
    let valid_params = json!({
        "file_path": "test.rs"
    });
    let result = tool.execute(valid_params, &host).await;
    assert!(result.is_ok());
    
    let response = result.unwrap();
    assert!(response.content.contains("LSP service not available"));
}

#[test]
fn test_lsp_language_detection() {
    use coderlib::lsp::LspServerConfig;
    
    let config = LspServerConfig::default();
    
    // Test language ID detection
    assert_eq!(config.get_language_id(&PathBuf::from("main.rs")), Some("rust".to_string()));
    assert_eq!(config.get_language_id(&PathBuf::from("main.go")), Some("go".to_string()));
    assert_eq!(config.get_language_id(&PathBuf::from("script.py")), Some("python".to_string()));
    assert_eq!(config.get_language_id(&PathBuf::from("app.js")), Some("javascript".to_string()));
    assert_eq!(config.get_language_id(&PathBuf::from("app.ts")), Some("typescript".to_string()));
    assert_eq!(config.get_language_id(&PathBuf::from("unknown.xyz")), None);
}