commit 3fd537525d955a22053715c436481e991820ed50
parent aeb160c6db51ccaf3cd4c532b044c9c4e98c5a89
Author: triesap <tyson@radroots.org>
Date: Thu, 30 Jul 2026 19:06:38 +0000
transport: complete package conformance coverage
- gate serialization assertions to the serde feature without losing core tests
- compile-check final root exports and reject adapter or runtime dependencies
- qualify no-default, std, serde, default, all-feature, and WASM matrices
- verify docs, doctests, API boundaries, dependencies, and workspace architecture
Diffstat:
3 files changed, 65 insertions(+), 10 deletions(-)
diff --git a/crates/transport/tests/delivery_contract.rs b/crates/transport/tests/delivery_contract.rs
@@ -255,6 +255,7 @@ fn retryability_and_terminality_are_explicit_normalized_data() {
}
#[test]
+#[cfg(feature = "serde")]
fn serde_revalidates_policy_outcome_and_receipt_invariants() {
let request = request(SatisfactionPolicy::new(
SatisfactionClass::Accepted,
diff --git a/crates/transport/tests/package_boundary.rs b/crates/transport/tests/package_boundary.rs
@@ -2,6 +2,8 @@ use std::collections::BTreeSet;
#[allow(unused_imports)]
use radroots_transport::{
+ DeliveryReceipt as _, DeliveryRequest as _, Error as _, EventSink as _, EventSource as _,
+ FetchPage as _, FetchRequest as _, Target as _, TargetSet as _, TransportId as _,
capability as _, endpoint as _, error as _, outcome as _, policy as _, sink as _, source as _,
target as _,
};
@@ -10,6 +12,7 @@ const MANIFEST: &str = include_str!("../Cargo.toml");
const ROOT: &str = include_str!("../src/lib.rs");
const SOURCE: &str = include_str!("../src/source.rs");
const SINK: &str = include_str!("../src/sink.rs");
+const ID: &str = include_str!("../src/id.rs");
const LEGACY_TRANSPORT: &str = include_str!("../src/transport.rs");
#[test]
@@ -91,6 +94,54 @@ fn source_and_sink_are_independent_dyn_compatible_host_spis() {
assert!(LEGACY_TRANSPORT.contains("#[doc(hidden)]\npub trait RadrootsTransport: Send + Sync"));
}
+#[test]
+fn public_api_excludes_adapter_runtime_storage_and_retry_authority() {
+ let dependency_keys = table_keys(MANIFEST, "[dependencies]");
+ for forbidden in [
+ "radroots_outbox",
+ "radroots_storage",
+ "radroots_transport_nostr",
+ "radroots_transport_reticulum",
+ "nostr-sdk",
+ "nostr_sdk",
+ "reqwest",
+ "sqlx",
+ "tokio",
+ ] {
+ assert!(
+ !dependency_keys.contains(forbidden),
+ "generic transport must not depend on `{forbidden}`"
+ );
+ }
+
+ assert!(ID.contains("pub struct TransportId("));
+ assert!(!ID.contains("pub enum TransportId"));
+ for forbidden in [
+ "RADROOTS_RETICULUM_",
+ "ReticulumDestination",
+ "RelayUrl",
+ "NostrRelay",
+ ] {
+ assert!(
+ !ROOT.contains(forbidden),
+ "generic transport root must not export adapter symbol `{forbidden}`"
+ );
+ }
+ for source in [SOURCE, SINK] {
+ for forbidden in [
+ "tokio::spawn",
+ "std::thread::spawn",
+ "retry_loop",
+ "fallback_transport",
+ ] {
+ assert!(
+ !source.contains(forbidden),
+ "transport SPI must not own runtime behavior `{forbidden}`"
+ );
+ }
+ }
+}
+
fn table_keys<'a>(source: &'a str, heading: &str) -> BTreeSet<&'a str> {
let mut in_table = false;
source
diff --git a/crates/transport/tests/source_contract.rs b/crates/transport/tests/source_contract.rs
@@ -120,16 +120,19 @@ fn page_preserves_cursor_provenance_and_partial_target_outcomes() {
);
assert!(matches!(page.next_page(), NextPage::Cursor(cursor) if cursor.as_str() == "page-2"));
- let encoded = serde_json::to_string(&page).expect("serialize page");
- assert!(!encoded.contains("admission"));
- assert!(!encoded.contains("storage"));
- assert_eq!(
- serde_json::from_str::<FetchPage>(&encoded).expect("deserialize page"),
- page
- );
- let mut invalid_time = serde_json::to_value(&page).expect("page value");
- invalid_time["events"][0]["provenance"]["observed_at_unix_ms"] = 0.into();
- assert!(serde_json::from_value::<FetchPage>(invalid_time).is_err());
+ #[cfg(feature = "serde")]
+ {
+ let encoded = serde_json::to_string(&page).expect("serialize page");
+ assert!(!encoded.contains("admission"));
+ assert!(!encoded.contains("storage"));
+ assert_eq!(
+ serde_json::from_str::<FetchPage>(&encoded).expect("deserialize page"),
+ page
+ );
+ let mut invalid_time = serde_json::to_value(&page).expect("page value");
+ invalid_time["events"][0]["provenance"]["observed_at_unix_ms"] = 0.into();
+ assert!(serde_json::from_value::<FetchPage>(invalid_time).is_err());
+ }
}
#[test]