qssh 0.0.2-alpha

Experimental quantum-safe SSH using post-quantum crypto. Research project - NOT for production. See LIMITATIONS.md
Documentation
//! Integration tests for QSSH
//!
//! These tests verify end-to-end functionality

use qssh::*;
use tokio::time::{timeout, Duration};

#[tokio::test]
async fn test_config_creation() {
    let config = QsshConfig {
        server: "example.com:22".to_string(),
        username: "testuser".to_string(),
        password: None,
        port_forwards: Vec::new(),
        use_qkd: false,
        pq_algorithm: PqAlgorithm::Falcon512,
        key_rotation_interval: 3600,
    };

    assert_eq!(config.server, "example.com:22");
    assert_eq!(config.username, "testuser");
    assert!(!config.use_qkd);
}

#[tokio::test]
async fn test_known_hosts_management() {
    use tempfile::TempDir;
    use qssh::known_hosts::KnownHosts;

    let temp_dir = TempDir::new().unwrap();
    let known_hosts_path = temp_dir.path().join("known_hosts");

    let mut known_hosts = KnownHosts::new(known_hosts_path.clone(), false).unwrap();

    // Add a test host
    let test_key = vec![1, 2, 3, 4, 5, 6, 7, 8];
    known_hosts.add("test.example.com", 22, PqAlgorithm::Falcon512, test_key.clone()).unwrap();

    // Reload and verify
    let known_hosts2 = KnownHosts::new(known_hosts_path, false).unwrap();
    let result = known_hosts2.verify("test.example.com", 22, PqAlgorithm::Falcon512, &test_key);

    assert!(matches!(result, qssh::known_hosts::VerificationResult::Known));
}

#[tokio::test]
async fn test_proxy_jump_parsing() {
    use qssh::proxy::ProxyConnection;

    let jump_spec = "user1@jump1.example.com:2222,jump2.example.com,user3@jump3.example.com:3333";
    let hosts = ProxyConnection::parse_jump_hosts(jump_spec).unwrap();

    assert_eq!(hosts.len(), 3);

    assert_eq!(hosts[0].username, "user1");
    assert_eq!(hosts[0].hostname, "jump1.example.com");
    assert_eq!(hosts[0].port, 2222);

    assert_eq!(hosts[1].hostname, "jump2.example.com");
    assert_eq!(hosts[1].port, 22);

    assert_eq!(hosts[2].username, "user3");
    assert_eq!(hosts[2].hostname, "jump3.example.com");
    assert_eq!(hosts[2].port, 3333);
}

#[tokio::test]
async fn test_multiplex_control_messages() {
    use qssh::multiplex::{ControlMessage, ControlMaster};
    use tempfile::TempDir;
    use std::sync::Arc;

    let temp_dir = TempDir::new().unwrap();
    let socket_path = temp_dir.path().join("control.sock");

    // Test message serialization
    let msg = ControlMessage::Ping;
    let serialized = bincode::serialize(&msg).unwrap();
    let deserialized: ControlMessage = bincode::deserialize(&serialized).unwrap();

    assert!(matches!(deserialized, ControlMessage::Ping));

    // Test that master is not running
    assert!(!ControlMaster::check_master(&socket_path).await);
}

#[tokio::test]
async fn test_agent_key_operations() {
    use qssh::agent::QsshAgent;
    use tempfile::TempDir;

    let temp_dir = TempDir::new().unwrap();
    let socket_path = temp_dir.path().join("agent.sock");
    let agent = QsshAgent::new(socket_path);

    // Generate test keys
    let private_key = vec![1; 64];
    let public_key = vec![2; 32];

    // Add key
    agent.add_key(
        PqAlgorithm::Falcon512,
        private_key,
        public_key.clone(),
        "test@example.com".to_string(),
        Some(60), // 60 second lifetime
    ).await.unwrap();

    // List keys
    let keys = agent.list_keys().await.unwrap();
    assert_eq!(keys.len(), 1);
    assert_eq!(keys[0].comment, "test@example.com");

    // Remove key
    agent.remove_key(public_key).await.unwrap();

    // Verify removal
    let keys = agent.list_keys().await.unwrap();
    assert_eq!(keys.len(), 0);
}

#[tokio::test]
async fn test_port_forward_parsing() {
    use qssh::PortForward;

    // Test local forward
    let local = PortForward::parse_forward_spec("-L", "8080:localhost:80").unwrap();
    assert_eq!(local.local_port, Some(8080));
    assert_eq!(local.remote_host, Some("localhost".to_string()));
    assert_eq!(local.remote_port, Some(80));

    // Test remote forward
    let remote = PortForward::parse_forward_spec("-R", "9090:localhost:90").unwrap();
    assert_eq!(remote.local_port, Some(9090));
    assert_eq!(remote.remote_host, Some("localhost".to_string()));
    assert_eq!(remote.remote_port, Some(90));

    // Test dynamic forward (SOCKS)
    let dynamic = PortForward::parse_forward_spec("-D", "1080").unwrap();
    assert_eq!(dynamic.local_port, Some(1080));
    assert!(dynamic.remote_host.is_none());
    assert!(dynamic.remote_port.is_none());
}

#[tokio::test]
async fn test_x11_config() {
    use qssh::x11::X11Config;

    let config = X11Config {
        enabled: true,
        display_number: 10,
        screen: 0,
        trusted: false,
        timeout: 300,
        ..Default::default()
    };

    assert!(config.enabled);
    assert_eq!(config.display_number, 10);
    assert!(!config.trusted);
}

#[test]
fn test_crypto_algorithms() {
    // Test that all quantum algorithms are available
    let algorithms = [
        PqAlgorithm::Falcon512,
        PqAlgorithm::SphincsPlus,
        PqAlgorithm::Kyber1024,
    ];

    for algo in &algorithms {
        // Just verify the enum values exist
        match algo {
            PqAlgorithm::Falcon512 => assert!(true),
            PqAlgorithm::SphincsPlus => assert!(true),
            PqAlgorithm::Kyber1024 => assert!(true),
            _ => panic!("Unexpected algorithm"),
        }
    }
}

#[tokio::test]
async fn test_sftp_protocol_messages() {
    use qssh::subsystems::sftp::protocol::{SftpMessage, SftpVersion};

    let version_msg = SftpMessage::Version {
        version: SftpVersion::V3,
        extensions: vec![],
    };

    let serialized = bincode::serialize(&version_msg).unwrap();
    let deserialized: SftpMessage = bincode::deserialize(&serialized).unwrap();

    match deserialized {
        SftpMessage::Version { version, .. } => {
            assert_eq!(version, SftpVersion::V3);
        }
        _ => panic!("Wrong message type"),
    }
}