From 2fff50ec0af8fc109199af779c39cc2ebdd3be03 Mon Sep 17 00:00:00 2001 From: Fergus Molloy Date: Fri, 18 Jul 2025 15:10:54 +0100 Subject: [PATCH] fix tests spinning up server --- Cargo.lock | 1 + Cargo.toml | 2 ++ src/lib.rs | 1 + tests/common/mod.rs | 16 ---------- tests/endpoint_test.rs | 63 +++++++++++++++++++++++++++++++++++++++ tests/healthcheck_test.rs | 14 --------- 6 files changed, 67 insertions(+), 30 deletions(-) create mode 100644 tests/endpoint_test.rs delete mode 100644 tests/healthcheck_test.rs diff --git a/Cargo.lock b/Cargo.lock index 37270b6..1c5bcd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -856,6 +856,7 @@ version = "0.1.0" dependencies = [ "axum", "clap", + "http-body-util", "reqwest", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index b0c981b..9caa677 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] axum = "0.8.4" clap = { version = "4.5.41", features = ["derive"] } +http-body-util = "0.1.3" serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" tokio = { version = "1.46.1", features = ["full"] } @@ -16,6 +17,7 @@ tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } [dev-dependencies] reqwest = { version = "0.12.22", features = ["json"] } +tower = { version = "0.5.2", features = ["util"] } [lib] path = "src/lib.rs" diff --git a/src/lib.rs b/src/lib.rs index 1b44a06..5fca529 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ mod routes; +pub use routes::app; use std::sync::mpsc; diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 12ddc04..e69de29 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,16 +0,0 @@ -use std::net::TcpListener; - -pub fn spawn_app() -> String { - let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port"); - let _ = listener.set_nonblocking(true); - // We retrieve the port assigned to us by the OS - let port = listener.local_addr().unwrap().port(); - let server = nuchat::run( - tokio::net::TcpListener::from_std(listener) - .expect("Failed to convert from_std to tokio listener"), - ) - .expect("Failed to bind address"); - tokio::spawn(server.into_future()); - // We return the application address to the caller! - format!("http://127.0.0.1:{port}") -} diff --git a/tests/endpoint_test.rs b/tests/endpoint_test.rs new file mode 100644 index 0000000..145b7f8 --- /dev/null +++ b/tests/endpoint_test.rs @@ -0,0 +1,63 @@ +use axum::{ + body::Body, + http::{Request, StatusCode}, +}; +use http_body_util::BodyExt; // for `collect` +use tower::ServiceExt; // for `oneshot` + +#[tokio::test] +async fn healthcheck_returns_healthy() { + let (app, _) = nuchat::app(); + + let response = app + .oneshot( + Request::builder() + .uri("/api/healthcheck") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + + let body = response.into_body().collect().await.unwrap().to_bytes(); + assert_eq!(&body[..], br#"{"healthy":true}"#); +} + +#[tokio::test] +async fn root_returns_spa() { + let (app, _) = nuchat::app(); + + let response = app + .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap()) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + + let body = response.into_body().collect().await.unwrap().to_bytes(); + let body = String::from_utf8(body.into_iter().collect()).unwrap(); + assert!(body.starts_with("")); +} + +#[tokio::test] +async fn unkown_fallsback_to_spa() { + let (app, _) = nuchat::app(); + + let response = app + .oneshot( + Request::builder() + .uri("/asdfasdfa") // unknown url + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::NOT_FOUND); + + let body = response.into_body().collect().await.unwrap().to_bytes(); + let body = String::from_utf8(body.into_iter().collect()).unwrap(); + assert!(body.starts_with("")); +} diff --git a/tests/healthcheck_test.rs b/tests/healthcheck_test.rs deleted file mode 100644 index d56e4aa..0000000 --- a/tests/healthcheck_test.rs +++ /dev/null @@ -1,14 +0,0 @@ -mod common; -use common::spawn_app; -use reqwest::StatusCode; - -#[tokio::test] -async fn test_healthcheck() -> reqwest::Result<()> { - let address = spawn_app(); - let response = reqwest::get(format!("{address}/api/healthcheck")).await?; - - assert_eq!(response.status(), StatusCode::OK); - assert_eq!(response.text().await?, r#"{"healthy":true}"#); - - Ok(()) -}