typhoon-protocol 0.1.0

A sample implementation of TYPHOON protocol
Documentation
use std::env;

use crate::defaults::DefaultExecutor;
use crate::settings::SettingsBuilder;
use crate::settings::keys::*;

fn builder() -> SettingsBuilder<DefaultExecutor> {
    SettingsBuilder::new()
}

// === Settings assertion tests ===

// Test: default settings pass all assertions.
#[test]
fn test_default_settings_pass_assertions() {
    assert!(builder().build().is_ok());
}

// Test: TIMEOUT_MIN > TIMEOUT_MAX fails assertion.
#[test]
fn test_settings_timeout_min_exceeds_max() {
    let result = builder().set(&TIMEOUT_MIN, 50000).set(&TIMEOUT_MAX, 1000).build();
    assert!(result.is_err());
}

// Test: RTT_MIN > RTT_MAX fails assertion.
#[test]
fn test_settings_rtt_min_exceeds_max() {
    let result = builder().set(&RTT_MIN, 10000).set(&RTT_MAX, 100).build();
    assert!(result.is_err());
}

// Test: RTT_ALPHA > 1.0 fails assertion.
#[test]
fn test_settings_rtt_alpha_exceeds_one() {
    let result = builder().set(&RTT_ALPHA, 1.5).build();
    assert!(result.is_err());
}

// Test: RTT_ALPHA = 0.0 fails assertion (must be exclusive).
#[test]
fn test_settings_rtt_alpha_zero() {
    let result = builder().set(&RTT_ALPHA, 0.0).build();
    assert!(result.is_err());
}

// Test: RTT_BETA negative fails assertion.
#[test]
fn test_settings_rtt_beta_negative() {
    let result = builder().set(&RTT_BETA, -0.1).build();
    assert!(result.is_err());
}

// Test: FAKE_HEADER_PROBABILITY > 1.0 fails assertion.
#[test]
fn test_settings_fake_header_probability_exceeds_one() {
    let result = builder().set(&FAKE_HEADER_PROBABILITY, 1.5).build();
    assert!(result.is_err());
}

// Test: FAKE_HEADER_PROBABILITY = 0.0 passes assertion (inclusive range).
#[test]
fn test_settings_fake_header_probability_zero_ok() {
    let result = builder().set(&FAKE_HEADER_PROBABILITY, 0.0).build();
    assert!(result.is_ok());
}

// Test: SEND_BYTES_JITTER > 1.0 fails assertion.
#[test]
fn test_settings_send_bytes_jitter_exceeds_one() {
    let result = builder().set(&SEND_BYTES_JITTER, 1.5).build();
    assert!(result.is_err());
}

// Test: SEND_BYTES_JITTER negative fails assertion.
#[test]
fn test_settings_send_bytes_jitter_negative() {
    let result = builder().set(&SEND_BYTES_JITTER, -0.1).build();
    assert!(result.is_err());
}

// Test: SEND_BYTES_JITTER = 0.0 and 1.0 pass assertion (inclusive range).
#[test]
fn test_settings_send_bytes_jitter_unit_bounds_ok() {
    assert!(builder().set(&SEND_BYTES_JITTER, 0.0).build().is_ok());
    assert!(builder().set(&SEND_BYTES_JITTER, 1.0).build().is_ok());
}

// Test: SEND_BYTES_CHUNK = 0 (sentinel) passes; positive ≤ MTU passes; > MTU fails.
#[test]
fn test_settings_send_bytes_chunk_bounds() {
    assert!(builder().set(&SEND_BYTES_CHUNK, 0).build().is_ok());
    assert!(builder().set(&SEND_BYTES_CHUNK, 512).build().is_ok());
    assert!(builder().set(&SEND_BYTES_CHUNK, 1500).build().is_ok());
    assert!(builder().set(&SEND_BYTES_CHUNK, 9000).build().is_err());
}

// Test: HEALTH_CHECK_NEXT_IN_MIN <= TIMEOUT_MAX fails assertion (next_in must be > timeout).
#[test]
fn test_settings_next_in_not_greater_than_timeout() {
    let result = builder().set(&HEALTH_CHECK_NEXT_IN_MIN, 30000).set(&TIMEOUT_MAX, 32000).build();
    assert!(result.is_err());
}

// Test: RTT_DEFAULT outside [RTT_MIN, RTT_MAX] fails assertion.
#[test]
fn test_settings_rtt_default_out_of_range() {
    let result = builder().set(&RTT_DEFAULT, 100_000).build();
    assert!(result.is_err());
}

// Test: DECOY_CURRENT_ALPHA > 1.0 fails assertion.
#[test]
fn test_settings_decoy_current_alpha_exceeds_one() {
    let result = builder().set(&DECOY_CURRENT_ALPHA, 2.0).build();
    assert!(result.is_err());
}

// Test: DECOY_LENGTH_MIN > DECOY_LENGTH_MAX fails assertion.
#[test]
fn test_settings_decoy_length_min_exceeds_max() {
    let result = builder().set(&DECOY_LENGTH_MIN, 2000).set(&DECOY_LENGTH_MAX, 100).build();
    assert!(result.is_err());
}

// Test: DECOY_FALLTHROUGH_PACKETS_MIN > DECOY_FALLTHROUGH_PACKETS_MAX fails assertion.
#[test]
fn test_settings_decoy_fallthrough_min_exceeds_max() {
    let result = builder().set(&DECOY_FALLTHROUGH_PACKETS_MIN, 0.5).set(&DECOY_FALLTHROUGH_PACKETS_MAX, 0.1).build();
    assert!(result.is_err());
}

// Test: DECOY_FALLTHROUGH_PACKETS_MIN outside [0, 1] fails assertion.
#[test]
fn test_settings_decoy_fallthrough_min_out_of_unit_range() {
    assert!(builder().set(&DECOY_FALLTHROUGH_PACKETS_MIN, -0.1).build().is_err());
    assert!(builder().set(&DECOY_FALLTHROUGH_PACKETS_MIN, 1.5).build().is_err());
}

// Test: DECOY_FALLTHROUGH_PACKETS_MAX outside [0, 1] fails assertion.
#[test]
fn test_settings_decoy_fallthrough_max_out_of_unit_range() {
    assert!(builder().set(&DECOY_FALLTHROUGH_PACKETS_MAX, -0.1).build().is_err());
    assert!(builder().set(&DECOY_FALLTHROUGH_PACKETS_MAX, 1.5).build().is_err());
}

// Test: DECOY_FALLTHROUGH_PACKETS_* unit bounds and equal bounds pass assertion.
#[test]
fn test_settings_decoy_fallthrough_unit_bounds_ok() {
    assert!(builder().set(&DECOY_FALLTHROUGH_PACKETS_MIN, 0.0).set(&DECOY_FALLTHROUGH_PACKETS_MAX, 1.0).build().is_ok());
    assert!(builder().set(&DECOY_FALLTHROUGH_PACKETS_MIN, 0.5).set(&DECOY_FALLTHROUGH_PACKETS_MAX, 0.5).build().is_ok());
}

// Test: valid custom settings pass assertions.
#[test]
fn test_settings_valid_custom_pass() {
    let result = builder().set(&RTT_ALPHA, 0.5).set(&RTT_BETA, 0.5).set(&FAKE_HEADER_PROBABILITY, 0.5).build();
    assert!(result.is_ok());
}

// Test: environment variable override is applied when no explicit override is set.
// Uses HANDSHAKE_NEXT_IN_FACTOR — a key no other test touches — to avoid parallel test races.
#[test]
fn test_env_var_override_applied() {
    let env_name = HANDSHAKE_NEXT_IN_FACTOR.name;
    let original = env::var(env_name).ok();

    // SAFETY: test-only, single-threaded access to this env var.
    unsafe { env::set_var(env_name, "0.5") };
    let settings = builder().build().expect("settings should build with env override");
    let val: f64 = settings.get(&HANDSHAKE_NEXT_IN_FACTOR);
    assert_eq!(val, 0.5, "env var override should take effect");

    // Cleanup.
    unsafe {
        match original {
            Some(v) => env::set_var(env_name, v),
            None => env::remove_var(env_name),
        }
    }
}

// Test: FAKE_HEADER_VOLATILE_CHANGE_PROB_MIN > MAX fails assertion.
#[test]
fn test_settings_fake_header_volatile_change_prob_min_exceeds_max() {
    let result = builder().set(&FAKE_HEADER_VOLATILE_CHANGE_PROB_MIN, 0.5).set(&FAKE_HEADER_VOLATILE_CHANGE_PROB_MAX, 0.1).build();
    assert!(result.is_err());
}

// Test: FAKE_HEADER_VOLATILE_CHANGE_PROB out of [0, 1] fails assertion.
#[test]
fn test_settings_fake_header_volatile_change_prob_exceeds_one() {
    let result = builder().set(&FAKE_HEADER_VOLATILE_CHANGE_PROB_MAX, 1.5).build();
    assert!(result.is_err());
}

// Test: FAKE_HEADER_SWITCHING_TIMEOUT_MIN > MAX fails assertion.
#[test]
fn test_settings_fake_header_switching_timeout_min_exceeds_max() {
    let result = builder().set(&FAKE_HEADER_SWITCHING_TIMEOUT_MIN_MS, 50_000).set(&FAKE_HEADER_SWITCHING_TIMEOUT_MAX_MS, 1_000).build();
    assert!(result.is_err());
}

// Test: FAKE_HEADER_SWITCHING_TIMEOUT_MIN = 0 fails assertion (must be positive).
#[test]
fn test_settings_fake_header_switching_timeout_min_zero() {
    let result = builder().set(&FAKE_HEADER_SWITCHING_TIMEOUT_MIN_MS, 0).build();
    assert!(result.is_err());
}

// Test: DECOY_NOISY_DECOY_LENGTH_MIN > DECOY_NOISY_LENGTH_MAX fails.
#[test]
fn test_settings_noisy_length_min_exceeds_max() {
    let result = builder().set(&DECOY_NOISY_DECOY_LENGTH_MIN, 1000).set(&DECOY_NOISY_LENGTH_MAX, 500).build();
    assert!(result.is_err());
}

// Test: DECOY_HEAVY_LENGTH_MIN > DECOY_LENGTH_MAX fails.
#[test]
fn test_settings_heavy_length_min_exceeds_decoy_max() {
    let result = builder().set(&DECOY_HEAVY_LENGTH_MIN, 2000).set(&DECOY_LENGTH_MAX, 1400).build();
    assert!(result.is_err());
}

// Test: explicit override takes precedence over environment variable.
#[test]
fn test_explicit_override_beats_env_var() {
    let env_name = TIMEOUT_RTT_FACTOR.name;
    let original = env::var(env_name).ok();

    // SAFETY: test-only, single-threaded access to this env var.
    unsafe { env::set_var(env_name, "7.5") };
    let settings = builder().set(&TIMEOUT_RTT_FACTOR, 3.0).build().expect("settings should build");
    let val: f64 = settings.get(&TIMEOUT_RTT_FACTOR);
    assert_eq!(val, 3.0, "explicit override should take precedence over env var");

    // Cleanup.
    unsafe {
        match original {
            Some(v) => env::set_var(env_name, v),
            None => env::remove_var(env_name),
        }
    }
}