rust-mc-status 3.0.0

High-performance asynchronous Rust library for querying Minecraft server status (Java & Bedrock)
Documentation
//! Unit tests for the `McError` two-level hierarchy.
//!
//! Tests that:
//! - `From` conversions work correctly
//! - Pattern matching works at both levels
//! - Error messages are human-readable
//! - Convenience constructors produce the right variants

use rust_mc_status::McError;
use rust_mc_status::error::{ConfigError, NetworkError, ProtocolError};

// ─── From conversions ─────────────────────────────────────────────────────────

#[test]
fn network_error_converts_to_mc_error() {
    let e: McError = NetworkError::Timeout.into();
    assert!(matches!(e, McError::Network(NetworkError::Timeout)));
}

#[test]
fn protocol_error_converts_to_mc_error() {
    let e: McError = ProtocolError::InvalidResponse("bad".into()).into();
    assert!(matches!(e, McError::Protocol(ProtocolError::InvalidResponse(_))));
}

#[test]
fn config_error_converts_to_mc_error() {
    let e: McError = ConfigError::InvalidEdition("mars".into()).into();
    assert!(matches!(e, McError::Config(ConfigError::InvalidEdition(_))));
}

#[test]
fn io_error_converts_to_mc_error() {
    let io = std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "refused");
    let e: McError = io.into();
    assert!(matches!(e, McError::Io(_)));
}

// ─── Convenience constructors ─────────────────────────────────────────────────

#[test]
fn timeout_constructor() {
    let e = McError::timeout();
    assert!(matches!(e, McError::Network(NetworkError::Timeout)));
}

#[test]
fn dns_constructor() {
    let e = McError::dns("lookup failed");
    assert!(matches!(e, McError::Network(NetworkError::Dns(_))));
    assert!(e.to_string().contains("lookup failed"));
}

#[test]
fn connection_constructor() {
    let e = McError::connection("connection refused");
    assert!(matches!(e, McError::Network(NetworkError::Connection(_))));
}

#[test]
fn invalid_response_constructor() {
    let e = McError::invalid_response("bad packet");
    assert!(matches!(e, McError::Protocol(ProtocolError::InvalidResponse(_))));
    assert!(e.to_string().contains("bad packet"));
}

#[test]
fn invalid_port_constructor() {
    let e = McError::invalid_port("99999");
    assert!(matches!(e, McError::Config(ConfigError::InvalidPort(_))));
}

#[test]
fn invalid_address_constructor() {
    let e = McError::invalid_address("missing bracket");
    assert!(matches!(e, McError::Config(ConfigError::InvalidAddress(_))));
}

#[test]
fn invalid_edition_constructor() {
    let e = McError::invalid_edition("xbox");
    assert!(matches!(e, McError::Config(ConfigError::InvalidEdition(_))));
    assert!(e.to_string().contains("xbox"));
}

// ─── Display messages ─────────────────────────────────────────────────────────

#[test]
fn timeout_display_is_human_readable() {
    let msg = McError::timeout().to_string();
    // Should not expose internal variant names like "NetworkError::Timeout"
    assert!(!msg.is_empty());
    assert!(msg.to_lowercase().contains("timeout") || msg.to_lowercase().contains("timed"));
}

#[test]
fn dns_display_contains_message() {
    let msg = McError::dns("NXDOMAIN").to_string();
    assert!(msg.contains("NXDOMAIN"));
}

#[test]
fn connection_display_contains_message() {
    let msg = McError::connection("port 25565 refused").to_string();
    assert!(msg.contains("port 25565 refused"));
}

#[test]
fn config_invalid_port_display_contains_value() {
    let msg = McError::invalid_port("99999").to_string();
    assert!(msg.contains("99999"));
}

// ─── ServerEdition::from_str ──────────────────────────────────────────────────

#[test]
fn java_parses_case_insensitive() {
    use rust_mc_status::ServerEdition;
    assert_eq!("java".parse::<ServerEdition>().unwrap(), ServerEdition::Java);
    assert_eq!("JAVA".parse::<ServerEdition>().unwrap(), ServerEdition::Java);
    assert_eq!("Java".parse::<ServerEdition>().unwrap(), ServerEdition::Java);
}

#[test]
fn bedrock_parses_case_insensitive() {
    use rust_mc_status::ServerEdition;
    assert_eq!("bedrock".parse::<ServerEdition>().unwrap(), ServerEdition::Bedrock);
    assert_eq!("BEDROCK".parse::<ServerEdition>().unwrap(), ServerEdition::Bedrock);
}

#[test]
fn unknown_edition_is_config_error() {
    use rust_mc_status::ServerEdition;
    let e = "xbox".parse::<ServerEdition>().unwrap_err();
    assert!(
        matches!(e, McError::Config(ConfigError::InvalidEdition(_))),
        "expected ConfigError::InvalidEdition, got {e:?}"
    );
}

// ─── Proxy errors (feature-gated) ────────────────────────────────────────────

#[cfg(feature = "proxy")]
mod proxy_errors {
    use rust_mc_status::McError;
    use rust_mc_status::error::ProxyError;

    #[test]
    fn proxy_error_constructor() {
        let e = McError::proxy_error("auth failed");
        assert!(matches!(e, McError::Proxy(ProxyError::Connection(_))));
        assert!(e.to_string().contains("auth failed"));
    }

    #[test]
    fn proxy_udp_unsupported_constructor() {
        let e = McError::proxy_udp_unsupported("127.0.0.1:1080");
        assert!(matches!(e, McError::Proxy(ProxyError::UdpUnsupported(_))));
        assert!(e.to_string().contains("127.0.0.1:1080"));
    }

    #[test]
    fn proxy_error_converts_to_mc_error() {
        let e: McError = ProxyError::Connection("refused".into()).into();
        assert!(matches!(e, McError::Proxy(ProxyError::Connection(_))));
    }
}