test: improve email client unit test

This commit is contained in:
Sandro Eiler 2024-02-27 15:00:57 +01:00
parent c021b989c2
commit e07e4dfe53
3 changed files with 34 additions and 6 deletions

View file

@ -67,3 +67,4 @@ quickcheck = "1.0.3"
quickcheck_macros = "1.0.0" quickcheck_macros = "1.0.0"
rand = "0.8.5" rand = "0.8.5"
wiremock = "0.6.0" wiremock = "0.6.0"
serde_json = "1"

View file

@ -40,7 +40,7 @@ impl EmailClient {
html_body: html_content.to_owned(), html_body: html_content.to_owned(),
text_body: text_content.to_owned(), text_body: text_content.to_owned(),
}; };
let builder = self let _builder = self
.http_client .http_client
.post(&url) .post(&url)
.header( .header(
@ -54,6 +54,7 @@ impl EmailClient {
} }
} }
#[derive(serde::Serialize)] #[derive(serde::Serialize)]
#[serde(rename_all = "PascalCase")]
struct SendEmailRequest { struct SendEmailRequest {
from: String, from: String,
to: String, to: String,
@ -71,17 +72,43 @@ mod tests {
use fake::faker::lorem::en::{Paragraph, Sentence}; use fake::faker::lorem::en::{Paragraph, Sentence};
use fake::{Fake, Faker}; use fake::{Fake, Faker};
use secrecy::Secret; use secrecy::Secret;
use wiremock::matchers::any; use wiremock::matchers::{header, header_exists, method, path};
use wiremock::{Mock, MockServer, ResponseTemplate}; use wiremock::{Mock, MockServer, Request, ResponseTemplate};
struct SendEmailBodyMatcher;
impl wiremock::Match for SendEmailBodyMatcher {
fn matches(&self, request: &Request) -> bool {
// Try to parse the body as a JSON value
let result: Result<serde_json::Value, _> = serde_json::from_slice(&request.body);
if let Ok(body) = result {
dbg!(&body);
// Check that all the mandatory fields are populated
// without inspecting the field values
body.get("From").is_some()
&& body.get("To").is_some()
&& body.get("Subject").is_some()
&& body.get("HtmlBody").is_some()
&& body.get("TextBody").is_some()
} else {
// If parsing failed, do not match the request
false
}
}
}
#[tokio::test] #[tokio::test]
async fn send_email_fires_a_request_to_base_url() { async fn send_email_sends_the_expected_request() {
// Arrange // Arrange
let mock_server = MockServer::start().await; let mock_server = MockServer::start().await;
let sender = SubscriberEmail::parse(SafeEmail().fake()).unwrap(); let sender = SubscriberEmail::parse(SafeEmail().fake()).unwrap();
let email_client = EmailClient::new(mock_server.uri(), sender, Secret::new(Faker.fake())); let email_client = EmailClient::new(mock_server.uri(), sender, Secret::new(Faker.fake()));
Mock::given(any()) Mock::given(header_exists("X-Postmark-Server-Token"))
.and(header("Content-Type", "application/json"))
.and(path("/email"))
.and(method("POST"))
.and(SendEmailBodyMatcher)
.respond_with(ResponseTemplate::new(200)) .respond_with(ResponseTemplate::new(200))
.expect(1) .expect(1)
.mount(&mock_server) .mount(&mock_server)
@ -97,5 +124,6 @@ mod tests {
.await; .await;
// Assert // Assert
// Mock expectations are checked on drop
} }
} }

View file

@ -1,6 +1,5 @@
use crate::domain::SubscriberEmail; use crate::domain::SubscriberEmail;
use crate::domain::{NewSubscriber, SubscriberName}; use crate::domain::{NewSubscriber, SubscriberName};
use crate::email_client;
use crate::startup::AppState; use crate::startup::AppState;
use axum::extract::State; use axum::extract::State;
use axum::routing::post; use axum::routing::post;