diff --git a/Cargo.lock b/Cargo.lock index 934b992..007d1e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -754,6 +754,7 @@ dependencies = [ "actix-rt", "actix-web", "reqwest", + "serde", "tokio", ] diff --git a/Cargo.toml b/Cargo.toml index 248babf..79e4218 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ path = "src/lib.rs" [dependencies] actix-web = "4.0.0-beta.8" +serde = { version = "1", features = ["derive"]} [dev-dependencies] actix-rt = "2" diff --git a/src/lib.rs b/src/lib.rs index 65a9ad8..2ac5a1e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,12 +6,23 @@ async fn health_check() -> HttpResponse { HttpResponse::Ok().finish() } +#[derive(serde::Deserialize)] +struct FormData { + email: String, + name: String +} + +async fn subscribe(_form: web::Form) -> HttpResponse { + HttpResponse::Ok().finish() +} + pub fn run(listener: TcpListener) -> Result { let server = HttpServer::new(|| { - App::new() - .route("/health_check", web::get().to(health_check)) - }) - .listen(listener)? - .run(); + App::new() + .route("/health_check", web::get().to(health_check)) + .route("/subscriptions", web::post().to(subscribe)) + }) + .listen(listener)? + .run(); Ok(server) } \ No newline at end of file diff --git a/tests/health_check.rs b/tests/health_check.rs index 617aee1..704e879 100644 --- a/tests/health_check.rs +++ b/tests/health_check.rs @@ -1,5 +1,17 @@ use std::net::TcpListener; +// Create new instance of the application on a random port and return address [`http://localhost:XXXX`] +fn spawn_app() -> String { + 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 = mail_app::run(listener).expect("Failed to bind address"); + + let _ = tokio::spawn(server); + + format!("http://127.0.0.1:{}", port) +} + #[actix_rt::test] async fn health_check_works() { // Arrange @@ -19,13 +31,53 @@ async fn health_check_works() { assert_eq!(Some(0), response.content_length()); } -fn spawn_app() -> String { - let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind to random port"); - let port = listener.local_addr().unwrap().port(); +#[actix_rt::test] +async fn subscribe_returns_200_for_valid_form_data() { + // Arrange + let app_address = spawn_app(); + let client = reqwest::Client::new(); + let body = "name=le%20guin&email=usrula_le_guin%40gmail.com"; - let server = mail_app::run(listener).expect("Failed to bind address"); + // 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."); - let _ = tokio::spawn(server); + // Assert test + assert_eq!(200, response.status().as_u16()); +} - format!("http://127.0.0.1:{}", port) +#[actix_rt::test] +async fn subscrib_returns_400_for_missing_form_data() { + //Arrange + let app_address = spawn_app(); + 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 + ); + } } \ No newline at end of file