sal-net 0.1.0

SAL Network - Network connectivity utilities for TCP, HTTP, and SSH
Documentation
use sal_net::SshConnectionBuilder;
use std::path::PathBuf;
use std::time::Duration;

#[tokio::test]
async fn test_ssh_connection_builder_new() {
    // Test that builder creates a functional connection with defaults
    let connection = SshConnectionBuilder::new().build();

    // Test that the connection can actually attempt operations
    // Use an invalid host to verify the connection object works but fails as expected
    let result = connection.execute("echo test").await;

    // Should fail because no host is configured, but the connection object should work
    match result {
        Ok((exit_code, _)) => assert!(exit_code != 0), // Should fail due to missing host
        Err(_) => {} // Error is expected when no host is configured
    }
}

#[tokio::test]
async fn test_ssh_connection_builder_host_functionality() {
    // Test that setting a host actually affects connection behavior
    let connection = SshConnectionBuilder::new()
        .host("nonexistent-host-12345.invalid")
        .user("testuser")
        .timeout(Duration::from_millis(100))
        .build();

    // This should fail because the host doesn't exist
    let result = connection.execute("echo test").await;
    match result {
        Ok((exit_code, _)) => assert!(exit_code != 0), // Should fail
        Err(_) => {}                                   // Error is expected for invalid hosts
    }
}

#[tokio::test]
async fn test_ssh_connection_builder_port_functionality() {
    // Test that setting a custom port affects connection behavior
    let connection = SshConnectionBuilder::new()
        .host("127.0.0.1")
        .port(12345) // Non-standard SSH port that should be closed
        .user("testuser")
        .timeout(Duration::from_millis(100))
        .build();

    // This should fail because port 12345 is not running SSH
    let result = connection.ping().await;
    match result {
        Ok(success) => assert!(!success), // Should fail to connect
        Err(_) => {}                      // Error is expected for closed ports
    }
}

#[tokio::test]
async fn test_ssh_connection_builder_user_functionality() {
    // Test that setting a user affects connection behavior
    let connection = SshConnectionBuilder::new()
        .host("127.0.0.1")
        .user("nonexistent-user-12345")
        .timeout(Duration::from_millis(100))
        .build();

    // This should fail because the user doesn't exist
    let result = connection.execute("whoami").await;
    match result {
        Ok((exit_code, _)) => assert!(exit_code != 0), // Should fail
        Err(_) => {}                                   // Error is expected for invalid users
    }
}

#[tokio::test]
async fn test_ssh_connection_builder_identity_file() {
    // Test that setting an identity file affects connection behavior
    let path = PathBuf::from("/nonexistent/path/to/key");
    let connection = SshConnectionBuilder::new()
        .host("127.0.0.1")
        .user("testuser")
        .identity_file(path)
        .timeout(Duration::from_millis(100))
        .build();

    // Test that connection with identity file attempts operations but fails as expected
    let result = connection.ping().await;

    // Should fail due to invalid key file or authentication, but connection should work
    match result {
        Ok(success) => assert!(!success), // Should fail due to invalid key or auth
        Err(_) => {}                      // Error is expected for invalid key file
    }
}

#[tokio::test]
async fn test_ssh_connection_builder_timeout_functionality() {
    // Test that timeout setting actually affects connection behavior
    let short_timeout = Duration::from_secs(1); // More reasonable timeout
    let connection = SshConnectionBuilder::new()
        .host("10.255.255.1") // Non-routable IP to trigger timeout
        .timeout(short_timeout)
        .build();

    let start = std::time::Instant::now();
    let result = connection.ping().await;
    let elapsed = start.elapsed();

    // Should timeout reasonably quickly (within 10 seconds)
    assert!(elapsed < Duration::from_secs(10));
    match result {
        Ok(success) => assert!(!success), // Should timeout/fail
        Err(_) => {}                      // Error is expected for timeouts
    }
}

#[tokio::test]
async fn test_ssh_connection_builder_chaining() {
    // Test that method chaining works and produces a functional connection
    let connection = SshConnectionBuilder::new()
        .host("invalid-host-12345.test")
        .port(2222)
        .user("testuser")
        .timeout(Duration::from_millis(100))
        .build();

    // Test that the chained configuration actually works
    let result = connection.ping().await;
    match result {
        Ok(success) => assert!(!success), // Should fail to connect to invalid host
        Err(_) => {}                      // Error is expected for invalid hosts
    }
}

#[tokio::test]
async fn test_ssh_execute_invalid_host() {
    let connection = SshConnectionBuilder::new()
        .host("this-host-definitely-does-not-exist-12345")
        .user("testuser")
        .timeout(Duration::from_secs(1))
        .build();

    let result = connection.execute("echo 'test'").await;

    // Should fail because host doesn't exist
    // Note: This test depends on SSH client being available
    match result {
        Ok((exit_code, _output)) => {
            // SSH might return various exit codes for connection failures
            assert!(exit_code != 0); // Should not succeed
        }
        Err(_) => {
            // Error is also acceptable (SSH client might not be available)
            // This is expected behavior for invalid hosts
        }
    }
}

#[tokio::test]
async fn test_ssh_execute_localhost_no_auth() {
    let connection = SshConnectionBuilder::new()
        .host("localhost")
        .user("nonexistentuser12345")
        .timeout(Duration::from_secs(1))
        .build();

    let result = connection.execute("echo 'test'").await;

    // Should fail due to authentication/user issues
    match result {
        Ok((exit_code, _output)) => {
            // SSH should fail with non-zero exit code
            assert!(exit_code != 0);
        }
        Err(_) => {
            // Error is also acceptable (SSH client might not be available)
            // This is expected behavior for authentication failures
        }
    }
}

#[tokio::test]
async fn test_ssh_ping_invalid_host() {
    let connection = SshConnectionBuilder::new()
        .host("this-host-definitely-does-not-exist-12345")
        .user("testuser")
        .timeout(Duration::from_secs(1))
        .build();

    let result = connection.ping().await;

    match result {
        Ok(success) => {
            assert!(!success); // Should not succeed
        }
        Err(_) => {
            // Error is also acceptable for invalid hosts
            // This is expected behavior
        }
    }
}

#[tokio::test]
async fn test_ssh_ping_localhost_no_auth() {
    let connection = SshConnectionBuilder::new()
        .host("localhost")
        .user("nonexistentuser12345")
        .timeout(Duration::from_secs(1))
        .build();

    let result = connection.ping().await;

    match result {
        Ok(success) => {
            // Should fail due to authentication issues
            assert!(!success);
        }
        Err(_) => {
            // Error is also acceptable for authentication failures
            // This is expected behavior
        }
    }
}

#[tokio::test]
async fn test_ssh_connection_builder_default_values() {
    // Test that builder creates connection with reasonable defaults
    let connection = SshConnectionBuilder::new().build();

    // Test that default connection can attempt operations but fails gracefully
    let result = connection.ping().await;

    // Should fail because no host is configured, but should handle it gracefully
    match result {
        Ok(success) => assert!(!success), // Should fail due to missing host
        Err(_) => {}                      // Error is expected when no host is configured
    }
}

#[tokio::test]
async fn test_ssh_connection_builder_full_config() {
    // Test builder with all options set
    let connection = SshConnectionBuilder::new()
        .host("nonexistent-host-12345.invalid")
        .port(2222)
        .user("testuser")
        .identity_file(PathBuf::from("/nonexistent/path/to/key"))
        .timeout(Duration::from_millis(100))
        .build();

    // Test that fully configured connection attempts operations but fails as expected
    let result = connection.ping().await;

    // Should fail because host doesn't exist, but all configuration should be applied
    match result {
        Ok(success) => assert!(!success), // Should fail due to invalid host
        Err(_) => {}                      // Error is expected for invalid host
    }
}

// Integration test that requires actual SSH setup
// This test is disabled by default as it requires SSH server and keys
#[tokio::test]
#[ignore]
async fn test_ssh_execute_real_connection() {
    // This test would require:
    // 1. SSH server running on localhost
    // 2. Valid SSH keys set up
    // 3. User account configured

    let connection = SshConnectionBuilder::new()
        .host("localhost")
        .user("testuser") // Replace with actual user
        .build();

    let result = connection.execute("echo 'Hello from SSH'").await;

    match result {
        Ok((exit_code, output)) => {
            assert_eq!(exit_code, 0);
            assert!(output.contains("Hello from SSH"));
        }
        Err(e) => {
            panic!("SSH execution failed: {}", e);
        }
    }
}