@@ -15,9 +15,14 @@ impl EmailClient {
1515 base_url : String ,
1616 sender : SubscriberEmail ,
1717 authorization_token : Secret < String > ,
18+ timeout : std:: time:: Duration ,
1819 ) -> Self {
20+ let http_client = Client :: builder ( )
21+ . timeout ( timeout)
22+ . build ( )
23+ . unwrap ( ) ;
1924 Self {
20- http_client : Client :: new ( ) ,
25+ http_client,
2126 base_url,
2227 sender,
2328 authorization_token,
@@ -51,14 +56,15 @@ impl EmailClient {
5156 )
5257 . json ( & request_body)
5358 . send ( )
54- . await ?;
59+ . await ?
60+ . error_for_status ( ) ?;
5561 Ok ( ( ) )
5662 }
5763}
5864
5965#[ derive( serde:: Serialize ) ]
6066#[ serde( rename_all = "PascalCase" ) ]
61- pub struct SendEmailRequest < ' a > {
67+ pub struct SendEmailRequest < ' a > {
6268 pub from : & ' a str ,
6369 pub to : & ' a str ,
6470 pub subject : & ' a str ,
@@ -69,14 +75,15 @@ pub struct SendEmailRequest <'a> {
6975#[ cfg( test) ]
7076mod tests {
7177 use crate :: { domain:: SubscriberEmail , email_client:: EmailClient } ;
78+ use claim:: { assert_err, assert_ok} ;
7279 use fake:: {
7380 faker:: internet:: en:: SafeEmail ,
7481 faker:: lorem:: en:: { Paragraph , Sentence } ,
7582 Fake , Faker ,
7683 } ;
7784 use secrecy:: Secret ;
7885 use wiremock:: {
79- matchers:: { header, header_exists, method, path} ,
86+ matchers:: { any , header, header_exists, method, path} ,
8087 Mock , MockServer , Request , ResponseTemplate ,
8188 } ;
8289
@@ -98,11 +105,31 @@ mod tests {
98105 }
99106 }
100107
108+ fn generate_subject ( ) -> String {
109+ Sentence ( 1 ..2 ) . fake ( )
110+ }
111+
112+ fn generate_content ( ) -> String {
113+ Paragraph ( 1 ..10 ) . fake ( )
114+ }
115+
116+ fn get_email_client ( base_url : String ) -> EmailClient {
117+ EmailClient :: new (
118+ base_url,
119+ SubscriberEmail :: parse ( SafeEmail ( ) . fake ( ) ) . unwrap ( ) ,
120+ Secret :: new ( Faker . fake ( ) ) ,
121+ std:: time:: Duration :: from_millis ( 200 ) ,
122+ )
123+ }
124+
125+ fn get_email ( ) -> SubscriberEmail {
126+ SubscriberEmail :: parse ( SafeEmail ( ) . fake ( ) ) . unwrap ( )
127+ }
128+
101129 #[ tokio:: test]
102130 async fn send_email_sends_the_expected_request ( ) {
103131 let mock_server = MockServer :: start ( ) . await ;
104- let sender = SubscriberEmail :: parse ( SafeEmail ( ) . fake ( ) ) . unwrap ( ) ;
105- let email_client = EmailClient :: new ( mock_server. uri ( ) , sender, Secret :: new ( Faker . fake ( ) ) ) ;
132+ let email_client = get_email_client ( mock_server. uri ( ) ) ;
106133
107134 Mock :: given ( header_exists ( "X-Postmark-Server-Token" ) )
108135 . and ( header ( "Content-Type" , "application/json" ) )
@@ -114,12 +141,92 @@ mod tests {
114141 . mount ( & mock_server)
115142 . await ;
116143
117- let recipient = SubscriberEmail :: parse ( SafeEmail ( ) . fake ( ) ) . unwrap ( ) ;
118- let subject: String = Sentence ( 1 ..2 ) . fake ( ) ;
119- let content: String = Paragraph ( 1 ..10 ) . fake ( ) ;
144+ let recipient = get_email ( ) ;
120145
121146 let _ = email_client
122- . send_email ( recipient, & subject, & content, & content)
147+ . send_email (
148+ recipient,
149+ & generate_subject ( ) ,
150+ & generate_content ( ) ,
151+ & generate_content ( ) ,
152+ )
153+ . await ;
154+ }
155+
156+ #[ tokio:: test]
157+ async fn send_email_succeeds_if_server_returns_200 ( ) {
158+ let mock_server = MockServer :: start ( ) . await ;
159+ let email_client = get_email_client ( mock_server. uri ( ) ) ;
160+
161+ let subscriber_email = get_email ( ) ;
162+
163+ Mock :: given ( any ( ) )
164+ . respond_with ( ResponseTemplate :: new ( 200 ) )
165+ . expect ( 1 )
166+ . mount ( & mock_server)
123167 . await ;
168+
169+ let outcome = email_client
170+ . send_email (
171+ subscriber_email,
172+ & generate_subject ( ) ,
173+ & generate_content ( ) ,
174+ & generate_content ( ) ,
175+ )
176+ . await ;
177+
178+ assert_ok ! ( outcome) ;
179+ }
180+
181+ #[ tokio:: test]
182+ async fn send_email_fails_if_server_returns_500 ( ) {
183+ let mock_server = MockServer :: start ( ) . await ;
184+ let email_client = get_email_client ( mock_server. uri ( ) ) ;
185+
186+ let subscriber_email = get_email ( ) ;
187+
188+ Mock :: given ( any ( ) )
189+ . respond_with ( ResponseTemplate :: new ( 500 ) )
190+ . expect ( 1 )
191+ . mount ( & mock_server)
192+ . await ;
193+
194+ let outcome = email_client
195+ . send_email (
196+ subscriber_email,
197+ & generate_subject ( ) ,
198+ & generate_content ( ) ,
199+ & generate_content ( ) ,
200+ )
201+ . await ;
202+
203+ assert_err ! ( outcome) ;
204+ }
205+
206+ #[ tokio:: test]
207+ async fn send_email_times_out_if_server_takes_too_long ( ) {
208+ let mock_server = MockServer :: start ( ) . await ;
209+ let email_client = get_email_client ( mock_server. uri ( ) ) ;
210+
211+ let subscriber_email = get_email ( ) ;
212+
213+ let response = ResponseTemplate :: new ( 200 ) . set_delay ( std:: time:: Duration :: from_secs ( 180 ) ) ;
214+
215+ Mock :: given ( any ( ) )
216+ . respond_with ( response)
217+ . expect ( 1 )
218+ . mount ( & mock_server)
219+ . await ;
220+
221+ let outcome = email_client
222+ . send_email (
223+ subscriber_email,
224+ & generate_subject ( ) ,
225+ & generate_content ( ) ,
226+ & generate_content ( ) ,
227+ )
228+ . await ;
229+
230+ assert_err ! ( outcome) ;
124231 }
125232}
0 commit comments