test: make email client tests better
This commit is contained in:
parent
170f34738b
commit
b61f02c03f
5 changed files with 81 additions and 39 deletions
|
|
@ -10,3 +10,4 @@ email_client:
|
||||||
base_url: "localhost"
|
base_url: "localhost"
|
||||||
sender_email: "test@fmail.com"
|
sender_email: "test@fmail.com"
|
||||||
authorization_token: "my-secret-token"
|
authorization_token: "my-secret-token"
|
||||||
|
timeout_milliseconds: 10000
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,16 @@ pub struct EmailClientSettings {
|
||||||
pub base_url: String,
|
pub base_url: String,
|
||||||
pub sender_email: String,
|
pub sender_email: String,
|
||||||
pub authorization_token: Secret<String>,
|
pub authorization_token: Secret<String>,
|
||||||
|
pub timeout_milliseconds: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EmailClientSettings {
|
impl EmailClientSettings {
|
||||||
pub fn sender(&self) -> Result<SubscriberEmail, String> {
|
pub fn sender(&self) -> Result<SubscriberEmail, String> {
|
||||||
SubscriberEmail::parse(self.sender_email.clone())
|
SubscriberEmail::parse(self.sender_email.clone())
|
||||||
}
|
}
|
||||||
|
pub fn timeout(&self) -> std::time::Duration {
|
||||||
|
std::time::Duration::from_millis(self.timeout_milliseconds)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,11 @@ impl EmailClient {
|
||||||
base_url: String,
|
base_url: String,
|
||||||
sender: SubscriberEmail,
|
sender: SubscriberEmail,
|
||||||
authorization_token: Secret<String>,
|
authorization_token: Secret<String>,
|
||||||
|
timeout: std::time::Duration,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let http_client = Client::builder().timeout(timeout).build().unwrap();
|
||||||
Self {
|
Self {
|
||||||
http_client: Client::new(),
|
http_client,
|
||||||
base_url,
|
base_url,
|
||||||
sender,
|
sender,
|
||||||
authorization_token,
|
authorization_token,
|
||||||
|
|
@ -98,38 +100,33 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
/// Generate a random email subject
|
||||||
async fn send_email_succeeds_if_the_server_returns_200() {
|
fn subject() -> String {
|
||||||
// Arrange
|
Sentence(1..2).fake()
|
||||||
let mock_server = MockServer::start().await;
|
}
|
||||||
let sender = SubscriberEmail::parse(SafeEmail().fake()).unwrap();
|
/// Generate a random email content
|
||||||
let email_client = EmailClient::new(mock_server.uri(), sender, Secret::new(Faker.fake()));
|
fn content() -> String {
|
||||||
|
Paragraph(1..10).fake()
|
||||||
let subscriber_email = SubscriberEmail::parse(SafeEmail().fake()).unwrap();
|
}
|
||||||
let subject: String = Sentence(1..2).fake();
|
/// Generate a random subscriber email
|
||||||
let content: String = Paragraph(1..10).fake();
|
fn email() -> SubscriberEmail {
|
||||||
|
SubscriberEmail::parse(SafeEmail().fake()).unwrap()
|
||||||
Mock::given(any())
|
}
|
||||||
.respond_with(ResponseTemplate::new(200))
|
/// Get a test instance of `EmailClient`.
|
||||||
.expect(1)
|
fn email_client(base_url: String) -> EmailClient {
|
||||||
.mount(&mock_server)
|
EmailClient::new(
|
||||||
.await;
|
base_url,
|
||||||
|
email(),
|
||||||
// Act
|
Secret::new(Faker.fake()),
|
||||||
let outcome = email_client
|
std::time::Duration::from_millis(200),
|
||||||
.send_email(subscriber_email, &subject, &content, &content)
|
)
|
||||||
.await;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assert_ok!(outcome);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn send_email_sends_the_expected_request() {
|
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 email_client = email_client(mock_server.uri());
|
||||||
let email_client = EmailClient::new(mock_server.uri(), sender, Secret::new(Faker.fake()));
|
|
||||||
|
|
||||||
Mock::given(header_exists("X-Postmark-Server-Token"))
|
Mock::given(header_exists("X-Postmark-Server-Token"))
|
||||||
.and(header("Content-Type", "application/json"))
|
.and(header("Content-Type", "application/json"))
|
||||||
|
|
@ -141,29 +138,41 @@ mod tests {
|
||||||
.mount(&mock_server)
|
.mount(&mock_server)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let subscriber_email = SubscriberEmail::parse(SafeEmail().fake()).unwrap();
|
|
||||||
let subject: String = Sentence(1..2).fake();
|
|
||||||
let content: String = Paragraph(1..10).fake();
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
let _ = email_client
|
let _ = email_client
|
||||||
.send_email(subscriber_email, &subject, &content, &content)
|
.send_email(email(), &subject(), &content(), &content())
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// Mock expectations are checked on drop
|
// Mock expectations are checked on drop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn send_email_succeeds_if_the_server_returns_200() {
|
||||||
|
// Arrange
|
||||||
|
let mock_server = MockServer::start().await;
|
||||||
|
let email_client = email_client(mock_server.uri());
|
||||||
|
|
||||||
|
Mock::given(any())
|
||||||
|
.respond_with(ResponseTemplate::new(200))
|
||||||
|
.expect(1)
|
||||||
|
.mount(&mock_server)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
let outcome = email_client
|
||||||
|
.send_email(email(), &subject(), &content(), &content())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assert_ok!(outcome);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn send_email_fails_if_the_server_returns_500() {
|
async fn send_email_fails_if_the_server_returns_500() {
|
||||||
// Arrange
|
// Arrange
|
||||||
let mock_server = MockServer::start().await;
|
let mock_server = MockServer::start().await;
|
||||||
let sender = SubscriberEmail::parse(SafeEmail().fake()).unwrap();
|
let email_client = email_client(mock_server.uri());
|
||||||
let email_client = EmailClient::new(mock_server.uri(), sender, Secret::new(Faker.fake()));
|
|
||||||
|
|
||||||
let subscriber_email = SubscriberEmail::parse(SafeEmail().fake()).unwrap();
|
|
||||||
let subject: String = Sentence(1..2).fake();
|
|
||||||
let content: String = Paragraph(1..10).fake();
|
|
||||||
|
|
||||||
Mock::given(any())
|
Mock::given(any())
|
||||||
// Not a 200 anymore!
|
// Not a 200 anymore!
|
||||||
|
|
@ -174,7 +183,31 @@ mod tests {
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
let outcome = email_client
|
let outcome = email_client
|
||||||
.send_email(subscriber_email, &subject, &content, &content)
|
.send_email(email(), &subject(), &content(), &content())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assert_err!(outcome);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn send_email_times_out_if_the_server_takes_too_long() {
|
||||||
|
// Arrange
|
||||||
|
let mock_server = MockServer::start().await;
|
||||||
|
let email_client = email_client(mock_server.uri());
|
||||||
|
|
||||||
|
let response = ResponseTemplate::new(200)
|
||||||
|
// 3 minutes!
|
||||||
|
.set_delay(std::time::Duration::from_secs(180));
|
||||||
|
Mock::given(any())
|
||||||
|
.respond_with(response)
|
||||||
|
.expect(1)
|
||||||
|
.mount(&mock_server)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
let outcome = email_client
|
||||||
|
.send_email(email(), &subject(), &content(), &content())
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,12 @@ async fn main() {
|
||||||
.email_client
|
.email_client
|
||||||
.sender()
|
.sender()
|
||||||
.expect("Invalid sender email address.");
|
.expect("Invalid sender email address.");
|
||||||
|
let timeout = configuration.email_client.timeout();
|
||||||
let email_client = EmailClient::new(
|
let email_client = EmailClient::new(
|
||||||
configuration.email_client.base_url,
|
configuration.email_client.base_url,
|
||||||
sender_email,
|
sender_email,
|
||||||
configuration.email_client.authorization_token,
|
configuration.email_client.authorization_token,
|
||||||
|
timeout,
|
||||||
);
|
);
|
||||||
startup::run(listener, connection_pool, email_client)
|
startup::run(listener, connection_pool, email_client)
|
||||||
.await
|
.await
|
||||||
|
|
|
||||||
|
|
@ -153,10 +153,12 @@ async fn spawn_app() -> TestApp {
|
||||||
.email_client
|
.email_client
|
||||||
.sender()
|
.sender()
|
||||||
.expect("Invalid sender email address.");
|
.expect("Invalid sender email address.");
|
||||||
|
let timeout = configuration.email_client.timeout();
|
||||||
let email_client = EmailClient::new(
|
let email_client = EmailClient::new(
|
||||||
configuration.email_client.base_url,
|
configuration.email_client.base_url,
|
||||||
sender_email,
|
sender_email,
|
||||||
configuration.email_client.authorization_token,
|
configuration.email_client.authorization_token,
|
||||||
|
timeout,
|
||||||
);
|
);
|
||||||
|
|
||||||
let service = learn_axum::startup::app(connection_pool.clone(), email_client);
|
let service = learn_axum::startup::app(connection_pool.clone(), email_client);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue