lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit eddf259ff2b4d679fba13265168c94a09d12da2a
parent eb67767535467a1271d75bb462d3193575b6bfca
Author: triesap <tyson@radroots.org>
Date:   Sat, 18 Jul 2026 08:30:22 +0000

mesh: close semantic protocol coverage

- Exercise accepted, budget-denied, and custom-scope admission decisions.
- Cover every delivery-policy label, message, and usability branch.
- Validate 32-bit CBOR widths, invalid payload majors, and frame versions.
- Remove duplicated payload and transport-error branches after validation.

Diffstat:
Mcrates/mesh/src/cbor.rs | 7+------
Mcrates/mesh/src/model.rs | 1-
Mcrates/mesh/tests/mesh.rs | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 94 insertions(+), 7 deletions(-)

diff --git a/crates/mesh/src/cbor.rs b/crates/mesh/src/cbor.rs @@ -65,12 +65,7 @@ fn encode_payload( payload: &RadrootsMeshPayload, ) -> Result<(), RadrootsMeshError> { payload.validate()?; - match payload { - RadrootsMeshPayload::EmptyMap => encode_map_len(output, 0), - RadrootsMeshPayload::Bytes(_) => { - return Err(RadrootsMeshError::PayloadTransmissionForbidden); - } - } + encode_map_len(output, 0); Ok(()) } diff --git a/crates/mesh/src/model.rs b/crates/mesh/src/model.rs @@ -68,7 +68,6 @@ impl RadrootsMeshScope { let scope = RadrootsTransportMeshScopeId::parse(value.as_ref()).map_err(|err| match err { RadrootsTransportError::EmptyTargetScope => RadrootsMeshError::EmptyCustomScope, - RadrootsTransportError::InvalidTargetScope => RadrootsMeshError::InvalidCustomScope, _ => RadrootsMeshError::InvalidCustomScope, })?; Ok(Self::Custom(scope.as_str().to_string())) diff --git a/crates/mesh/tests/mesh.rs b/crates/mesh/tests/mesh.rs @@ -131,6 +131,72 @@ fn reticulum_policy_denies_real_payload_admission_deterministically() { } #[test] +fn enabled_policy_evaluates_all_admission_decisions() { + let mut policy = RadrootsMeshPayloadPolicy::reticulum_unavailable(); + policy.max_payload_bytes = 10; + assert!(!policy.usable_for_delivery()); + policy.max_frame_bytes = 20; + assert!(policy.usable_for_delivery()); + + let local = |payload_bytes, frame_bytes| { + RadrootsMeshAdmissionInput::new( + RadrootsMeshScope::Local, + RadrootsMeshPrivacyClass::PublicEvent, + payload_bytes, + frame_bytes, + ) + }; + let accepted = policy.evaluate(&local(10, 20)); + assert_eq!(accepted, RadrootsMeshAdmissionDecision::Accepted); + assert_eq!(accepted.label(), "accepted"); + assert_eq!(accepted.deny_reason(), None); + assert_eq!(accepted.message(), "mesh payload delivery is admitted"); + assert!(accepted.usable_for_delivery()); + + for input in [local(11, 20), local(10, 21)] { + let denied = policy.evaluate(&input); + assert_eq!( + denied.deny_reason(), + Some(RadrootsMeshPolicyDenyReason::PayloadBudgetExceeded) + ); + } + assert_eq!( + RadrootsMeshPolicyDenyReason::PayloadBudgetExceeded.label(), + "payload_budget_exceeded" + ); + assert_eq!( + RadrootsMeshPolicyDenyReason::PayloadBudgetExceeded.message(), + "mesh payload exceeds the configured delivery budget" + ); + + let custom_input = RadrootsMeshAdmissionInput::new( + RadrootsMeshScope::custom("farm-north").unwrap(), + RadrootsMeshPrivacyClass::PrivateEvent, + 1, + 1, + ); + let custom_denied = policy.evaluate(&custom_input); + assert_eq!( + custom_denied.deny_reason(), + Some(RadrootsMeshPolicyDenyReason::CustomScopeUnavailable) + ); + assert_eq!( + RadrootsMeshPolicyDenyReason::CustomScopeUnavailable.label(), + "custom_scope_unavailable" + ); + assert_eq!( + RadrootsMeshPolicyDenyReason::CustomScopeUnavailable.message(), + "mesh custom scopes are not enabled for the configured policy" + ); + + policy.custom_scopes_enabled = true; + assert_eq!( + policy.evaluate(&custom_input), + RadrootsMeshAdmissionDecision::Accepted + ); +} + +#[test] fn custom_scope_has_explicit_namespace() { let scope = RadrootsMeshScope::custom("farm-north.mesh_1").expect("custom scope"); assert_eq!(scope.label(), "farm-north.mesh_1"); @@ -210,6 +276,15 @@ fn mesh_parsers_and_validation_reject_unknown_empty_or_invalid_values() { zero_ttl.validate().expect_err("zero ttl"), RadrootsMeshError::InvalidTtl ); + + let mut unsupported_version = default_frame(); + unsupported_version.version += 1; + assert_eq!( + unsupported_version + .validate() + .expect_err("unsupported version"), + RadrootsMeshError::UnsupportedVersion + ); } #[test] @@ -286,6 +361,19 @@ fn cbor_codec_covers_extended_integer_widths() { let decoded = decode_mesh_frame_cbor(&encoded).expect("decode wide frame"); assert_eq!(decoded, frame); + + let frame_32 = RadrootsMeshFrame::new( + RadrootsMeshFrameType::EventAck, + RadrootsMeshScope::Community, + "32-bit-created-at", + 0x1_0000, + 0x1_0000, + ); + let encoded_32 = encode_mesh_frame_cbor(&frame_32).expect("encode 32-bit frame"); + assert_eq!( + decode_mesh_frame_cbor(&encoded_32).expect("decode 32-bit frame"), + frame_32 + ); } #[test] @@ -426,6 +514,11 @@ fn decoder_rejects_malformed_cbor_shapes() { b'm', b'e', b's', b's', b'a', b'g', b'e', b'-', b'1', 0x04, 0x18, 0x2a, 0x05, 0x19, 0xea, 0x60, 0x06, 0xa1, ], + vec![ + 0xa7, 0x00, 0x01, 0x01, 0x00, 0x02, 0x65, b'l', b'o', b'c', b'a', b'l', 0x03, 0x69, + b'm', b'e', b's', b's', b'a', b'g', b'e', b'-', b'1', 0x04, 0x18, 0x2a, 0x05, 0x19, + 0xea, 0x60, 0x06, 0xf6, + ], ]; for malformed in cases {