Skip to content

Commit

Permalink
Integrated email sender into main
Browse files Browse the repository at this point in the history
  • Loading branch information
dmcclung committed Mar 9, 2024
1 parent 71f93fd commit 3c58786
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 46 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ anyhow = "1.0.80"
dotenv = "0.15.0"
lettre = "0.11.4"
log = "0.4.20"
once_cell = "1.19.0"
regex = "1.10.3"
serde = { version = "1.0.195", features = ["derive"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
Expand Down
63 changes: 32 additions & 31 deletions src/email.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,13 @@ pub struct Email<'a> {
}

pub trait EmailSender {
fn send(&mut self, port: u16, host: &str, creds: Credentials, message: Message) -> Result<()>;
fn send(&self, port: u16, host: &str, creds: Credentials, message: Message) -> Result<()>;
}

pub struct LettreEmailSender;

impl EmailSender for LettreEmailSender {
fn send(&mut self, port: u16, host: &str, creds: Credentials, message: Message) -> Result<()> {
fn send(&self, port: u16, host: &str, creds: Credentials, message: Message) -> Result<()> {
let mailer = SmtpTransport::relay(host)?
.port(port)
.credentials(creds)
Expand All @@ -120,11 +120,11 @@ impl EmailSender for LettreEmailSender {

pub struct EmailService<'a, T: EmailSender> {
config: SmtpConfig,
email_sender: &'a mut T,
email_sender: &'a T,
}

impl<'a, T: EmailSender> EmailService<'a, T> {
pub fn new(config: SmtpConfig, email_sender: &'a mut T) -> Self {
pub fn new(config: SmtpConfig, email_sender: &'a T) -> Self {
Self {
config,
email_sender,
Expand Down Expand Up @@ -175,9 +175,35 @@ mod tests {
use fake::Fake;
use lettre::Message;
use std::env::{remove_var, set_var};
use std::sync::{Arc, Mutex};

use super::{Email, EmailSender, EmailService, SmtpConfig};

pub struct MockEmailSender {
pub sent_messages: Arc<Mutex<Vec<Message>>>,
}

impl MockEmailSender {
fn new() -> Self {
Self {
sent_messages: Arc::new(Mutex::new(Vec::new())),
}
}
}

impl EmailSender for MockEmailSender {
fn send(
&self,
_port: u16,
_host: &str,
_creds: lettre::transport::smtp::authentication::Credentials,
message: lettre::Message,
) -> Result<()> {
self.sent_messages.lock().unwrap().push(message);
Ok(())
}
}

#[test]
fn smtp_config_from_env() {
let hostname = generate_hostname();
Expand Down Expand Up @@ -213,31 +239,6 @@ mod tests {
format!("smtp.{}.{}", domain, domain_suffix)
}

struct MockEmailSender {
sent_messages: Vec<Message>,
}

impl MockEmailSender {
fn new() -> Self {
Self {
sent_messages: Vec::new(),
}
}
}

impl EmailSender for MockEmailSender {
fn send(
&mut self,
_port: u16,
_host: &str,
_creds: lettre::transport::smtp::authentication::Credentials,
message: lettre::Message,
) -> Result<()> {
self.sent_messages.push(message);
Ok(())
}
}

#[test]
fn send_valid_email() {
let smtp_config = SmtpConfig::new(
Expand All @@ -247,7 +248,7 @@ mod tests {
Password(8..16).fake(),
SafeEmail().fake(),
);
let email_sender = &mut MockEmailSender::new();
let email_sender = &MockEmailSender::new();

let to: String = SafeEmail().fake();
let from: String = SafeEmail().fake();
Expand All @@ -268,6 +269,6 @@ mod tests {
let res = email_service.send_email(email);

assert_ok!(res);
assert_eq!(1, email_sender.sent_messages.len());
assert_eq!(1, email_sender.sent_messages.lock().unwrap().len());
}
}
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ async fn main() -> Result<()> {
let config = Config::new();
let addr = format!("[::]:{}", config.port);

static mut email_sender: LettreEmailSender = LettreEmailSender {};
static EMAIL_SENDER: LettreEmailSender = LettreEmailSender {};

let email_service = EmailService::new(config.smtp_config.clone(), &mut email_sender);
let email_service = EmailService::new(config.smtp_config.clone(), &EMAIL_SENDER);

let app = Application::build(&config, addr, email_service).await?;
app.run_until_stopped().await?;
Expand Down
28 changes: 15 additions & 13 deletions tests/api/utils.rs
Original file line number Diff line number Diff line change
@@ -1,45 +1,47 @@
//! tests/api/utils.rs

use anyhow::Result;
use std::sync::{Arc, Mutex};

use anyhow::Result;
use lettre::Message;
use once_cell::sync::Lazy;
use reqwest::Response;
use sqlx::postgres::PgPoolOptions;
use sqlx::{Pool, Postgres};
use zero2prod::app::Application;
use zero2prod::config::Config;
use zero2prod::email::{EmailSender, EmailService};

pub struct TestApp {
address: String,
pool: Pool<Postgres>,
}

struct MockEmailSender {
sent_messages: Vec<Message>,
pub struct MockEmailSender {
pub sent_messages: Arc<Mutex<Vec<Message>>>,
}

impl MockEmailSender {
fn new() -> Self {
Self {
sent_messages: Vec::new(),
sent_messages: Arc::new(Mutex::new(Vec::new())),
}
}
}

impl EmailSender for MockEmailSender {
fn send(
&mut self,
&self,
_port: u16,
_host: &str,
_creds: lettre::transport::smtp::authentication::Credentials,
message: lettre::Message,
) -> Result<()> {
self.sent_messages.push(message);
self.sent_messages.lock().unwrap().push(message);
Ok(())
}
}

pub struct TestApp {
address: String,
pool: Pool<Postgres>,
}

impl TestApp {
pub fn address(&self) -> &str {
&self.address
Expand Down Expand Up @@ -82,8 +84,8 @@ impl TestApp {
pub async fn spawn_app() -> Result<TestApp> {
let config = Config::new();

let email_sender = &mut MockEmailSender::new();
let email_service = EmailService::new(config.smtp_config.clone(), email_sender);
static EMAIL_SENDER: Lazy<MockEmailSender> = Lazy::new(|| MockEmailSender::new());
let email_service = EmailService::new(config.smtp_config.clone(), &*EMAIL_SENDER);

let app = Application::build(&config, "127.0.0.1:0".into(), email_service).await?;
let address = format!("http://127.0.0.1:{}", app.port());
Expand Down

0 comments on commit 3c58786

Please sign in to comment.