lib

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

package_boundary.rs (7143B)


      1 use std::{collections::BTreeSet, fs, path::Path};
      2 
      3 use radroots_blossom::{
      4     AuthorizationClaim, BlobDescriptor, BlobUrl, ByteVerifiedDescriptor, Error, MediaType, Sha256,
      5     authorization::{
      6         AuthoredUploadClaim, AuthorizationAction, AuthorizationContent, AuthorizationTarget,
      7         AuthorizationValidation, AuthorizationWireParts, ServerDomain, ServerScopeRequirement,
      8         ValidatedAuthorizationClaim,
      9     },
     10     descriptor::{ApprovedDescriptor, ByteCommitment},
     11     hash::{FileExtension, HashPath},
     12     url::ApprovedBlobUrl,
     13 };
     14 
     15 const MANIFEST: &str = include_str!("../Cargo.toml");
     16 const ROOT: &str = include_str!("../src/lib.rs");
     17 
     18 #[test]
     19 fn manifest_has_final_identity_features_and_no_radroots_dependencies() {
     20     assert!(MANIFEST.contains("name = \"radroots_blossom\""));
     21     assert!(MANIFEST.contains("version = \"0.1.0-alpha\""));
     22     assert!(MANIFEST.contains("publish = [\"crates-io\"]"));
     23     assert!(MANIFEST.contains("[lib]\nname = \"radroots_blossom\""));
     24     assert!(MANIFEST.contains("default = [\"std\", \"serde\"]"));
     25     assert_eq!(
     26         table_keys(MANIFEST, "[features]"),
     27         BTreeSet::from(["default", "serde", "std"])
     28     );
     29 
     30     for heading in ["[dependencies]", "[dev-dependencies]"] {
     31         assert!(
     32             table_keys(MANIFEST, heading)
     33                 .iter()
     34                 .all(|dependency| !dependency.starts_with("radroots_")),
     35             "{heading} must not contain Radroots dependencies"
     36         );
     37     }
     38 }
     39 
     40 #[test]
     41 fn manifest_dependency_surface_is_protocol_only() {
     42     assert_eq!(
     43         table_keys(MANIFEST, "[dependencies]"),
     44         BTreeSet::from([
     45             "mediatype",
     46             "serde",
     47             "sha2",
     48             "unicode-general-category",
     49             "url_nostd",
     50         ])
     51     );
     52     assert_eq!(
     53         table_keys(MANIFEST, "[dev-dependencies]"),
     54         BTreeSet::from(["hex", "serde_json"])
     55     );
     56 }
     57 
     58 #[test]
     59 fn crate_root_matches_the_approved_module_skeleton() {
     60     assert!(ROOT.contains("#![cfg_attr(not(feature = \"std\"), no_std)]"));
     61     assert_eq!(
     62         root_declarations("pub mod "),
     63         BTreeSet::from(["authorization", "descriptor", "hash", "media_type", "url"])
     64     );
     65     assert_eq!(root_declarations("mod "), BTreeSet::from(["error"]));
     66     assert_eq!(
     67         ROOT.lines()
     68             .map(str::trim)
     69             .filter(|line| line.starts_with("pub use "))
     70             .collect::<BTreeSet<_>>(),
     71         BTreeSet::from([
     72             "pub use authorization::AuthorizationClaim;",
     73             "pub use descriptor::BlobDescriptor;",
     74             "pub use descriptor::ByteVerifiedDescriptor;",
     75             "pub use error::Error;",
     76             "pub use hash::Sha256;",
     77             "pub use media_type::MediaType;",
     78             "pub use url::BlobUrl;",
     79         ])
     80     );
     81 }
     82 
     83 #[test]
     84 fn final_root_and_module_paths_compile() {
     85     fn assert_public_value<T: Clone + core::fmt::Debug + Eq + Send + Sync>() {}
     86 
     87     assert_public_value::<ApprovedBlobUrl>();
     88     assert_public_value::<ApprovedDescriptor>();
     89     assert_public_value::<AuthoredUploadClaim>();
     90     assert_public_value::<AuthorizationAction>();
     91     assert_public_value::<AuthorizationClaim>();
     92     assert_public_value::<AuthorizationContent>();
     93     assert_public_value::<AuthorizationTarget>();
     94     assert_public_value::<AuthorizationValidation>();
     95     assert_public_value::<AuthorizationWireParts>();
     96     assert_public_value::<BlobDescriptor>();
     97     assert_public_value::<BlobUrl>();
     98     assert_public_value::<ByteCommitment>();
     99     assert_public_value::<ByteVerifiedDescriptor>();
    100     assert_public_value::<Error>();
    101     assert_public_value::<FileExtension>();
    102     assert_public_value::<HashPath>();
    103     assert_public_value::<MediaType>();
    104     assert_public_value::<ServerDomain>();
    105     assert_public_value::<ServerScopeRequirement>();
    106     assert_public_value::<Sha256>();
    107     assert_public_value::<ValidatedAuthorizationClaim>();
    108 }
    109 
    110 #[test]
    111 fn production_types_do_not_repeat_the_crate_name() {
    112     for (path, production) in production_sources() {
    113         assert!(
    114             !production.contains("RadrootsBlossom"),
    115             "Blossom production type repeats its crate name: {}",
    116             path.display()
    117         );
    118     }
    119 }
    120 
    121 #[test]
    122 fn production_surface_exposes_no_public_traits() {
    123     for (path, production) in production_sources() {
    124         assert!(
    125             !production
    126                 .lines()
    127                 .map(str::trim_start)
    128                 .any(|line| line.starts_with("pub trait ")),
    129             "Blossom must expose concrete protocol values, not public traits: {}",
    130             path.display()
    131         );
    132     }
    133 }
    134 
    135 #[test]
    136 fn production_surface_owns_no_io_or_application_media_policy() {
    137     const FORBIDDEN_MARKERS: &[&str] = &[
    138         "std::fs",
    139         "std::io",
    140         "std::net",
    141         "std::path",
    142         "std::process",
    143         "std::thread",
    144         "std::time",
    145         "tokio::",
    146         "reqwest::",
    147         "hyper::",
    148         "ureq::",
    149         "tower::",
    150         "HttpClient",
    151         "BlobClient",
    152         "UploadClient",
    153         "DownloadClient",
    154         "UploadQueue",
    155         "DownloadQueue",
    156         "CachePolicy",
    157         "FileCache",
    158         "RetryPolicy",
    159         "BackoffPolicy",
    160         "MediaPolicy",
    161         "Filesystem",
    162         "FileSystem",
    163     ];
    164 
    165     for (path, production) in production_sources() {
    166         for marker in FORBIDDEN_MARKERS {
    167             assert!(
    168                 !production.contains(marker),
    169                 "Blossom production source owns forbidden I/O or application policy marker `{marker}`: {}",
    170                 path.display()
    171             );
    172         }
    173     }
    174 }
    175 
    176 fn production_sources() -> Vec<(std::path::PathBuf, String)> {
    177     let source_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
    178     fs::read_dir(source_root)
    179         .expect("read Blossom source directory")
    180         .map(|entry| entry.expect("source directory entry").path())
    181         .filter(|path| path.extension().and_then(|value| value.to_str()) == Some("rs"))
    182         .map(|path| {
    183             let source = fs::read_to_string(&path).expect("read Blossom source");
    184             let production = source
    185                 .split("\n#[cfg(test)]")
    186                 .next()
    187                 .unwrap_or(&source)
    188                 .to_owned();
    189             (path, production)
    190         })
    191         .collect()
    192 }
    193 
    194 fn table_keys<'a>(manifest: &'a str, heading: &str) -> BTreeSet<&'a str> {
    195     let table = manifest
    196         .split_once(heading)
    197         .unwrap_or_else(|| panic!("missing manifest table {heading}"))
    198         .1;
    199     table
    200         .lines()
    201         .skip(1)
    202         .take_while(|line| !line.trim_start().starts_with('['))
    203         .filter_map(|line| {
    204             let line = line.trim();
    205             (!line.is_empty() && !line.starts_with('#'))
    206                 .then(|| line.split_once('=').map(|(key, _)| key.trim()))
    207                 .flatten()
    208         })
    209         .collect()
    210 }
    211 
    212 fn root_declarations(prefix: &str) -> BTreeSet<&str> {
    213     ROOT.lines()
    214         .map(str::trim)
    215         .filter_map(|line| {
    216             line.strip_prefix(prefix)
    217                 .and_then(|name| name.strip_suffix(';'))
    218         })
    219         .collect()
    220 }