expect_json/expect/ops/expect_string/
expect_string.rs

1use crate::expect::ops::expect_string::ExpectStringSubOp;
2use crate::expect_core::expect_op;
3use crate::expect_core::Context;
4use crate::expect_core::ExpectOp;
5use crate::expect_core::ExpectOpResult;
6use crate::JsonType;
7
8#[expect_op(internal, name = "string")]
9#[derive(Debug, Clone, Default, PartialEq)]
10pub struct ExpectString {
11    sub_ops: Vec<ExpectStringSubOp>,
12}
13
14impl ExpectString {
15    pub(crate) fn new() -> Self {
16        Self { sub_ops: vec![] }
17    }
18
19    pub fn empty(mut self) -> Self {
20        self.sub_ops.push(ExpectStringSubOp::Empty);
21        self
22    }
23
24    pub fn not_empty(mut self) -> Self {
25        self.sub_ops.push(ExpectStringSubOp::NotEmpty);
26        self
27    }
28
29    pub fn len(mut self, len: usize) -> Self {
30        self.sub_ops.push(ExpectStringSubOp::Len(len));
31        self
32    }
33
34    pub fn min_len(mut self, min_len: usize) -> Self {
35        self.sub_ops.push(ExpectStringSubOp::MinLen(min_len));
36        self
37    }
38
39    pub fn max_len(mut self, max_len: usize) -> Self {
40        self.sub_ops.push(ExpectStringSubOp::MaxLen(max_len));
41        self
42    }
43
44    ///
45    /// Expect a string containing a subset of the string given.
46    ///
47    /// ```rust
48    /// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
49    /// #
50    /// # use axum::Router;
51    /// # use axum::extract::Json;
52    /// # use axum::routing::get;
53    /// # use axum_test::TestServer;
54    /// # use serde_json::json;
55    /// #
56    /// # let server = TestServer::new(Router::new())?;
57    /// #
58    /// use axum_test::expect_json;
59    ///
60    /// let server = TestServer::new(Router::new())?;
61    ///
62    /// server.get(&"/user")
63    ///     .await
64    ///     .assert_json(&json!({
65    ///         "name": expect_json::string().contains("apples"),
66    ///     }));
67    /// #
68    /// # Ok(()) }
69    /// ```
70    pub fn contains<S>(mut self, expected_sub_string: S) -> Self
71    where
72        S: Into<String>,
73    {
74        self.sub_ops
75            .push(ExpectStringSubOp::Contains(expected_sub_string.into()));
76        self
77    }
78
79    ///
80    /// Expect a string matching the regex given.
81    ///
82    /// ```rust
83    /// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
84    /// #
85    /// # use axum::Router;
86    /// # use axum::extract::Json;
87    /// # use axum::routing::get;
88    /// # use axum_test::TestServer;
89    /// # use serde_json::json;
90    /// #
91    /// # let server = TestServer::new(Router::new())?;
92    /// #
93    ///
94    /// use expect_json::expect;
95    /// let server = TestServer::new(Router::new())?;
96    ///
97    /// server.get(&"/user")
98    ///     .await
99    ///     .assert_json(&json!({
100    ///         "email": expect::string().matches_regex(r#"\w+@(?:\w+\.)+\w+"#),
101    ///     }));
102    /// #
103    /// # Ok(()) }
104    /// ```
105    pub fn matches_regex<S>(mut self, pattern: S) -> Self
106    where
107        S: Into<String>,
108    {
109        self.sub_ops
110            .push(ExpectStringSubOp::MatchesRegex(pattern.into()));
111        self
112    }
113}
114
115impl ExpectOp for ExpectString {
116    fn on_string(&self, context: &mut Context, received: &str) -> ExpectOpResult<()> {
117        for sub_op in &self.sub_ops {
118            sub_op.on_string(self, context, received)?;
119        }
120
121        Ok(())
122    }
123
124    fn debug_supported_types(&self) -> &'static [JsonType] {
125        &[JsonType::String]
126    }
127}
128
129#[cfg(test)]
130mod test_contains {
131    use crate::expect;
132    use crate::expect_json_eq;
133    use pretty_assertions::assert_eq;
134    use serde_json::json;
135
136    #[test]
137    fn it_should_be_equal_for_identical_strings() {
138        let left = json!("1, 2, 3");
139        let right = json!(expect::string().contains("1, 2, 3"));
140
141        let output = expect_json_eq(&left, &right);
142        assert!(output.is_ok());
143    }
144
145    #[test]
146    fn it_should_be_equal_for_partial_matches_in_middle() {
147        let left = json!("0, 1, 2, 3, 4");
148        let right = json!(expect::string().contains("1, 2, 3"));
149
150        let output = expect_json_eq(&left, &right);
151        assert!(output.is_ok());
152    }
153
154    #[test]
155    fn it_should_be_ok_for_empty_contains() {
156        let left = json!("0, 1, 2, 3, 4, 5");
157        let right = json!(expect::string().contains(""));
158
159        let output = expect_json_eq(&left, &right);
160        assert!(output.is_ok());
161    }
162
163    #[test]
164    fn it_should_error_for_totall_different_values() {
165        let left = json!("1, 2, 3");
166        let right = json!(expect::string().contains("a, b, c"));
167
168        let output = expect_json_eq(&left, &right).unwrap_err().to_string();
169        assert_eq!(
170            output,
171            r#"Json string at root does not contain expected value:
172    expected string to contain "a, b, c", but it was not found.
173    received "1, 2, 3""#
174        );
175    }
176}
177
178#[cfg(test)]
179mod test_empty {
180    use crate::expect;
181    use crate::expect_json_eq;
182    use pretty_assertions::assert_eq;
183    use serde_json::json;
184
185    #[test]
186    fn it_should_pass_when_string_is_empty() {
187        let left = json!("");
188        let right = json!(expect::string().empty());
189
190        let output = expect_json_eq(&left, &right);
191        assert!(output.is_ok(), "assertion error: {output:#?}");
192    }
193
194    #[test]
195    fn it_should_fail_when_string_is_not_empty() {
196        let left = json!("🦊");
197        let right = json!(expect::string().empty());
198
199        let output = expect_json_eq(&left, &right).unwrap_err().to_string();
200        assert_eq!(
201            output,
202            r#"Json expect::string() error at root:
203    expected empty string
204    received "🦊""#
205        );
206    }
207}
208
209#[cfg(test)]
210mod test_not_empty {
211    use crate::expect;
212    use crate::expect_json_eq;
213    use pretty_assertions::assert_eq;
214    use serde_json::json;
215
216    #[test]
217    fn it_should_pass_when_string_is_not_empty() {
218        let left = json!("🦊");
219        let right = json!(expect::string().not_empty());
220
221        let output = expect_json_eq(&left, &right);
222        assert!(output.is_ok(), "assertion error: {output:#?}");
223    }
224
225    #[test]
226    fn it_should_fail_when_string_is_empty() {
227        let left = json!("");
228        let right = json!(expect::string().not_empty());
229
230        let output = expect_json_eq(&left, &right).unwrap_err().to_string();
231        assert_eq!(
232            output,
233            r#"Json expect::string() error at root:
234    expected non-empty string
235    received """#
236        );
237    }
238}
239
240#[cfg(test)]
241mod test_len {
242    use crate::expect;
243    use crate::expect_json_eq;
244    use pretty_assertions::assert_eq;
245    use serde_json::json;
246
247    #[test]
248    fn it_should_pass_when_string_has_same_number_of_characters() {
249        let left = json!("123");
250        let right = json!(expect::string().len(3));
251
252        let output = expect_json_eq(&left, &right);
253        assert!(output.is_ok(), "assertion error: {output:#?}");
254    }
255
256    #[test]
257    fn it_should_fail_when_string_is_too_short() {
258        let left = json!("12");
259        let right = json!(expect::string().len(3));
260
261        let output = expect_json_eq(&left, &right).unwrap_err().to_string();
262        assert_eq!(
263            output,
264            r#"Json expect::string() error at root:
265    expected string to have 3 characters, but it has 2,
266    received "12""#
267        );
268    }
269
270    #[test]
271    fn it_should_fail_when_string_is_too_long() {
272        let left = json!("1234");
273        let right = json!(expect::string().len(3));
274
275        let output = expect_json_eq(&left, &right).unwrap_err().to_string();
276        assert_eq!(
277            output,
278            r#"Json expect::string() error at root:
279    expected string to have 3 characters, but it has 4,
280    received "1234""#
281        );
282    }
283}
284
285#[cfg(test)]
286mod test_min_len {
287    use crate::expect;
288    use crate::expect_json_eq;
289    use pretty_assertions::assert_eq;
290    use serde_json::json;
291
292    #[test]
293    fn it_should_pass_when_string_has_exactly_enough_characters() {
294        let left = json!("123");
295        let right = json!(expect::string().min_len(3));
296
297        let output = expect_json_eq(&left, &right);
298        assert!(output.is_ok(), "assertion error: {output:#?}");
299    }
300
301    #[test]
302    fn it_should_pass_when_string_has_more_than_enough_characters() {
303        let left = json!("12345");
304        let right = json!(expect::string().min_len(3));
305
306        let output = expect_json_eq(&left, &right);
307        assert!(output.is_ok(), "assertion error: {output:#?}");
308    }
309
310    #[test]
311    fn it_should_fail_when_string_is_too_short() {
312        let left = json!("12");
313        let right = json!(expect::string().min_len(3));
314
315        let output = expect_json_eq(&left, &right).unwrap_err().to_string();
316        assert_eq!(
317            output,
318            r#"Json expect::string() error at root:
319    expected string to have at least 3 characters, but it has 2,
320    received "12""#
321        );
322    }
323}
324
325#[cfg(test)]
326mod test_max_len {
327    use crate::expect;
328    use crate::expect_json_eq;
329    use pretty_assertions::assert_eq;
330    use serde_json::json;
331
332    #[test]
333    fn it_should_pass_when_string_has_exactly_enough_characters() {
334        let left = json!("123");
335        let right = json!(expect::string().max_len(3));
336
337        let output = expect_json_eq(&left, &right);
338        assert!(output.is_ok(), "assertion error: {output:#?}");
339    }
340
341    #[test]
342    fn it_should_pass_when_string_has_less_than_enough_characters() {
343        let left = json!("12");
344        let right = json!(expect::string().max_len(5));
345
346        let output = expect_json_eq(&left, &right);
347        assert!(output.is_ok(), "assertion error: {output:#?}");
348    }
349
350    #[test]
351    fn it_should_fail_when_string_is_too_long() {
352        let left = json!("🦊🦊🦊🦊🦊🦊");
353        let right = json!(expect::string().max_len(3));
354
355        let output = expect_json_eq(&left, &right).unwrap_err().to_string();
356        assert_eq!(
357            output,
358            r#"Json expect::string() error at root:
359    expected string to have at most 3 characters, but it has 24,
360    received "🦊🦊🦊🦊🦊🦊""#
361        );
362    }
363}
364
365#[cfg(test)]
366mod test_matches_regex {
367    use crate::expect;
368    use crate::expect_json_eq;
369    use pretty_assertions::assert_eq;
370    use serde_json::json;
371
372    #[test]
373    fn it_should_pass_when_string_matches_regex() {
374        let left = json!("abc123xyz");
375        let right = json!(expect::string().matches_regex(r"^[a-z]+[0-9]+[a-z]+$"));
376        let output = expect_json_eq(&left, &right);
377        assert!(output.is_ok(), "assertion error: {output:#?}");
378    }
379
380    #[test]
381    fn it_should_fail_when_string_does_not_match_regex() {
382        let left = json!("abcxyz");
383        let right = json!(expect::string().matches_regex(r"^[a-z]+[0-9]+[a-z]+$"));
384        let output = expect_json_eq(&left, &right).unwrap_err().to_string();
385        assert_eq!(
386            output,
387            r#"Json string error at root, regex did not match:
388    expected string to match regex pattern '^[a-z]+[0-9]+[a-z]+$',
389    received "abcxyz""#
390        );
391    }
392
393    #[test]
394    fn it_should_fail_when_regex_is_invalid() {
395        let left = json!("abc123xyz");
396        let right = json!(expect::string().matches_regex(r"([a-z]+"));
397        let output = expect_json_eq(&left, &right).unwrap_err().to_string();
398        // For robustness, we don't specify the error message coming from the regex crate.
399        assert!(
400            output.starts_with(r#"Json expect::string() error at root:"#),
401            "Unexpected error output: {output:#?}"
402        );
403    }
404}