json_matcher/
u16_matcher.rs

1use crate::{JsonMatcher, JsonMatcherError};
2
3pub struct U16Matcher {
4    allow_strings: bool,
5}
6
7impl Default for U16Matcher {
8    fn default() -> Self {
9        Self::new()
10    }
11}
12
13impl U16Matcher {
14    pub fn new() -> Self {
15        Self {
16            allow_strings: false,
17        }
18    }
19
20    pub fn new_allow_strings() -> Self {
21        Self {
22            allow_strings: true,
23        }
24    }
25}
26
27impl JsonMatcher for U16Matcher {
28    fn json_matches(&self, value: &serde_json::Value) -> Vec<JsonMatcherError> {
29        match self.allow_strings {
30            true => match value.as_str() {
31                Some(s) if s.parse::<u16>().is_ok() => vec![],
32                Some(_) => vec![JsonMatcherError::at_root("Expected number fitting u16")],
33                None => vec![JsonMatcherError::at_root("Expected string fitting u16")],
34            },
35            false => match value.as_i64() {
36                Some(s) if (0..=65535).contains(&s) => vec![],
37                Some(_) => vec![JsonMatcherError::at_root("Integer out of bounds for u16")],
38                None => vec![JsonMatcherError::at_root("Expected number fitting u16")],
39            },
40        }
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use crate::assert_jm;
47    use serde_json::Value;
48
49    use super::*;
50
51    #[test]
52    fn test_u16_matcher_valid_values() {
53        let get_matcher = || U16Matcher::new();
54
55        // Test valid u16 values
56        assert_jm!(Value::Number(0.into()), get_matcher());
57        assert_jm!(Value::Number(1.into()), get_matcher());
58        assert_jm!(Value::Number(65535.into()), get_matcher());
59        assert_jm!(Value::Number(32768.into()), get_matcher());
60        assert_jm!(Value::Number(100.into()), get_matcher());
61    }
62
63    #[test]
64    fn test_u16_matcher_out_of_bounds() {
65        let get_matcher = || U16Matcher::new();
66
67        // Test negative values
68        assert_eq!(
69            *std::panic::catch_unwind(|| { assert_jm!(Value::Number((-1).into()), get_matcher()) })
70                .err()
71                .unwrap()
72                .downcast::<String>()
73                .unwrap(),
74            r#"
75Json matcher failed:
76  - $: Integer out of bounds for u16
77
78Actual:
79-1"#
80        );
81
82        // Test values above u16 max
83        assert_eq!(
84            *std::panic::catch_unwind(|| {
85                assert_jm!(Value::Number(65536.into()), get_matcher())
86            })
87            .err()
88            .unwrap()
89            .downcast::<String>()
90            .unwrap(),
91            r#"
92Json matcher failed:
93  - $: Integer out of bounds for u16
94
95Actual:
9665536"#
97        );
98
99        assert_eq!(
100            *std::panic::catch_unwind(|| {
101                assert_jm!(Value::Number(100000.into()), get_matcher())
102            })
103            .err()
104            .unwrap()
105            .downcast::<String>()
106            .unwrap(),
107            r#"
108Json matcher failed:
109  - $: Integer out of bounds for u16
110
111Actual:
112100000"#
113        );
114    }
115
116    #[test]
117    fn test_u16_matcher_non_numeric_values() {
118        let get_matcher = || U16Matcher::new();
119
120        // Test string value
121        assert_eq!(
122            *std::panic::catch_unwind(|| {
123                assert_jm!(Value::String("42".to_string()), get_matcher())
124            })
125            .err()
126            .unwrap()
127            .downcast::<String>()
128            .unwrap(),
129            r#"
130Json matcher failed:
131  - $: Expected number fitting u16
132
133Actual:
134"42""#
135        );
136
137        // Test boolean value
138        assert_eq!(
139            *std::panic::catch_unwind(|| { assert_jm!(Value::Bool(true), get_matcher()) })
140                .err()
141                .unwrap()
142                .downcast::<String>()
143                .unwrap(),
144            r#"
145Json matcher failed:
146  - $: Expected number fitting u16
147
148Actual:
149true"#
150        );
151
152        // Test null value
153        assert_eq!(
154            *std::panic::catch_unwind(|| { assert_jm!(Value::Null, get_matcher()) })
155                .err()
156                .unwrap()
157                .downcast::<String>()
158                .unwrap(),
159            r#"
160Json matcher failed:
161  - $: Expected number fitting u16
162
163Actual:
164null"#
165        );
166
167        // Test array value
168        assert_eq!(
169            *std::panic::catch_unwind(|| {
170                assert_jm!(Value::Array(vec![Value::Number(42.into())]), get_matcher())
171            })
172            .err()
173            .unwrap()
174            .downcast::<String>()
175            .unwrap(),
176            r#"
177Json matcher failed:
178  - $: Expected number fitting u16
179
180Actual:
181[
182  42
183]"#
184        );
185
186        // Test object value
187        assert_eq!(
188            *std::panic::catch_unwind(|| {
189                assert_jm!(Value::Object(serde_json::Map::new()), get_matcher())
190            })
191            .err()
192            .unwrap()
193            .downcast::<String>()
194            .unwrap(),
195            r#"
196Json matcher failed:
197  - $: Expected number fitting u16
198
199Actual:
200{}"#
201        );
202    }
203
204    #[test]
205    fn test_u16_matcher_floating_point_numbers() {
206        let get_matcher = || U16Matcher::new();
207
208        // Test floating point numbers - these should fail because as_i64() returns None for floats
209        assert_eq!(
210            *std::panic::catch_unwind(|| {
211                assert_jm!(
212                    Value::Number(serde_json::Number::from_f64(42.5).unwrap()),
213                    get_matcher()
214                )
215            })
216            .err()
217            .unwrap()
218            .downcast::<String>()
219            .unwrap(),
220            r#"
221Json matcher failed:
222  - $: Expected number fitting u16
223
224Actual:
22542.5"#
226        );
227
228        assert_eq!(
229            *std::panic::catch_unwind(|| {
230                assert_jm!(
231                    Value::Number(serde_json::Number::from_f64(0.0).unwrap()),
232                    get_matcher()
233                )
234            })
235            .err()
236            .unwrap()
237            .downcast::<String>()
238            .unwrap(),
239            r#"
240Json matcher failed:
241  - $: Expected number fitting u16
242
243Actual:
2440.0"#
245        );
246    }
247
248    #[test]
249    fn test_u16_matcher_edge_cases() {
250        let get_matcher = || U16Matcher::new();
251
252        // Test boundary values
253        assert_jm!(Value::Number(0.into()), get_matcher());
254        assert_jm!(Value::Number(65535.into()), get_matcher());
255
256        // Test just outside boundaries
257        assert_eq!(
258            *std::panic::catch_unwind(|| { assert_jm!(Value::Number((-1).into()), get_matcher()) })
259                .err()
260                .unwrap()
261                .downcast::<String>()
262                .unwrap(),
263            r#"
264Json matcher failed:
265  - $: Integer out of bounds for u16
266
267Actual:
268-1"#
269        );
270
271        assert_eq!(
272            *std::panic::catch_unwind(|| {
273                assert_jm!(Value::Number(65536.into()), get_matcher())
274            })
275            .err()
276            .unwrap()
277            .downcast::<String>()
278            .unwrap(),
279            r#"
280Json matcher failed:
281  - $: Integer out of bounds for u16
282
283Actual:
28465536"#
285        );
286    }
287
288    #[test]
289    fn test_raw_json_matches_method() {
290        let matcher = U16Matcher::new();
291
292        // Test successful match
293        assert_eq!(matcher.json_matches(&Value::Number(42.into())), vec![]);
294
295        // Test out of bounds error
296        let errors = matcher.json_matches(&Value::Number((-1).into()));
297        assert_eq!(errors.len(), 1);
298        assert_eq!(errors[0].to_string(), "$: Integer out of bounds for u16");
299
300        // Test non-numeric error
301        let errors = matcher.json_matches(&Value::String("test".to_string()));
302        assert_eq!(errors.len(), 1);
303        assert_eq!(errors[0].to_string(), "$: Expected number fitting u16");
304    }
305
306    // Tests for allow_strings mode
307    #[test]
308    fn test_u16_matcher_allow_strings_valid_values() {
309        let get_matcher = || U16Matcher::new_allow_strings();
310        
311        // Test valid u16 string values
312        assert_jm!(Value::String("0".to_string()), get_matcher());
313        assert_jm!(Value::String("1".to_string()), get_matcher());
314        assert_jm!(Value::String("65535".to_string()), get_matcher());
315        assert_jm!(Value::String("32768".to_string()), get_matcher());
316        assert_jm!(Value::String("100".to_string()), get_matcher());
317        assert_jm!(Value::String("42".to_string()), get_matcher());
318    }
319
320    #[test]
321    fn test_u16_matcher_allow_strings_invalid_string_values() {
322        let get_matcher = || U16Matcher::new_allow_strings();
323        
324        // Test negative string values
325        assert_eq!(
326            *std::panic::catch_unwind(|| { assert_jm!(Value::String("-1".to_string()), get_matcher()) })
327                .err()
328                .unwrap()
329                .downcast::<String>()
330                .unwrap(),
331            r#"
332Json matcher failed:
333  - $: Expected number fitting u16
334
335Actual:
336"-1""#
337        );
338
339        // Test values above u16 max
340        assert_eq!(
341            *std::panic::catch_unwind(|| { assert_jm!(Value::String("65536".to_string()), get_matcher()) })
342                .err()
343                .unwrap()
344                .downcast::<String>()
345                .unwrap(),
346            r#"
347Json matcher failed:
348  - $: Expected number fitting u16
349
350Actual:
351"65536""#
352        );
353
354        assert_eq!(
355            *std::panic::catch_unwind(|| { assert_jm!(Value::String("100000".to_string()), get_matcher()) })
356                .err()
357                .unwrap()
358                .downcast::<String>()
359                .unwrap(),
360            r#"
361Json matcher failed:
362  - $: Expected number fitting u16
363
364Actual:
365"100000""#
366        );
367
368        // Test non-numeric strings
369        assert_eq!(
370            *std::panic::catch_unwind(|| { assert_jm!(Value::String("hello".to_string()), get_matcher()) })
371                .err()
372                .unwrap()
373                .downcast::<String>()
374                .unwrap(),
375            r#"
376Json matcher failed:
377  - $: Expected number fitting u16
378
379Actual:
380"hello""#
381        );
382
383        assert_eq!(
384            *std::panic::catch_unwind(|| { assert_jm!(Value::String("".to_string()), get_matcher()) })
385                .err()
386                .unwrap()
387                .downcast::<String>()
388                .unwrap(),
389            r#"
390Json matcher failed:
391  - $: Expected number fitting u16
392
393Actual:
394"""#
395        );
396
397        // Test floating point strings
398        assert_eq!(
399            *std::panic::catch_unwind(|| { assert_jm!(Value::String("42.5".to_string()), get_matcher()) })
400                .err()
401                .unwrap()
402                .downcast::<String>()
403                .unwrap(),
404            r#"
405Json matcher failed:
406  - $: Expected number fitting u16
407
408Actual:
409"42.5""#
410        );
411
412        // Test strings with leading/trailing spaces
413        assert_eq!(
414            *std::panic::catch_unwind(|| { assert_jm!(Value::String(" 42 ".to_string()), get_matcher()) })
415                .err()
416                .unwrap()
417                .downcast::<String>()
418                .unwrap(),
419            r#"
420Json matcher failed:
421  - $: Expected number fitting u16
422
423Actual:
424" 42 ""#
425        );
426
427        // Test strings with leading zeros (should still work)
428        assert_jm!(Value::String("0042".to_string()), get_matcher());
429        assert_jm!(Value::String("00000".to_string()), get_matcher());
430    }
431
432    #[test]
433    fn test_u16_matcher_allow_strings_non_string_values() {
434        let get_matcher = || U16Matcher::new_allow_strings();
435        
436        // Test numeric value (should fail because we expect strings)
437        assert_eq!(
438            *std::panic::catch_unwind(|| { assert_jm!(Value::Number(42.into()), get_matcher()) })
439                .err()
440                .unwrap()
441                .downcast::<String>()
442                .unwrap(),
443            r#"
444Json matcher failed:
445  - $: Expected string fitting u16
446
447Actual:
44842"#
449        );
450
451        // Test boolean value
452        assert_eq!(
453            *std::panic::catch_unwind(|| { assert_jm!(Value::Bool(true), get_matcher()) })
454                .err()
455                .unwrap()
456                .downcast::<String>()
457                .unwrap(),
458            r#"
459Json matcher failed:
460  - $: Expected string fitting u16
461
462Actual:
463true"#
464        );
465
466        // Test null value
467        assert_eq!(
468            *std::panic::catch_unwind(|| { assert_jm!(Value::Null, get_matcher()) })
469                .err()
470                .unwrap()
471                .downcast::<String>()
472                .unwrap(),
473            r#"
474Json matcher failed:
475  - $: Expected string fitting u16
476
477Actual:
478null"#
479        );
480
481        // Test array value
482        assert_eq!(
483            *std::panic::catch_unwind(|| { assert_jm!(Value::Array(vec![Value::String("42".to_string())]), get_matcher()) })
484                .err()
485                .unwrap()
486                .downcast::<String>()
487                .unwrap(),
488            r#"
489Json matcher failed:
490  - $: Expected string fitting u16
491
492Actual:
493[
494  "42"
495]"#
496        );
497
498        // Test object value
499        assert_eq!(
500            *std::panic::catch_unwind(|| { 
501                assert_jm!(Value::Object(serde_json::Map::new()), get_matcher()) 
502            })
503                .err()
504                .unwrap()
505                .downcast::<String>()
506                .unwrap(),
507            r#"
508Json matcher failed:
509  - $: Expected string fitting u16
510
511Actual:
512{}"#
513        );
514    }
515
516    #[test]
517    fn test_u16_matcher_allow_strings_edge_cases() {
518        let get_matcher = || U16Matcher::new_allow_strings();
519        
520        // Test boundary values as strings
521        assert_jm!(Value::String("0".to_string()), get_matcher());
522        assert_jm!(Value::String("65535".to_string()), get_matcher());
523
524        // Test just outside boundaries as strings
525        assert_eq!(
526            *std::panic::catch_unwind(|| { assert_jm!(Value::String("-1".to_string()), get_matcher()) })
527                .err()
528                .unwrap()
529                .downcast::<String>()
530                .unwrap(),
531            r#"
532Json matcher failed:
533  - $: Expected number fitting u16
534
535Actual:
536"-1""#
537        );
538
539        assert_eq!(
540            *std::panic::catch_unwind(|| { assert_jm!(Value::String("65536".to_string()), get_matcher()) })
541                .err()
542                .unwrap()
543                .downcast::<String>()
544                .unwrap(),
545            r#"
546Json matcher failed:
547  - $: Expected number fitting u16
548
549Actual:
550"65536""#
551        );
552    }
553
554    #[test]
555    fn test_u16_matcher_allow_strings_raw_method() {
556        let matcher = U16Matcher::new_allow_strings();
557        
558        // Test successful string match
559        assert_eq!(
560            matcher.json_matches(&Value::String("42".to_string())),
561            vec![]
562        );
563
564        // Test invalid string error
565        let errors = matcher.json_matches(&Value::String("invalid".to_string()));
566        assert_eq!(errors.len(), 1);
567        assert_eq!(errors[0].to_string(), "$: Expected number fitting u16");
568
569        // Test non-string error
570        let errors = matcher.json_matches(&Value::Number(42.into()));
571        assert_eq!(errors.len(), 1);
572        assert_eq!(errors[0].to_string(), "$: Expected string fitting u16");
573    }
574
575    #[test]
576    fn test_u16_matcher_modes_comparison() {
577        let number_matcher = U16Matcher::new();
578        let string_matcher = U16Matcher::new_allow_strings();
579        
580        // Valid number should work for number matcher but not string matcher
581        assert_eq!(number_matcher.json_matches(&Value::Number(42.into())), vec![]);
582        assert_eq!(string_matcher.json_matches(&Value::Number(42.into())).len(), 1);
583        
584        // Valid string should work for string matcher but not number matcher
585        assert_eq!(string_matcher.json_matches(&Value::String("42".to_string())), vec![]);
586        assert_eq!(number_matcher.json_matches(&Value::String("42".to_string())).len(), 1);
587        
588        // Invalid values should fail for both
589        assert_eq!(number_matcher.json_matches(&Value::String("invalid".to_string())).len(), 1);
590        assert_eq!(string_matcher.json_matches(&Value::String("invalid".to_string())).len(), 1);
591        assert_eq!(number_matcher.json_matches(&Value::Number((-1).into())).len(), 1);
592        assert_eq!(string_matcher.json_matches(&Value::String("-1".to_string())).len(), 1);
593    }
594}