lib

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

package_boundary.rs (11378B)


      1 use std::{collections::BTreeSet, fs, path::Path};
      2 
      3 #[allow(unused_imports)]
      4 use radroots_transport::{
      5     DeliveryReceipt as _, DeliveryRequest as _, Error as _, EventSink as _, EventSource as _,
      6     EventSubscriber as _, EventSubscription as _, FetchPage as _, FetchRequest as _,
      7     SubscriptionEnd as _, SubscriptionEvent as _, SubscriptionRequest as _, Target as _,
      8     TargetNetworkPolicy as _, TargetSet as _, TransportId as _, capability as _, endpoint as _,
      9     error as _, outcome as _, policy as _, sink as _, source as _, target as _,
     10 };
     11 
     12 const MANIFEST: &str = include_str!("../Cargo.toml");
     13 const EXAMPLE: &str = include_str!("../examples/host_transport.rs");
     14 const PUBLIC_API: &str = include_str!("../../../contracts/api_baselines/radroots_transport.txt");
     15 const README: &str = include_str!("../README.md");
     16 const ROOT: &str = include_str!("../src/lib.rs");
     17 const SOURCE: &str = include_str!("../src/source.rs");
     18 const SINK: &str = include_str!("../src/sink.rs");
     19 const ID: &str = include_str!("../src/id.rs");
     20 const TARGET: &str = include_str!("../src/target.rs");
     21 const DEVIATIONS: &str = include_str!("../../../contracts/architecture/deviations.toml");
     22 
     23 #[test]
     24 fn manifest_has_final_identity_features_and_required_radroots_dependencies() {
     25     for required in [
     26         "name = \"radroots_transport\"",
     27         "version = \"0.1.0-alpha\"",
     28         "publish = [\"crates-io\"]",
     29         "[lib]\nname = \"radroots_transport\"",
     30         "default = [\"std\", \"serde\"]",
     31         "radroots_event = { workspace = true, default-features = false }",
     32         "radroots_identity = { workspace = true, default-features = false }",
     33         "radroots_protocol = { workspace = true, default-features = false }",
     34     ] {
     35         assert!(
     36             MANIFEST.contains(required),
     37             "manifest is missing {required}"
     38         );
     39     }
     40 
     41     assert_eq!(
     42         table_keys(MANIFEST, "[features]"),
     43         BTreeSet::from(["default", "serde", "std"])
     44     );
     45     assert_eq!(
     46         table_keys(MANIFEST, "[dependencies]")
     47             .into_iter()
     48             .filter(|dependency| dependency.starts_with("radroots_"))
     49             .collect::<BTreeSet<_>>(),
     50         BTreeSet::from(["radroots_event", "radroots_identity", "radroots_protocol"])
     51     );
     52 }
     53 
     54 #[test]
     55 fn crate_root_declares_the_approved_public_module_skeleton() {
     56     assert!(ROOT.contains("#![doc = include_str!(\"../README.md\")]"));
     57     assert!(ROOT.contains("#![cfg_attr(not(feature = \"std\"), no_std)]"));
     58     assert_eq!(
     59         root_declarations("pub mod "),
     60         BTreeSet::from([
     61             "capability",
     62             "endpoint",
     63             "error",
     64             "outcome",
     65             "policy",
     66             "sink",
     67             "source",
     68             "target",
     69         ])
     70     );
     71 }
     72 
     73 #[test]
     74 fn package_documentation_and_reviewed_api_baseline_are_complete() {
     75     for required in [
     76         "## Typical flow",
     77         "## Host SPI contract",
     78         "## Targets and extensible identity",
     79         "## Bounds, deadlines, cancellation, and commit points",
     80         "## Outcomes, partial success, and retry",
     81         "## Serialization and provenance",
     82         "## Security and side effects",
     83         "## Features",
     84         "## Intended consumers",
     85         "radroots_crates_release_v1.toml",
     86         "examples/host_transport.rs",
     87         "indexed single-letter tag values",
     88         "TargetNetworkPolicy::PrivateDevice",
     89         "literal RFC1918 IPv4 or ULA IPv6 endpoints",
     90         "not connection authority",
     91     ] {
     92         assert!(README.contains(required), "README is missing {required}");
     93     }
     94     for required in [
     95         "impl EventSource for HostTransport",
     96         "impl EventSubscriber for HostTransport",
     97         "impl EventSink for HostTransport",
     98         "fn fetch(&self, _request: FetchRequest) -> BoxFuture",
     99         "_request: SubscriptionRequest",
    100         "_request: DeliveryRequest",
    101         "let source: &dyn EventSource",
    102         "let subscriber: &dyn EventSubscriber",
    103         "let sink: &dyn EventSink",
    104         "drop(future)",
    105     ] {
    106         assert!(EXAMPLE.contains(required), "example is missing {required}");
    107     }
    108     for required in [
    109         "pub mod radroots_transport::capability",
    110         "pub mod radroots_transport::endpoint",
    111         "pub mod radroots_transport::error",
    112         "pub mod radroots_transport::outcome",
    113         "pub mod radroots_transport::policy",
    114         "pub mod radroots_transport::sink",
    115         "pub mod radroots_transport::source",
    116         "pub mod radroots_transport::target",
    117         "pub trait radroots_transport::EventSource",
    118         "pub trait radroots_transport::EventSubscriber",
    119         "pub trait radroots_transport::EventSubscription",
    120         "pub trait radroots_transport::EventSink",
    121         "pub struct radroots_transport::source::SubscriptionBounds",
    122         "pub struct radroots_transport::source::SubscriptionCheckpoint",
    123         "pub struct radroots_transport::SubscriptionRequest",
    124         "pub enum radroots_transport::SubscriptionEndReason",
    125         "pub const radroots_transport::source::SUBSCRIPTION_MAX_EVENTS: u16",
    126         "pub const radroots_transport::source::FETCH_SELECTOR_MAX_TAG_VALUES: usize",
    127         "pub const radroots_transport::source::FETCH_SELECTOR_MAX_TAG_KEYS: usize",
    128         "pub const radroots_transport::source::FETCH_SELECTOR_TAG_VALUE_MAX_BYTES: usize",
    129         "pub fn radroots_transport::source::FetchSelector::with_exact_tag_value",
    130         "pub fn radroots_transport::source::FetchSelector::exact_tag_filters",
    131         "pub fn radroots_transport::target::Target::new(radroots_transport::TransportId",
    132         "pub fn radroots_transport::target::Target::kind(&self) -> &radroots_transport::TransportId",
    133         "pub enum radroots_transport::target::TargetNetworkPolicy",
    134         "pub fn radroots_transport::target::Target::nostr_relay_with_policy",
    135     ] {
    136         assert!(
    137             PUBLIC_API.contains(required),
    138             "public API baseline is missing {required}"
    139         );
    140     }
    141     for forbidden in [
    142         "pub trait radroots_transport::RadrootsTransport",
    143         "RadrootsTransportFuture",
    144         "RadrootsTransportKind",
    145         "RadrootsTransportTargetSet",
    146         "Target::reticulum",
    147         "TargetScope::local_reticulum",
    148     ] {
    149         assert!(
    150             !PUBLIC_API.contains(forbidden),
    151             "reviewed public API baseline still exposes `{forbidden}`"
    152         );
    153     }
    154 }
    155 
    156 #[test]
    157 fn every_public_module_has_crate_level_documentation() {
    158     let source_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
    159     for module in [
    160         "capability",
    161         "endpoint",
    162         "error",
    163         "outcome",
    164         "policy",
    165         "sink",
    166         "source",
    167         "target",
    168     ] {
    169         let path = source_root.join(format!("{module}.rs"));
    170         let source = fs::read_to_string(&path).expect("read module source");
    171         assert!(
    172             source.starts_with("//! "),
    173             "public module {module} must start with module documentation"
    174         );
    175     }
    176 }
    177 
    178 #[test]
    179 fn source_subscription_and_sink_are_independent_dyn_compatible_host_spis() {
    180     for required in [
    181         "pub trait EventSource: Send + Sync",
    182         "fn status(&self)",
    183         "fn fetch(",
    184         "Dropping a returned future requests cancellation.",
    185         "explicit request deadline",
    186     ] {
    187         assert!(
    188             SOURCE.contains(required),
    189             "source SPI is missing {required}"
    190         );
    191     }
    192     assert!(!SOURCE.contains("fn deliver("));
    193 
    194     for required in [
    195         "pub trait EventSubscriber: Send + Sync",
    196         "pub trait EventSubscription: Send",
    197         "fn subscribe(",
    198         "fn next(&mut self)",
    199         "fn cancel(&mut self)",
    200         "Once [`SubscriptionNext::End`] has been",
    201         "exact same",
    202     ] {
    203         assert!(
    204             SOURCE.contains(required),
    205             "subscription SPI is missing {required}"
    206         );
    207     }
    208 
    209     for required in [
    210         "pub trait EventSink: Send + Sync",
    211         "fn status(&self)",
    212         "fn deliver(",
    213         "Dropping a returned future requests cancellation.",
    214         "explicit request deadline",
    215     ] {
    216         assert!(SINK.contains(required), "sink SPI is missing {required}");
    217     }
    218     assert!(!SINK.contains("fn fetch("));
    219 
    220     for retired in [
    221         "Publish-frozen external-consumer",
    222         "pub fn reticulum(",
    223         "pub fn reticulum_with_metadata(",
    224         "pub fn local_reticulum(",
    225         "RadrootsTransportTarget",
    226     ] {
    227         assert!(
    228             !TARGET.contains(retired),
    229             "retired external-consumer surface remains: `{retired}`"
    230         );
    231     }
    232     for retired in [
    233         "RadrootsTransportKind",
    234         "pub const Local",
    235         "pub const Nostr",
    236         "pub const Reticulum",
    237     ] {
    238         assert!(
    239             !ID.contains(retired),
    240             "retired transport identity remains: `{retired}`"
    241         );
    242     }
    243     for required in [
    244         "id = \"RCRV1-DEV-007\"",
    245         "status = \"closed\"",
    246         "affected_steps = [\"122\", \"170\", \"215\", \"235\", \"305\"]",
    247         "Step 313 source census confirmed every external-consumer alias and helper is absent",
    248     ] {
    249         assert!(
    250             DEVIATIONS.contains(required),
    251             "temporary downstream ownership record is missing `{required}`"
    252         );
    253     }
    254 }
    255 
    256 #[test]
    257 fn public_api_excludes_adapter_runtime_storage_and_retry_authority() {
    258     let dependency_keys = table_keys(MANIFEST, "[dependencies]");
    259     for forbidden in [
    260         "radroots_outbox",
    261         "radroots_storage",
    262         "radroots_transport_nostr",
    263         "radroots_transport_reticulum",
    264         "nostr-sdk",
    265         "nostr_sdk",
    266         "reqwest",
    267         "sqlx",
    268         "tokio",
    269     ] {
    270         assert!(
    271             !dependency_keys.contains(forbidden),
    272             "generic transport must not depend on `{forbidden}`"
    273         );
    274     }
    275 
    276     assert!(ID.contains("pub struct TransportId("));
    277     assert!(!ID.contains("pub enum TransportId"));
    278     for forbidden in [
    279         "RADROOTS_RETICULUM_",
    280         "ReticulumDestination",
    281         "RelayUrl",
    282         "NostrRelay",
    283     ] {
    284         assert!(
    285             !ROOT.contains(forbidden),
    286             "generic transport root must not export adapter symbol `{forbidden}`"
    287         );
    288     }
    289     for source in [SOURCE, SINK] {
    290         for forbidden in [
    291             "tokio::spawn",
    292             "std::thread::spawn",
    293             "retry_loop",
    294             "fallback_transport",
    295         ] {
    296             assert!(
    297                 !source.contains(forbidden),
    298                 "transport SPI must not own runtime behavior `{forbidden}`"
    299             );
    300         }
    301     }
    302 }
    303 
    304 fn table_keys<'a>(source: &'a str, heading: &str) -> BTreeSet<&'a str> {
    305     let mut in_table = false;
    306     source
    307         .lines()
    308         .filter_map(|line| {
    309             let line = line.trim();
    310             if line == heading {
    311                 in_table = true;
    312                 return None;
    313             }
    314             if in_table && line.starts_with('[') {
    315                 in_table = false;
    316             }
    317             in_table
    318                 .then(|| line.split_once('=').map(|(key, _)| key.trim()))
    319                 .flatten()
    320         })
    321         .collect()
    322 }
    323 
    324 fn root_declarations(prefix: &str) -> BTreeSet<&str> {
    325     ROOT.lines()
    326         .map(str::trim)
    327         .filter_map(|line| line.strip_prefix(prefix))
    328         .filter_map(|line| line.strip_suffix(';'))
    329         .collect()
    330 }