camber 0.1.6

Opinionated async Rust for IO-bound services on top of Tokio
Documentation
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant};

use camber::RuntimeError;

#[camber::test]
async fn race_returns_first_to_complete() {
    let fast = async {
        tokio::time::sleep(Duration::from_millis(10)).await;
        "fast"
    };
    let slow = async {
        tokio::time::sleep(Duration::from_millis(200)).await;
        "slow"
    };

    let start = Instant::now();
    let result = camber::task::race(fast, slow).await;
    let elapsed = start.elapsed();

    assert_eq!(result, "fast");
    assert!(elapsed < Duration::from_millis(100));
}

#[camber::test]
async fn race_all_returns_first_from_vec() {
    let delays = [50, 200, 10, 300, 100];
    let futures: Vec<_> = delays
        .iter()
        .enumerate()
        .map(|(i, &ms)| {
            Box::pin(async move {
                tokio::time::sleep(Duration::from_millis(ms)).await;
                i
            })
        })
        .collect();

    let result = camber::task::race_all(futures).await.unwrap();
    assert_eq!(result, 2);
}

#[camber::test]
async fn race_cancels_loser() {
    let completed = Arc::new(AtomicBool::new(false));
    let completed_inner = Arc::clone(&completed);

    let fast = async {
        tokio::time::sleep(Duration::from_millis(10)).await;
        "done"
    };
    let slow = async move {
        tokio::time::sleep(Duration::from_millis(200)).await;
        completed_inner.store(true, Ordering::Release);
        "slow"
    };

    let _ = camber::task::race(fast, slow).await;
    tokio::time::sleep(Duration::from_millis(100)).await;
    assert!(!completed.load(Ordering::Acquire));
}

#[camber::test]
async fn race_propagates_error_from_first() {
    let fast = async {
        tokio::time::sleep(Duration::from_millis(10)).await;
        Err::<&str, RuntimeError>(RuntimeError::Timeout)
    };
    let slow = async {
        tokio::time::sleep(Duration::from_millis(200)).await;
        Ok("ok")
    };

    let result = camber::task::race(fast, slow).await;
    assert!(matches!(result, Err(RuntimeError::Timeout)));
}

#[camber::test]
async fn race_all_empty_returns_error() {
    let futures: Vec<std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>>> = vec![];
    let result = camber::task::race_all(futures).await;
    assert!(matches!(result, Err(RuntimeError::InvalidArgument(_))));
}