lib

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

commit 3388d3404b709628bfd30088be04802dbe0134da
parent c613bec538cc83523f456b467d86496a9b2b83ac
Author: triesap <tyson@radroots.org>
Date:   Tue,  7 Jul 2026 20:20:52 +0000

transport: enforce Reticulum preview endpoint exactness

- add the shared Reticulum preview endpoint constant
- reject non-exact Reticulum target URIs before canonicalization
- consume the shared endpoint in preview and publish protocol crates
- cover endpoint drift with transport, protocol, and source-boundary tests

Diffstat:
Mcrates/transport/src/lib.rs | 4+++-
Mcrates/transport/src/message.rs | 2++
Mcrates/transport/src/target.rs | 12++++++++++--
Mcrates/transport/tests/source_boundary.rs | 39+++++++++++++++++++++++++++++++++------
Mcrates/transport/tests/transport.rs | 43+++++++++++++++++++++++++++++++++++--------
Mcrates/transport_publish_protocol/src/lib.rs | 33+++++++++++++++++++++++++++------
Mcrates/transport_reticulum/src/lib.rs | 21+++++++++++----------
Mcrates/transport_reticulum/tests/reticulum_preview.rs | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
8 files changed, 177 insertions(+), 55 deletions(-)

diff --git a/crates/transport/src/lib.rs b/crates/transport/src/lib.rs @@ -17,7 +17,9 @@ pub use delivery::{ }; pub use error::RadrootsTransportError; pub use kind::{RadrootsTransportImplementationState, RadrootsTransportKind}; -pub use message::RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE; +pub use message::{ + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, +}; pub use status::{RadrootsTransportDeliveryTargetStatus, RadrootsTransportOutcome}; pub use target::{ RadrootsTransportTarget, RadrootsTransportTargetFingerprint, RadrootsTransportTargetSet, diff --git a/crates/transport/src/message.rs b/crates/transport/src/message.rs @@ -1,3 +1,5 @@ +pub const RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI: &str = "reticulum:preview-unavailable"; + pub const RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE: &str = concat!( "Reticulum transport is configured for future compatibility, ", "but this build does not implement Reticulum delivery." diff --git a/crates/transport/src/target.rs b/crates/transport/src/target.rs @@ -1,4 +1,6 @@ -use crate::{RadrootsTransportError, RadrootsTransportKind}; +use crate::{ + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, RadrootsTransportError, RadrootsTransportKind, +}; use alloc::collections::BTreeSet; use alloc::format; use alloc::string::{String, ToString}; @@ -82,7 +84,13 @@ impl RadrootsTransportTarget { kind: RadrootsTransportKind, uri: impl AsRef<str>, ) -> Result<Self, RadrootsTransportError> { - let uri = RadrootsTransportTargetUri::parse(uri)?; + let raw_uri = uri.as_ref(); + if kind == RadrootsTransportKind::Reticulum + && raw_uri != RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI + { + return Err(RadrootsTransportError::InvalidTargetUri); + } + let uri = RadrootsTransportTargetUri::parse(raw_uri)?; let fingerprint = RadrootsTransportTargetFingerprint::from_target(&kind, &uri); Ok(Self { kind, diff --git a/crates/transport/tests/source_boundary.rs b/crates/transport/tests/source_boundary.rs @@ -68,11 +68,12 @@ fn transport_hardening_sources_reject_removed_protocol_identifiers() { for relative_root in TRANSPORT_HARDENING_CRATE_SOURCE_ROOTS { for path in rust_source_files(crates_root.join(relative_root).as_path()) { - let source = read_source(path.as_path()); + let source_raw = read_source(path.as_path()); + let source = production_source(source_raw.as_str()); let relative_path = relative_path(crates_root, path.as_path()); for concept in FORBIDDEN_TRANSPORT_CONCEPTS { - if contains_forbidden_concept(source.as_str(), concept.pattern) { + if contains_forbidden_concept(source, concept.pattern) { findings.push(format!( "{} contains removed transport concept `{}`: {}", relative_path, concept.pattern, concept.reason @@ -80,7 +81,7 @@ fn transport_hardening_sources_reject_removed_protocol_identifiers() { } } - for line in removed_reticulum_preview_endpoint_lines(source.as_str()) { + for line in removed_reticulum_preview_endpoint_lines(source) { findings.push(format!( "{relative_path}:{line} contains removed Reticulum preview endpoint `reticulum:preview`" )); @@ -97,13 +98,14 @@ fn transport_hardening_sources_reject_removed_protocol_identifiers() { #[test] fn transport_publish_capabilities_keep_readiness_and_usability_fields() { - let source = read_source( + let source_raw = read_source( Path::new(env!("CARGO_MANIFEST_DIR")) .parent() .expect("transport crate parent") .join("transport_publish_protocol/src/lib.rs") .as_path(), ); + let source = production_source(source_raw.as_str()); for required in [ "pub implementation_state: TransportPublishImplementationState,", @@ -121,7 +123,7 @@ fn transport_publish_capabilities_keep_readiness_and_usability_fields() { } #[test] -fn transport_hardening_sources_keep_proxy_and_reticulum_message_contracts() { +fn transport_hardening_sources_keep_proxy_and_reticulum_contracts() { let crates_root = Path::new(env!("CARGO_MANIFEST_DIR")) .parent() .expect("transport crate parent"); @@ -140,6 +142,8 @@ fn transport_hardening_sources_keep_proxy_and_reticulum_message_contracts() { let transport_message_source = read_source(crates_root.join("transport/src/message.rs").as_path()); for required in [ + "RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI", + "reticulum:preview-unavailable", "RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE", "Reticulum transport is configured for future compatibility, ", "but this build does not implement Reticulum delivery.", @@ -153,6 +157,14 @@ fn transport_hardening_sources_keep_proxy_and_reticulum_message_contracts() { let reticulum_source = read_source(crates_root.join("transport_reticulum/src/lib.rs").as_path()); assert!( + reticulum_source.contains("RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI"), + "Reticulum preview source must consume the shared endpoint URI constant" + ); + assert!( + !reticulum_source.contains("reticulum:preview-unavailable"), + "Reticulum preview source must not duplicate the shared endpoint URI" + ); + assert!( reticulum_source.contains("RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE"), "Reticulum preview source must consume the shared unavailable message constant" ); @@ -161,11 +173,20 @@ fn transport_hardening_sources_keep_proxy_and_reticulum_message_contracts() { "Reticulum preview source must not duplicate the shared unavailable message" ); - let protocol_source = read_source( + let protocol_source_raw = read_source( crates_root .join("transport_publish_protocol/src/lib.rs") .as_path(), ); + let protocol_source = production_source(protocol_source_raw.as_str()); + assert!( + protocol_source.contains("RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI"), + "transport publish protocol must consume the shared Reticulum endpoint URI constant" + ); + assert!( + !protocol_source.contains("reticulum:preview-unavailable"), + "transport publish protocol must not duplicate the shared endpoint URI" + ); assert!( protocol_source.contains("RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE"), "transport publish capabilities must consume the shared Reticulum unavailable message" @@ -202,6 +223,12 @@ fn read_source(path: &Path) -> String { .unwrap_or_else(|error| panic!("failed to read source {}: {error}", path.display())) } +fn production_source(source: &str) -> &str { + source + .find("\n#[cfg(test)]") + .map_or(source, |index| &source[..index]) +} + fn relative_path(root: &Path, path: &Path) -> String { path.strip_prefix(root) .expect("source path is under crate root") diff --git a/crates/transport/tests/transport.rs b/crates/transport/tests/transport.rs @@ -1,10 +1,10 @@ use radroots_transport::{ - RadrootsTransportDeliveryReceipt, RadrootsTransportDeliveryRequest, - RadrootsTransportDeliveryTargetStatus, RadrootsTransportError, RadrootsTransportKind, - RadrootsTransportOutcome, RadrootsTransportSatisfactionClass, - RadrootsTransportSatisfactionPolicy, RadrootsTransportTarget, - RadrootsTransportTargetFingerprint, RadrootsTransportTargetReceipt, RadrootsTransportTargetSet, - RadrootsTransportTargetUri, + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, RadrootsTransportDeliveryReceipt, + RadrootsTransportDeliveryRequest, RadrootsTransportDeliveryTargetStatus, + RadrootsTransportError, RadrootsTransportKind, RadrootsTransportOutcome, + RadrootsTransportSatisfactionClass, RadrootsTransportSatisfactionPolicy, + RadrootsTransportTarget, RadrootsTransportTargetFingerprint, RadrootsTransportTargetReceipt, + RadrootsTransportTargetSet, RadrootsTransportTargetUri, }; #[test] @@ -17,7 +17,7 @@ fn target_fingerprints_are_stable_and_transport_scoped() { .expect("nostr target"); let reticulum = RadrootsTransportTarget::new( RadrootsTransportKind::Reticulum, - "wss://relay.example/Events", + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, ) .expect("reticulum target"); @@ -161,7 +161,7 @@ fn satisfaction_policy_counts_target_statuses() { fn deferred_transport_outcomes_are_terminal_but_not_satisfied() { let target = RadrootsTransportTarget::new( RadrootsTransportKind::Reticulum, - "reticulum:preview-unavailable", + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, ) .expect("target"); let receipt = RadrootsTransportDeliveryReceipt { @@ -308,6 +308,33 @@ fn transport_kind_and_target_parsers_cover_negative_edges() { } #[test] +fn reticulum_transport_targets_require_exact_preview_endpoint() { + let target = RadrootsTransportTarget::new( + RadrootsTransportKind::Reticulum, + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, + ) + .expect("exact Reticulum preview endpoint"); + assert_eq!(target.uri.as_str(), RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI); + + for invalid in [ + " reticulum:preview-unavailable", + "reticulum:preview-unavailable ", + "RETICULUM:preview-unavailable", + "reticulum:Preview-Unavailable", + "reticulum:preview", + "reticulum:preview-unavailable-alt", + "reticulum:custom", + "wss://relay.example/Events", + ] { + assert_eq!( + RadrootsTransportTarget::new(RadrootsTransportKind::Reticulum, invalid) + .expect_err("invalid Reticulum preview endpoint"), + RadrootsTransportError::InvalidTargetUri + ); + } +} + +#[test] fn target_fingerprints_and_sets_cover_accessors_and_validation() { let target = RadrootsTransportTarget::new(RadrootsTransportKind::Mesh, "mesh://node.example") .expect("mesh target"); diff --git a/crates/transport_publish_protocol/src/lib.rs b/crates/transport_publish_protocol/src/lib.rs @@ -11,7 +11,8 @@ use std::{string::String, vec::Vec}; use core::fmt; use radroots_transport::{ - RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, RadrootsTransportError, RadrootsTransportKind, + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, + RadrootsTransportError, RadrootsTransportKind, }; pub const API_VERSION: &str = "radrootsd.transport_publish.v2"; @@ -20,7 +21,6 @@ pub const METHOD_CAPABILITIES: &str = "transport.publish.capabilities"; pub const METHOD_EVENT: &str = "transport.publish.event"; pub const METHOD_JOB_GET: &str = "transport.publish.job.get"; pub const METHOD_JOB_LIST: &str = "transport.publish.job.list"; -pub const RETICULUM_PREVIEW_ENDPOINT_URI: &str = "reticulum:preview-unavailable"; #[derive(Clone, Debug, PartialEq, Eq)] pub enum TransportPublishProtocolError { @@ -81,7 +81,7 @@ impl fmt::Display for TransportPublishProtocolError { } Self::InvalidReticulumPreviewEndpoint { index } => write!( f, - "transport target {index} Reticulum preview endpoint must be {RETICULUM_PREVIEW_ENDPOINT_URI}" + "transport target {index} Reticulum preview endpoint must be {RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI}" ), Self::TargetLimitExceeded { max, actual } => { write!(f, "transport target count {actual} exceeds limit {max}") @@ -166,7 +166,7 @@ impl TransportPublishTarget { pub fn reticulum_preview(behavior: TransportPublishPreviewBehavior) -> Self { Self { transport_kind: "reticulum".to_owned(), - endpoint_uri: RETICULUM_PREVIEW_ENDPOINT_URI.to_owned(), + endpoint_uri: RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI.to_owned(), preview_behavior: Some(behavior), } } @@ -181,7 +181,7 @@ impl TransportPublishTarget { return Err(TransportPublishProtocolError::EmptyEndpointUri { index }); } if transport_kind == RadrootsTransportKind::Reticulum - && self.endpoint_uri != RETICULUM_PREVIEW_ENDPOINT_URI + && self.endpoint_uri != RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI { return Err(TransportPublishProtocolError::InvalidReticulumPreviewEndpoint { index }); } @@ -776,7 +776,7 @@ mod tests { noncanonical_reticulum_kind.target_policy = TransportPublishTargetPolicy::explicit_targets(vec![TransportPublishTarget { transport_kind: "Reticulum".to_owned(), - endpoint_uri: RETICULUM_PREVIEW_ENDPOINT_URI.to_owned(), + endpoint_uri: RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI.to_owned(), preview_behavior: Some(TransportPublishPreviewBehavior::RejectDeliveryAttempts), }]); assert_eq!( @@ -784,6 +784,27 @@ mod tests { Err(TransportPublishProtocolError::InvalidTransportKind { index: 0 }) ); + for invalid in [ + " reticulum:preview-unavailable", + "reticulum:preview-unavailable ", + "RETICULUM:preview-unavailable", + "reticulum:Preview-Unavailable", + "reticulum:preview", + "reticulum:custom", + ] { + let mut invalid_reticulum_endpoint = request.clone(); + invalid_reticulum_endpoint.target_policy = + TransportPublishTargetPolicy::explicit_targets(vec![TransportPublishTarget { + transport_kind: "reticulum".to_owned(), + endpoint_uri: invalid.to_owned(), + preview_behavior: Some(TransportPublishPreviewBehavior::RejectDeliveryAttempts), + }]); + assert_eq!( + invalid_reticulum_endpoint.validate(1), + Err(TransportPublishProtocolError::InvalidReticulumPreviewEndpoint { index: 0 }) + ); + } + let mut removed_proxy_kind = request.clone(); removed_proxy_kind.target_policy = TransportPublishTargetPolicy::explicit_targets(vec![TransportPublishTarget { diff --git a/crates/transport_reticulum/src/lib.rs b/crates/transport_reticulum/src/lib.rs @@ -8,14 +8,14 @@ use alloc::string::String; use alloc::vec::Vec; use core::fmt; use radroots_transport::{ - RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, RadrootsTransportDeliveryReceipt, - RadrootsTransportDeliveryRequest, RadrootsTransportDeliveryTargetStatus, - RadrootsTransportImplementationState, RadrootsTransportKind, RadrootsTransportOutcome, - RadrootsTransportTarget, RadrootsTransportTargetReceipt, + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, + RadrootsTransportDeliveryReceipt, RadrootsTransportDeliveryRequest, + RadrootsTransportDeliveryTargetStatus, RadrootsTransportImplementationState, + RadrootsTransportKind, RadrootsTransportOutcome, RadrootsTransportTarget, + RadrootsTransportTargetReceipt, }; const DEFAULT_PROFILE_ID: &str = "transport.reticulum.preview"; -pub const RETICULUM_PREVIEW_ENDPOINT_URI: &str = "reticulum:preview-unavailable"; const UNAVAILABLE_CODE: &str = "transport_unavailable"; const DEFERRED_CODE: &str = "deferred_until_implemented"; const DEFERRED_MESSAGE: &str = "Reticulum preview delivery is deferred until implementation"; @@ -47,12 +47,12 @@ pub struct RadrootsReticulumPreviewEndpoint { impl RadrootsReticulumPreviewEndpoint { pub fn parse(raw: impl AsRef<str>) -> Result<Self, RadrootsReticulumPreviewError> { - let uri = raw.as_ref().trim(); - if uri != RETICULUM_PREVIEW_ENDPOINT_URI { + let uri = raw.as_ref(); + if uri != RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI { return Err(RadrootsReticulumPreviewError::InvalidEndpoint); } Ok(Self { - uri: RETICULUM_PREVIEW_ENDPOINT_URI.to_owned(), + uri: RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI.to_owned(), }) } @@ -67,7 +67,8 @@ impl RadrootsReticulumPreviewEndpoint { impl Default for RadrootsReticulumPreviewEndpoint { fn default() -> Self { - Self::parse(RETICULUM_PREVIEW_ENDPOINT_URI).expect("default Reticulum preview endpoint") + Self::parse(RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI) + .expect("default Reticulum preview endpoint") } } @@ -274,7 +275,7 @@ fn ensure_reticulum_targets( if target.kind != RadrootsTransportKind::Reticulum { return Err(RadrootsReticulumPreviewError::NonReticulumTarget); } - if target.uri.as_str() != RETICULUM_PREVIEW_ENDPOINT_URI { + if target.uri.as_str() != RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI { return Err(RadrootsReticulumPreviewError::InvalidEndpoint); } } diff --git a/crates/transport_reticulum/tests/reticulum_preview.rs b/crates/transport_reticulum/tests/reticulum_preview.rs @@ -1,7 +1,8 @@ use radroots_transport::{ - RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, RadrootsTransportDeliveryRequest, - RadrootsTransportDeliveryTargetStatus, RadrootsTransportImplementationState, - RadrootsTransportKind, RadrootsTransportSatisfactionClass, RadrootsTransportSatisfactionPolicy, + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, + RadrootsTransportDeliveryRequest, RadrootsTransportDeliveryTargetStatus, + RadrootsTransportImplementationState, RadrootsTransportKind, + RadrootsTransportSatisfactionClass, RadrootsTransportSatisfactionPolicy, RadrootsTransportTarget, RadrootsTransportTargetSet, }; use radroots_transport_reticulum::{ @@ -35,7 +36,10 @@ fn default_profile_is_configured_preview_unavailable_and_rejecting() { let status = profile.status(); assert_eq!(profile.profile_id(), "transport.reticulum.preview"); - assert_eq!(profile.endpoint().as_str(), "reticulum:preview-unavailable"); + assert_eq!( + profile.endpoint().as_str(), + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI + ); assert_eq!( profile.behavior(), RadrootsReticulumPreviewBehavior::RejectDeliveryAttempts @@ -52,17 +56,20 @@ fn default_profile_is_configured_preview_unavailable_and_rejecting() { #[test] fn endpoint_and_profile_validation_are_strict_and_canonical() { - let endpoint = - RadrootsReticulumPreviewEndpoint::parse("reticulum:preview-unavailable").expect("endpoint"); - assert_eq!(endpoint.as_str(), "reticulum:preview-unavailable"); - assert_eq!(endpoint.to_string(), "reticulum:preview-unavailable"); + let endpoint = RadrootsReticulumPreviewEndpoint::parse(RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI) + .expect("endpoint"); + assert_eq!(endpoint.as_str(), RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI); + assert_eq!( + endpoint.to_string(), + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI + ); assert_eq!( endpoint.clone().into_string(), - "reticulum:preview-unavailable" + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI ); assert_eq!( RadrootsReticulumPreviewEndpoint::default().as_str(), - "reticulum:preview-unavailable" + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI ); assert_eq!( @@ -83,6 +90,16 @@ fn endpoint_and_profile_validation_are_strict_and_canonical() { RadrootsReticulumPreviewError::InvalidEndpoint ); assert_eq!( + RadrootsReticulumPreviewEndpoint::parse(" reticulum:preview-unavailable") + .expect_err("leading whitespace endpoint"), + RadrootsReticulumPreviewError::InvalidEndpoint + ); + assert_eq!( + RadrootsReticulumPreviewEndpoint::parse("reticulum:preview-unavailable ") + .expect_err("trailing whitespace endpoint"), + RadrootsReticulumPreviewError::InvalidEndpoint + ); + assert_eq!( RadrootsReticulumPreviewEndpoint::parse("reticulum:custom").expect_err("custom endpoint"), RadrootsReticulumPreviewError::InvalidEndpoint ); @@ -121,7 +138,10 @@ fn endpoint_and_profile_validation_are_strict_and_canonical() { ) .expect("custom behavior profile"); assert_eq!(profile.profile_id(), "transport.reticulum.custom"); - assert_eq!(profile.endpoint().as_str(), "reticulum:preview-unavailable"); + assert_eq!( + profile.endpoint().as_str(), + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI + ); assert_eq!( profile.behavior(), RadrootsReticulumPreviewBehavior::DeferDeliveryPlans @@ -131,7 +151,9 @@ fn endpoint_and_profile_validation_are_strict_and_canonical() { #[test] fn reject_delivery_attempts_returns_unavailable_without_success_or_nostr_routing() { let transport = RadrootsReticulumPreviewTransport::default(); - let request = delivery_request(vec![reticulum_target("reticulum:preview-unavailable")]); + let request = delivery_request(vec![reticulum_target( + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, + )]); let receipt = transport.deliver(request).expect("delivery receipt"); assert_eq!(receipt.target_receipts.len(), 1); @@ -158,14 +180,21 @@ fn reject_delivery_attempts_returns_unavailable_without_success_or_nostr_routing #[test] fn noncanonical_reticulum_preview_targets_are_rejected() { - let transport = RadrootsReticulumPreviewTransport::default(); - let err = transport - .deliver(delivery_request(vec![reticulum_target( - "reticulum:preview-unavailable-alt", - )])) - .expect_err("noncanonical Reticulum endpoint"); - - assert_eq!(err, RadrootsReticulumPreviewError::InvalidEndpoint); + for invalid in [ + " reticulum:preview-unavailable", + "reticulum:preview-unavailable ", + "RETICULUM:preview-unavailable", + "reticulum:Preview-Unavailable", + "reticulum:preview", + "reticulum:preview-unavailable-alt", + "reticulum:custom", + ] { + assert_eq!( + RadrootsTransportTarget::new(RadrootsTransportKind::Reticulum, invalid) + .expect_err("noncanonical Reticulum target"), + radroots_transport::RadrootsTransportError::InvalidTargetUri + ); + } } #[test] @@ -174,7 +203,9 @@ fn deferred_delivery_plan_mode_never_counts_as_satisfied() { RadrootsReticulumPreviewProfile::default() .with_behavior(RadrootsReticulumPreviewBehavior::DeferDeliveryPlans), ); - let request = delivery_request(vec![reticulum_target("reticulum:preview-unavailable")]); + let request = delivery_request(vec![reticulum_target( + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, + )]); let receipt = transport.deliver(request).expect("delivery receipt"); assert_eq!(receipt.target_receipts.len(), 1); @@ -226,7 +257,10 @@ fn fetch_reports_preview_unavailable_without_observed_events() { .expect("fetch receipt"); assert_eq!(receipt.request_id, "fetch-1"); - assert_eq!(receipt.endpoint_uri, "reticulum:preview-unavailable"); + assert_eq!( + receipt.endpoint_uri, + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI + ); assert_eq!(receipt.observed_event_count, 0); assert_eq!( receipt.implementation_state,