smirrors 0.1.0

Automatic mirror list updater for Linux distributions
Documentation
use smirrors::core::{Mirror, MirrorTester};
use url::Url;

#[tokio::test]
async fn test_mirror_creation() {
    let url = Url::parse("https://mirror.example.com/repo").unwrap();
    let mirror = Mirror::new(url.clone());

    assert_eq!(mirror.url, url);
    assert_eq!(mirror.is_static, false);
    assert!(mirror.score.is_none());
}

#[tokio::test]
async fn test_static_mirror_creation() {
    let url = Url::parse("https://mirror.example.com/repo").unwrap();
    let mirror = Mirror::new_static(url);

    assert_eq!(mirror.is_static, true);
}

#[tokio::test]
async fn test_mirror_score_calculation() {
    let url = Url::parse("https://mirror.example.com/repo").unwrap();
    let mut mirror = Mirror::new(url);

    mirror.speed = Some(50.0);
    mirror.latency = Some(std::time::Duration::from_millis(100));
    mirror.calculate_score(0.7, 0.3);

    assert!(mirror.score.is_some());
    let score = mirror.score.unwrap();

    // Speed score: 50/100 = 0.5
    // Latency score: 1 - (100/1000) = 0.9
    // Total: 0.5 * 0.7 + 0.9 * 0.3 = 0.35 + 0.27 = 0.62
    assert!((score - 0.62).abs() < 0.01);
}

#[tokio::test]
async fn test_mirror_hostname_extraction() {
    let url = Url::parse("https://mirrors.kernel.org/debian/").unwrap();
    let mirror = Mirror::new(url);

    assert_eq!(mirror.hostname(), "mirrors.kernel.org");
    assert_eq!(mirror.domain(), "kernel.org");
}

#[tokio::test]
async fn test_mirror_ordering_by_score() {
    let url1 = Url::parse("https://mirror1.example.com/repo").unwrap();
    let url2 = Url::parse("https://mirror2.example.com/repo").unwrap();

    let mut mirror1 = Mirror::new(url1);
    let mut mirror2 = Mirror::new(url2);

    mirror1.score = Some(0.8);
    mirror2.score = Some(0.6);

    // Higher score should be "less than" for ascending sort
    assert!(mirror1 < mirror2);
}

#[tokio::test]
async fn test_mirror_needs_retest() {
    let url = Url::parse("https://mirror.example.com/repo").unwrap();
    let mut mirror = Mirror::new(url);

    // Should need retest if never tested
    assert!(mirror.needs_retest(3600));

    // Should not need retest if recently tested
    mirror.last_tested = Some(chrono::Utc::now());
    assert!(!mirror.needs_retest(3600));

    // Should need retest if tested long ago
    mirror.last_tested = Some(
        chrono::Utc::now() - chrono::Duration::hours(2)
    );
    assert!(mirror.needs_retest(3600));
}

#[tokio::test]
async fn test_mirror_formatting() {
    let url = Url::parse("https://mirror.example.com/repo").unwrap();
    let mut mirror = Mirror::new(url);

    mirror.speed = Some(50.5);
    mirror.latency = Some(std::time::Duration::from_millis(150));
    mirror.score = Some(0.75);

    assert_eq!(mirror.format_speed(), "50.50 MB/s");
    assert_eq!(mirror.format_latency(), "150 ms");
    assert_eq!(mirror.format_score(), "75.0%");
}

#[tokio::test]
async fn test_mirror_tester_creation() {
    let tester = MirrorTester::new(
        1024 * 1024,  // 1MB
        10,           // 10s timeout
        5,            // 5 concurrent
        0.7,          // speed weight
        0.3,          // latency weight
        3,            // 3 retries
    );

    assert!(tester.is_ok());
}

#[tokio::test]
async fn test_filter_successful_results() {
    use smirrors::core::TestResult;

    let url1 = Url::parse("https://mirror1.example.com").unwrap();
    let url2 = Url::parse("https://mirror2.example.com").unwrap();

    let mirror1 = Mirror::new(url1);
    let mirror2 = Mirror::new(url2);

    let results = vec![
        TestResult::success(
            mirror1,
            50.0,
            std::time::Duration::from_millis(100),
            0.8,
        ),
        TestResult::failure(mirror2, "Error".to_string()),
    ];

    let successful = MirrorTester::filter_successful(results);
    assert_eq!(successful.len(), 1);
    assert!(successful[0].success);
}

#[tokio::test]
async fn test_sort_results_by_score() {
    use smirrors::core::TestResult;

    let url1 = Url::parse("https://mirror1.example.com").unwrap();
    let url2 = Url::parse("https://mirror2.example.com").unwrap();
    let url3 = Url::parse("https://mirror3.example.com").unwrap();

    let mirror1 = Mirror::new(url1);
    let mirror2 = Mirror::new(url2);
    let mirror3 = Mirror::new(url3);

    let results = vec![
        TestResult::success(
            mirror1,
            30.0,
            std::time::Duration::from_millis(200),
            0.5,
        ),
        TestResult::success(
            mirror2,
            60.0,
            std::time::Duration::from_millis(50),
            0.9,
        ),
        TestResult::success(
            mirror3,
            45.0,
            std::time::Duration::from_millis(100),
            0.7,
        ),
    ];

    let sorted = MirrorTester::sort_by_score(results);

    // Should be sorted descending by score
    assert_eq!(sorted[0].score.unwrap(), 0.9);
    assert_eq!(sorted[1].score.unwrap(), 0.7);
    assert_eq!(sorted[2].score.unwrap(), 0.5);
}

#[tokio::test]
async fn test_extract_mirrors_from_results() {
    use smirrors::core::TestResult;

    let url1 = Url::parse("https://mirror1.example.com").unwrap();
    let url2 = Url::parse("https://mirror2.example.com").unwrap();

    let mirror1 = Mirror::new(url1.clone());
    let mirror2 = Mirror::new(url2.clone());

    let results = vec![
        TestResult::success(
            mirror1,
            50.0,
            std::time::Duration::from_millis(100),
            0.8,
        ),
        TestResult::success(
            mirror2,
            60.0,
            std::time::Duration::from_millis(80),
            0.85,
        ),
    ];

    let mirrors = MirrorTester::extract_mirrors(results);

    assert_eq!(mirrors.len(), 2);
    assert_eq!(mirrors[0].url, url1);
    assert_eq!(mirrors[1].url, url2);
}