use noretry::Retryable;

#[tokio::test]
async fn test_can_call() -> anyhow::Result<()> {
    let res = noretry::retry_async(my_func)
        .await
        .map_err(|e| e.into_inner())?;

    assert_eq!("output", res.as_str());

    Ok(())
}

#[tokio::test]
async fn test_can_fail() {
    let res = noretry::builder()
        .with_max_attempts(2)
        .build()
        .run_async(fail)
        .await;

    if let Err(noretry::RetryError::Exhausted {
        attempts,
        last_error,
    }) = res
    {
        assert_eq!(2, attempts);
        assert_eq!("fails", last_error.to_string());
    } else {
        panic!("expected error")
    }
}

async fn my_func() -> Result<String, Retryable<anyhow::Error>> {
    println!("my func was called");

    Ok("output".into())
}

async fn fail() -> Result<String, Retryable<anyhow::Error>> {
    Err(anyhow::anyhow!("fails"))?
}

#[tokio::test]
async fn test_permanent_error() {
    let res = noretry::builder()
        .with_max_attempts(5)
        .build()
        .run_async(fail_permanent)
        .await;

    if let Err(noretry::RetryError::Permanent { error }) = res {
        assert_eq!("not retryable", error.to_string());
    } else {
        panic!("expected permanent error")
    }
}

async fn fail_permanent() -> Result<String, Retryable<anyhow::Error>> {
    Err(anyhow::anyhow!("not retryable")).map_err(Retryable::permanent)?
}

#[test]
fn test_sync_retry() -> anyhow::Result<()> {
    let res = noretry::retry(|| Ok::<_, Retryable<anyhow::Error>>("hello".to_string()))
        .map_err(|e| e.into_inner())?;

    assert_eq!("hello", res.as_str());

    Ok(())
}

#[test]
fn test_sync_retry_exhausted() {
    let res = noretry::builder()
        .with_max_attempts(2)
        .with_initial_delay(std::time::Duration::from_millis(1))
        .build()
        .run(|| -> Result<String, Retryable<anyhow::Error>> {
            Err(anyhow::anyhow!("sync fail"))?
        });

    if let Err(noretry::RetryError::Exhausted {
        attempts,
        last_error,
    }) = res
    {
        assert_eq!(2, attempts);
        assert_eq!("sync fail", last_error.to_string());
    } else {
        panic!("expected exhausted error")
    }
}
