use forma_derive::{Deserialize, Serialize};
use forma_json::{from_str, to_string, to_string_pretty, Value};
use forma_json::value::{from_value, to_value, Number};
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque, LinkedList, BinaryHeap};
use std::borrow::Cow;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::time::Duration;
use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroI8, NonZeroI32};
use std::sync::{Mutex, RwLock, Arc};
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use std::path::PathBuf;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Primitives {
b: bool,
i8v: i8,
i16v: i16,
i32v: i32,
i64v: i64,
u8v: u8,
u16v: u16,
u32v: u32,
u64v: u64,
f32v: f32,
f64v: f64,
c: char,
s: String,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Collections {
vec: Vec<i32>,
deque: VecDeque<String>,
list: LinkedList<bool>,
bmap: BTreeMap<String, i32>,
bset: BTreeSet<i32>,
hmap: HashMap<String, String>,
hset: HashSet<i32>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct SmartPointers {
boxed: Box<i32>,
cow_owned: String,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Nested {
inner: Box<Option<Nested>>,
value: i32,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[forma(rename_all = "camelCase")]
struct CamelCaseStruct {
first_name: String,
last_name: String,
email_address: Option<String>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[forma(rename_all = "SCREAMING_SNAKE_CASE")]
struct ScreamingStruct {
my_field: i32,
another_field: String,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
enum Shape {
Circle { radius: f64 },
Rectangle { width: f64, height: f64 },
Point,
Line(f64, f64, f64, f64),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[forma(tag = "kind")]
enum Event {
Click { x: i32, y: i32 },
KeyPress { key: String, modifiers: Vec<String> },
Resize { width: u32, height: u32 },
Close,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[forma(tag = "type", content = "data")]
enum Message {
Text(String),
Binary(Vec<u8>),
Ping,
Structured { from: String, to: String, body: String },
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[forma(untagged)]
enum AnyValue {
Int(i64),
Float(f64),
Str(String),
Arr(Vec<i64>),
Obj { key: String },
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
enum Permission {
Read,
Write,
Admin,
#[forma(other)]
Unknown,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Config {
name: String,
#[forma(default)]
debug: bool,
#[forma(default)]
#[forma(skip_serializing_if = "Option::is_none")]
description: Option<String>,
#[forma(flatten)]
extra: BTreeMap<String, Value>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[forma(transparent)]
struct Wrapper<T>(T);
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Meters(f64);
#[derive(Debug, PartialEq, Clone)]
struct External(String);
impl From<String> for External {
fn from(s: String) -> Self {
External(s)
}
}
impl From<External> for String {
fn from(e: External) -> String {
e.0
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[forma(into = "String", from = "String")]
struct MyExternal(String);
impl From<String> for MyExternal {
fn from(s: String) -> Self {
MyExternal(s)
}
}
impl From<MyExternal> for String {
fn from(e: MyExternal) -> String {
e.0
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Aliased {
#[forma(alias = "userName", alias = "user")]
username: String,
#[forma(alias = "emailAddress")]
email: String,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[forma(deny_unknown_fields)]
struct Strict {
x: i32,
y: i32,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[forma(default)]
struct WithDefaults {
count: i32,
label: String,
active: bool,
}
impl Default for WithDefaults {
fn default() -> Self {
WithDefaults {
count: 42,
label: "default".into(),
active: true,
}
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[forma(rename_all = "snake_case")]
enum Status {
InProgress,
Completed,
NotStarted,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct WithCustom {
#[forma(serialize_with = "serialize_uppercase")]
#[forma(deserialize_with = "deserialize_lowercase")]
name: String,
value: i32,
}
fn serialize_uppercase<S: forma_core::ser::Serializer>(v: &String, s: S) -> Result<S::Ok, S::Error> {
s.serialize_str(&v.to_uppercase())
}
fn deserialize_lowercase<'de, D: forma_core::de::Deserializer<'de>>(d: D) -> Result<String, D::Error> {
use forma_core::de::Deserialize;
let s = String::deserialize(d)?;
Ok(s.to_lowercase())
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct MegaStruct {
primitives: Primitives,
collections: Collections,
shapes: Vec<Shape>,
events: Vec<Event>,
messages: Vec<Message>,
any_values: Vec<AnyValue>,
permissions: Vec<Permission>,
config: Config,
wrapped_int: Wrapper<i32>,
wrapped_string: Wrapper<String>,
meters: Meters,
nested: Nested,
camel: CamelCaseStruct,
screaming: ScreamingStruct,
status: Status,
optional_vec: Option<Vec<Option<i32>>>,
tuple: (i32, String, bool),
array: [i32; 5],
unit: (),
}
#[test]
fn stress_all_primitives() {
let p = Primitives {
b: true,
i8v: -128,
i16v: -32768,
i32v: -2147483648,
i64v: -9223372036854775808,
u8v: 255,
u16v: 65535,
u32v: 4294967295,
u64v: 18446744073709551615,
f32v: 3.14,
f64v: 2.718281828459045,
c: 'Z',
s: "hello world".into(),
};
let json = to_string(&p).unwrap();
let back: Primitives = from_str(&json).unwrap();
assert_eq!(back, p);
}
#[test]
fn stress_primitive_boundaries() {
let jsons: Vec<(&str, String)> = vec![
("i8 min", to_string(&-128i8).unwrap()),
("i8 max", to_string(&127i8).unwrap()),
("i16 min", to_string(&-32768i16).unwrap()),
("i16 max", to_string(&32767i16).unwrap()),
("i32 min", to_string(&i32::MIN).unwrap()),
("i32 max", to_string(&i32::MAX).unwrap()),
("i64 min", to_string(&i64::MIN).unwrap()),
("i64 max", to_string(&i64::MAX).unwrap()),
("u8 max", to_string(&u8::MAX).unwrap()),
("u16 max", to_string(&u16::MAX).unwrap()),
("u32 max", to_string(&u32::MAX).unwrap()),
("u64 max", to_string(&u64::MAX).unwrap()),
];
for (label, json) in &jsons {
let v: Value = from_str(json).unwrap();
assert!(v.is_number(), "failed for {label}: {json}");
}
}
#[test]
fn stress_i128_u128() {
let big_neg: i128 = i128::MIN + 1; let big_pos: u128 = u128::MAX;
let json_neg = to_string(&big_neg).unwrap();
let json_pos = to_string(&big_pos).unwrap();
let back_neg: i128 = from_str(&json_neg).unwrap();
let back_pos: u128 = from_str(&json_pos).unwrap();
assert_eq!(back_neg, big_neg);
assert_eq!(back_pos, big_pos);
}
#[test]
fn stress_float_edge_cases() {
let cases: Vec<f64> = vec![
0.0, -0.0, 1e-308, 1e308, f64::MIN_POSITIVE,
std::f64::consts::PI, std::f64::consts::E,
];
for v in cases {
let json = to_string(&v).unwrap();
let back: f64 = from_str(&json).unwrap();
assert!((back - v).abs() < 1e-10 || (v == 0.0 && back == 0.0),
"float roundtrip failed for {v}: got {back}");
}
}
#[test]
fn stress_collections_roundtrip() {
let mut hmap = HashMap::new();
hmap.insert("key1".into(), "val1".into());
hmap.insert("key2".into(), "val2".into());
let mut hset = HashSet::new();
hset.insert(1);
hset.insert(2);
hset.insert(3);
let c = Collections {
vec: vec![1, 2, 3, 4, 5],
deque: VecDeque::from(vec!["a".into(), "b".into(), "c".into()]),
list: LinkedList::from([true, false, true]),
bmap: BTreeMap::from([("x".into(), 10), ("y".into(), 20)]),
bset: BTreeSet::from([100, 200, 300]),
hmap,
hset,
};
let json = to_string(&c).unwrap();
let back: Collections = from_str(&json).unwrap();
assert_eq!(back.vec, c.vec);
assert_eq!(back.deque, c.deque);
assert_eq!(back.list, c.list);
assert_eq!(back.bmap, c.bmap);
assert_eq!(back.bset, c.bset);
assert_eq!(back.hmap.len(), c.hmap.len());
assert_eq!(back.hset.len(), c.hset.len());
}
#[test]
fn stress_empty_collections() {
let c = Collections {
vec: vec![],
deque: VecDeque::new(),
list: LinkedList::new(),
bmap: BTreeMap::new(),
bset: BTreeSet::new(),
hmap: HashMap::new(),
hset: HashSet::new(),
};
let json = to_string(&c).unwrap();
let back: Collections = from_str(&json).unwrap();
assert_eq!(back.vec.len(), 0);
assert_eq!(back.deque.len(), 0);
assert_eq!(back.bmap.len(), 0);
}
#[test]
fn stress_smart_pointers() {
let sp = SmartPointers {
boxed: Box::new(42),
cow_owned: "owned string".into(),
};
let json = to_string(&sp).unwrap();
let back: SmartPointers = from_str(&json).unwrap();
assert_eq!(back, sp);
}
#[test]
fn stress_deep_nesting() {
let mut current = Nested {
inner: Box::new(None),
value: 100,
};
for i in (0..100).rev() {
current = Nested {
inner: Box::new(Some(current)),
value: i,
};
}
let json = to_string(¤t).unwrap();
let back: Nested = from_str(&json).unwrap();
assert_eq!(back.value, 0);
let mut n = &back;
for i in 0..10 {
assert_eq!(n.value, i);
n = n.inner.as_ref().as_ref().unwrap();
}
}
#[test]
fn stress_all_shapes() {
let shapes = vec![
Shape::Circle { radius: 5.0 },
Shape::Rectangle { width: 10.0, height: 20.0 },
Shape::Point,
Shape::Line(0.0, 0.0, 100.0, 200.0),
];
let json = to_string(&shapes).unwrap();
let back: Vec<Shape> = from_str(&json).unwrap();
assert_eq!(back, shapes);
}
#[test]
fn stress_internally_tagged_enums() {
let events = vec![
Event::Click { x: 100, y: 200 },
Event::KeyPress { key: "Enter".into(), modifiers: vec!["Ctrl".into(), "Shift".into()] },
Event::Resize { width: 1920, height: 1080 },
Event::Close,
];
for event in &events {
let json = to_string(event).unwrap();
let back: Event = from_str(&json).unwrap();
assert_eq!(&back, event);
}
}
#[test]
fn stress_adjacently_tagged_enums() {
let messages = vec![
Message::Text("hello".into()),
Message::Binary(vec![1, 2, 3, 4]),
Message::Ping,
Message::Structured {
from: "alice".into(),
to: "bob".into(),
body: "hi!".into(),
},
];
for msg in &messages {
let json = to_string(msg).unwrap();
let back: Message = from_str(&json).unwrap();
assert_eq!(&back, msg);
}
}
#[test]
fn stress_untagged_enums() {
let values = vec![
AnyValue::Int(42),
AnyValue::Str("hello".into()),
AnyValue::Arr(vec![1, 2, 3]),
AnyValue::Obj { key: "test".into() },
];
for val in &values {
let json = to_string(val).unwrap();
let back: AnyValue = from_str(&json).unwrap();
assert_eq!(&back, val);
}
}
#[test]
fn stress_other_variant() {
let known: Permission = from_str("\"Read\"").unwrap();
assert_eq!(known, Permission::Read);
let unknown: Permission = from_str("\"SuperAdmin\"").unwrap();
assert_eq!(unknown, Permission::Unknown);
}
#[test]
fn stress_config_with_flatten() {
let mut extra = BTreeMap::new();
extra.insert("timeout".into(), Value::from(30i64));
extra.insert("retries".into(), Value::from(3i64));
let config = Config {
name: "myapp".into(),
debug: false,
description: Some("A test config".into()),
extra: extra.clone(),
};
let json = to_string(&config).unwrap();
let back: Config = from_str(&json).unwrap();
assert_eq!(back.name, config.name);
assert_eq!(back.debug, config.debug);
assert_eq!(back.description, config.description);
assert_eq!(back.extra, extra);
}
#[test]
fn stress_config_skip_none() {
let config = Config {
name: "minimal".into(),
debug: false,
description: None,
extra: BTreeMap::new(),
};
let json = to_string(&config).unwrap();
assert!(!json.contains("description"), "description should be skipped when None: {json}");
}
#[test]
fn stress_config_default_field() {
let json = r#"{"name":"test"}"#;
let config: Config = from_str(json).unwrap();
assert_eq!(config.debug, false);
}
#[test]
fn stress_transparent_wrappers() {
let w = Wrapper(42i32);
let json = to_string(&w).unwrap();
assert_eq!(json, "42");
let back: Wrapper<i32> = from_str(&json).unwrap();
assert_eq!(back, w);
let ws = Wrapper("hello".to_string());
let json = to_string(&ws).unwrap();
assert_eq!(json, "\"hello\"");
let back: Wrapper<String> = from_str(&json).unwrap();
assert_eq!(back, ws);
}
#[test]
fn stress_newtype() {
let m = Meters(42.5);
let json = to_string(&m).unwrap();
let back: Meters = from_str(&json).unwrap();
assert_eq!(back, m);
}
#[test]
fn stress_from_into() {
let e = MyExternal("hello".into());
let json = to_string(&e).unwrap();
assert_eq!(json, "\"hello\"");
let back: MyExternal = from_str(&json).unwrap();
assert_eq!(back, e);
}
#[test]
fn stress_aliases() {
let json1 = r#"{"username":"alice","email":"a@b.com"}"#;
let a1: Aliased = from_str(json1).unwrap();
assert_eq!(a1.username, "alice");
let json2 = r#"{"userName":"bob","emailAddress":"b@c.com"}"#;
let a2: Aliased = from_str(json2).unwrap();
assert_eq!(a2.username, "bob");
assert_eq!(a2.email, "b@c.com");
let json3 = r#"{"user":"charlie","email":"c@d.com"}"#;
let a3: Aliased = from_str(json3).unwrap();
assert_eq!(a3.username, "charlie");
}
#[test]
fn stress_deny_unknown_fields() {
let ok = r#"{"x":1,"y":2}"#;
let s: Strict = from_str(ok).unwrap();
assert_eq!(s, Strict { x: 1, y: 2 });
let bad = r#"{"x":1,"y":2,"z":3}"#;
let err = from_str::<Strict>(bad);
assert!(err.is_err(), "should reject unknown field z");
}
#[test]
fn stress_container_default() {
let wd: WithDefaults = from_str("{}").unwrap();
assert_eq!(wd.count, 0); assert_eq!(wd.label, ""); assert_eq!(wd.active, false);
let wd2: WithDefaults = from_str(r#"{"count":10}"#).unwrap();
assert_eq!(wd2.count, 10);
assert_eq!(wd2.label, ""); }
#[test]
fn stress_rename_all_camel_case() {
let c = CamelCaseStruct {
first_name: "Alice".into(),
last_name: "Smith".into(),
email_address: Some("alice@example.com".into()),
};
let json = to_string(&c).unwrap();
assert!(json.contains("firstName"));
assert!(json.contains("lastName"));
assert!(json.contains("emailAddress"));
let back: CamelCaseStruct = from_str(&json).unwrap();
assert_eq!(back, c);
}
#[test]
fn stress_rename_all_screaming() {
let s = ScreamingStruct {
my_field: 42,
another_field: "test".into(),
};
let json = to_string(&s).unwrap();
assert!(json.contains("MY_FIELD"));
assert!(json.contains("ANOTHER_FIELD"));
let back: ScreamingStruct = from_str(&json).unwrap();
assert_eq!(back, s);
}
#[test]
fn stress_status_snake_case() {
let s = Status::InProgress;
let json = to_string(&s).unwrap();
assert_eq!(json, "\"in_progress\"");
let back: Status = from_str(&json).unwrap();
assert_eq!(back, s);
let s2 = Status::NotStarted;
let json2 = to_string(&s2).unwrap();
assert_eq!(json2, "\"not_started\"");
}
#[test]
fn stress_custom_serialization() {
let wc = WithCustom {
name: "alice".into(),
value: 42,
};
let json = to_string(&wc).unwrap();
assert!(json.contains("ALICE"), "should be uppercased: {json}");
let back: WithCustom = from_str(&json).unwrap();
assert_eq!(back.name, "alice"); assert_eq!(back.value, 42);
}
#[test]
fn stress_tuples() {
let t2 = (42i32, "hello".to_string());
let json = to_string(&t2).unwrap();
let back: (i32, String) = from_str(&json).unwrap();
assert_eq!(back, t2);
let t5 = (1i32, 2.0f64, true, "x".to_string(), 'A');
let json5 = to_string(&t5).unwrap();
let back5: (i32, f64, bool, String, char) = from_str(&json5).unwrap();
assert_eq!(back5, t5);
let json_unit = to_string(&()).unwrap();
assert_eq!(json_unit, "null");
}
#[test]
fn stress_arrays() {
let arr: [i32; 5] = [1, 2, 3, 4, 5];
let json = to_string(&arr).unwrap();
let back: [i32; 5] = from_str(&json).unwrap();
assert_eq!(back, arr);
let empty: [i32; 0] = [];
let json_empty = to_string(&empty).unwrap();
assert_eq!(json_empty, "[]");
let back_empty: [i32; 0] = from_str(&json_empty).unwrap();
assert_eq!(back_empty, empty);
}
#[test]
fn stress_option_nesting() {
let v: Option<Option<Option<i32>>> = Some(Some(Some(42)));
let json = to_string(&v).unwrap();
let back: Option<Option<Option<i32>>> = from_str(&json).unwrap();
assert_eq!(back, v);
let none: Option<Option<i32>> = None;
let json_none = to_string(&none).unwrap();
assert_eq!(json_none, "null");
}
#[test]
fn stress_result_type() {
let ok: Result<i32, String> = Ok(42);
let json = to_string(&ok).unwrap();
let back: Result<i32, String> = from_str(&json).unwrap();
assert_eq!(back, ok);
let err: Result<i32, String> = Err("oops".into());
let json_err = to_string(&err).unwrap();
let back_err: Result<i32, String> = from_str(&json_err).unwrap();
assert_eq!(back_err, err);
}
#[test]
fn stress_nonzero_types() {
let nz_u8 = NonZeroU8::new(42).unwrap();
let nz_u16 = NonZeroU16::new(1000).unwrap();
let nz_u32 = NonZeroU32::new(100000).unwrap();
let nz_u64 = NonZeroU64::new(10000000).unwrap();
let nz_i8 = NonZeroI8::new(-42).unwrap();
let nz_i32 = NonZeroI32::new(-100000).unwrap();
for (json, expected) in [
(to_string(&nz_u8).unwrap(), "42"),
(to_string(&nz_u16).unwrap(), "1000"),
(to_string(&nz_u32).unwrap(), "100000"),
(to_string(&nz_u64).unwrap(), "10000000"),
(to_string(&nz_i8).unwrap(), "-42"),
(to_string(&nz_i32).unwrap(), "-100000"),
] {
assert_eq!(json, expected);
}
let back: NonZeroU32 = from_str("100000").unwrap();
assert_eq!(back, nz_u32);
}
#[test]
fn stress_net_types() {
let ipv4 = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1));
let ipv6 = IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
let sock = SocketAddr::new(ipv4, 8080);
let json_ipv4 = to_string(&ipv4).unwrap();
let json_ipv6 = to_string(&ipv6).unwrap();
let json_sock = to_string(&sock).unwrap();
let back_ipv4: IpAddr = from_str(&json_ipv4).unwrap();
let back_ipv6: IpAddr = from_str(&json_ipv6).unwrap();
let back_sock: SocketAddr = from_str(&json_sock).unwrap();
assert_eq!(back_ipv4, ipv4);
assert_eq!(back_ipv6, ipv6);
assert_eq!(back_sock, sock);
}
#[test]
fn stress_duration() {
let d = Duration::new(42, 123456789);
let json = to_string(&d).unwrap();
let back: Duration = from_str(&json).unwrap();
assert_eq!(back, d);
}
#[test]
fn stress_path_types() {
let p = PathBuf::from("/usr/local/bin");
let json = to_string(&p).unwrap();
let back: PathBuf = from_str(&json).unwrap();
assert_eq!(back, p);
}
#[test]
fn stress_cell_types() {
let cell = Cell::new(42i32);
let json = to_string(&cell).unwrap();
assert_eq!(json, "42");
let refcell = RefCell::new("hello".to_string());
let json_rc = to_string(&refcell).unwrap();
assert_eq!(json_rc, "\"hello\"");
}
#[test]
fn stress_sync_types() {
let mutex = Mutex::new(42i32);
let json = to_string(&mutex).unwrap();
assert_eq!(json, "42");
let rwlock = RwLock::new("world".to_string());
let json_rw = to_string(&rwlock).unwrap();
assert_eq!(json_rw, "\"world\"");
}
#[test]
fn stress_unicode_strings() {
let cases = vec![
"",
"hello",
"hello\nworld",
"tab\there",
"quote\"inside",
"backslash\\here",
"emoji: \u{1F600}\u{1F601}\u{1F602}", "chinese: \u{4e16}\u{754c}", "arabic: \u{0627}\u{0644}\u{0633}\u{0644}\u{0627}\u{0645}",
"null\u{0000}byte", "surrogate: \u{1F4A9}", "\u{0001}\u{001F}", "mixed: hello \u{4e16}\u{754c} \u{1F600}",
];
for s in cases {
let json = to_string(&s.to_string()).unwrap();
let back: String = from_str(&json).unwrap();
assert_eq!(back, s, "unicode roundtrip failed");
}
}
#[test]
fn stress_escaped_json_strings() {
let cases = vec![
(r#""hello""#, "hello"),
(r#""line\nbreak""#, "line\nbreak"),
(r#""tab\there""#, "tab\there"),
(r#""quote\"inside""#, "quote\"inside"),
(r#""back\\slash""#, "back\\slash"),
(r#""slash\/forward""#, "slash/forward"),
(r#""\u0041\u0042\u0043""#, "ABC"), (r#""\uD83D\uDE00""#, "\u{1F600}"), ];
for (input, expected) in cases {
let result: String = from_str(input).unwrap();
assert_eq!(result, expected, "failed for input: {input}");
}
}
#[test]
fn stress_whitespace_handling() {
let json = r#" {
"x" : 1 ,
"y" : 2
} "#;
let s: Strict = from_str(json).unwrap();
assert_eq!(s, Strict { x: 1, y: 2 });
}
#[test]
fn stress_error_cases() {
assert!(from_str::<Value>("").is_err());
assert!(from_str::<Value>("{").is_err());
assert!(from_str::<Value>("[1,").is_err());
assert!(from_str::<Value>("tru").is_err());
assert!(from_str::<Value>("nul").is_err());
assert!(from_str::<Value>("\"unterminated").is_err());
assert!(from_str::<Value>("42 extra").is_err());
assert!(from_str::<Value>("{}[]").is_err());
assert!(from_str::<i32>("\"not a number\"").is_err());
assert!(from_str::<bool>("42").is_err());
assert!(from_str::<String>("42").is_err());
assert!(from_str::<String>(r#""\x00""#).is_err());
assert!(from_str::<String>(r#""\uZZZZ""#).is_err());
assert!(from_str::<String>(r#""\uD800""#).is_err());
assert!(from_str::<String>(r#""\uD800\u0041""#).is_err());
}
#[test]
fn stress_pretty_printing() {
let shapes = vec![
Shape::Circle { radius: 5.0 },
Shape::Point,
];
let pretty = to_string_pretty(&shapes).unwrap();
assert!(pretty.contains('\n'));
assert!(pretty.contains(" ")); let back: Vec<Shape> = from_str(&pretty).unwrap();
assert_eq!(back, shapes);
}
#[test]
fn stress_value_type_comprehensive() {
let mut obj = BTreeMap::new();
obj.insert("null".into(), Value::Null);
obj.insert("bool".into(), Value::Bool(true));
obj.insert("posint".into(), Value::Number(Number::PosInt(42)));
obj.insert("negint".into(), Value::Number(Number::NegInt(-42)));
obj.insert("float".into(), Value::Number(Number::Float(3.14)));
obj.insert("string".into(), Value::String("hello".into()));
obj.insert("array".into(), Value::Array(vec![
Value::from(1i64),
Value::from("two"),
Value::Bool(true),
Value::Null,
]));
obj.insert("nested".into(), Value::Object({
let mut inner = BTreeMap::new();
inner.insert("deep".into(), Value::from(99i64));
inner
}));
let v = Value::Object(obj);
let json = to_string(&v).unwrap();
let back: Value = from_str(&json).unwrap();
assert_eq!(back, v);
assert_eq!(back["null"], Value::Null);
assert_eq!(back["bool"].as_bool(), Some(true));
assert_eq!(back["posint"].as_u64(), Some(42));
assert_eq!(back["negint"].as_i64(), Some(-42));
assert_eq!(back["array"][0].as_i64(), Some(1));
assert_eq!(back["array"][1].as_str(), Some("two"));
assert_eq!(back["nested"]["deep"].as_u64(), Some(99));
assert!(back["nonexistent"].is_null());
assert!(back["array"][999].is_null());
}
#[test]
fn stress_to_value_from_value() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Complex {
name: String,
items: Vec<i32>,
nested: Option<Box<Complex>>,
}
let c = Complex {
name: "root".into(),
items: vec![1, 2, 3],
nested: Some(Box::new(Complex {
name: "child".into(),
items: vec![4, 5],
nested: None,
})),
};
let val = to_value(&c).unwrap();
assert_eq!(val["name"].as_str(), Some("root"));
assert_eq!(val["items"][0].as_i64(), Some(1));
assert_eq!(val["nested"]["name"].as_str(), Some("child"));
let back: Complex = from_value(val).unwrap();
assert_eq!(back, c);
}
#[test]
fn stress_mega_struct() {
let mut hmap = HashMap::new();
hmap.insert("k".into(), "v".into());
let mut hset = HashSet::new();
hset.insert(1);
let mut extra = BTreeMap::new();
extra.insert("bonus".into(), Value::from(true));
let mega = MegaStruct {
primitives: Primitives {
b: true,
i8v: 42,
i16v: 1000,
i32v: 100000,
i64v: 9999999999,
u8v: 200,
u16v: 50000,
u32v: 3000000000,
u64v: 10000000000000,
f32v: 1.5,
f64v: 2.718281828459045,
c: 'X',
s: "mega".into(),
},
collections: Collections {
vec: vec![1, 2, 3],
deque: VecDeque::from(vec!["a".into()]),
list: LinkedList::from([true]),
bmap: BTreeMap::from([("k".into(), 1)]),
bset: BTreeSet::from([10]),
hmap,
hset,
},
shapes: vec![
Shape::Circle { radius: 1.0 },
Shape::Point,
],
events: vec![
Event::Click { x: 10, y: 20 },
Event::Close,
],
messages: vec![
Message::Text("hi".into()),
Message::Ping,
],
any_values: vec![
AnyValue::Int(42),
AnyValue::Str("hello".into()),
],
permissions: vec![
Permission::Read,
Permission::Admin,
],
config: Config {
name: "mega-config".into(),
debug: true,
description: Some("all the things".into()),
extra,
},
wrapped_int: Wrapper(99),
wrapped_string: Wrapper("wrapped".into()),
meters: Meters(42.195),
nested: Nested {
inner: Box::new(Some(Nested {
inner: Box::new(None),
value: 2,
})),
value: 1,
},
camel: CamelCaseStruct {
first_name: "Alice".into(),
last_name: "Smith".into(),
email_address: Some("alice@test.com".into()),
},
screaming: ScreamingStruct {
my_field: 42,
another_field: "LOUD".into(),
},
status: Status::InProgress,
optional_vec: Some(vec![Some(1), None, Some(3)]),
tuple: (42, "hello".into(), true),
array: [10, 20, 30, 40, 50],
unit: (),
};
let json = to_string(&mega).unwrap();
let back: MegaStruct = from_str(&json).unwrap();
assert_eq!(back.primitives, mega.primitives);
assert_eq!(back.shapes, mega.shapes);
assert_eq!(back.events, mega.events);
assert_eq!(back.messages, mega.messages);
assert_eq!(back.config.name, mega.config.name);
assert_eq!(back.nested.value, mega.nested.value);
assert_eq!(back.camel, mega.camel);
assert_eq!(back.screaming, mega.screaming);
assert_eq!(back.status, mega.status);
assert_eq!(back.tuple, mega.tuple);
assert_eq!(back.array, mega.array);
let pretty = to_string_pretty(&mega).unwrap();
let back_pretty: MegaStruct = from_str(&pretty).unwrap();
assert_eq!(back_pretty.primitives, mega.primitives);
assert_eq!(back_pretty.shapes, mega.shapes);
let val = to_value(&mega).unwrap();
assert!(val.is_object());
}
#[test]
fn stress_large_array() {
let arr: Vec<i32> = (0..10_000).collect();
let json = to_string(&arr).unwrap();
let back: Vec<i32> = from_str(&json).unwrap();
assert_eq!(back.len(), 10_000);
assert_eq!(back[0], 0);
assert_eq!(back[9999], 9999);
}
#[test]
fn stress_large_object() {
let mut map = BTreeMap::new();
for i in 0..1000 {
map.insert(format!("key_{i:04}"), i);
}
let json = to_string(&map).unwrap();
let back: BTreeMap<String, i32> = from_str(&json).unwrap();
assert_eq!(back.len(), 1000);
assert_eq!(back["key_0000"], 0);
assert_eq!(back["key_0999"], 999);
}
#[test]
fn stress_large_string() {
let big = "a".repeat(100_000);
let json = to_string(&big).unwrap();
let back: String = from_str(&json).unwrap();
assert_eq!(back.len(), 100_000);
assert_eq!(back, big);
}
#[test]
fn stress_deeply_nested_json_value() {
let mut v = Value::from(42i64);
for _ in 0..50 {
let mut m = BTreeMap::new();
m.insert("a".to_string(), v);
v = Value::Object(m);
}
let json = to_string(&v).unwrap();
let back: Value = from_str(&json).unwrap();
let mut current = &back;
for _ in 0..50 {
current = ¤t["a"];
}
assert_eq!(current.as_i64(), Some(42));
}
#[test]
fn stress_mixed_enum_vector() {
let shapes = vec![
Shape::Circle { radius: 1.0 },
Shape::Rectangle { width: 2.0, height: 3.0 },
Shape::Point,
Shape::Line(0.0, 1.0, 2.0, 3.0),
Shape::Circle { radius: 4.0 },
];
let json = to_string(&shapes).unwrap();
let back: Vec<Shape> = from_str(&json).unwrap();
assert_eq!(back, shapes);
}
#[test]
fn stress_nested_options_in_map() {
let mut map: BTreeMap<String, Option<Vec<Option<i32>>>> = BTreeMap::new();
map.insert("full".into(), Some(vec![Some(1), Some(2), Some(3)]));
map.insert("partial".into(), Some(vec![Some(1), None, Some(3)]));
map.insert("empty".into(), Some(vec![]));
map.insert("none".into(), None);
let json = to_string(&map).unwrap();
let back: BTreeMap<String, Option<Vec<Option<i32>>>> = from_str(&json).unwrap();
assert_eq!(back, map);
}
#[test]
fn stress_btreemap_with_integer_keys() {
let mut map = BTreeMap::new();
map.insert(1i32, "one".to_string());
map.insert(2, "two".to_string());
map.insert(-3, "neg three".to_string());
let json = to_string(&map).unwrap();
assert!(json.contains("\"1\""));
assert!(json.contains("\"2\""));
let back: BTreeMap<String, String> = from_str(&json).unwrap();
assert_eq!(back.len(), 3);
assert_eq!(back["1"], "one");
assert_eq!(back["2"], "two");
assert_eq!(back["-3"], "neg three");
}
#[test]
fn stress_pretty_vs_compact_equivalence() {
let data = vec![
Shape::Circle { radius: 5.0 },
Shape::Rectangle { width: 10.0, height: 20.0 },
];
let compact = to_string(&data).unwrap();
let pretty = to_string_pretty(&data).unwrap();
let from_compact: Vec<Shape> = from_str(&compact).unwrap();
let from_pretty: Vec<Shape> = from_str(&pretty).unwrap();
assert_eq!(from_compact, from_pretty);
}
#[test]
fn stress_value_display() {
let v = Value::from("hello");
assert_eq!(format!("{v}"), "\"hello\"");
let v = Value::from(42i64);
assert_eq!(format!("{v}"), "42");
let v = Value::Null;
assert_eq!(format!("{v}"), "null");
let v = Value::Bool(true);
assert_eq!(format!("{v}"), "true");
}
#[test]
fn stress_rc_arc() {
let r = Rc::new(42i32);
let json = to_string(&r).unwrap();
assert_eq!(json, "42");
let back: Rc<i32> = from_str(&json).unwrap();
assert_eq!(*back, 42);
let a = Arc::new("hello".to_string());
let json_a = to_string(&a).unwrap();
assert_eq!(json_a, "\"hello\"");
let back_a: Arc<String> = from_str(&json_a).unwrap();
assert_eq!(*back_a, "hello");
}
#[test]
fn stress_binary_heap() {
let heap = BinaryHeap::from(vec![3, 1, 4, 1, 5, 9, 2, 6]);
let json = to_string(&heap).unwrap();
let back: Vec<i32> = from_str(&json).unwrap();
assert_eq!(back.len(), 8);
let mut sorted = back.clone();
sorted.sort();
assert_eq!(sorted, vec![1, 1, 2, 3, 4, 5, 6, 9]);
}
#[test]
fn stress_cow_borrowed_and_owned() {
let owned: Cow<'static, str> = Cow::Owned("owned".into());
let json = to_string(&owned).unwrap();
let back: String = from_str(&json).unwrap();
assert_eq!(back, "owned");
let borrowed: Cow<str> = Cow::Borrowed("borrowed");
let json_b = to_string(&borrowed).unwrap();
assert_eq!(json_b, "\"borrowed\"");
let back_b: String = from_str(&json_b).unwrap();
assert_eq!(back_b, "borrowed");
}
#[test]
fn stress_wrapping_reverse() {
use std::num::Wrapping;
use std::cmp::Reverse;
let w = Wrapping(42i32);
let json = to_string(&w).unwrap();
let back: Wrapping<i32> = from_str(&json).unwrap();
assert_eq!(back, w);
let r = Reverse(42i32);
let json_r = to_string(&r).unwrap();
let back_r: Reverse<i32> = from_str(&json_r).unwrap();
assert_eq!(back_r, r);
}
#[test]
fn stress_ranges() {
use std::ops::{Range, RangeInclusive, Bound};
let range = Range { start: 1i32, end: 10 };
let json = to_string(&range).unwrap();
let back: Range<i32> = from_str(&json).unwrap();
assert_eq!(back, range);
let inclusive = RangeInclusive::new(1i32, 10);
let json_inc = to_string(&inclusive).unwrap();
let back_inc: RangeInclusive<i32> = from_str(&json_inc).unwrap();
assert_eq!(back_inc, inclusive);
let bound_inc = Bound::Included(42i32);
let json_bound = to_string(&bound_inc).unwrap();
let back_bound: Bound<i32> = from_str(&json_bound).unwrap();
assert_eq!(back_bound, bound_inc);
let bound_exc = Bound::Excluded(42i32);
let json_bexc = to_string(&bound_exc).unwrap();
let back_bexc: Bound<i32> = from_str(&json_bexc).unwrap();
assert_eq!(back_bexc, bound_exc);
let unbound: Bound<i32> = Bound::Unbounded;
let json_unb = to_string(&unbound).unwrap();
let back_unb: Bound<i32> = from_str(&json_unb).unwrap();
assert_eq!(back_unb, unbound);
}
#[test]
fn stress_cross_feature_flatten_with_rename() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[forma(rename_all = "camelCase")]
struct Outer {
my_name: String,
#[forma(flatten)]
extra: BTreeMap<String, Value>,
}
let mut extra = BTreeMap::new();
extra.insert("customField".into(), Value::from(42i64));
let o = Outer {
my_name: "test".into(),
extra,
};
let json = to_string(&o).unwrap();
assert!(json.contains("myName"), "should be camelCased: {json}");
let back: Outer = from_str(&json).unwrap();
assert_eq!(back.my_name, "test");
assert_eq!(back.extra["customField"].as_i64(), Some(42));
}
#[test]
fn stress_cross_feature_default_with_skip() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Mixed {
required: String,
#[forma(default = "default_count")]
count: i32,
#[forma(default)]
#[forma(skip_serializing_if = "Option::is_none")]
optional: Option<String>,
#[forma(skip)]
ignored: i32,
}
fn default_count() -> i32 { 42 }
let json = r#"{"required":"hello"}"#;
let m: Mixed = from_str(json).unwrap();
assert_eq!(m.required, "hello");
assert_eq!(m.count, 42); assert_eq!(m.optional, None);
assert_eq!(m.ignored, 0);
let m2 = Mixed {
required: "world".into(),
count: 10,
optional: None,
ignored: 999,
};
let json2 = to_string(&m2).unwrap();
assert!(!json2.contains("optional"), "None should be skipped");
assert!(!json2.contains("ignored"), "skipped field should be absent");
assert!(!json2.contains("999"), "skipped field value should be absent");
}
#[test]
fn stress_generic_struct_multiple_type_params() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Triple<A, B, C> {
first: A,
second: B,
third: C,
}
let t = Triple {
first: 42i32,
second: "hello".to_string(),
third: vec![true, false],
};
let json = to_string(&t).unwrap();
let back: Triple<i32, String, Vec<bool>> = from_str(&json).unwrap();
assert_eq!(back, t);
}
#[test]
fn stress_enum_in_struct_in_vec_in_option() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Container {
items: Option<Vec<Shape>>,
}
let c = Container {
items: Some(vec![
Shape::Circle { radius: 1.0 },
Shape::Point,
]),
};
let json = to_string(&c).unwrap();
let back: Container = from_str(&json).unwrap();
assert_eq!(back, c);
let empty = Container { items: None };
let json_empty = to_string(&empty).unwrap();
let back_empty: Container = from_str(&json_empty).unwrap();
assert_eq!(back_empty, empty);
}
#[test]
fn stress_map_of_enums() {
let mut map = BTreeMap::new();
map.insert("circle".to_string(), Shape::Circle { radius: 5.0 });
map.insert("point".to_string(), Shape::Point);
map.insert("rect".to_string(), Shape::Rectangle { width: 10.0, height: 20.0 });
let json = to_string(&map).unwrap();
let back: BTreeMap<String, Shape> = from_str(&json).unwrap();
assert_eq!(back, map);
}
#[test]
fn stress_number_precision() {
let max = u64::MAX;
let json = to_string(&max).unwrap();
assert_eq!(json, "18446744073709551615");
let back: u64 = from_str(&json).unwrap();
assert_eq!(back, max);
let min = i64::MIN;
let json_min = to_string(&min).unwrap();
assert_eq!(json_min, "-9223372036854775808");
let back_min: i64 = from_str(&json_min).unwrap();
assert_eq!(back_min, min);
}
#[test]
fn stress_scientific_notation() {
let v: f64 = from_str("1e10").unwrap();
assert_eq!(v, 1e10);
let v2: f64 = from_str("2.5E-3").unwrap();
assert!((v2 - 0.0025).abs() < 1e-15);
let v3: f64 = from_str("-1.23e+4").unwrap();
assert!((v3 - (-12300.0)).abs() < 1e-10);
}
#[test]
fn stress_value_from_impls() {
assert_eq!(Value::from(true), Value::Bool(true));
assert_eq!(Value::from(false), Value::Bool(false));
assert_eq!(Value::from(42i64), Value::Number(Number::PosInt(42)));
assert_eq!(Value::from(-1i64), Value::Number(Number::NegInt(-1)));
assert_eq!(Value::from(42u64), Value::Number(Number::PosInt(42)));
assert_eq!(Value::from(3.14f64), Value::Number(Number::Float(3.14)));
assert_eq!(Value::from("hello"), Value::String("hello".into()));
assert_eq!(Value::from("hello".to_string()), Value::String("hello".into()));
assert_eq!(Value::from(()), Value::Null);
assert_eq!(Value::from(None::<i64>), Value::Null);
assert_eq!(Value::from(Some(42i64)), Value::Number(Number::PosInt(42)));
}
#[test]
fn stress_simultaneous_features() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[forma(tag = "action")]
enum Action {
Create { name: String },
Delete { id: i32 },
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Request {
action: Action,
#[forma(default)]
#[forma(skip_serializing_if = "Option::is_none")]
trace_id: Option<String>,
}
let req = Request {
action: Action::Create { name: "test".into() },
trace_id: Some("abc-123".into()),
};
let json = to_string(&req).unwrap();
let back: Request = from_str(&json).unwrap();
assert_eq!(back, req);
let req2 = Request {
action: Action::Delete { id: 42 },
trace_id: None,
};
let json2 = to_string(&req2).unwrap();
assert!(!json2.contains("trace_id"));
let back2: Request = from_str(&json2).unwrap();
assert_eq!(back2, req2);
}
#[test]
fn stress_complex_json_parsing() {
let json = r#"{
"users": [
{
"id": 1,
"name": "Alice",
"email": "alice@example.com",
"roles": ["admin", "user"],
"settings": {
"theme": "dark",
"notifications": true,
"timeout": 300
},
"address": null
},
{
"id": 2,
"name": "Bob",
"email": "bob@example.com",
"roles": ["user"],
"settings": {
"theme": "light",
"notifications": false,
"timeout": 600
},
"address": {
"street": "123 Main St",
"city": "Springfield"
}
}
],
"total": 2,
"page": 1,
"has_more": false
}"#;
let v: Value = from_str(json).unwrap();
assert_eq!(v["users"][0]["name"].as_str(), Some("Alice"));
assert_eq!(v["users"][1]["settings"]["timeout"].as_u64(), Some(600));
assert!(v["users"][0]["address"].is_null());
assert_eq!(v["total"].as_u64(), Some(2));
assert_eq!(v["has_more"].as_bool(), Some(false));
let json2 = to_string(&v).unwrap();
let v2: Value = from_str(&json2).unwrap();
assert_eq!(v, v2);
}
#[test]
fn stress_empty_everything() {
let es: String = from_str("\"\"").unwrap();
assert_eq!(es, "");
let ea: Vec<i32> = from_str("[]").unwrap();
assert!(ea.is_empty());
let eo: BTreeMap<String, i32> = from_str("{}").unwrap();
assert!(eo.is_empty());
let on: Option<i32> = from_str("null").unwrap();
assert_eq!(on, None);
}
#[test]
fn stress_special_characters_in_keys() {
let mut map = BTreeMap::new();
map.insert("key with spaces".to_string(), 1i32);
map.insert("key\"with\"quotes".to_string(), 2);
map.insert("key\\with\\backslashes".to_string(), 3);
map.insert("key\nwith\nnewlines".to_string(), 4);
map.insert("".to_string(), 5);
let json = to_string(&map).unwrap();
let back: BTreeMap<String, i32> = from_str(&json).unwrap();
assert_eq!(back, map);
}
#[test]
fn stress_from_reader_basic() {
let json = b"{\"name\":\"Alice\",\"age\":30}";
let cursor = std::io::Cursor::new(json);
let v: Value = forma_json::from_reader(cursor).unwrap();
assert_eq!(v["name"].as_str(), Some("Alice"));
assert_eq!(v["age"].as_u64(), Some(30));
}
#[test]
fn stress_from_reader_struct() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Person {
name: String,
age: u32,
}
let json = br#"{"name":"Bob","age":25}"#;
let person: Person = forma_json::from_reader(std::io::Cursor::new(json)).unwrap();
assert_eq!(person, Person { name: "Bob".into(), age: 25 });
}
#[test]
fn stress_from_reader_large() {
let data: Vec<i32> = (0..5000).collect();
let json = to_string(&data).unwrap();
let cursor = std::io::Cursor::new(json.as_bytes());
let back: Vec<i32> = forma_json::from_reader(cursor).unwrap();
assert_eq!(back, data);
}
#[test]
fn stress_from_reader_nested() {
let json = br#"{"users":[{"name":"Alice"},{"name":"Bob"}]}"#;
let v: Value = forma_json::from_reader(std::io::Cursor::new(json)).unwrap();
assert_eq!(v["users"][0]["name"].as_str(), Some("Alice"));
assert_eq!(v["users"][1]["name"].as_str(), Some("Bob"));
}
#[test]
fn stress_from_reader_empty_error() {
let cursor = std::io::Cursor::new(b"" as &[u8]);
let result: Result<Value, _> = forma_json::from_reader(cursor);
assert!(result.is_err());
}
#[test]
fn stress_from_reader_file_simulation() {
let json = b" \n\n { \n \"x\" : 1 , \n \"y\" : 2 \n } \n";
let cursor = std::io::Cursor::new(json);
let v: Value = forma_json::from_reader(cursor).unwrap();
assert_eq!(v["x"].as_u64(), Some(1));
assert_eq!(v["y"].as_u64(), Some(2));
}
#[test]
fn stress_from_reader_unicode() {
let json = r#"{"emoji":"😀","chinese":"世界"}"#;
let cursor = std::io::Cursor::new(json.as_bytes());
let v: Value = forma_json::from_reader(cursor).unwrap();
assert_eq!(v["emoji"].as_str(), Some("😀"));
assert_eq!(v["chinese"].as_str(), Some("世界"));
}
#[derive(Serialize)]
struct Circle {
radius: f64,
#[forma(getter = "Circle::computed_area")]
area: f64,
}
impl Circle {
fn new(radius: f64) -> Self {
Circle { radius, area: 0.0 } }
fn computed_area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
}
#[test]
fn stress_getter_standalone() {
let c = Circle::new(1.0);
let json = to_string(&c).unwrap();
let parsed: HashMap<String, f64> = from_str(&json).unwrap();
assert_eq!(parsed["radius"], 1.0);
assert!((parsed["area"] - std::f64::consts::PI).abs() < 1e-10);
}
fn display_name(p: &Person) -> String {
format!("{} {}", p.first, p.last)
}
#[derive(Serialize)]
struct Person {
#[forma(skip)]
first: String,
#[forma(skip)]
last: String,
#[forma(getter = "display_name")]
name: String,
}
#[test]
fn stress_getter_computed() {
let p = Person { first: "Ada".into(), last: "Lovelace".into(), name: String::new() };
let json = to_string(&p).unwrap();
assert_eq!(json, r#"{"name":"Ada Lovelace"}"#);
}
fn validate_positive(v: &i32) -> Result<(), String> {
if *v > 0 { Ok(()) } else { Err("must be positive".into()) }
}
fn validate_non_empty(v: &str) -> Result<(), String> {
if !v.is_empty() { Ok(()) } else { Err("must not be empty".into()) }
}
fn validate_user(u: &ValidatedUser) -> Result<(), String> {
if u.age < 150 { Ok(()) } else { Err("unrealistic age".into()) }
}
#[derive(Deserialize, Debug)]
#[forma(validate = "validate_user")]
struct ValidatedUser {
#[forma(validate = "validate_non_empty")]
name: String,
#[forma(validate = "validate_positive")]
age: i32,
}
#[test]
fn stress_validation_passes() {
let json = r#"{"name":"Ada","age":36}"#;
let user: ValidatedUser = from_str(json).unwrap();
assert_eq!(user.name, "Ada");
assert_eq!(user.age, 36);
}
#[test]
fn stress_validation_field_fails() {
let json = r#"{"name":"Ada","age":-1}"#;
let result: Result<ValidatedUser, _> = from_str(json);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("must be positive"), "got: {err}");
}
#[test]
fn stress_validation_container_fails() {
let json = r#"{"name":"Methuselah","age":969}"#;
let result: Result<ValidatedUser, _> = from_str(json);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("unrealistic age"), "got: {err}");
}
#[test]
fn stress_validation_empty_string_fails() {
let json = r#"{"name":"","age":25}"#;
let result: Result<ValidatedUser, _> = from_str(json);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("must not be empty"), "got: {err}");
}
#[test]
fn stress_recursion_limit_arrays() {
let mut json = String::new();
for _ in 0..200 {
json.push('[');
}
json.push('1');
for _ in 0..200 {
json.push(']');
}
let result: Result<Value, _> = from_str(&json);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("recursion limit"), "expected recursion error, got: {err}");
}
#[test]
fn stress_recursion_limit_objects() {
let mut json = String::new();
for _ in 0..200 {
json.push_str(r#"{"a":"#);
}
json.push_str("1");
for _ in 0..200 {
json.push('}');
}
let result: Result<Value, _> = from_str(&json);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("recursion limit"), "expected recursion error, got: {err}");
}
#[test]
fn stress_recursion_limit_custom() {
let mut json = String::new();
for _ in 0..10 {
json.push('[');
}
json.push('1');
for _ in 0..10 {
json.push(']');
}
let mut de = forma_json::de::Deserializer::from_str(&json);
de.set_depth_limit(5);
use forma_core::de::Deserialize;
let result = Value::deserialize(&mut de);
assert!(result.is_err());
}
#[test]
fn stress_recursion_within_limit() {
let mut json = String::new();
for _ in 0..100 {
json.push('[');
}
json.push('1');
for _ in 0..100 {
json.push(']');
}
let result: Result<Value, _> = from_str(&json);
assert!(result.is_ok());
}
#[test]
fn stress_lenient_string_as_i64() {
let v: i64 = forma_json::from_str_lenient(r#""42""#).unwrap();
assert_eq!(v, 42);
}
#[test]
fn stress_lenient_string_as_u64() {
let v: u64 = forma_json::from_str_lenient(r#""100""#).unwrap();
assert_eq!(v, 100);
}
#[test]
fn stress_lenient_string_as_f64() {
let v: f64 = forma_json::from_str_lenient(r#""3.14""#).unwrap();
assert!((v - 3.14).abs() < 1e-10);
}
#[test]
fn stress_lenient_string_as_bool() {
let v: bool = forma_json::from_str_lenient(r#""true""#).unwrap();
assert!(v);
let v: bool = forma_json::from_str_lenient(r#""false""#).unwrap();
assert!(!v);
}
#[test]
fn stress_lenient_number_as_bool() {
let v: bool = forma_json::from_str_lenient("1").unwrap();
assert!(v);
let v: bool = forma_json::from_str_lenient("0").unwrap();
assert!(!v);
}
#[test]
fn stress_lenient_single_to_array() {
let v: Vec<i32> = forma_json::from_str_lenient("42").unwrap();
assert_eq!(v, vec![42]);
}
#[test]
fn stress_lenient_single_string_to_array() {
let v: Vec<String> = forma_json::from_str_lenient(r#""hello""#).unwrap();
assert_eq!(v, vec!["hello"]);
}
#[test]
fn stress_lenient_struct_with_string_numbers() {
#[derive(Deserialize, Debug, PartialEq)]
struct AiOutput {
value: i64,
score: f64,
enabled: bool,
}
let json = r#"{"value":"42","score":"0.95","enabled":"true"}"#;
let v: AiOutput = forma_json::from_str_lenient(json).unwrap();
assert_eq!(v.value, 42);
assert!((v.score - 0.95).abs() < 1e-10);
assert!(v.enabled);
}
#[test]
fn stress_lenient_normal_still_works() {
let v: Vec<i32> = forma_json::from_str_lenient("[1,2,3]").unwrap();
assert_eq!(v, vec![1, 2, 3]);
let v: bool = forma_json::from_str_lenient("true").unwrap();
assert!(v);
}
#[test]
fn stress_strict_rejects_string_as_number() {
let result: Result<i64, _> = from_str(r#""42""#);
assert!(result.is_err());
}
#[test]
fn stress_range_full() {
let r = ..;
let json = to_string(&r).unwrap();
assert_eq!(json, "null");
let back: std::ops::RangeFull = from_str(&json).unwrap();
let _ = back; }
#[test]
fn stress_range_to_inclusive() {
let r = ..=10i32;
let json = to_string(&r).unwrap();
let back: std::ops::RangeToInclusive<i32> = from_str(&json).unwrap();
assert_eq!(back.end, 10);
}
use forma_derive::Schema;
use forma_core::schema::JsonSchema;
#[derive(Serialize, Schema)]
struct ToolInput {
query: String,
max_results: Option<u32>,
verbose: bool,
}
#[test]
fn stress_schema_struct() {
let schema = ToolInput::schema();
let json = to_string(&schema).unwrap();
let v: Value = from_str(&json).unwrap();
assert_eq!(v["type"].as_str(), Some("object"));
assert_eq!(v["title"].as_str(), Some("ToolInput"));
let required = v["required"].as_array().unwrap();
let req_strs: Vec<&str> = required.iter().map(|v| v.as_str().unwrap()).collect();
assert!(req_strs.contains(&"query"));
assert!(req_strs.contains(&"verbose"));
assert!(!req_strs.contains(&"max_results"));
assert!(v["properties"]["query"]["type"].as_str() == Some("string"));
assert!(v["properties"]["verbose"]["type"].as_str() == Some("boolean"));
assert!(v["properties"]["max_results"]["anyOf"].as_array().is_some());
}
#[derive(Serialize, Schema)]
enum Color {
Red,
Green,
Blue,
}
#[test]
fn stress_schema_string_enum() {
let schema = Color::schema();
let json = to_string(&schema).unwrap();
let v: Value = from_str(&json).unwrap();
let variants = v["enum"].as_array().unwrap();
assert_eq!(variants.len(), 3);
let names: Vec<&str> = variants.iter().map(|v| v.as_str().unwrap()).collect();
assert_eq!(names, vec!["Red", "Green", "Blue"]);
}
#[derive(Serialize, Schema)]
#[forma(rename_all = "camelCase")]
struct CamelSchema {
first_name: String,
last_name: String,
}
#[test]
fn stress_schema_rename_all() {
let schema = CamelSchema::schema();
let json = to_string(&schema).unwrap();
let v: Value = from_str(&json).unwrap();
let props = &v["properties"];
assert!(props["firstName"]["type"].as_str() == Some("string"));
assert!(props["lastName"]["type"].as_str() == Some("string"));
}
#[derive(Serialize, Schema)]
#[forma(tag = "type")]
enum UiEvent {
Click { x: i32, y: i32 },
Scroll { delta: f64 },
}
#[test]
fn stress_schema_internal_tag() {
let schema = UiEvent::schema();
let json = to_string(&schema).unwrap();
let v: Value = from_str(&json).unwrap();
let variants = v["oneOf"].as_array().unwrap();
assert_eq!(variants.len(), 2);
let click = &variants[0];
assert_eq!(click["type"].as_str(), Some("object"));
}
#[test]
fn stress_schema_primitives() {
assert_eq!(to_string(&bool::schema()).unwrap(), r#"{"type":"boolean"}"#);
assert_eq!(to_string(&i32::schema()).unwrap(), r#"{"type":"integer"}"#);
assert_eq!(to_string(&f64::schema()).unwrap(), r#"{"type":"number"}"#);
assert_eq!(to_string(&String::schema()).unwrap(), r#"{"type":"string"}"#);
}
#[test]
fn stress_schema_vec() {
let schema = Vec::<String>::schema();
let json = to_string(&schema).unwrap();
let v: Value = from_str(&json).unwrap();
assert_eq!(v["type"].as_str(), Some("array"));
assert_eq!(v["items"]["type"].as_str(), Some("string"));
}
#[test]
fn stress_schema_option() {
let schema = Option::<i32>::schema();
let json = to_string(&schema).unwrap();
let v: Value = from_str(&json).unwrap();
let any_of = v["anyOf"].as_array().unwrap();
assert_eq!(any_of.len(), 2);
}