expect_json/expect/ops/
expect_email.rs1use crate::expect_op;
2use crate::expect_op::Context;
3use crate::expect_op::ExpectOp;
4use crate::expect_op::ExpectOpError;
5use crate::expect_op::ExpectOpResult;
6use crate::JsonType;
7use core::str::FromStr;
8use email_address::EmailAddress;
9
10#[expect_op(internal, name = "email")]
16#[derive(Debug, Clone, Default, PartialEq)]
17pub struct ExpectEmail {
18 expected_domain: Option<String>,
19 expected_local_part: Option<String>,
20}
21
22impl ExpectEmail {
23 pub(crate) fn new() -> Self {
24 Self {
25 expected_domain: None,
26 expected_local_part: None,
27 }
28 }
29
30 pub fn local_part<S>(mut self, local_part: S) -> Self
61 where
62 S: Into<String>,
63 {
64 self.expected_local_part = Some(local_part.into());
65 self
66 }
67
68 pub fn domain<S>(mut self, domain: S) -> Self
98 where
99 S: Into<String>,
100 {
101 self.expected_domain = Some(domain.into());
102 self
103 }
104}
105
106impl ExpectOp for ExpectEmail {
107 fn on_string(&self, context: &mut Context, received: &str) -> ExpectOpResult<()> {
108 let email = EmailAddress::from_str(received).map_err(|e| {
109 let error_message = format!("Invalid email address, received '{received}'");
110 ExpectOpError::custom_error(context, self, error_message, e)
111 })?;
112
113 if let Some(expected_local_part) = &self.expected_local_part {
114 if email.local_part() != expected_local_part {
115 return Err(ExpectOpError::custom(
116 context,
117 self,
118 format!(
119 "Local part mismatch, expected '{expected_local_part}', received '{received}'"
120 ),
121 ));
122 }
123 }
124
125 if let Some(expected_domain) = &self.expected_domain {
126 if email.domain() != expected_domain {
127 return Err(ExpectOpError::custom(
128 context,
129 self,
130 format!("Domain mismatch, expected '{expected_domain}', received '{received}'"),
131 ));
132 }
133 }
134
135 Ok(())
136 }
137
138 fn supported_types(&self) -> &'static [JsonType] {
139 &[JsonType::String]
140 }
141}
142
143#[cfg(test)]
144mod test_email {
145 use crate::expect;
146 use crate::expect_json_eq;
147 use pretty_assertions::assert_eq;
148 use serde_json::json;
149
150 #[test]
151 fn it_should_accept_valid_email() {
152 let left = json!("test@example.com");
153 let right = json!(expect::email());
154
155 let output = expect_json_eq(&left, &right);
156 assert!(output.is_ok());
157 }
158
159 #[test]
160 fn it_should_accept_valid_email_with_plus_sign() {
161 let left = json!("test+test@example.com");
162 let right = json!(expect::email());
163
164 let output = expect_json_eq(&left, &right);
165 assert!(output.is_ok());
166 }
167
168 #[test]
169 fn it_should_accept_email_inside_object() {
170 let left = json!({ "name": "Joe", "email": "test@example.com" });
171 let right = json!({
172 "name": "Joe",
173 "email": expect::email(),
174 });
175
176 let output = expect_json_eq(&left, &right);
177 assert!(output.is_ok());
178 }
179
180 #[test]
181 fn it_should_reject_invalid_email() {
182 let left = json!("🦊");
183 let right = json!(expect::email());
184
185 let output = expect_json_eq(&left, &right).unwrap_err().to_string();
186 assert_eq!(
187 output,
188 format!(
189 r#"Json expect::email() error at root:
190 Invalid email address, received '🦊',
191 Missing separator character '@'."#
192 )
193 );
194 }
195}
196
197#[cfg(test)]
198mod test_local_part {
199 use crate::expect;
200 use crate::expect_json_eq;
201 use pretty_assertions::assert_eq;
202 use serde_json::json;
203
204 #[test]
205 fn it_should_accept_valid_local_part() {
206 let left = json!("test@example.com");
207 let right = json!(expect::email().local_part("test"));
208
209 let output = expect_json_eq(&left, &right);
210 assert!(output.is_ok());
211 }
212
213 #[test]
214 fn it_should_reject_invalid_local_part() {
215 let left = json!("test@example.com");
216 let right = json!(expect::email().local_part("🦊"));
217
218 let output = expect_json_eq(&left, &right).unwrap_err().to_string();
219 assert_eq!(
220 output,
221 format!(
222 r#"Json expect::email() error at root:
223 Local part mismatch, expected '🦊', received 'test@example.com'"#
224 )
225 );
226 }
227}
228
229#[cfg(test)]
230mod test_domain {
231 use crate::expect;
232 use crate::expect_json_eq;
233 use pretty_assertions::assert_eq;
234 use serde_json::json;
235
236 #[test]
237 fn it_should_accept_valid_domain() {
238 let left = json!("test@example.com");
239 let right = json!(expect::email().domain("example.com"));
240
241 let output = expect_json_eq(&left, &right);
242 assert!(output.is_ok());
243 }
244
245 #[test]
246 fn it_should_reject_invalid_domain() {
247 let left = json!("test@example.com");
248 let right = json!(expect::email().domain("🦊.fox"));
249
250 let output = expect_json_eq(&left, &right).unwrap_err().to_string();
251 assert_eq!(
252 output,
253 format!(
254 r#"Json expect::email() error at root:
255 Domain mismatch, expected '🦊.fox', received 'test@example.com'"#
256 )
257 );
258 }
259}