mirror of
https://github.com/LukeMathWalker/zero-to-production.git
synced 2024-12-18 05:56:35 +00:00
Chapter 3 Part 1 - Patches (#4)
* Adjust content encoding. * Refactor assertion. * Add one more test case * Fix assertions. * Fix route. * Stricter assertions * Few fixes. * Formatting
This commit is contained in:
parent
656ee46c09
commit
24dccda00c
9 changed files with 49 additions and 23 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -444,7 +444,6 @@ dependencies = [
|
|||
"config 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"reqwest 0.10.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sqlx 0.4.0-beta.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -25,4 +25,3 @@ chrono = "0.4.15"
|
|||
|
||||
[dev-dependencies]
|
||||
reqwest = { version = "0.10.7", features = ["json"] }
|
||||
serde_json = "1.0.57"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
application_port: 8000
|
||||
database:
|
||||
host: "localhost"
|
||||
port: 5432
|
||||
|
|
|
@ -10,8 +10,6 @@ DB_PASSWORD="${POSTGRES_PASSWORD:=password}"
|
|||
DB_NAME="${POSTGRES_DB:=newsletter}"
|
||||
# Check if a custom port has been set, otherwise default to '5432'
|
||||
DB_PORT="${POSTGRES_PORT:=5432}"
|
||||
# Check if a custom host has been set, otherwise default to 'localhost'
|
||||
DB_HOST="${POSTGRES_HOST:=localhost}"
|
||||
|
||||
# Allow to skip Docker if a dockerized Postgres database is already running
|
||||
if [[ -z "${SKIP_DOCKER}" ]]
|
||||
|
@ -28,14 +26,14 @@ then
|
|||
fi
|
||||
|
||||
# Keep pinging Postgres until it's ready to accept commands
|
||||
until PGPASSWORD="${DB_PASSWORD}" psql -h "${DB_HOST}" -U "${DB_USER}" -p "${DB_PORT}" -d "postgres" -c '\q'; do
|
||||
until PGPASSWORD="${DB_PASSWORD}" psql -h "localhost" -U "${DB_USER}" -p "${DB_PORT}" -d "postgres" -c '\q'; do
|
||||
>&2 echo "Postgres is still unavailable - sleeping"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
>&2 echo "Postgres is up and running on port ${DB_PORT} - running migrations now!"
|
||||
|
||||
export DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}
|
||||
export DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@localhost:${DB_PORT}/${DB_NAME}
|
||||
sqlx database create
|
||||
sqlx migrate run
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#[derive(serde::Deserialize)]
|
||||
pub struct Settings {
|
||||
pub database: DatabaseSettings,
|
||||
pub application_port: u16,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
|
|
|
@ -11,7 +11,8 @@ async fn main() -> Result<(), anyhow::Error> {
|
|||
.await
|
||||
.map_err(anyhow::Error::from)
|
||||
.with_context(|| "Failed to connect to Postgres.")?;
|
||||
let address = TcpListener::bind("127.0.0.1:8000")?;
|
||||
run(address, connection_pool)?.await?;
|
||||
let address = format!("127.0.0.1:{}", configuration.application_port);
|
||||
let listener = TcpListener::bind(address)?;
|
||||
run(listener, connection_pool)?.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ pub struct SubscribeRequest {
|
|||
}
|
||||
|
||||
pub async fn subscribe(
|
||||
payload: web::Json<SubscribeRequest>,
|
||||
payload: web::Form<SubscribeRequest>,
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, HttpResponse> {
|
||||
sqlx::query!(
|
||||
|
|
|
@ -14,6 +14,7 @@ pub async fn spawn_app() -> TestApp {
|
|||
let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port");
|
||||
// We retrieve the port assigned to us by the OS
|
||||
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();
|
||||
|
@ -22,9 +23,8 @@ pub async fn spawn_app() -> TestApp {
|
|||
|
||||
let server = run(listener, connection_pool.clone()).expect("Failed to bind address");
|
||||
let _ = tokio::spawn(server);
|
||||
// We return the application address to the caller!
|
||||
TestApp {
|
||||
address: format!("http://127.0.0.1:{}", port),
|
||||
address,
|
||||
db_pool: connection_pool,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +1,61 @@
|
|||
use crate::helpers::spawn_app;
|
||||
use serde_json::json;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn subscribe_works() {
|
||||
async fn subscribe_accepts_valid_form_data() {
|
||||
// Arrange
|
||||
let app = spawn_app().await;
|
||||
let client = reqwest::Client::new();
|
||||
let email = "myemail@mydomain.com";
|
||||
let name = "my name";
|
||||
let payload = json!({
|
||||
"email": email,
|
||||
"name": name
|
||||
});
|
||||
let body = "name=le%20guin&email=ursula_le_guin%40gmail.com";
|
||||
|
||||
// Act
|
||||
let response = client
|
||||
.post(&format!("{}/subscriptions", &app.address))
|
||||
.json(&payload)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.body(body)
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to execute request.");
|
||||
|
||||
// Assert
|
||||
assert!(response.status().is_success());
|
||||
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);
|
||||
assert_eq!(saved.name, name);
|
||||
assert_eq!(saved.email, "ursula_le_guin@gmail.com");
|
||||
assert_eq!(saved.name, "le guin");
|
||||
}
|
||||
|
||||
#[actix_rt::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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue