aro-web 1.0.0

HTTP/ADR layer for the Aro web framework
Documentation
//! Tests for `App::on_startup` hooks and health check.

use aro_web::App;
use aro_web::health;
use axum::body::Body;
use axum::http::Request;
use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
use tower::ServiceExt;

#[tokio::test]
async fn on_startup_hook_not_executed_by_build() {
    // build() does NOT execute hooks (only serve() does).
    let counter = Arc::new(AtomicU32::new(0));
    let counter_clone = counter.clone();

    let _router = App::new()
        .on_startup(move |_state| {
            Box::pin(async move {
                counter_clone.fetch_add(1, Ordering::SeqCst);
                Ok(())
            })
        })
        .build();

    // Hook was NOT executed by build()
    assert_eq!(counter.load(Ordering::SeqCst), 0);
}

#[tokio::test]
async fn build_with_hooks_executes_startup_hooks() {
    let counter = Arc::new(AtomicU32::new(0));
    let c1 = counter.clone();
    let c2 = counter.clone();

    let _router = App::new()
        .on_startup(move |_state| {
            Box::pin(async move {
                c1.fetch_add(1, Ordering::SeqCst);
                Ok(())
            })
        })
        .on_startup(move |_state| {
            Box::pin(async move {
                c2.fetch_add(10, Ordering::SeqCst);
                Ok(())
            })
        })
        .build_with_hooks()
        .await
        .unwrap();

    // Both hooks were executed
    assert_eq!(counter.load(Ordering::SeqCst), 11);
}

#[tokio::test]
async fn build_with_hooks_returns_working_router() {
    let app = App::new()
        .without_defaults()
        .on_startup(|_state| Box::pin(async { Ok(()) }))
        .routes(health::routes())
        .build_with_hooks()
        .await
        .unwrap();

    let req = Request::builder()
        .uri("/health")
        .body(Body::empty())
        .unwrap();

    let resp = app.oneshot(req).await.unwrap();
    assert_eq!(resp.status(), 200);
}

#[tokio::test]
async fn build_with_hooks_propagates_hook_error() {
    let result = App::new()
        .on_startup(|_state| Box::pin(async { Err("startup failed".into()) }))
        .build_with_hooks()
        .await;

    let err = result.unwrap_err();
    assert_eq!(err.to_string(), "startup failed");
}

#[tokio::test]
async fn health_check_returns_200_with_correct_json() {
    let app = App::new()
        .without_defaults()
        .routes(health::routes())
        .build();

    let req = Request::builder()
        .uri("/health")
        .body(Body::empty())
        .unwrap();

    let resp = app.oneshot(req).await.unwrap();
    assert_eq!(resp.status(), 200);

    let body = axum::body::to_bytes(resp.into_body(), usize::MAX)
        .await
        .unwrap();
    let json: serde_json::Value = serde_json::from_slice(&body).unwrap();
    assert_eq!(json["status"], "ok");
}