1f06f5e66f
Logs in much greater detail using the tracing packages and formatted using Bunyan. Advised to install bunyan on machine for more pretty logs over raw json.
147 lines
4.7 KiB
Rust
147 lines
4.7 KiB
Rust
use std::net::TcpListener;
|
|
use sqlx::{Connection, Executor, PgConnection, PgPool};
|
|
use uuid::Uuid;
|
|
use once_cell::sync::Lazy;
|
|
|
|
use mail_app::startup::run;
|
|
use mail_app::configuration::{get_configuration, DatabaseSettings};
|
|
use mail_app::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);
|
|
};
|
|
});
|
|
|
|
// Create new instance of the application on a random port and return address [`http://localhost:XXXX`]
|
|
async fn spawn_app() -> TestApp {
|
|
Lazy::force(&TRACING);
|
|
|
|
let listener = TcpListener::bind("127.0.0.1:0")
|
|
.expect("Failed to bind to random port");
|
|
let port = listener.local_addr().unwrap().port();
|
|
let address = 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(); // Adjust database string to be random!
|
|
let connection_pool = configure_database(&configuration.database).await;
|
|
|
|
let server = run(listener, connection_pool.clone())
|
|
.expect("Failed to bind address");
|
|
let _ = tokio::spawn(server);
|
|
TestApp {
|
|
address,
|
|
db_pool: connection_pool,
|
|
}
|
|
}
|
|
|
|
pub async fn configure_database(config: &DatabaseSettings) -> PgPool {
|
|
// Create database
|
|
let mut connection = PgConnection::connect(&config.connection_string_without_db())
|
|
.await
|
|
.expect("Failed to connect to Postgres");
|
|
connection
|
|
.execute(&*format!(r#"CREATE DATABASE "{}";"#, config.database_name))
|
|
.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");
|
|
|
|
connection_pool
|
|
}
|
|
|
|
#[actix_rt::test]
|
|
async fn health_check_works() {
|
|
// Arrange
|
|
let app = spawn_app().await;
|
|
let client = reqwest::Client::new();
|
|
|
|
// Act
|
|
let response = client
|
|
.get(&format!("{}/health_check", &app.address))
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request.");
|
|
|
|
// Assert our test
|
|
assert!(response.status().is_success());
|
|
assert_eq!(Some(0), response.content_length());
|
|
}
|
|
|
|
#[actix_rt::test]
|
|
async fn subscribe_returns_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 test
|
|
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, "ursula_le_guin@gmail.com");
|
|
assert_eq!(saved.name, "le guin");
|
|
}
|
|
|
|
#[actix_rt::test]
|
|
async fn subscribe_returns_400_for_missing_form_data() {
|
|
//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(),
|
|
// Customised error message on test failure
|
|
"The API did not fail with 400 Bad Request when the payload was {}.", error_message
|
|
);
|
|
}
|
|
} |