use std::collections::BTreeMap;
use serde_json::json;
use super::Value;
fn map_of(pairs: &[(&str, Value)]) -> Value {
Value::Map(
pairs
.iter()
.map(|(key, value)| ((*key).to_owned(), value.clone()))
.collect::<BTreeMap<_, _>>(),
)
}
#[test]
fn string_serializes_bare_not_externally_tagged() {
let encoded = serde_json::to_value(Value::String("foo".to_owned())).unwrap();
assert_eq!(encoded, json!("foo"));
assert_ne!(encoded, json!({ "String": "foo" }));
}
#[test]
fn empty_map_serializes_as_bare_object() {
let encoded = serde_json::to_value(Value::Map(BTreeMap::new())).unwrap();
assert_eq!(encoded, json!({}));
assert_ne!(encoded, json!({ "Map": {} }));
}
#[test]
fn list_serializes_as_bare_array() {
let value = Value::List(vec![Value::Int(1), Value::String("a".to_owned())]);
assert_eq!(serde_json::to_value(value).unwrap(), json!([1, "a"]));
}
#[test]
fn wire_shape_matches_to_plain_json_exactly() {
let value = Value::List(vec![
map_of(&[("nested", Value::Bool(true)), ("n", Value::Int(-7))]),
Value::Float(1.5),
Value::Null,
Value::String("x".to_owned()),
]);
assert_eq!(serde_json::to_value(&value).unwrap(), value.to_plain_json());
}
#[test]
fn json_round_trips_every_variant_except_bytes() {
for value in [
Value::Null,
Value::Bool(true),
Value::Bool(false),
Value::Int(0),
Value::Int(-42),
Value::Int(i64::MAX),
Value::Int(i64::MIN),
Value::Float(1.5),
Value::String(String::new()),
Value::String("héllo ⚙".to_owned()),
Value::List(vec![]),
Value::List(vec![Value::Int(1), Value::Null]),
Value::Map(BTreeMap::new()),
map_of(&[("a", Value::Int(1)), ("b", Value::List(vec![Value::Null]))]),
] {
let encoded = serde_json::to_string(&value).unwrap();
let decoded: Value = serde_json::from_str(&encoded).unwrap();
assert_eq!(decoded, value, "json round-trip failed for {value:?}");
}
}
#[test]
fn json_decodes_what_any_other_producer_writes() {
let decoded: Value = serde_json::from_str(r#"{"models":["gpt-4","gpt-4o"]}"#).unwrap();
assert_eq!(
decoded,
map_of(&[(
"models",
Value::List(vec![
Value::String("gpt-4".to_owned()),
Value::String("gpt-4o".to_owned()),
])
)])
);
}
#[test]
fn json_bytes_are_base64_and_decode_back_as_string() {
let value = Value::Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF]);
let encoded = serde_json::to_value(&value).unwrap();
assert_eq!(encoded, json!("3q2+7w=="));
assert_eq!(encoded, value.to_plain_json());
let decoded: Value = serde_json::from_value(encoded).unwrap();
assert_eq!(decoded, Value::String("3q2+7w==".to_owned()));
}
#[test]
fn cbor_round_trips_every_variant_including_bytes() {
for value in [
Value::Null,
Value::Bool(true),
Value::Int(0),
Value::Int(-42),
Value::Int(i64::MAX),
Value::Int(i64::MIN),
Value::Float(1.5),
Value::String("héllo ⚙".to_owned()),
Value::Bytes(vec![]),
Value::Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF]),
Value::List(vec![]),
Value::List(vec![Value::Int(1), Value::Null]),
Value::Map(BTreeMap::new()),
map_of(&[("a", Value::Bytes(vec![1, 2]))]),
] {
let encoded = minicbor_serde::to_vec(&value).unwrap();
let decoded: Value = minicbor_serde::from_slice(&encoded).unwrap();
assert_eq!(decoded, value, "cbor round-trip failed for {value:?}");
}
}
#[test]
fn cbor_null_is_rfc8949_null_not_an_empty_array() {
assert_eq!(minicbor_serde::to_vec(&Value::Null).unwrap(), vec![0xf6]);
}
#[test]
fn cbor_bytes_are_a_native_byte_string() {
assert_eq!(
minicbor_serde::to_vec(Value::Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF])).unwrap(),
vec![0x44, 0xDE, 0xAD, 0xBE, 0xEF]
);
}
#[test]
fn cbor_string_is_bare() {
assert_eq!(
minicbor_serde::to_vec(Value::String("foo".to_owned())).unwrap(),
vec![0x63, b'f', b'o', b'o']
);
}
#[test]
fn cbor_unsigned_integers_decode_as_int() {
let encoded = minicbor_serde::to_vec(Value::Int(7)).unwrap();
let decoded: Value = minicbor_serde::from_slice(&encoded).unwrap();
assert_eq!(decoded, Value::Int(7));
}
#[test]
fn oversized_unsigned_degrades_to_float_rather_than_erroring() {
let encoded = minicbor_serde::to_vec(i64::MAX as u64 + 1).unwrap();
let decoded: Value = minicbor_serde::from_slice(&encoded).unwrap();
assert!(matches!(decoded, Value::Float(_)), "got {decoded:?}");
}
#[test]
fn deeply_nested_structures_survive_both_formats() {
let value = map_of(&[
(
"meta",
map_of(&[
("tags", Value::List(vec![Value::String("a".to_owned())])),
("count", Value::Int(2)),
("missing", Value::Null),
]),
),
("flag", Value::Bool(false)),
]);
let json_encoded = serde_json::to_string(&value).unwrap();
assert_eq!(
serde_json::from_str::<Value>(&json_encoded).unwrap(),
value.clone()
);
let cbor_encoded = minicbor_serde::to_vec(&value).unwrap();
assert_eq!(
minicbor_serde::from_slice::<Value>(&cbor_encoded).unwrap(),
value
);
}