#[rustfmt::skip]
#[path = "../examples/bench/verit_gen.rs"]
mod vg;
use verit::{
codegen, encode, Budget, Dt, Error, Message, Resolver, SchemaBuilder, SchemaMode, Value,
};
fn sample_args<'a>(points: &'a [vg::PointArgs], codes: &'a [u32]) -> vg::LogEventArgs<'a> {
vg::LogEventArgs {
service: Some("checkout"),
level: Some(2),
message: Some("gateway timed out"),
latency_us: Some(2_500_000),
points: Some(points),
codes: Some(codes),
}
}
#[test]
fn generated_module_is_current() {
let regenerated = codegen::generate_rust(&vg::schema()).unwrap();
let committed = include_str!("../examples/bench/verit_gen.rs");
assert_eq!(
regenerated.replace("\r\n", "\n"),
committed.replace("\r\n", "\n"),
"examples/bench/verit_gen.rs is stale — run `cargo run --example gen`"
);
}
#[test]
fn generated_encoder_roundtrips_through_dynamic_reader() {
let points = vec![
vg::PointArgs { x: 1.5, y: -0.5 },
vg::PointArgs { x: 2.0, y: 4.0 },
];
let codes = vec![7u32, 11, 13];
let bytes = vg::encode_log_event(&sample_args(&points, &codes), SchemaMode::HashOnly).unwrap();
let schema = vg::schema();
let resolver = Resolver::identity(&schema).unwrap();
let msg = Message::parse(&bytes).unwrap();
assert_eq!(msg.schema_id(), vg::SCHEMA_ID);
let root = msg.root(&resolver).unwrap();
assert_eq!(root.get_str(1).unwrap(), Some("checkout"));
assert_eq!(root.get_u32(2).unwrap(), Some(2));
assert_eq!(root.get_str(3).unwrap(), Some("gateway timed out"));
assert_eq!(root.get_u32(4).unwrap(), Some(2_500_000));
let pts = root.get_list(5).unwrap().unwrap();
assert_eq!(pts.len(), 2);
match pts.get(1).unwrap() {
verit::Ref::Struct(p) => {
assert_eq!(p.get_f64(1).unwrap(), Some(2.0));
assert_eq!(p.get_f64(2).unwrap(), Some(4.0));
}
other => panic!("expected struct, got {}", other.kind()),
}
let cs = root.get_list(6).unwrap().unwrap();
assert_eq!(cs.len(), 3);
}
#[test]
fn fast_path_matches_dynamic_reader_exactly() {
let points = vec![vg::PointArgs { x: 9.25, y: -3.5 }];
let codes = vec![42u32];
let bytes = vg::encode_log_event(&sample_args(&points, &codes), SchemaMode::HashOnly).unwrap();
let schema = vg::schema();
let resolver = Resolver::identity(&schema).unwrap();
let msg = Message::parse(&bytes).unwrap();
let fast = vg::LogEventRef::read(&msg, &resolver).unwrap();
assert!(
matches!(fast, vg::LogEventRef::Fast { .. }),
"identity must take the fast path"
);
let dynamic = msg.root(&resolver).unwrap();
assert_eq!(fast.service().unwrap(), dynamic.get_str(1).unwrap());
assert_eq!(fast.level().unwrap(), dynamic.get_u32(2).unwrap());
assert_eq!(fast.message().unwrap(), dynamic.get_str(3).unwrap());
assert_eq!(fast.latency_us().unwrap(), dynamic.get_u32(4).unwrap());
let fp = fast.points().unwrap().unwrap();
assert_eq!(fp.len(), 1);
let p0 = fp.get(0).unwrap();
assert_eq!(p0.x().unwrap(), Some(9.25));
assert_eq!(p0.y().unwrap(), Some(-3.5));
assert_eq!(fast.codes().unwrap().unwrap().get(0).unwrap(), 42);
}
#[test]
fn absent_fields_are_none_on_the_fast_path() {
let args = vg::LogEventArgs {
service: None,
level: Some(1),
message: None,
latency_us: None,
points: None,
codes: None,
};
let bytes = vg::encode_log_event(&args, SchemaMode::HashOnly).unwrap();
let schema = vg::schema();
let resolver = Resolver::identity(&schema).unwrap();
let msg = Message::parse(&bytes).unwrap();
let root = vg::LogEventRef::read(&msg, &resolver).unwrap();
assert_eq!(root.service().unwrap(), None);
assert_eq!(root.level().unwrap(), Some(1));
assert!(root.points().unwrap().is_none());
}
#[test]
fn evolution_falls_back_to_dynamic_transparently() {
let v0 = SchemaBuilder::new()
.add_struct(
"LogEvent",
vec![
(1, "service", Dt::Str),
(4, "latency_us", Dt::U16), ],
)
.build("LogEvent")
.unwrap();
let bytes = encode(
&v0,
&Value::Struct(vec![(1, Value::str("legacy")), (4, Value::U16(999))]),
SchemaMode::HashOnly,
)
.unwrap();
let reader_schema = vg::schema();
let resolver = Resolver::new(&v0, &reader_schema).unwrap();
let msg = Message::parse(&bytes).unwrap();
let root = vg::LogEventRef::read(&msg, &resolver).unwrap();
assert!(
matches!(root, vg::LogEventRef::Dynamic(_)),
"foreign schema id must take the dynamic path"
);
assert_eq!(root.service().unwrap(), Some("legacy"));
assert_eq!(root.latency_us().unwrap(), Some(999), "u16 widened to u32");
assert_eq!(root.message().unwrap(), None, "field absent in v0");
assert!(root.points().unwrap().is_none(), "list absent in v0");
}
#[test]
fn inline_mode_embeds_the_schema() {
let bytes = vg::encode_log_event(&sample_args(&[], &[]), SchemaMode::Inline).unwrap();
let msg = Message::parse(&bytes).unwrap();
let recovered = msg.writer_schema().unwrap().unwrap();
assert_eq!(recovered.id(), vg::SCHEMA_ID);
assert!(verit::dump_json(&bytes)
.unwrap()
.contains("\"service\":\"checkout\""));
}
#[test]
fn bounded_fast_path_matches_unbounded_and_charges() {
let points = vec![
vg::PointArgs { x: 1.0, y: 2.0 },
vg::PointArgs { x: 3.0, y: 4.0 },
];
let codes = vec![5u32, 7, 9];
let bytes = vg::encode_log_event(&sample_args(&points, &codes), SchemaMode::HashOnly).unwrap();
let schema = vg::schema();
let resolver = Resolver::identity(&schema).unwrap();
let msg = Message::parse(&bytes).unwrap();
let budget = Budget::new(msg.suggested_budget());
let start = budget.remaining();
let root = vg::LogEventRef::read_bounded(&msg, &resolver, &budget).unwrap();
assert!(
matches!(root, vg::LogEventRef::Fast { .. }),
"identity must stay on the fast path"
);
assert_eq!(root.service().unwrap(), Some("checkout"));
assert_eq!(root.latency_us().unwrap(), Some(2_500_000));
let pts = root.points().unwrap().unwrap();
assert_eq!(pts.get(1).unwrap().y().unwrap(), Some(4.0));
let cs = root.codes().unwrap().unwrap();
assert_eq!(cs.get(2).unwrap(), 9);
assert_eq!(cs.values().unwrap().sum::<u32>(), 21);
assert!(
budget.remaining() < start,
"offset-follows must charge the budget"
);
}
#[test]
fn bounded_fast_path_trips_on_hostile_list_count() {
let points = vec![vg::PointArgs { x: 1.0, y: 2.0 }];
let codes = vec![5u32];
let mut bytes =
vg::encode_log_event(&sample_args(&points, &codes), SchemaMode::HashOnly).unwrap();
let schema = vg::schema();
let resolver = Resolver::identity(&schema).unwrap();
let root_off = Message::parse(&bytes).unwrap().root_offset() as usize;
let list_off =
u32::from_le_bytes(bytes[root_off + 24..root_off + 28].try_into().unwrap()) as usize;
bytes[list_off..list_off + 4].copy_from_slice(&u32::MAX.to_le_bytes());
let msg = Message::parse(&bytes).unwrap();
let budget = Budget::new(msg.suggested_budget());
let root = vg::LogEventRef::read_bounded(&msg, &resolver, &budget).unwrap();
match root.codes() {
Err(Error::TraversalBudgetExceeded) => {}
Err(other) => panic!("expected TraversalBudgetExceeded, got {other}"),
Ok(_) => panic!("hostile count must trip the budget"),
}
let free = vg::LogEventRef::read(&msg, &resolver).unwrap();
let list = free.codes().unwrap().unwrap();
assert!(list.get(1_000_000).is_err());
}
#[test]
fn codegen_rejects_unsupported_shapes() {
let s = SchemaBuilder::new()
.add_struct("S", vec![(1, "m", Dt::list(Dt::list(Dt::U8)))])
.build("S")
.unwrap();
assert!(codegen::generate_rust(&s).is_err());
let bad_name = SchemaBuilder::new()
.add_struct("S", vec![(1, "match", Dt::U8)])
.build("S")
.unwrap();
assert!(codegen::generate_rust(&bad_name).is_err());
}