1mod drivers;
2mod mail;
3
4pub mod config;
5pub mod error;
6pub mod errors;
7pub mod mailable;
8
9#[cfg(feature = "axum")]
10pub mod mail_layer;
11
12#[cfg(feature = "queue")]
13pub mod queue_job;
14
15pub use config::MailConfig;
16pub use error::MailError;
17pub use mail::{global_config, set_global_config, Mail};
18pub use mailable::Mailable;
19
20#[cfg(feature = "markdown")]
21pub use mailable::MarkdownMailable;
22
23#[cfg(feature = "axum")]
24pub use mail_layer::MailLayer;
25
26#[cfg(test)]
29mod tests {
30 use crate::{Mail, MailConfig, MailError, Mailable};
31
32 struct Welcome {
33 name: String,
34 }
35
36 impl Mailable for Welcome {
37 fn subject(&self) -> &str {
38 "Welcome!"
39 }
40 fn body(&self) -> String {
41 format!("Hi, {}!", self.name)
42 }
43 fn html_body(&self) -> Option<String> {
44 Some(format!("<p>Hi, <b>{}</b>!</p>", self.name))
45 }
46 }
47
48 fn log_config() -> MailConfig {
49 MailConfig {
50 driver: "log".into(),
51 ..MailConfig::default()
52 }
53 }
54
55 #[test]
56 fn mailable_subject_and_body() {
57 let m = Welcome {
58 name: "Alice".into(),
59 };
60 assert_eq!(m.subject(), "Welcome!");
61 assert!(m.body().contains("Alice"));
62 assert!(m.html_body().unwrap().contains("Alice"));
63 }
64
65 #[test]
66 fn mail_config_defaults() {
67 let c = MailConfig::default();
68 assert_eq!(c.driver, "log");
69 assert_eq!(c.from_address, "noreply@rok-app.com");
70 assert_eq!(c.smtp_port, 587);
71 assert_eq!(c.smtp_encryption, "starttls");
72 assert!(c.postmark_api_key.is_none());
73 assert!(c.resend_api_key.is_none());
74 }
75
76 #[tokio::test]
77 async fn send_with_log_driver_succeeds() {
78 let result = Mail::send_with(
79 Welcome { name: "Bob".into() },
80 "bob@example.com",
81 &log_config(),
82 )
83 .await;
84 assert!(result.is_ok());
85 }
86
87 #[tokio::test]
88 async fn send_with_unknown_driver_returns_error() {
89 let config = MailConfig {
90 driver: "carrier_pigeon".into(),
91 ..MailConfig::default()
92 };
93 let result = Mail::send_with(
94 Welcome {
95 name: "Carol".into(),
96 },
97 "carol@example.com",
98 &config,
99 )
100 .await;
101 assert!(matches!(result, Err(MailError::UnknownDriver(d)) if d == "carrier_pigeon"));
102 }
103
104 #[tokio::test]
105 async fn send_without_layer_returns_not_configured() {
106 let result = Mail::send(
107 Welcome {
108 name: "Dave".into(),
109 },
110 "dave@example.com",
111 )
112 .await;
113 assert!(matches!(result, Err(MailError::NotConfigured)));
114 }
115}