use verit::{encode, Dt, Error, Message, Resolver, Schema, SchemaBuilder, SchemaMode, Value};
fn v1() -> Schema {
SchemaBuilder::new()
.add_struct("Person", vec![(1, "name", Dt::Str), (2, "age", Dt::U8)])
.build("Person")
.unwrap()
}
fn v2() -> Schema {
SchemaBuilder::new()
.add_struct(
"Person",
vec![
(1, "full_name", Dt::Str),
(2, "age", Dt::U8),
(3, "email", Dt::Str),
],
)
.build("Person")
.unwrap()
}
fn v3() -> Schema {
SchemaBuilder::new()
.add_struct("Person", vec![(1, "name", Dt::Str), (2, "age", Dt::U64)])
.build("Person")
.unwrap()
}
#[test]
fn old_data_new_reader() {
let (v1, v2) = (v1(), v2());
let bytes = encode(
&v1,
&Value::Struct(vec![(1, Value::str("Ada")), (2, Value::U8(36))]),
SchemaMode::HashOnly,
)
.unwrap();
let resolver = Resolver::new(&v1, &v2).unwrap();
let msg = Message::parse(&bytes).unwrap();
let root = msg.root(&resolver).unwrap();
assert_eq!(root.get_str(1).unwrap(), Some("Ada"));
assert_eq!(root.get_u8(2).unwrap(), Some(36));
assert_eq!(root.get_str(3).unwrap(), None);
}
#[test]
fn new_data_old_reader() {
let (v1, v2) = (v1(), v2());
let bytes = encode(
&v2,
&Value::Struct(vec![
(1, Value::str("Grace")),
(2, Value::U8(51)),
(3, Value::str("grace@navy.mil")),
]),
SchemaMode::HashOnly,
)
.unwrap();
let resolver = Resolver::new(&v2, &v1).unwrap();
let msg = Message::parse(&bytes).unwrap();
let root = msg.root(&resolver).unwrap();
assert_eq!(root.get_str(1).unwrap(), Some("Grace"));
assert_eq!(root.get_u8(2).unwrap(), Some(51));
assert!(matches!(root.get(3), Err(Error::UnknownFieldId(3))));
}
#[test]
fn integer_widening_converts_on_load() {
let (v1, v3) = (v1(), v3());
let bytes = encode(
&v1,
&Value::Struct(vec![(1, Value::str("Ada")), (2, Value::U8(36))]),
SchemaMode::HashOnly,
)
.unwrap();
let resolver = Resolver::new(&v1, &v3).unwrap();
let msg = Message::parse(&bytes).unwrap();
let root = msg.root(&resolver).unwrap();
assert_eq!(root.get_u64(2).unwrap(), Some(36));
}
#[test]
fn narrowing_fails_loudly_at_plan_time() {
let (v1, v3) = (v1(), v3());
match Resolver::new(&v3, &v1) {
Err(Error::Incompatible(msg)) => {
assert!(msg.contains("age"), "error should name the field: {msg}")
}
other => panic!("expected Incompatible, got {other:?}"),
}
}
#[test]
fn declaration_order_never_changes_schema_id() {
let a = SchemaBuilder::new()
.add_struct("B", vec![(1, "x", Dt::U32), (2, "y", Dt::Str)])
.add_enum("A", vec![(0, "Off"), (1, "On")])
.build("B")
.unwrap();
let b = SchemaBuilder::new()
.add_enum("A", vec![(1, "On"), (0, "Off")])
.add_struct("B", vec![(2, "y", Dt::Str), (1, "x", Dt::U32)])
.build("B")
.unwrap();
assert_eq!(a.id(), b.id());
assert_eq!(a.canonical_bytes(), b.canonical_bytes());
}
#[test]
fn rename_changes_id_but_still_resolves() {
let (v1, v2) = (v1(), v2());
assert_ne!(v1.id(), v2.id());
assert!(Resolver::new(&v1, &v2).is_ok());
assert!(Resolver::new(&v2, &v1).is_ok());
}
#[test]
fn wrong_resolver_is_rejected_by_schema_id() {
let (v1, v2) = (v1(), v2());
let bytes = encode(
&v2,
&Value::Struct(vec![(1, Value::str("Grace"))]),
SchemaMode::HashOnly,
)
.unwrap();
let msg = Message::parse(&bytes).unwrap();
let wrong = Resolver::identity(&v1).unwrap();
assert!(matches!(
msg.root(&wrong),
Err(Error::SchemaIdMismatch { .. })
));
}
#[test]
fn nested_struct_evolution() {
let old = SchemaBuilder::new()
.add_struct("Point", vec![(1, "x", Dt::F64)])
.add_struct("Shape", vec![(1, "center", Dt::named("Point"))])
.build("Shape")
.unwrap();
let new = SchemaBuilder::new()
.add_struct("Point", vec![(1, "x", Dt::F64), (2, "z", Dt::F64)])
.add_struct(
"Shape",
vec![
(1, "center", Dt::named("Point")),
(2, "corners", Dt::list(Dt::named("Point"))),
],
)
.build("Shape")
.unwrap();
let bytes = encode(
&old,
&Value::Struct(vec![(1, Value::Struct(vec![(1, Value::F64(4.5))]))]),
SchemaMode::HashOnly,
)
.unwrap();
let resolver = Resolver::new(&old, &new).unwrap();
let msg = Message::parse(&bytes).unwrap();
let root = msg.root(&resolver).unwrap();
let center = root.get_struct(1).unwrap().unwrap();
assert_eq!(center.get_f64(1).unwrap(), Some(4.5));
assert_eq!(center.get_f64(2).unwrap(), None, "added nested field");
assert_eq!(
root.get_list(2).unwrap().map(|_| ()),
None,
"added list field"
);
}
#[test]
fn list_element_widening() {
let old = SchemaBuilder::new()
.add_struct("Data", vec![(1, "readings", Dt::list(Dt::U16))])
.build("Data")
.unwrap();
let new = SchemaBuilder::new()
.add_struct("Data", vec![(1, "readings", Dt::list(Dt::U32))])
.build("Data")
.unwrap();
let bytes = encode(
&old,
&Value::Struct(vec![(
1,
Value::List(vec![Value::U16(7), Value::U16(65535)]),
)]),
SchemaMode::HashOnly,
)
.unwrap();
let resolver = Resolver::new(&old, &new).unwrap();
let msg = Message::parse(&bytes).unwrap();
let root = msg.root(&resolver).unwrap();
let readings = root.get_list(1).unwrap().unwrap();
assert_eq!(readings.len(), 2);
assert!(matches!(readings.get(0).unwrap(), verit::Ref::U32(7)));
assert!(matches!(readings.get(1).unwrap(), verit::Ref::U32(65535)));
}