From 305bc5f5a5adeafaf311b0e226240d31836fc153 Mon Sep 17 00:00:00 2001 From: Jon Seager Date: Wed, 4 Feb 2026 16:37:08 +0000 Subject: [PATCH] fix(tests): abort server tasks on test cleanup to prevent zombie processes Store the tokio AbortHandle in TestApp and implement Drop to abort the spawned server task. Previously the JoinHandle was silently dropped, leaving server tasks running indefinitely after tests completed. --- tests/server/helpers.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/server/helpers.rs b/tests/server/helpers.rs index 14bf390..762aefd 100644 --- a/tests/server/helpers.rs +++ b/tests/server/helpers.rs @@ -24,6 +24,7 @@ use brewlog::infrastructure::repositories::tokens::SqlTokenRepository; use brewlog::infrastructure::repositories::users::SqlUserRepository; use reqwest::Client; use tokio::net::TcpListener; +use tokio::task::AbortHandle; pub struct TestApp { pub address: String, @@ -40,6 +41,7 @@ pub struct TestApp { pub auth_token: Option, #[allow(dead_code)] pub mock_server: Option, + server_handle: AbortHandle, } impl TestApp { @@ -48,6 +50,12 @@ impl TestApp { } } +impl Drop for TestApp { + fn drop(&mut self) { + self.server_handle.abort(); + } +} + pub async fn spawn_app() -> TestApp { // Use in-memory SQLite database for testing let database = Database::connect("sqlite::memory:") @@ -146,11 +154,12 @@ async fn spawn_app_inner( let address = format!("http://{}", local_addr); // Spawn the server in a background task - tokio::spawn(async move { + let server_handle = tokio::spawn(async move { axum::serve(listener, app) .await .expect("Server failed to start"); - }); + }) + .abort_handle(); TestApp { address, @@ -162,6 +171,7 @@ async fn spawn_app_inner( token_repo: Some(token_repo), auth_token: None, mock_server, + server_handle, } }