File: generic_claims_wrap_value_prop_test.rs

package info (click to toggle)
rust-rusty-paseto 0.7.1-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,580 kB
  • sloc: makefile: 2
file content (139 lines) | stat: -rw-r--r-- 6,020 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*!
 * Property Tests for wrap_value Function
 *
 * This file contains property tests designed to validate the correctness and robustness
 * of the `wrap_value` function. The `wrap_value` function ensures that all values are
 * recursively wrapped to maintain valid JSON objects, handling various edge cases
 * including empty objects, arrays, and null values.
 *
 * The primary goals of these tests are:
 * 1. **Validation**: Ensure the `wrap_value` function correctly handles different types of JSON values.
 * 2. **Robustness**: Identify and address potential edge cases that may not be covered by unit tests.
 * 3. **Consistency**: Verify that the function maintains the expected structure and behavior for all inputs.
 *
 * ## Test Strategy
 *
 * The property tests leverage the `proptest` crate to generate a wide range of JSON values,
 * including nested structures. The generated values are then passed to the `wrap_value`
 * function, and the resulting wrapped values are compared against the expected outcomes.
 *
 * ## Key Test Scenarios
 *
 * - **Null Values**: Ensure null values remain null and are not wrapped unnecessarily.
 * - **Empty Objects**: Verify that empty maps are wrapped as empty JSON objects.
 * - **Primitive Values**: Confirm that primitive values (e.g., strings, numbers) remain unchanged.
 * - **Arrays**: Ensure arrays, including empty arrays, are wrapped correctly and consistently.
 * - **Nested Structures**: Validate the recursive wrapping of nested JSON objects and arrays.
 *
 * ## Findings
 *
 * - The `wrap_value` function correctly handles a wide range of input values, passing all property tests.
 * - No significant corner cases were identified during the testing process, indicating that the function
 *   is robust and reliable for most practical use cases.
 *
 * ## Conclusion
 *
 * The property tests demonstrate that the `wrap_value` function is robust and reliable for most practical
 * use cases. The function's behavior is consistent with the expected outcomes for a wide range of input values.
 *
 * This approach ensures comprehensive validation of the `wrap_value` function, contributing to the overall
 * stability and reliability of the system.
 */

use std::collections::HashMap;

use proptest::prelude::*;
use serde_json::{Map, Value};

// Define a strategy to generate arbitrary JSON values
fn arb_json() -> impl Strategy<Value=Json> {
    let leaf = prop_oneof![
        Just(Json::Null),
        any::<bool>().prop_map(Json::Bool),
        any::<f64>().prop_map(Json::Number),
        "[a-zA-Z0-9_]+".prop_map(Json::String),
    ];
    leaf.prop_recursive(
        3, // 3 levels deep
        64, // Shoot for maximum size of 64 nodes
        10, // We put up to 10 items per collection
        |inner| prop_oneof![
            prop::collection::vec(inner.clone(), 0..10).prop_map(Json::Array),
            prop::collection::hash_map("[a-zA-Z_][a-zA-Z0-9_]*", inner, 0..10).prop_map(Json::Map),
        ],
    )
}

#[derive(Clone, Debug)]
enum Json {
    Null,
    Bool(bool),
    Number(f64),
    String(String),
    Array(Vec<Json>),
    Map(HashMap<String, Json>),
}

// Convert our custom Json enum to serde_json::Value
impl From<Json> for Value {
    fn from(json: Json) -> Self {
        match json {
            Json::Null => Value::Null,
            Json::Bool(b) => Value::Bool(b),
            Json::Number(n) => Value::Number(serde_json::Number::from_f64(n).unwrap()),
            Json::String(s) => Value::String(s),
            Json::Array(arr) => Value::Array(arr.into_iter().map(Value::from).collect()),
            Json::Map(map) => Value::Object(map.into_iter().map(|(k, v)| (k, Value::from(v))).collect()),
        }
    }
}

fn wrap_value(value: Value) -> Value {
    match value {
        Value::Object(map) => {
            if map.is_empty() {
                Value::Object(Map::new()) // Ensure empty map is wrapped as an empty object
            } else {
                Value::Object(map.into_iter().map(|(k, v)| (k, wrap_value(v))).collect())
            }
        }
        Value::Array(arr) => Value::Array(arr.into_iter().map(wrap_value).collect()),
        Value::Null => Value::Null, // Do not wrap null values
        other => other, // Do not wrap primitive values
    }
}
proptest! {
    #[test]
    fn test_wrap_value(input in arb_json()) {
        let value: Value = input.into();
        let wrapped_value = wrap_value(value.clone());

        // Ensure null values remain null
        if let Value::Null = value {
            prop_assert_eq!(wrapped_value, Value::Null);
        } else if let Value::Object(map) = &value {
            if map.is_empty() {
                prop_assert_eq!(wrapped_value, Value::Object(Map::new()));
            } else {
                // For non-empty maps, ensure they are wrapped correctly
                for (k, v) in map {
                    let wrapped_sub_value = wrapped_value.get(k).expect("Key should exist in wrapped map");
                    let expected_sub_value = wrap_value(v.clone());
                    prop_assert_eq!(wrapped_sub_value, &expected_sub_value, "Key '{}' not wrapped correctly", k);
                }
            }
        } else if let Value::Array(arr) = &value {
            // Ensure arrays are wrapped correctly, including empty arrays
            for (original, wrapped) in arr.iter().zip(wrapped_value.as_array().expect("Wrapped value should be an array")) {
                let expected_sub_value = wrap_value(original.clone());
                prop_assert_eq!(wrapped, &expected_sub_value, "Array element not wrapped correctly");
            }
            if arr.is_empty() {
                prop_assert_eq!(wrapped_value, Value::Array(vec![]));
            }
        } else {
            // For other values, ensure they remain unchanged
            prop_assert_eq!(wrapped_value, value);
        }
    }
}