use verit::{
dump_json, encode, Budget, Dt, Error, Message, Ref, Resolver, SchemaBuilder, SchemaMode, Value,
};
fn wrap(field_ty: Dt) -> verit::Schema {
SchemaBuilder::new()
.add_struct("Doc", vec![(1, "m", field_ty)])
.build("Doc")
.unwrap()
}
fn map_val(entries: Vec<(Value, Value)>) -> Value {
Value::Struct(vec![(1, Value::Map(entries))])
}
#[test]
fn string_keyed_map_round_trips_and_reads_in_key_order() {
let schema = wrap(Dt::map(Dt::Str, Dt::U32));
let bytes = encode(
&schema,
&map_val(vec![
(Value::str("gamma"), Value::U32(3)),
(Value::str("alpha"), Value::U32(1)),
(Value::str("beta"), Value::U32(2)),
]),
SchemaMode::Inline,
)
.unwrap();
let resolver = Resolver::identity(&schema).unwrap();
let msg = Message::parse(&bytes).unwrap();
let root = msg.root(&resolver).unwrap();
let m = root.get_map(1).unwrap().unwrap();
assert_eq!(m.len(), 3);
let got: Vec<(String, u32)> = m
.iter()
.map(|e| {
let (k, v) = e.unwrap();
match (k, v) {
(Ref::Str(k), Ref::U32(v)) => (k.to_string(), v),
_ => panic!("wrong ref types"),
}
})
.collect();
assert_eq!(
got,
vec![("alpha".into(), 1), ("beta".into(), 2), ("gamma".into(), 3)]
);
assert_eq!(
dump_json(&bytes).unwrap(),
r#"{"m":{"alpha":1,"beta":2,"gamma":3}}"#
);
}
#[test]
fn insertion_order_does_not_affect_bytes() {
let schema = wrap(Dt::map(Dt::U16, Dt::Str));
let a = encode(
&schema,
&map_val(vec![
(Value::U16(10), Value::str("ten")),
(Value::U16(2), Value::str("two")),
(Value::U16(7), Value::str("seven")),
]),
SchemaMode::HashOnly,
)
.unwrap();
let b = encode(
&schema,
&map_val(vec![
(Value::U16(7), Value::str("seven")),
(Value::U16(10), Value::str("ten")),
(Value::U16(2), Value::str("two")),
]),
SchemaMode::HashOnly,
)
.unwrap();
assert_eq!(
a, b,
"canonical key ordering makes encoding order-independent"
);
}
#[test]
fn duplicate_keys_are_rejected() {
let schema = wrap(Dt::map(Dt::Str, Dt::U8));
let err = encode(
&schema,
&map_val(vec![
(Value::str("k"), Value::U8(1)),
(Value::str("k"), Value::U8(2)),
]),
SchemaMode::HashOnly,
)
.unwrap_err();
assert!(matches!(err, Error::DuplicateMapKey));
}
#[test]
fn map_of_struct_values() {
let schema = SchemaBuilder::new()
.add_dense_struct("Point", vec![(1, "x", Dt::F64), (2, "y", Dt::F64)])
.add_struct("Doc", vec![(1, "m", Dt::map(Dt::Str, Dt::named("Point")))])
.build("Doc")
.unwrap();
let bytes = encode(
&schema,
&map_val(vec![
(
Value::str("origin"),
Value::Struct(vec![(1, Value::F64(0.0)), (2, Value::F64(0.0))]),
),
(
Value::str("unit"),
Value::Struct(vec![(1, Value::F64(1.0)), (2, Value::F64(1.0))]),
),
]),
SchemaMode::Inline,
)
.unwrap();
assert_eq!(
dump_json(&bytes).unwrap(),
r#"{"m":{"origin":{"x":0,"y":0},"unit":{"x":1,"y":1}}}"#
);
let resolver = Resolver::identity(&schema).unwrap();
let msg = Message::parse(&bytes).unwrap();
let budget = Budget::new(msg.suggested_budget());
msg.verify(&resolver, &budget).unwrap();
}
#[test]
fn map_value_widens_under_evolution_but_key_change_is_incompatible() {
let writer = wrap(Dt::map(Dt::Str, Dt::U16));
let bytes = encode(
&writer,
&map_val(vec![(Value::str("n"), Value::U16(60000))]),
SchemaMode::HashOnly,
)
.unwrap();
let reader = wrap(Dt::map(Dt::Str, Dt::U32));
let resolver = Resolver::new(&writer, &reader).unwrap();
let msg = Message::parse(&bytes).unwrap();
let root = msg.root(&resolver).unwrap();
let m = root.get_map(1).unwrap().unwrap();
let (k, v) = m.get(0).unwrap();
assert!(matches!(k, Ref::Str("n")));
assert!(matches!(v, Ref::U32(60000)));
let bad_reader = wrap(Dt::map(Dt::U32, Dt::U32));
assert!(matches!(
Resolver::new(&writer, &bad_reader),
Err(Error::Incompatible(_))
));
}
#[test]
fn nested_and_listed_maps_round_trip() {
let schema = wrap(Dt::map(Dt::Str, Dt::map(Dt::U8, Dt::U8)));
let bytes = encode(
&schema,
&map_val(vec![(
Value::str("outer"),
Value::Map(vec![
(Value::U8(2), Value::U8(20)),
(Value::U8(1), Value::U8(10)),
]),
)]),
SchemaMode::Inline,
)
.unwrap();
assert_eq!(
dump_json(&bytes).unwrap(),
r#"{"m":{"outer":{"1":10,"2":20}}}"#
);
}
#[test]
fn invalid_key_type_is_rejected_at_build() {
assert!(SchemaBuilder::new()
.add_struct("Doc", vec![(1, "m", Dt::map(Dt::F64, Dt::U8))])
.build("Doc")
.is_err());
assert!(SchemaBuilder::new()
.add_struct("Doc", vec![(1, "m", Dt::map(Dt::Bytes, Dt::U8))])
.build("Doc")
.is_err());
}
#[test]
fn empty_map_round_trips() {
let schema = wrap(Dt::map(Dt::Str, Dt::U8));
let bytes = encode(&schema, &map_val(vec![]), SchemaMode::Inline).unwrap();
assert_eq!(dump_json(&bytes).unwrap(), r#"{"m":{}}"#);
let resolver = Resolver::identity(&schema).unwrap();
let msg = Message::parse(&bytes).unwrap();
let root = msg.root(&resolver).unwrap();
assert_eq!(root.get_map(1).unwrap().unwrap().len(), 0);
}
#[test]
fn idl_parses_map_syntax() {
let schema = verit::idl::parse("struct Doc { 1: m map<string, u32> } root Doc").unwrap();
let hand = wrap(Dt::map(Dt::Str, Dt::U32));
assert_eq!(schema.id(), hand.id());
}