use anyhow::{Context, Result}; use futures_util::FutureExt; use std::net::SocketAddr; use tokio::{sync::oneshot, task::JoinHandle}; use tracing::info; use zero_to_axum::ZeroToAxum; pub struct TestServer { server_task_handle: JoinHandle<()>, shutdown_handle: oneshot::Sender<()>, addr: SocketAddr, } impl TestServer { pub async fn spawn() -> TestServer { info!("start server"); let mut server = ZeroToAxum::serve("[::]:0".parse().unwrap()); let shutdown_handle = server.take_shutdown_handle().unwrap(); let addr = server.local_addr(); let server_task_handle = tokio::spawn(server.map(|res| res.unwrap())); info!("server spawned"); TestServer { server_task_handle, shutdown_handle, addr, } } /// format a URL for the given path pub fn url(&self, path: &str) -> String { format!("http://{}{path}", self.addr) } /// Request a graceful shutdown and then wait for shutdown to complete pub async fn shutdown(self) -> Result<()> { self.shutdown_handle.send(()).ok(); self.server_task_handle .await .context("wait for server shutdown") } }