107 lines
3.6 KiB
Rust
107 lines
3.6 KiB
Rust
//! # Server Infrastructure Utilities for Loco Framework Testing
|
|
//!
|
|
//! This module provides utility functions to test a server using the Loco
|
|
//! framework. It includes helper functions to start the server from different
|
|
//! configurations, such as from boot parameters, application context, or a
|
|
//! custom route. These utilities are designed for test environments and use
|
|
//! hardcoded ports and bindings.
|
|
|
|
use loco_rs::{boot, controller::AppRoutes, prelude::*, tests_cfg::db::AppHook};
|
|
|
|
/// A simple asynchronous handler for GET requests.
|
|
async fn get_action() -> Result<Response> {
|
|
format::render().text("text response")
|
|
}
|
|
|
|
/// A simple asynchronous handler for POST requests.
|
|
async fn post_action(_body: axum::body::Bytes) -> Result<Response> {
|
|
format::render().text("text response")
|
|
}
|
|
|
|
/// Starts the server using the provided Loco [`boot::BootResult`] result.
|
|
/// It uses hardcoded server parameters such as the port and binding address.
|
|
///
|
|
/// After spawning the server task, this polls the bound address until it
|
|
/// accepts a TCP connection (or a bounded timeout elapses), so callers can
|
|
/// issue requests the instant the listener is actually up — no fixed sleep.
|
|
pub async fn start_from_boot(
|
|
boot_result: boot::BootResult,
|
|
port: Option<i32>,
|
|
) -> tokio::task::JoinHandle<()> {
|
|
let port = port.unwrap_or(TEST_PORT_SERVER);
|
|
let handle = tokio::spawn(async move {
|
|
boot::start::<AppHook>(
|
|
boot_result,
|
|
boot::ServeParams {
|
|
port,
|
|
binding: TEST_BINDING_SERVER.to_string(),
|
|
},
|
|
false,
|
|
)
|
|
.await
|
|
.expect("start the server");
|
|
});
|
|
|
|
wait_until_ready(TEST_BINDING_SERVER, port).await;
|
|
handle
|
|
}
|
|
|
|
/// Polls `binding:port` until it accepts a TCP connection so a test can proceed
|
|
/// the moment the server is listening, replacing a fixed-duration sleep. Gives
|
|
/// up after a bounded number of attempts (~5s) rather than hanging a broken
|
|
/// boot forever — a failure then surfaces as the test's own request erroring,
|
|
/// which is the correct signal.
|
|
async fn wait_until_ready(binding: &str, port: i32) {
|
|
let addr = format!("{binding}:{port}");
|
|
for _ in 0..200 {
|
|
if tokio::net::TcpStream::connect(&addr).await.is_ok() {
|
|
return;
|
|
}
|
|
tokio::time::sleep(tokio::time::Duration::from_millis(25)).await;
|
|
}
|
|
}
|
|
|
|
/// Starts the server with a basic route (GET and POST) at the root (`/`), using
|
|
/// the given application context.
|
|
pub async fn start_from_ctx(ctx: AppContext, port: Option<i32>) -> tokio::task::JoinHandle<()> {
|
|
let app_router = AppRoutes::empty()
|
|
.add_route(
|
|
Routes::new()
|
|
.add("/", get(get_action))
|
|
.add("/", post(post_action)),
|
|
)
|
|
.to_router::<AppHook>(ctx.clone(), axum::Router::new())
|
|
.expect("to router");
|
|
|
|
let boot = boot::BootResult {
|
|
app_context: ctx,
|
|
router: Some(app_router),
|
|
worker: None,
|
|
run_scheduler: false,
|
|
};
|
|
|
|
start_from_boot(boot, port).await
|
|
}
|
|
|
|
/// Starts the server with a custom route specified by the URI and the HTTP
|
|
/// method handler.
|
|
pub async fn start_with_route(
|
|
ctx: AppContext,
|
|
uri: &str,
|
|
method: axum::routing::MethodRouter<AppContext>,
|
|
port: Option<i32>,
|
|
) -> tokio::task::JoinHandle<()> {
|
|
let app_router = AppRoutes::empty()
|
|
.add_route(Routes::new().add(uri, method))
|
|
.to_router::<AppHook>(ctx.clone(), axum::Router::new())
|
|
.expect("to router");
|
|
|
|
let boot = boot::BootResult {
|
|
app_context: ctx,
|
|
router: Some(app_router),
|
|
worker: None,
|
|
run_scheduler: false,
|
|
};
|
|
start_from_boot(boot, port).await
|
|
}
|