package_boundary.rs (9477B)
1 use std::collections::BTreeSet; 2 3 const MANIFEST: &str = include_str!("../Cargo.toml"); 4 const README: &str = include_str!("../README.md"); 5 const PUBLIC_API: &str = include_str!("../../../contracts/api_baselines/radroots_service_host.txt"); 6 const ROOT: &str = include_str!("../src/lib.rs"); 7 const CONFIG_DOCUMENT_SOURCE: &str = include_str!("../src/config/document.rs"); 8 const CONFIG_VALUE_SOURCE: &str = include_str!("../src/config/value.rs"); 9 const ADMIN_SOURCE: &str = concat!( 10 include_str!("../src/admin/mod.rs"), 11 include_str!("../src/admin/client.rs"), 12 include_str!("../src/admin/limits.rs"), 13 include_str!("../src/admin/model.rs"), 14 include_str!("../src/admin/peer.rs"), 15 include_str!("../src/admin/server.rs"), 16 include_str!("../src/admin/unix.rs"), 17 ); 18 const STATUS_SOURCE: &str = concat!( 19 include_str!("../src/status/mod.rs"), 20 include_str!("../src/status/phase.rs"), 21 include_str!("../src/status/reason.rs"), 22 include_str!("../src/status/service.rs"), 23 ); 24 const LIFECYCLE_SOURCE: &str = concat!( 25 include_str!("../src/lifecycle/mod.rs"), 26 include_str!("../src/lifecycle/cancel.rs"), 27 include_str!("../src/lifecycle/shutdown.rs"), 28 include_str!("../src/lifecycle/signal.rs"), 29 include_str!("../src/lifecycle/supervisor.rs"), 30 include_str!("../src/lifecycle/task.rs"), 31 ); 32 const OPERATIONS_PRIMITIVES_SOURCE: &str = concat!( 33 include_str!("../src/operations/mod.rs"), 34 include_str!("../src/operations/config.rs"), 35 include_str!("../src/operations/health.rs"), 36 include_str!("../src/operations/metrics.rs"), 37 ); 38 const OPERATIONS_SOURCE: &str = concat!( 39 include_str!("../src/operations/mod.rs"), 40 include_str!("../src/operations/server.rs"), 41 ); 42 43 #[test] 44 fn service_host_is_unpublished_lint_governed_and_dependency_bounded() { 45 for required in [ 46 "name = \"radroots_service_host\"", 47 "publish = false", 48 "version = \"0.1.0-alpha\"", 49 "[lints]\nworkspace = true", 50 ] { 51 assert!( 52 MANIFEST.contains(required), 53 "manifest is missing `{required}`" 54 ); 55 } 56 57 assert_eq!( 58 dependency_keys(MANIFEST), 59 BTreeSet::from([ 60 "bytes", 61 "fs2", 62 "getrandom", 63 "http", 64 "http-body-util", 65 "hyper", 66 "hyper-util", 67 "radroots_runtime_paths", 68 "rustix", 69 "serde", 70 "serde_json", 71 "tokio", 72 "tokio-util", 73 "toml", 74 ]) 75 ); 76 assert!(public_modules(ROOT).is_empty()); 77 assert_eq!( 78 private_modules(ROOT), 79 BTreeSet::from([ 80 "admin", 81 "build_info", 82 "config", 83 "entropy", 84 "error", 85 "lifecycle", 86 "operations", 87 "status", 88 "time", 89 ]) 90 ); 91 assert!(ROOT.contains("pub use radroots_runtime_paths::{InstanceId, ServiceId};")); 92 assert!(!STATUS_SOURCE.contains("serde(untagged)")); 93 assert!(!ADMIN_SOURCE.contains("serde(untagged)")); 94 for required in [ 95 ".take(REASON_CODES_MAX_ITEMS + 1)", 96 "deserialize_seq(ReasonCodesVisitor)", 97 "deserializer.deserialize_str(ReasonCodeVisitor)", 98 ] { 99 assert!(STATUS_SOURCE.contains(required)); 100 } 101 for required in [ 102 "struct NonNullSerializer", 103 "struct StrictJsonPayload", 104 "encode_bounded(result, self.response_body_limit)", 105 ] { 106 assert!(ADMIN_SOURCE.contains(required)); 107 } 108 for forbidden in [ 109 "serde_json::Value", 110 "serde_json::to_value", 111 "StrictJsonValue", 112 ] { 113 assert!(!ADMIN_SOURCE.contains(forbidden)); 114 } 115 for source in [ 116 CONFIG_DOCUMENT_SOURCE, 117 CONFIG_VALUE_SOURCE, 118 ADMIN_SOURCE, 119 STATUS_SOURCE, 120 LIFECYCLE_SOURCE, 121 OPERATIONS_PRIMITIVES_SOURCE, 122 ] { 123 for forbidden in [ 124 "impl Into<String>", 125 "impl Into<Box<str>>", 126 "let value = value.into();", 127 "String::deserialize", 128 ] { 129 assert!( 130 !source.contains(forbidden), 131 "bounded text boundary still contains `{forbidden}`" 132 ); 133 } 134 } 135 for forbidden in ["tokio::signal", "ctrl_c", "signal_hook"] { 136 assert!(!LIFECYCLE_SOURCE.contains(forbidden)); 137 } 138 for forbidden in ["TcpListener", "TcpStream", "tokio::spawn"] { 139 assert!(!OPERATIONS_PRIMITIVES_SOURCE.contains(forbidden)); 140 } 141 assert!(!OPERATIONS_SOURCE.contains("process::exit")); 142 for source in [CONFIG_DOCUMENT_SOURCE, CONFIG_VALUE_SOURCE] { 143 let production = source.split_once("#[cfg(test)]").unwrap().0; 144 for forbidden in [ 145 "create_dir", 146 "read_dir", 147 "std::env", 148 "TcpStream", 149 "UdpSocket", 150 "SystemTime", 151 "MonotonicClock", 152 "tokio::", 153 "process::", 154 ] { 155 assert!(!production.contains(forbidden)); 156 } 157 } 158 } 159 160 #[test] 161 fn documentation_and_reviewed_public_api_are_complete_and_dependency_safe() { 162 for required in [ 163 "## Strict configuration values", 164 "## Cached state and explicit cancellation", 165 "## Local administration and operations", 166 "## Process and runtime ownership", 167 "## Supported targets and publication", 168 "services_hardening_host.v1.json", 169 "contracts/api_baselines/radroots_service_host.txt", 170 "BoundedCount::<64>::new(8)", 171 "cached_service_state(CachedServiceState::new", 172 "parent.child_token()", 173 "streamed directly into the capped response writer", 174 "validates borrowed UTF-8 before it creates the", 175 "Administration operation and correlation identifiers are closed ASCII values", 176 "Their first byte is alphanumeric; later bytes are", 177 "alphanumeric or `.`, `_`, `:`, or `-`", 178 "Ordinary `Debug` is redacted and no", 179 "`Display` implementation exposes the retained value", 180 "explicit borrowed `as_str` accessor for serialization", 181 "Shutdown task cancellation is phase aware", 182 "Entering a phase cancels only tasks", 183 "assigned to that phase and does not advance until their joins are observed", 184 "bounded one-shot work drains without", 185 "cancellation during `DrainOperations`", 186 "a fatal task outcome still", 187 "cancels and joins the complete graph", 188 "retains one absolute", 189 "deadline plus the completed handler/drain boundary across cancellation and", 190 "no phase or cleanup attempt receives a fresh grace period", 191 "incomplete handler may be entered again and must be idempotent and", 192 "cancellation safe", 193 "first phase or task failure is retained while later", 194 "close phases continue as long as the original deadline remains", 195 ] { 196 assert!(README.contains(required), "README is missing `{required}`"); 197 } 198 199 for required in [ 200 "pub struct radroots_service_host::BuildInfo", 201 "pub struct radroots_service_host::ConfigDocumentExpectation", 202 "pub struct radroots_service_host::PositiveDuration", 203 "pub struct radroots_service_host::CancellationToken", 204 "pub struct radroots_service_host::TaskSupervisor", 205 "pub struct radroots_service_host::AdminRouter", 206 "pub struct radroots_service_host::OperationsServer", 207 "pub struct radroots_service_host::BoundedMetricsSnapshot", 208 "pub struct radroots_service_host::AdminOperationId", 209 "pub struct radroots_service_host::AdminCorrelationId", 210 "pub struct radroots_service_host::ServiceStatus", 211 "pub trait radroots_service_host::MonotonicClock", 212 "pub trait radroots_service_host::EntropySource", 213 ] { 214 assert!( 215 PUBLIC_API.contains(required), 216 "reviewed API baseline is missing `{required}`" 217 ); 218 } 219 220 for forbidden in [ 221 "pub mod radroots_service_host::admin", 222 "pub mod radroots_service_host::config", 223 "pub mod radroots_service_host::lifecycle", 224 "pub mod radroots_service_host::operations", 225 "pub mod radroots_service_host::status", 226 "hyper::", 227 "rustix::", 228 "serde_json::", 229 "tokio::", 230 "tokio_util::", 231 "impl core::fmt::Display for radroots_service_host::AdminOperationId", 232 "impl core::fmt::Display for radroots_service_host::AdminCorrelationId", 233 ] { 234 assert!( 235 !PUBLIC_API.contains(forbidden), 236 "reviewed API baseline exposes `{forbidden}`" 237 ); 238 } 239 } 240 241 fn public_modules(root: &str) -> BTreeSet<&str> { 242 root.lines() 243 .filter_map(|line| line.strip_prefix("pub mod ")) 244 .filter_map(|module| module.strip_suffix(';')) 245 .collect() 246 } 247 248 fn private_modules(root: &str) -> BTreeSet<&str> { 249 root.lines() 250 .filter_map(|line| line.strip_prefix("mod ")) 251 .filter_map(|module| module.strip_suffix(';')) 252 .collect() 253 } 254 255 fn dependency_keys(manifest: &str) -> BTreeSet<&str> { 256 manifest 257 .split_once("[dependencies]") 258 .map(|(_, dependencies)| dependencies) 259 .unwrap_or_default() 260 .lines() 261 .skip(1) 262 .take_while(|line| !line.starts_with('[')) 263 .filter_map(|line| line.split_once('=').map(|(key, _)| key.trim())) 264 .filter(|key| !key.is_empty()) 265 .collect() 266 }