lib

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

package_boundary.rs (5027B)


      1 use std::collections::BTreeSet;
      2 
      3 const MANIFEST: &str = include_str!("../Cargo.toml");
      4 const ROOT: &str = include_str!("../src/lib.rs");
      5 const README: &str = include_str!("../README.md");
      6 const SOURCES: &[(&str, &str)] = &[
      7     ("engine.rs", include_str!("../src/engine.rs")),
      8     ("ingest.rs", include_str!("../src/ingest.rs")),
      9     ("policy.rs", include_str!("../src/policy.rs")),
     10     ("projection.rs", include_str!("../src/projection.rs")),
     11     ("pull.rs", include_str!("../src/pull.rs")),
     12     ("pull/summary.rs", include_str!("../src/pull/summary.rs")),
     13     ("pull/wire.rs", include_str!("../src/pull/wire.rs")),
     14     ("push.rs", include_str!("../src/push.rs")),
     15     ("status.rs", include_str!("../src/status.rs")),
     16 ];
     17 
     18 #[test]
     19 fn pull_summary_contract_retains_existing_bounds_and_final_outcome_compatibility() {
     20     let contract: serde_json::Value = serde_json::from_str(include_str!(
     21         "../../../contracts/architecture/decisions/pull_target_evidence.v1.json"
     22     ))
     23     .expect("pull target evidence contract");
     24     assert_eq!(contract["owner"], "radroots_sync");
     25     assert_eq!(
     26         contract["max_targets"],
     27         radroots_transport::target::TARGET_SET_MAX_ITEMS
     28     );
     29     assert_eq!(contract["max_pages"], radroots_sync::pull::PULL_MAX_PAGES);
     30     assert_eq!(
     31         contract["summary_fields"],
     32         serde_json::json!([
     33             "target",
     34             "pages_observed",
     35             "incomplete_pages",
     36             "missing_outcome_pages",
     37             "last_incomplete"
     38         ])
     39     );
     40     assert!(
     41         contract["compatibility"]
     42             .as_str()
     43             .unwrap()
     44             .contains("legacy")
     45     );
     46 }
     47 
     48 #[test]
     49 fn sync_depends_only_on_final_orchestration_boundaries() {
     50     for required in [
     51         "name = \"radroots_sync\"",
     52         "version = \"0.1.0-alpha\"",
     53         "publish = [\"crates-io\"]",
     54         "[lib]\nname = \"radroots_sync\"",
     55         "default = [\"serde\"]",
     56     ] {
     57         assert!(
     58             MANIFEST.contains(required),
     59             "manifest is missing `{required}`"
     60         );
     61     }
     62     assert_eq!(
     63         dependency_keys(MANIFEST)
     64             .into_iter()
     65             .filter(|dependency| dependency.starts_with("radroots_"))
     66             .collect::<BTreeSet<_>>(),
     67         BTreeSet::from([
     68             "radroots_event",
     69             "radroots_event_codec",
     70             "radroots_protocol",
     71             "radroots_signing",
     72             "radroots_storage",
     73             "radroots_trade",
     74             "radroots_transport",
     75         ])
     76     );
     77     assert!(MANIFEST.contains("serde = { workspace = true, optional = true }"));
     78     for forbidden in [
     79         "radroots_event_store",
     80         "radroots_event_index",
     81         "radroots_outbox",
     82         "radroots_runtime_store",
     83         "radroots_transport_nostr",
     84     ] {
     85         assert!(!MANIFEST.contains(forbidden));
     86         assert!(!ROOT.contains(forbidden));
     87     }
     88     assert_eq!(
     89         declarations(ROOT, "pub mod "),
     90         BTreeSet::from(["ingest", "policy", "projection", "pull", "push", "status"])
     91     );
     92 }
     93 
     94 #[test]
     95 fn sync_has_no_runtime_scheduler_or_process_lifecycle_authority() {
     96     for forbidden_dependency in [
     97         "tokio",
     98         "async-std",
     99         "smol",
    100         "rayon",
    101         "signal-hook",
    102         "ctrlc",
    103     ] {
    104         assert!(
    105             !dependency_keys(MANIFEST).contains(forbidden_dependency),
    106             "sync must not depend on runtime package `{forbidden_dependency}`"
    107         );
    108     }
    109     for (path, source) in SOURCES {
    110         for forbidden in [
    111             "tokio::",
    112             "async_std::",
    113             "smol::",
    114             "std::thread",
    115             "thread::spawn",
    116             ".spawn(",
    117             "Runtime::new",
    118             "new_multi_thread",
    119             "new_current_thread",
    120             "tokio::time",
    121             "SystemTime::now",
    122             "Instant::now",
    123             "ctrl_c",
    124             "signal_hook",
    125             "process::exit",
    126         ] {
    127             assert!(
    128                 !source.contains(forbidden),
    129                 "{path} must not own runtime behavior `{forbidden}`"
    130             );
    131         }
    132     }
    133     let readme = README.split_whitespace().collect::<Vec<_>>().join(" ");
    134     for explicit_boundary in [
    135         "does not create an executor",
    136         "spawn workers",
    137         "install timers",
    138         "own process lifecycle",
    139     ] {
    140         assert!(readme.contains(explicit_boundary));
    141     }
    142 }
    143 
    144 fn declarations<'a>(source: &'a str, prefix: &str) -> BTreeSet<&'a str> {
    145     source
    146         .lines()
    147         .filter_map(|line| line.trim().strip_prefix(prefix))
    148         .filter_map(|name| name.strip_suffix(';'))
    149         .collect()
    150 }
    151 
    152 fn dependency_keys(manifest: &str) -> BTreeSet<&str> {
    153     manifest
    154         .split_once("[dependencies]")
    155         .map(|(_, dependencies)| dependencies)
    156         .unwrap_or_default()
    157         .lines()
    158         .skip(1)
    159         .take_while(|line| !line.starts_with('['))
    160         .filter_map(|line| line.split_once('=').map(|(key, _)| key.trim()))
    161         .filter(|key| !key.is_empty())
    162         .collect()
    163 }