From de7c1a5fa3bfc1372352c45cb4acd9b0a1814914 Mon Sep 17 00:00:00 2001 From: Nick Bland Date: Tue, 12 Mar 2024 20:32:48 +1000 Subject: [PATCH] Chapter 3.10 ~ Finish Chapter 3 + Adjusted tests to accomodate a new DB with every run + Add in a no db option to database --- src/configuration.rs | 7 +++++ tests/health_check.rs | 71 +++++++++++++++++++++++++++++++------------ 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/src/configuration.rs b/src/configuration.rs index 6ef033e..f8efebf 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -31,4 +31,11 @@ impl DatabaseSettings { self.username, self.password, self.host, self.port, self.database_name ) } + + pub fn connection_string_without_db(&self) -> String { + format!( + "postgres://{}:{}@{}:{}", + self.username, self.password, self.host, self.port + ) + } } diff --git a/tests/health_check.rs b/tests/health_check.rs index dbc7d16..fcf97ae 100644 --- a/tests/health_check.rs +++ b/tests/health_check.rs @@ -1,27 +1,65 @@ -use mail_app::configuration::get_configuration; +use mail_app::configuration::{get_configuration, DatabaseSettings}; use mail_app::startup::run; -use sqlx::{Connection, PgConnection}; +use sqlx::{Executor, PgPool}; use std::net::TcpListener; +use uuid::Uuid; -fn spawn_app() -> String { +pub struct TestApp { + pub address: String, + pub db_pool: PgPool, +} + +async fn spawn_app() -> TestApp { let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind to random port."); let port = listener.local_addr().unwrap().port(); - let server = run(listener).expect("Failed to bind address"); - // Launch in background - let _spawn = tokio::spawn(server); + let address = format!("http://127.0.0.1:{}", port); - format!("http://127.0.0.1:{}", port) + let mut configuration = get_configuration().expect("Failed to read configuration"); + configuration.database.database_name = Uuid::new_v4().to_string(); + let connection_pool = configure_database(&configuration.database).await; + + let server = run(listener, connection_pool.clone()).expect("Failed to bind address"); + // Launch in background + let _ = tokio::spawn(server); + + TestApp { + address, + db_pool: connection_pool, + } +} + +pub async fn configure_database(config: &DatabaseSettings) -> PgPool { + // Create Database + let connection = PgPool::connect(&config.connection_string_without_db()) + .await + .expect("Failed to connect to Postgres."); + connection + .execute(format!(r#"CREATE DATABASE "{}";"#, config.database_name).as_str()) + .await + .expect("Failed to create database."); + + // Migrate Database + let connection_pool = PgPool::connect(&config.connection_string()) + .await + .expect("Failed to connect to Postgres."); + sqlx::migrate!("./migrations") + .run(&connection_pool) + .await + .expect("Failed to migrate the database."); + + // Return connection pool + connection_pool } #[tokio::test] async fn health_check_works() { // Arrange - let address = spawn_app(); + let app = spawn_app().await; let client = reqwest::Client::new(); // Act let response = client - .get(&format!("{}/health_check", &address)) + .get(&format!("{}/health_check", &app.address)) .send() .await .expect("Failed to execute request"); @@ -34,18 +72,13 @@ async fn health_check_works() { #[tokio::test] async fn subscribe_returns_a_200_for_valid_form_data() { // Arrange - let app_address = spawn_app(); - let configuration = get_configuration().expect("Failed to get config"); - let connection_string = configuration.database.connection_string(); - let mut connection = PgConnection::connect(&connection_string) - .await - .expect("Failed to connect to Postgres Database."); + let app = spawn_app().await; let client = reqwest::Client::new(); // Act let body = "name=le%20guin&email=ursula_le_guin%40gmail.com"; let response = client - .post(&format!("{}/subscriptions", &app_address)) + .post(&format!("{}/subscriptions", &app.address)) .header("Content-Type", "application/x-www-form-urlencoded") .body(body) .send() @@ -56,7 +89,7 @@ async fn subscribe_returns_a_200_for_valid_form_data() { assert_eq!(200, response.status().as_u16()); let saved = sqlx::query!("SELECT email, name FROM subscriptions",) - .fetch_one(&mut connection) + .fetch_one(&app.db_pool) .await .expect("Failed to fetch saved subscription."); @@ -67,7 +100,7 @@ async fn subscribe_returns_a_200_for_valid_form_data() { #[tokio::test] async fn subscribe_returns_a_400_when_data_is_missing() { // Arrange - let app_address = spawn_app(); + let app = spawn_app().await; let client = reqwest::Client::new(); let test_cases = vec![ ("name=le%20guin", "missing email"), @@ -78,7 +111,7 @@ async fn subscribe_returns_a_400_when_data_is_missing() { for (invalid_body, error_message) in test_cases { // Act let response = client - .post(&format!("{}/subscriptions", &app_address)) + .post(&format!("{}/subscriptions", &app.address)) .header("Content-Type", "application/x-www-form-urlencoded") .body(invalid_body) .send()