use rand::SeedableRng;
use rand::rngs::SmallRng;
use super::*;
use crate::cbor_utils::cbor_map;
use crate::native::core::NativeTestCase;
fn fresh_ntc() -> NativeTestCase {
NativeTestCase::new_random(SmallRng::seed_from_u64(1))
}
#[test]
fn interpret_schema_unknown_type_is_invalid_argument() {
let mut ntc = fresh_ntc();
let schema = cbor_map! { "type" => "mystery" };
let err = interpret_schema(&mut ntc, &schema).unwrap_err();
assert!(matches!(err, EngineError::InvalidArgument(_)));
assert!(err.to_string().contains("unknown schema type"));
}
#[test]
fn interpret_schema_missing_type_is_invalid_argument() {
let mut ntc = fresh_ntc();
let schema = cbor_map! { "min_value" => 0 };
let err = interpret_schema(&mut ntc, &schema).unwrap_err();
assert!(matches!(err, EngineError::InvalidArgument(_)));
assert!(err.to_string().contains("\"type\""));
}
#[test]
fn interpret_schema_records_leaf_span_for_integer() {
let mut ntc = fresh_ntc();
let schema = cbor_map! {
"type" => "integer",
"min_value" => 0,
"max_value" => 0,
};
interpret_schema(&mut ntc, &schema).ok().unwrap();
assert_eq!(ntc.spans.len(), 1);
assert_eq!(ntc.spans[0usize].label, "integer");
assert_eq!(ntc.spans[0usize].start, 0);
assert_eq!(ntc.spans[0usize].end, 1);
assert_eq!(ntc.spans[0usize].parent, None);
assert_eq!(ntc.spans[0usize].depth, 0);
}
#[test]
fn interpret_schema_records_enclosing_span_for_tuple() {
let mut ntc = fresh_ntc();
let schema = cbor_map! {
"type" => "tuple",
"elements" => vec![
cbor_map! { "type" => "integer", "min_value" => 0, "max_value" => 0 },
cbor_map! { "type" => "integer", "min_value" => 7, "max_value" => 7 },
],
};
interpret_schema(&mut ntc, &schema).ok().unwrap();
assert_eq!(ntc.spans.len(), 3);
assert_eq!(ntc.spans[0usize].label, "tuple");
assert_eq!(ntc.spans[0usize].start, 0);
assert_eq!(ntc.spans[0usize].end, 2);
assert_eq!(ntc.spans[0usize].parent, None);
assert_eq!(ntc.spans[0usize].depth, 0);
for child in ntc.spans.iter().skip(1) {
assert_eq!(child.label, "integer");
assert_eq!(child.parent, Some(0));
assert_eq!(child.depth, 1);
}
}
#[test]
fn interpret_schema_records_enclosing_span_for_list() {
let mut ntc = fresh_ntc();
let schema = cbor_map! {
"type" => "list",
"elements" => cbor_map! { "type" => "boolean" },
"min_size" => 0,
"max_size" => 5,
};
interpret_schema(&mut ntc, &schema).ok().unwrap();
assert_eq!(ntc.spans[0usize].label, "list");
assert_eq!(ntc.spans[0usize].parent, None);
assert_eq!(ntc.spans[0usize].depth, 0);
for child in ntc.spans.iter().skip(1) {
assert_eq!(child.parent, Some(0));
assert_eq!(child.depth, 1);
}
}
#[test]
fn interpret_schema_records_enclosing_span_for_one_of() {
let mut ntc = fresh_ntc();
let schema = cbor_map! {
"type" => "one_of",
"generators" => vec![
cbor_map! { "type" => "integer", "min_value" => 10, "max_value" => 10 },
cbor_map! { "type" => "integer", "min_value" => 20, "max_value" => 20 },
],
};
interpret_schema(&mut ntc, &schema).ok().unwrap();
assert_eq!(ntc.spans[0usize].label, "one_of");
assert_eq!(ntc.spans[0usize].parent, None);
assert_eq!(ntc.spans[0usize].depth, 0);
}
#[test]
fn interpret_schema_nests_spans_for_tuple_of_tuples() {
let mut ntc = fresh_ntc();
let inner_tuple = cbor_map! {
"type" => "tuple",
"elements" => vec![
cbor_map! { "type" => "integer", "min_value" => 0, "max_value" => 0 },
],
};
let schema = cbor_map! {
"type" => "tuple",
"elements" => vec![inner_tuple.clone(), inner_tuple],
};
interpret_schema(&mut ntc, &schema).ok().unwrap();
assert_eq!(ntc.spans[0usize].label, "tuple");
assert_eq!(ntc.spans[0usize].depth, 0);
let inner_tuple_indices: Vec<usize> = ntc
.spans
.iter()
.enumerate()
.filter_map(|(i, s)| (s.label == "tuple" && i != 0).then_some(i))
.collect();
assert_eq!(inner_tuple_indices.len(), 2);
for &i in &inner_tuple_indices {
assert_eq!(ntc.spans[i].parent, Some(0));
assert_eq!(ntc.spans[i].depth, 1);
}
for span in ntc.spans.iter().filter(|s| s.label == "integer") {
assert_eq!(span.depth, 2);
assert!(inner_tuple_indices.contains(&span.parent.unwrap()));
}
}
#[test]
fn interpret_schema_records_zero_node_span_for_null() {
let mut ntc = fresh_ntc();
let schema = cbor_map! { "type" => "null" };
interpret_schema(&mut ntc, &schema).ok().unwrap();
assert_eq!(ntc.spans.len(), 1);
assert_eq!(ntc.spans[0usize].label, "null");
assert_eq!(ntc.spans[0usize].start, ntc.spans[0usize].end);
}
#[test]
fn many_reject_marks_invalid_when_cannot_reach_min_size() {
let mut ntc = fresh_ntc();
let mut state = ManyState::new(6, Some(10));
state.count = 5;
state.rejections = 9;
let result = many_reject(&mut ntc, &mut state);
assert!(
result.is_err(),
"expected StopTest once rejections overflow"
);
assert_eq!(ntc.status, Some(Status::Invalid));
}
#[test]
fn cbor_to_bigint_tag2_non_bytes_is_invalid_argument() {
let bad = Value::Tag(2, Box::new(Value::Integer(1.into())));
let err = cbor_to_bigint(&bad).unwrap_err();
assert!(matches!(err, EngineError::InvalidArgument(_)));
assert!(err.to_string().contains("bignum tag 2"));
}
#[test]
fn cbor_to_bigint_tag3_non_bytes_is_invalid_argument() {
let bad = Value::Tag(3, Box::new(Value::Integer(1.into())));
let err = cbor_to_bigint(&bad).unwrap_err();
assert!(matches!(err, EngineError::InvalidArgument(_)));
assert!(err.to_string().contains("bignum tag 3"));
}
#[test]
fn cbor_to_bigint_non_integer_is_invalid_argument() {
let err = cbor_to_bigint(&Value::Bool(true)).unwrap_err();
assert!(matches!(err, EngineError::InvalidArgument(_)));
assert!(err.to_string().contains("CBOR integer"));
}
#[test]
fn cbor_to_bigint_plain_integer() {
assert_eq!(
cbor_to_bigint(&Value::Integer(42.into())).unwrap(),
BigInt::from(42)
);
assert_eq!(
cbor_to_bigint(&Value::Integer((-7).into())).unwrap(),
BigInt::from(-7)
);
}
#[test]
fn cbor_to_bigint_positive_bignum_tag2() {
let v = Value::Tag(2, Box::new(Value::Bytes(vec![0x01, 0x00])));
assert_eq!(cbor_to_bigint(&v).unwrap(), BigInt::from(256));
}
#[test]
fn cbor_to_bigint_negative_bignum_tag3() {
let v = Value::Tag(3, Box::new(Value::Bytes(vec![0xFF])));
assert_eq!(cbor_to_bigint(&v).unwrap(), BigInt::from(-256));
}
#[test]
fn bigint_to_cbor_roundtrips_across_magnitudes() {
let cases = [
BigInt::from(0),
BigInt::from(-5),
BigInt::from(u64::MAX),
BigInt::from(u128::MAX),
BigInt::from(i128::MIN) * BigInt::from(1_000_000),
];
for original in cases {
let encoded = bigint_to_cbor(&original);
assert_eq!(cbor_to_bigint(&encoded).unwrap(), original);
}
}
#[test]
fn interpret_integer_draws_real_bigint_beyond_u128() {
let u128_max = BigInt::from(u128::MAX);
let min = &u128_max * BigInt::from(-1_000_000);
let max = &u128_max * BigInt::from(1_000_000);
let schema = cbor_map! {
"type" => "integer",
"min_value" => bigint_to_cbor(&min),
"max_value" => bigint_to_cbor(&max),
};
let neg_u128_max = -&u128_max;
let mut saw_beyond_u128 = false;
for seed in 0..200u64 {
let mut ntc = NativeTestCase::new_random(SmallRng::seed_from_u64(seed));
let value = interpret_schema(&mut ntc, &schema).ok().unwrap();
let decoded = cbor_to_bigint(&value).unwrap();
assert!(decoded >= min && decoded <= max, "out of range: {decoded}");
if decoded > u128_max || decoded < neg_u128_max {
saw_beyond_u128 = true;
}
}
assert!(
saw_beyond_u128,
"expected at least one value beyond the u128 range"
);
}