@@ -15,9 +15,14 @@ impl EmailClient {
15
15
base_url : String ,
16
16
sender : SubscriberEmail ,
17
17
authorization_token : Secret < String > ,
18
+ timeout : std:: time:: Duration ,
18
19
) -> Self {
20
+ let http_client = Client :: builder ( )
21
+ . timeout ( timeout)
22
+ . build ( )
23
+ . unwrap ( ) ;
19
24
Self {
20
- http_client : Client :: new ( ) ,
25
+ http_client,
21
26
base_url,
22
27
sender,
23
28
authorization_token,
@@ -51,14 +56,15 @@ impl EmailClient {
51
56
)
52
57
. json ( & request_body)
53
58
. send ( )
54
- . await ?;
59
+ . await ?
60
+ . error_for_status ( ) ?;
55
61
Ok ( ( ) )
56
62
}
57
63
}
58
64
59
65
#[ derive( serde:: Serialize ) ]
60
66
#[ serde( rename_all = "PascalCase" ) ]
61
- pub struct SendEmailRequest < ' a > {
67
+ pub struct SendEmailRequest < ' a > {
62
68
pub from : & ' a str ,
63
69
pub to : & ' a str ,
64
70
pub subject : & ' a str ,
@@ -69,14 +75,15 @@ pub struct SendEmailRequest <'a> {
69
75
#[ cfg( test) ]
70
76
mod tests {
71
77
use crate :: { domain:: SubscriberEmail , email_client:: EmailClient } ;
78
+ use claim:: { assert_err, assert_ok} ;
72
79
use fake:: {
73
80
faker:: internet:: en:: SafeEmail ,
74
81
faker:: lorem:: en:: { Paragraph , Sentence } ,
75
82
Fake , Faker ,
76
83
} ;
77
84
use secrecy:: Secret ;
78
85
use wiremock:: {
79
- matchers:: { header, header_exists, method, path} ,
86
+ matchers:: { any , header, header_exists, method, path} ,
80
87
Mock , MockServer , Request , ResponseTemplate ,
81
88
} ;
82
89
@@ -98,11 +105,31 @@ mod tests {
98
105
}
99
106
}
100
107
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
+
101
129
#[ tokio:: test]
102
130
async fn send_email_sends_the_expected_request ( ) {
103
131
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 ( ) ) ;
106
133
107
134
Mock :: given ( header_exists ( "X-Postmark-Server-Token" ) )
108
135
. and ( header ( "Content-Type" , "application/json" ) )
@@ -114,12 +141,92 @@ mod tests {
114
141
. mount ( & mock_server)
115
142
. await ;
116
143
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 ( ) ;
120
145
121
146
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)
123
167
. 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) ;
124
231
}
125
232
}
0 commit comments