protovalidate-buffa 0.7.2

Runtime for protoc-gen-protovalidate-buffa — Validate trait, ValidationError types, rule helpers, and the CelScalar / Duration / Timestamp helpers that compile-time-expanded CEL rules call into. No CEL interpreter at runtime.
Documentation
#![cfg(feature = "protos")]

mod support;

use buffa::{Message, MessageName};
use protovalidate_buffa::{FieldPath, FieldPathElement, FieldType, Subscript, proto};
use support::single_violation;

#[test]
fn lossless_details_preserve_all_selector_variants() {
    use proto::__buffa::oneof::field_path_element::Subscript as P;
    let variants = [
        (Subscript::Index(u64::MAX), P::Index(u64::MAX)),
        (Subscript::BoolKey(false), P::BoolKey(false)),
        (Subscript::IntKey(i64::MIN), P::IntKey(i64::MIN)),
        (Subscript::UintKey(u64::MAX), P::UintKey(u64::MAX)),
        (
            Subscript::StringKey("ключ\"\n".into()),
            P::StringKey("ключ\"\n".into()),
        ),
    ];
    for (input, expected) in variants {
        let mut error = single_violation();
        error.violations[0].field.elements[0].subscript = Some(input.clone());
        error.violations[0].rule.elements[0].subscript = Some(input);
        let details = error.to_proto();
        assert_eq!(proto::Violations::FULL_NAME, "buf.validate.Violations");
        let decoded = proto::Violations::decode_from_slice(&details.encode_to_vec()).unwrap();
        assert_eq!(decoded, details);
        let v = &decoded.violations[0];
        assert_eq!(v.field.elements[0].subscript.as_ref(), Some(&expected));
        assert_eq!(v.rule.elements[0].subscript.as_ref(), Some(&expected));
        assert_eq!(v.rule_id.as_deref(), Some("address.code"));
        assert_eq!(v.message.as_deref(), Some("invalid value: private-value"));
        assert_eq!(v.for_key, Some(true));
    }
}

#[test]
fn all_descriptor_types_keep_their_canonical_numbers() {
    let variants = [
        FieldType::Double,
        FieldType::Float,
        FieldType::Int64,
        FieldType::Uint64,
        FieldType::Int32,
        FieldType::Fixed64,
        FieldType::Fixed32,
        FieldType::Bool,
        FieldType::String,
        FieldType::Group,
        FieldType::Message,
        FieldType::Bytes,
        FieldType::Uint32,
        FieldType::Enum,
        FieldType::Sfixed32,
        FieldType::Sfixed64,
        FieldType::Sint32,
        FieldType::Sint64,
    ];
    for (number, variant) in (1..=18).zip(variants) {
        let mut error = single_violation();
        let element = &mut error.violations[0].field.elements[0];
        element.field_type = Some(variant);
        element.key_type = Some(variant);
        element.value_type = Some(variant);
        let details = error.to_proto();
        let element = &details.violations[0].field.elements[0];
        assert_eq!(element.field_type.unwrap() as i32, number);
        assert_eq!(element.key_type.unwrap() as i32, number);
        assert_eq!(element.value_type.unwrap() as i32, number);
    }
}

#[test]
fn absent_identity_is_not_invented_and_diagnostics_are_not_truncated() {
    let mut error = single_violation();
    let v = &mut error.violations[0];
    v.field = FieldPath::default();
    v.rule = FieldPath {
        elements: vec![FieldPathElement::default()],
    };
    v.rule_id = "".into();
    v.for_key = false;
    v.message = "diagnostic".repeat(10_000).into();
    let details = error.to_proto();
    let v = &details.violations[0];
    assert!(v.field.as_option().is_none());
    assert_eq!(v.rule.elements[0], proto::FieldPathElement::default());
    assert!(v.rule_id.is_none());
    assert!(v.for_key.is_none());
    assert_eq!(v.message.as_ref().unwrap().len(), 100_000);
}