-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor tests to re-use application build process
- Loading branch information
Showing
8 changed files
with
267 additions
and
229 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
use crate::helpers::spawn_app; | ||
|
||
#[tokio::test] | ||
async fn health_check_works() { | ||
// Arrange | ||
let app = spawn_app().await; | ||
let client = reqwest::Client::new(); | ||
|
||
// Act | ||
let response = client | ||
// Use the returned application address | ||
.get(&format!("{}/health_check", &app.address)) | ||
.send() | ||
.await | ||
.expect("Failed to execute request."); | ||
|
||
// Assert | ||
assert!(response.status().is_success()); | ||
assert_eq!(Some(0), response.content_length()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
use once_cell::sync::Lazy; | ||
use sqlx::{Connection, Executor, PgConnection, PgPool}; | ||
use sqlx::postgres::PgPoolOptions; | ||
use uuid::Uuid; | ||
use zero2prod::configuration::{DatabaseSettings, get_configuration}; | ||
use zero2prod::startup::{Application, get_connection_pool}; | ||
use zero2prod::telemetry::{get_subscriber, init_subscriber}; | ||
|
||
pub struct TestApp { | ||
pub address: String, | ||
pub db_pool: PgPool, | ||
} | ||
|
||
static TRACING: Lazy<()> = Lazy::new(|| { | ||
let default_filter_level = "info".to_string(); | ||
let subscriber_name = "test".to_string(); | ||
if std::env::var("TEST_LOG").is_ok() { | ||
let subscriber = get_subscriber(subscriber_name, default_filter_level, std::io::stdout); | ||
init_subscriber(subscriber); | ||
} else { | ||
let subscriber = get_subscriber(subscriber_name, default_filter_level, std::io::sink); | ||
init_subscriber(subscriber); | ||
}; | ||
}); | ||
|
||
// Launch our application in the background | ||
pub async fn spawn_app() -> TestApp { | ||
// The first time `initialize` is invoked the code in `TRACING` is executed. | ||
// All other invocations will instead skip execution. | ||
Lazy::force(&TRACING); | ||
|
||
let configuration = { | ||
let mut c = get_configuration().expect("Failed to read configuration."); | ||
// Use a different database for each test case | ||
c.database.name = Uuid::new_v4().to_string(); | ||
// Use a random OS-assigned port | ||
c.application.port = 0; | ||
c | ||
}; | ||
|
||
// Create and migrate the database | ||
configure_database(&configuration.database).await; | ||
|
||
// Launch the application as a background task | ||
let application = Application::build(configuration.clone()).await.expect("Failed to build application."); | ||
let address = format!("http://127.0.0.1:{}", application.port()); | ||
let _ = tokio::spawn(application.run_until_stopped()); | ||
TestApp { | ||
address, | ||
db_pool: get_connection_pool(&configuration.database), | ||
} | ||
} | ||
|
||
async fn configure_database(config: &DatabaseSettings) -> PgPool { | ||
// Create database | ||
let mut connection = | ||
PgConnection::connect_with(&config.without_db()) | ||
.await | ||
.expect("Failed to connect to Postgres"); | ||
connection | ||
.execute(&*format!(r#"CREATE DATABASE "{}";"#, config.name)) | ||
.await | ||
.expect("Failed to create database."); | ||
|
||
// Migrate database | ||
let connection_pool = PgPoolOptions::new().connect_lazy_with(config.with_db()); | ||
sqlx::migrate!("./migrations") | ||
.run(&connection_pool) | ||
.await | ||
.expect("Failed to migrate the database"); | ||
|
||
connection_pool | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
mod helpers; | ||
mod health_check; | ||
mod subscriptions; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
use crate::helpers::spawn_app; | ||
|
||
#[tokio::test] | ||
async fn subscribe_returns_a_200_for_valid_form_data() { | ||
// Arrange | ||
let app = spawn_app().await; | ||
let client = reqwest::Client::new(); | ||
let body = "name=le%20guin&email=ursula_le_guin%40gmail.com"; | ||
|
||
// Act | ||
let response = client | ||
.post(&format!("{}/subscriptions", &app.address)) | ||
.header("Content-Type", "application/x-www-form-urlencoded") | ||
.body(body) | ||
.send() | ||
.await | ||
.expect("Failed to execute request."); | ||
|
||
// Assert | ||
assert_eq!(200, response.status().as_u16()); | ||
|
||
let saved = sqlx::query!("SELECT email, name FROM subscriptions",) | ||
.fetch_one(&app.db_pool) | ||
.await | ||
.expect("Failed to fetch saved subscription."); | ||
|
||
assert_eq!(saved.email, "[email protected]"); | ||
assert_eq!(saved.name, "le guin"); | ||
} | ||
|
||
#[tokio::test] | ||
async fn subscribe_returns_a_400_when_data_is_missing() { | ||
// Arrange | ||
let app = spawn_app().await; | ||
let client = reqwest::Client::new(); | ||
let test_cases = vec![ | ||
("name=le%20guin", "missing the email"), | ||
("email=ursula_le_guin%40gmail.com", "missing the name"), | ||
("", "missing both name and email"), | ||
]; | ||
|
||
for (invalid_body, error_message) in test_cases { | ||
// Act | ||
let response = client | ||
.post(&format!("{}/subscriptions", &app.address)) | ||
.header("Content-Type", "application/x-www-form-urlencoded") | ||
.body(invalid_body) | ||
.send() | ||
.await | ||
.expect("Failed to execute request."); | ||
|
||
// Assert | ||
assert_eq!( | ||
400, | ||
response.status().as_u16(), | ||
// Additional customised error message on test failure | ||
"The API did not fail with 400 Bad Request when the payload was {}.", | ||
error_message | ||
); | ||
} | ||
} | ||
|
||
|
||
#[tokio::test] | ||
async fn subscribe_returns_a_400_when_fields_are_present_but_invalid() { | ||
// Arrange | ||
let app = spawn_app().await; | ||
let client = reqwest::Client::new(); | ||
let test_cases = vec![ | ||
("name=&email=ursula_le_guin%40gmail.com", "empty name"), | ||
("name=Ursula&email=", "empty email"), | ||
("name=&email=", "empty name and email"), | ||
]; | ||
|
||
for (body, description) in test_cases { | ||
// Act | ||
let response = client | ||
.post(&format!("{}/subscriptions", &app.address)) | ||
.header("Content-Type", "application/x-www-form-urlencoded") | ||
.body(body) | ||
.send() | ||
.await | ||
.expect("Failed to execute request."); | ||
|
||
// Assert | ||
assert_eq!( | ||
400, | ||
response.status().as_u16(), | ||
"The API returned a 400 when the payload was {}.", | ||
description | ||
); | ||
} | ||
} |
Oops, something went wrong.