/// End-to-end tests that bind a real TCP listener and exercise the HTTP layer. /// /// These tests differ from the router-level `oneshot` tests in that they verify /// the full Axum `serve` path: `TcpListener` binding, HTTP framing, and response /// serialization. Each test spins up a server on an ephemeral port, issues a /// real HTTP request via reqwest, then shuts down. mod common; use std::net::SocketAddr; use tokio::net::TcpListener; /// Bind a listener on an ephemeral port, spawn the server in the background, /// and return the bound address as a string. /// /// Uses `into_make_service_with_connect_info` so that the governor rate /// limiter can extract `ConnectInfo` from real TCP connections. async fn bind_and_serve() -> String { let app = common::setup_app().await; let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let addr = listener.local_addr().unwrap(); tokio::spawn(async move { axum::serve( listener, app.into_make_service_with_connect_info::(), ) .await .unwrap(); }); format!("http://{addr}") } #[tokio::test] async fn health_endpoint_responds_over_real_tcp() { let base = bind_and_serve().await; let resp = reqwest::get(format!("{base}/api/v1/health")) .await .expect("health request failed"); assert_eq!(resp.status(), 200); let body: serde_json::Value = resp.json().await.expect("body not JSON"); assert!( body["status"].is_string(), "expected health response to contain 'status' field" ); } #[tokio::test] async fn media_list_responds_over_real_tcp() { // setup_app has authentication_disabled=true; verifies the router serves // real TCP traffic, not just in-process oneshot requests. let base = bind_and_serve().await; let resp = reqwest::get(format!("{base}/api/v1/media")) .await .expect("media list request failed"); assert_eq!(resp.status(), 200); } #[tokio::test] async fn unknown_route_returns_404_over_real_tcp() { let base = bind_and_serve().await; let resp = reqwest::get(format!("{base}/api/v1/nonexistent-route")) .await .expect("request failed"); assert_eq!(resp.status(), 404); }