feat(test): use random ports for testing

- port is assigned by the OS.
This commit is contained in:
minhtrannhat 2024-03-29 16:20:53 -04:00
parent cd677039b1
commit 83915acbd1
Signed by: minhtrannhat
GPG Key ID: E13CFA85C53F8062
3 changed files with 20 additions and 7 deletions

View File

@ -1,16 +1,17 @@
use actix_web::{web, App, HttpResponse, HttpServer}; use actix_web::{web, App, HttpResponse, HttpServer};
use actix_web::dev::Server; use actix_web::dev::Server;
use std::net::TcpListener;
async fn healthcheck_route() -> HttpResponse { async fn healthcheck_route() -> HttpResponse {
return HttpResponse::Ok().finish() return HttpResponse::Ok().finish()
} }
pub fn run() -> Result<Server, std::io::Error>{ pub fn run(listener: TcpListener) -> Result<Server, std::io::Error>{
let server = HttpServer::new(||{ let server = HttpServer::new(||{
App::new() App::new()
.route("/health_check", web::get().to(healthcheck_route)) .route("/health_check", web::get().to(healthcheck_route))
}) })
.bind("127.0.0.1:8000")? .listen(listener)?
.run(); .run();
Ok(server) Ok(server)

View File

@ -1,8 +1,12 @@
use std::net::TcpListener;
use email_newsletter_api::run; use email_newsletter_api::run;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), std::io::Error>{ async fn main() -> Result<(), std::io::Error>{
let listener = TcpListener::bind("127.0.0.1:8000").expect("Failed to bind to port 8000");
// Move the error up the call stack // Move the error up the call stack
// otherwise await for the HttpServer // otherwise await for the HttpServer
run()?.await run(listener)?.await
} }

View File

@ -1,24 +1,32 @@
use std::{fmt::format, net::TcpListener};
// tokio spins up a new async runtime every time // tokio spins up a new async runtime every time
// at the beginning of each test case and shutdown at // at the beginning of each test case and shutdown at
// the end of each test case // the end of each test case
// the spawn_app() function therefore only survives as long as the runtime // the spawn_app() function therefore only survives as long as the runtime
#[tokio::test] #[tokio::test]
async fn health_check_works(){ async fn health_check_works(){
spawn_app(); let server_address = spawn_app();
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let response = client.get("http://127.0.0.1:8000/health_check").send().await.expect("Failed to execute health_check request."); let response = client.get(&format!("{}/health_check", &server_address)).send().await.expect("Failed to execute health_check request.");
assert!(response.status().is_success()); assert!(response.status().is_success());
assert_eq!(Some(0), response.content_length()); assert_eq!(Some(0), response.content_length());
} }
fn spawn_app() { fn spawn_app() -> String {
let server = email_newsletter_api::run().expect("Failed to bind address"); let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind to a random port");
let port = listener.local_addr().unwrap().port();
let server = email_newsletter_api::run(listener).expect("Failed to bind address");
// run() returns an instance of HttpServer that will run forever. // run() returns an instance of HttpServer that will run forever.
// We don't want this behavior // We don't want this behavior
// Therefore we want to spawn our server, run our test logic // Therefore we want to spawn our server, run our test logic
// and then tear down the entire test suite // and then tear down the entire test suite
let _ = tokio::spawn(server); let _ = tokio::spawn(server);
format!("http://127.0.0.1:{}", port)
} }