conformance.rs (4254B)
1 use radroots_signing::{ 2 Error, Signer, 3 capability::{CancellationSupport, SignerCapability, SignerKind}, 4 error::{CATALOG, Kind}, 5 recovery::ReplayCapability, 6 request::{CancellationPolicy, SignPolicy}, 7 }; 8 9 #[test] 10 fn public_error_catalog_is_unique_consistent_and_protocol_redacted() { 11 let mut codes = std::collections::BTreeSet::new(); 12 for descriptor in CATALOG.iter().copied() { 13 assert!(codes.insert(descriptor.code())); 14 let error = Error::new(descriptor.kind()); 15 assert_eq!(error.kind(), descriptor.kind()); 16 assert_eq!(error.code(), descriptor.code()); 17 assert_eq!(error.class(), descriptor.class()); 18 assert_eq!(error.retryable(), descriptor.retryable()); 19 assert_eq!(error.recovery_actions(), descriptor.recovery_actions()); 20 let report = error.to_report(None); 21 assert_eq!(report.code().as_str(), descriptor.code()); 22 assert_eq!(report.message().as_str(), "[redacted]"); 23 } 24 assert_eq!(codes.len(), Kind::ALL.len()); 25 } 26 27 #[test] 28 fn deadline_and_cancellation_contracts_are_explicit() { 29 let error = SignPolicy::new(0, CancellationPolicy::LocalCooperative) 30 .expect_err("zero deadline must fail"); 31 assert_eq!(error.kind(), Kind::InvalidArgument); 32 33 let local = SignPolicy::new(42, CancellationPolicy::LocalCooperative).expect("local policy"); 34 let remote = 35 SignPolicy::new(42, CancellationPolicy::PreservePublishedRequest).expect("remote policy"); 36 assert_eq!(local.deadline_unix_ms(), 42); 37 assert_eq!(local.cancellation(), CancellationPolicy::LocalCooperative); 38 assert_eq!( 39 remote.cancellation(), 40 CancellationPolicy::PreservePublishedRequest 41 ); 42 43 let capability = SignerCapability::new( 44 SignerKind::Remote, 45 ReplayCapability::ExactReplayByRequestId, 46 CancellationSupport::BeforeAndAfterPublication, 47 true, 48 true, 49 ); 50 assert_eq!(capability.kind(), SignerKind::Remote); 51 assert_eq!( 52 capability.replay(), 53 ReplayCapability::ExactReplayByRequestId 54 ); 55 assert_eq!( 56 capability.cancellation(), 57 CancellationSupport::BeforeAndAfterPublication 58 ); 59 assert!(capability.reports_progress()); 60 assert!(capability.may_require_authentication()); 61 } 62 63 #[cfg(feature = "serde")] 64 #[test] 65 fn policy_wire_labels_are_stable_and_round_trip() { 66 let policy = 67 SignPolicy::new(42, CancellationPolicy::PreservePublishedRequest).expect("remote policy"); 68 let json = serde_json::to_string(&policy).expect("serialize policy"); 69 assert!(json.contains("preserve_published_request")); 70 assert_eq!( 71 serde_json::from_str::<SignPolicy>(&json).expect("deserialize policy"), 72 policy 73 ); 74 assert!(serde_json::from_str::<SignPolicy>( 75 r#"{"deadline_unix_ms":0,"cancellation":"local_cooperative","deprecated_plan":"deny","managed_signing":"any_validated_source"}"# 76 ) 77 .is_err()); 78 } 79 80 #[test] 81 fn public_service_types_preserve_dyn_and_thread_safety() { 82 fn assert_dyn(_: &dyn Signer) {} 83 fn assert_send_sync<T: Send + Sync + ?Sized>() {} 84 85 let _ = assert_dyn; 86 assert_send_sync::<dyn Signer>(); 87 assert_send_sync::<Error>(); 88 } 89 90 #[cfg(feature = "std")] 91 #[test] 92 fn native_sources_are_opt_in_and_never_appear_in_diagnostics() { 93 use std::error::Error as _; 94 95 #[derive(Debug)] 96 struct Sensitive; 97 98 impl core::fmt::Display for Sensitive { 99 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 100 formatter.write_str("nsec1-integration-secret") 101 } 102 } 103 104 impl std::error::Error for Sensitive {} 105 106 let error = Error::with_source(Kind::SignerUnavailable, Sensitive); 107 assert!(error.source().is_some()); 108 assert!(!error.to_string().contains("nsec1")); 109 assert!(!format!("{error:?}").contains("nsec1")); 110 assert!(!serde_or_debug_report(&error).contains("nsec1")); 111 } 112 113 #[cfg(all(feature = "std", feature = "serde"))] 114 fn serde_or_debug_report(error: &Error) -> String { 115 serde_json::to_string(&error.to_report(None)).expect("serialize report") 116 } 117 118 #[cfg(all(feature = "std", not(feature = "serde")))] 119 fn serde_or_debug_report(error: &Error) -> String { 120 format!("{:?}", error.to_report(None)) 121 }