Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: Use diffrent ports for tests #1093

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[test-groups]
serial = { max-threads = 1 }

[[profile.default.overrides]]
filter = 'test(bgworker::)'
test-group = 'serial'
97 changes: 59 additions & 38 deletions tests/controller/middlewares.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use axum::http::StatusCode;
use insta::assert_debug_snapshot;
use loco_rs::{controller::middleware, prelude::*, tests_cfg};
use rstest::rstest;
use serial_test::serial;

use crate::infra_cfg;

Expand All @@ -21,7 +20,6 @@ macro_rules! configure_insta {
#[case(true)]
#[case(false)]
#[tokio::test]
#[serial]
async fn panic(#[case] enable: bool) {
configure_insta!();

Expand All @@ -34,8 +32,10 @@ async fn panic(#[case] enable: bool) {
ctx.config.server.middlewares.catch_panic =
Some(middleware::catch_panic::CatchPanic { enable });

let handle = infra_cfg::server::start_with_route(ctx, "/", get(action)).await;
let res = reqwest::get(infra_cfg::server::get_base_url()).await;
let port = infra_cfg::server::get_available_port().await;
let handle =
infra_cfg::server::start_with_route(ctx, "/", get(action), Some(port.clone())).await;
let res = reqwest::get(infra_cfg::server::get_base_url_port(port)).await;

if enable {
let res = res.expect("valid response");
Expand All @@ -54,7 +54,6 @@ async fn panic(#[case] enable: bool) {
#[case(true)]
#[case(false)]
#[tokio::test]
#[serial]
async fn etag(#[case] enable: bool) {
async fn action() -> Result<Response> {
format::render().etag("loco-etag")?.text("content")
Expand All @@ -64,10 +63,12 @@ async fn etag(#[case] enable: bool) {

ctx.config.server.middlewares.etag = Some(middleware::etag::Etag { enable });

let handle = infra_cfg::server::start_with_route(ctx, "/", get(action)).await;
let port = infra_cfg::server::get_available_port().await;
let handle =
infra_cfg::server::start_with_route(ctx, "/", get(action), Some(port.clone())).await;

let res = reqwest::Client::new()
.get(infra_cfg::server::get_base_url())
.get(infra_cfg::server::get_base_url_port(port))
.header("if-none-match", "loco-etag")
.send()
.await
Expand All @@ -86,7 +87,6 @@ async fn etag(#[case] enable: bool) {
#[case(true, "remote: 51.50.51.50")]
#[case(false, "--")]
#[tokio::test]
#[serial]
async fn remote_ip(#[case] enable: bool, #[case] expected: &str) {
#[allow(clippy::items_after_statements)]
async fn action(remote_ip: RemoteIP) -> Result<Response> {
Expand All @@ -100,10 +100,12 @@ async fn remote_ip(#[case] enable: bool, #[case] expected: &str) {
trusted_proxies: Some(vec!["192.1.1.1/8".to_string()]),
});

let handle = infra_cfg::server::start_with_route(ctx, "/", get(action)).await;
let port = infra_cfg::server::get_available_port().await;
let handle =
infra_cfg::server::start_with_route(ctx, "/", get(action), Some(port.clone())).await;

let res = reqwest::Client::new()
.get(infra_cfg::server::get_base_url())
.get(infra_cfg::server::get_base_url_port(port))
.header(
"x-forwarded-for",
reqwest::header::HeaderValue::from_static("51.50.51.50,192.1.1.1"),
Expand All @@ -121,7 +123,6 @@ async fn remote_ip(#[case] enable: bool, #[case] expected: &str) {
#[case(true)]
#[case(false)]
#[tokio::test]
#[serial]
async fn timeout(#[case] enable: bool) {
#[allow(clippy::items_after_statements)]
async fn action() -> Result<Response> {
Expand All @@ -134,9 +135,11 @@ async fn timeout(#[case] enable: bool) {
ctx.config.server.middlewares.timeout_request =
Some(middleware::timeout::TimeOut { enable, timeout: 2 });

let handle = infra_cfg::server::start_with_route(ctx, "/", get(action)).await;
let port = infra_cfg::server::get_available_port().await;
let handle =
infra_cfg::server::start_with_route(ctx, "/", get(action), Some(port.clone())).await;

let res = reqwest::get(infra_cfg::server::get_base_url())
let res = reqwest::get(infra_cfg::server::get_base_url_port(port))
.await
.expect("response");

Expand All @@ -156,7 +159,6 @@ async fn timeout(#[case] enable: bool) {
#[case(true, "with_max_age", None, None, Some(20))]
#[case(false, "disabled", None, None, None)]
#[tokio::test]
#[serial]
async fn cors(
#[case] enable: bool,
#[case] test_name: &str,
Expand All @@ -183,10 +185,14 @@ async fn cors(

ctx.config.server.middlewares.cors = Some(middleware);

let handle = infra_cfg::server::start_from_ctx(ctx).await;
let port = infra_cfg::server::get_available_port().await;
let handle = infra_cfg::server::start_from_ctx(ctx, Some(port.clone())).await;

let res = reqwest::Client::new()
.request(reqwest::Method::OPTIONS, infra_cfg::server::get_base_url())
.request(
reqwest::Method::OPTIONS,
infra_cfg::server::get_base_url_port(port),
)
.send()
.await
.expect("valid response");
Expand Down Expand Up @@ -218,7 +224,6 @@ async fn cors(
#[case(true)]
#[case(false)]
#[tokio::test]
#[serial]
async fn limit_payload(#[case] enable: bool) {
configure_insta!();

Expand All @@ -229,10 +234,14 @@ async fn limit_payload(#[case] enable: bool) {
body_limit: 0x1B,
});

let handle = infra_cfg::server::start_from_ctx(ctx).await;
let port = infra_cfg::server::get_available_port().await;
let handle = infra_cfg::server::start_from_ctx(ctx, Some(port.clone())).await;

let res = reqwest::Client::new()
.request(reqwest::Method::POST, infra_cfg::server::get_base_url())
.request(
reqwest::Method::POST,
infra_cfg::server::get_base_url_port(port),
)
.body("send body".repeat(100))
.send()
.await
Expand All @@ -248,7 +257,6 @@ async fn limit_payload(#[case] enable: bool) {
}

#[tokio::test]
#[serial]
async fn static_assets() {
configure_insta!();

Expand Down Expand Up @@ -279,20 +287,27 @@ async fn static_assets() {
precompressed: false,
});

let handle = infra_cfg::server::start_from_ctx(ctx).await;
let port = infra_cfg::server::get_available_port().await;
let handle = infra_cfg::server::start_from_ctx(ctx, Some(port.clone())).await;

let get_static_html = reqwest::get("http://localhost:5555/static/static.html")
.await
.expect("valid response");
let get_static_html = reqwest::get(format!(
"{}static/static.html",
infra_cfg::server::get_base_url_port(port)
))
.await
.expect("valid response");

assert_eq!(
get_static_html.text().await.expect("text response"),
"<h1>static content</h1>".to_string()
);

let get_fallback = reqwest::get("http://localhost:5555/static/logo.png")
.await
.expect("valid response");
let get_fallback = reqwest::get(format!(
"{}static/logo.png",
infra_cfg::server::get_base_url_port(port)
))
.await
.expect("valid response");

assert_eq!(
get_fallback.text().await.expect("text response"),
Expand All @@ -310,7 +325,6 @@ async fn static_assets() {
"default-src 'self' https".to_string(),
)])))]
#[tokio::test]
#[serial]
async fn secure_headers(
#[case] preset: Option<String>,
#[case] overrides: Option<BTreeMap<String, String>>,
Expand All @@ -327,10 +341,14 @@ async fn secure_headers(
},
);

let handle = infra_cfg::server::start_from_ctx(ctx).await;
let port = infra_cfg::server::get_available_port().await;
let handle = infra_cfg::server::start_from_ctx(ctx, Some(port.clone())).await;

let res = reqwest::Client::new()
.request(reqwest::Method::POST, infra_cfg::server::get_base_url())
.request(
reqwest::Method::POST,
infra_cfg::server::get_base_url_port(port),
)
.send()
.await
.expect("response");
Expand Down Expand Up @@ -360,7 +378,6 @@ async fn secure_headers(
#[case(None, true, None)]
#[case(None, false, Some("text fallback response".to_string()))]
#[tokio::test]
#[serial]
async fn fallback(
#[case] code: Option<StatusCode>,
#[case] file: bool,
Expand Down Expand Up @@ -402,11 +419,15 @@ async fn fallback(

ctx.config.server.middlewares.fallback = Some(fallback_config);

let handle = infra_cfg::server::start_from_ctx(ctx).await;
let port = infra_cfg::server::get_available_port().await;
let handle = infra_cfg::server::start_from_ctx(ctx, Some(port.clone())).await;

let res = reqwest::get(format!("{}not-found", infra_cfg::server::get_base_url()))
.await
.expect("valid response");
let res = reqwest::get(format!(
"{}not-found",
infra_cfg::server::get_base_url_port(port)
))
.await
.expect("valid response");

if let Some(code) = code {
assert_eq!(res.status(), code);
Expand All @@ -430,17 +451,17 @@ async fn fallback(
#[case(None)]
#[case(Some("custom".to_string()))]
#[tokio::test]
#[serial]
async fn powered_by_header(#[case] ident: Option<String>) {
configure_insta!();

let mut ctx: AppContext = tests_cfg::app::get_app_context().await;

ctx.config.server.ident.clone_from(&ident);

let handle = infra_cfg::server::start_from_ctx(ctx).await;
let port = infra_cfg::server::get_available_port().await;
let handle = infra_cfg::server::start_from_ctx(ctx, Some(port.clone())).await;

let res = reqwest::get(infra_cfg::server::get_base_url())
let res = reqwest::get(infra_cfg::server::get_base_url_port(port))
.await
.expect("valid response");

Expand Down
35 changes: 30 additions & 5 deletions tests/infra_cfg/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//! hardcoded ports and bindings.

use loco_rs::{boot, controller::AppRoutes, prelude::*, tests_cfg::db::AppHook};
use tokio::net::TcpListener;

/// The port on which the test server will run.
const TEST_PORT_SERVER: i32 = 5555;
Expand All @@ -15,10 +16,30 @@ const TEST_PORT_SERVER: i32 = 5555;
const TEST_BINDING_SERVER: &str = "localhost";

/// Constructs and returns the base URL used for the test server.
#[allow(dead_code)]
pub fn get_base_url() -> String {
format!("http://{TEST_BINDING_SERVER}:{TEST_PORT_SERVER}/")
}

/// Constructs and returns the base URL used for the test server.
pub fn get_base_url_port(port: i32) -> String {
format!("http://{TEST_BINDING_SERVER}:{port}/")
}

/// Returns the base URL with a unique port number. Increments by 1
/// starting from 59126
pub async fn get_available_port() -> i32 {
let addr = format!("{}:0", TEST_BINDING_SERVER);
let listener = TcpListener::bind(addr)
.await
.expect("Failed to bind to address");
let port = listener
.local_addr()
.expect("Failed to get local address")
.port() as i32;
port
}

/// A simple asynchronous handler for GET requests.
async fn get_action() -> Result<Response> {
format::render().text("text response")
Expand All @@ -34,12 +55,15 @@ async fn post_action(_body: axum::body::Bytes) -> Result<Response> {
///
/// This function spawns a server task that runs asynchronously and sleeps for 2
/// seconds to ensure the server is fully initialized before handling requests.
pub async fn start_from_boot(boot_result: boot::BootResult) -> tokio::task::JoinHandle<()> {
pub async fn start_from_boot(
boot_result: boot::BootResult,
port: Option<i32>,
) -> tokio::task::JoinHandle<()> {
let handle = tokio::spawn(async move {
boot::start::<AppHook>(
boot_result,
boot::ServeParams {
port: TEST_PORT_SERVER,
port: port.unwrap_or(TEST_PORT_SERVER),
binding: TEST_BINDING_SERVER.to_string(),
},
false,
Expand All @@ -54,7 +78,7 @@ pub async fn start_from_boot(boot_result: boot::BootResult) -> tokio::task::Join

/// 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) -> tokio::task::JoinHandle<()> {
pub async fn start_from_ctx(ctx: AppContext, port: Option<i32>) -> tokio::task::JoinHandle<()> {
let app_router = AppRoutes::empty()
.add_route(
Routes::new()
Expand All @@ -70,7 +94,7 @@ pub async fn start_from_ctx(ctx: AppContext) -> tokio::task::JoinHandle<()> {
run_worker: false,
};

start_from_boot(boot).await
start_from_boot(boot, port).await
}

/// Starts the server with a custom route specified by the URI and the HTTP
Expand All @@ -79,6 +103,7 @@ 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))
Expand All @@ -90,5 +115,5 @@ pub async fn start_with_route(
router: Some(app_router),
run_worker: false,
};
start_from_boot(boot).await
start_from_boot(boot, port).await
}
Loading