use arbitrary_json::ArbitraryValue;
use proptest::prelude::*;
use proptest::proptest;
use proptest_arbitrary_interop::arb;
use proptest_derive::Arbitrary;
#[derive(serde_implicit_proc::Deserialize, serde::Serialize, Debug, PartialEq, Arbitrary)]
#[serde(untagged)]
enum MultiTypeTag {
StringVariant {
#[serde_implicit(tag)]
string_tag: String,
value: u32,
},
NumberVariant {
#[serde_implicit(tag)]
number_tag: u64,
value: String,
},
BoolVariant {
#[serde_implicit(tag)]
bool_tag: bool,
value: Vec<String>,
},
}
#[derive(serde_implicit_proc::Deserialize, serde::Serialize, Debug, PartialEq, Arbitrary)]
#[serde(untagged)]
enum OverlappingFields {
Variant1 {
#[serde_implicit(tag)]
type_tag: String,
common_field: u32,
variant1_specific: bool,
},
Variant2 {
#[serde_implicit(tag)]
version: u32,
common_field: u32,
variant2_specific: String,
},
}
#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Arbitrary)]
struct NestedData {
field1: String,
field2: u32,
}
#[derive(serde_implicit_proc::Deserialize, serde::Serialize, Debug, PartialEq, Arbitrary)]
#[serde(untagged)]
enum NestedEnum {
Simple {
#[serde_implicit(tag)]
tag: String,
value: u32,
},
Complex {
#[serde_implicit(tag)]
complex_tag: bool,
nested: NestedData,
optional: Option<String>,
},
}
#[derive(serde_implicit_proc::Deserialize, serde::Serialize, Debug, PartialEq)]
enum RecursiveEnum {
Leaf {
#[serde_implicit(tag)]
is_leaf: bool,
value: String,
},
Node {
#[serde_implicit(tag)]
has_children: bool,
children: Vec<RecursiveEnum>,
metadata: String,
},
}
mod edge_cases {
#[derive(serde_implicit_proc::Deserialize, serde::Serialize, Debug, PartialEq)]
enum EmptyEnum {}
#[derive(serde_implicit_proc::Deserialize, serde::Serialize, Debug, PartialEq)]
enum SingleVariant {
OnlyVariant {
#[serde_implicit(tag)]
this_is_it: bool,
data: String,
},
}
}
#[derive(serde_implicit::Deserialize, serde::Serialize, Debug, PartialEq, Arbitrary)]
#[serde(untagged)]
enum TupleEnum {
BoolU32(bool, u32),
StringOnly(String),
U64Vec(u64, Vec<u32>),
}
#[derive(serde_implicit::Deserialize, serde::Serialize, Debug, PartialEq, Arbitrary)]
#[serde(untagged)]
enum TupleCustomTag {
MiddleTag(bool, #[serde_implicit(tag)] String, u32),
FirstTag(#[serde_implicit(tag)] u64, bool),
LastTag(u32, bool, #[serde_implicit(tag)] String),
}
#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Arbitrary)]
struct FlattenInner(String, bool);
#[derive(serde_implicit::Deserialize, serde::Serialize, Debug, PartialEq, Arbitrary)]
#[serde(untagged)]
enum TupleFlatten {
Normal(bool),
Tagged(u64, #[serde_implicit(tag)] u32),
Fallback(#[serde_implicit(flatten)] FlattenInner),
}
proptest! {
#[test]
fn test_tags_different_types(tag in any::<MultiTypeTag>()) {
let serialized = serde_json::to_string(&tag).unwrap();
let deserialized: MultiTypeTag = serde_json::from_str(&serialized).unwrap();
assert_eq!(tag, deserialized);
}
#[test]
fn test_tags_overlapping_fields(tag in any::<OverlappingFields>()) {
let serialized = serde_json::to_string(&tag).unwrap();
let deserialized = serde_json::from_str(&serialized).unwrap();
assert_eq!(tag, deserialized);
}
#[test]
fn test_tags_nested_enum(tag in any::<NestedEnum>()) {
let serialized = serde_json::to_string(&tag).unwrap();
let deserialized = serde_json::from_str(&serialized).unwrap();
assert_eq!(tag, deserialized);
}
#[test]
fn test_agrees_with_serde(rand in any::<MultiTypeTag>()) {
#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq)]
#[serde(untagged)]
enum SerdeMultiTypeTag {
StringVariant {
string_tag: String,
value: u32,
},
NumberVariant {
number_tag: u64,
value: String,
},
BoolVariant {
bool_tag: bool,
value: Vec<String>,
},
}
let serialized = serde_json::to_string(&rand).unwrap();
let deserialized1 : MultiTypeTag = serde_json::from_str(&serialized).unwrap();
let deserialized2 : SerdeMultiTypeTag = serde_json::from_str(&serialized).unwrap();
assert_eq!(serde_json::to_string(&deserialized1).unwrap(), serde_json::to_string(&deserialized2).unwrap());
}
#[test]
fn fuzz_multi_type_tag_no_panic(json in arb::<ArbitraryValue>()) {
let _ = serde_json::from_value::<MultiTypeTag>(json.into());
}
#[test]
fn fuzz_overlapping_fields_no_panic(json in arb::<ArbitraryValue>()) {
let _ = serde_json::from_value::<OverlappingFields>(json.into());
}
#[test]
fn fuzz_nested_enum_no_panic(json in arb::<ArbitraryValue>()) {
let _ = serde_json::from_value::<NestedEnum>(json.into());
}
#[test]
fn fuzz_tuple_enum_no_panic(json in arb::<ArbitraryValue>()) {
let _ = serde_json::from_value::<TupleEnum>(json.into());
}
#[test]
fn fuzz_tuple_custom_tag_no_panic(json in arb::<ArbitraryValue>()) {
let _ = serde_json::from_value::<TupleCustomTag>(json.into());
}
#[test]
fn fuzz_tuple_flatten_no_panic(json in arb::<ArbitraryValue>()) {
let _ = serde_json::from_value::<TupleFlatten>(json.into());
}
#[test]
fn test_tuple_enum_roundtrip(value in any::<TupleEnum>()) {
let serialized = serde_json::to_value(&value).unwrap();
let deserialized: TupleEnum = serde_json::from_value(serialized).unwrap();
assert_eq!(value, deserialized);
}
#[test]
fn test_tuple_custom_tag_roundtrip(value in any::<TupleCustomTag>()) {
let serialized = serde_json::to_value(&value).unwrap();
let deserialized: TupleCustomTag = serde_json::from_value(serialized).unwrap();
assert_eq!(value, deserialized);
}
#[test]
fn test_tuple_flatten_roundtrip(value in any::<TupleFlatten>()) {
let serialized = serde_json::to_value(&value).unwrap();
let deserialized: TupleFlatten = serde_json::from_value(serialized).unwrap();
assert_eq!(value, deserialized);
}
}