test: implement coverage #005 Tier-A suggestions (S1-S4, S8)
Add 165 tests covering the directly-testable surface identified in coverage review #005. Workspace coverage rises 87.1% -> 91.2% (5759/6615 -> 6505/7135); all 389 tests pass, clippy clean. - S1 (connection.rs): dispatch_envelope across all five event-type arms for Call + Subscribe, plus SubscriptionStream poll_next branches and SubscriptionStream::closed. - S2 (types.rs): map_quinn/iroh_connection_error for TimedOut/Reset/ ApplicationClosed/other, plus HandlerError + StreamError Debug/Display/ source for every variant. - S3 (config.rs): Ed25519SecretKey from_bytes/as_bytes round-trip, sign+verify, tampered-message rejection, Debug non-leakage. - S4 (endpoint.rs): build_rustls_server_config RawKey/SelfSigned/Acme arms, build_quinn_server_config_from_rustls, load_private_key/ load_cert_chain error paths, has_iroh_identity branches, AcceptAnyCertVerifier trait methods, Ed25519SigningKey trait impls (choose_scheme both branches, algorithm, public_key, sign, scheme), RawKeyCertResolver + AlknetEndpoint Debug. endpoint.rs 56% -> 73%. - S8 (vault protocol.rs): the existing redacted-deserialize test passed for the wrong reason (JSON string failed Vec<u8> coercion before the guard). Two new tests exercise the guard directly via a [REDACTED] byte array (rejected) and a real payload (accepted). protocol.rs -> 100%. Deferred to follow-up: S5 (loopback quinn integration test, the real unlock for accept/dispatch/stream paths), S6 (ACME event-loop extraction), S7 (adapter abort arm). Review #005 updated with the resolution.
This commit is contained in:
@@ -423,4 +423,60 @@ mod tests {
|
||||
"fingerprint-resolved identities must have empty resources (Option B — scopes only)"
|
||||
);
|
||||
}
|
||||
|
||||
// --- Ed25519SecretKey -------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn ed25519_secret_key_round_trips_bytes() {
|
||||
let key = Ed25519SecretKey::generate();
|
||||
let bytes = key.as_bytes();
|
||||
let restored = Ed25519SecretKey::from_bytes(&bytes);
|
||||
assert_eq!(restored.as_bytes(), bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ed25519_secret_key_sign_verifies_against_public_key() {
|
||||
use ed25519_dalek::{Signature, Verifier};
|
||||
let key = Ed25519SecretKey::generate();
|
||||
let public = key.public();
|
||||
let message = b"alknet coverage check";
|
||||
let signature: Signature = key.sign(message);
|
||||
assert_eq!(signature.to_bytes().len(), 64);
|
||||
assert!(
|
||||
public.verify(message, &signature).is_ok(),
|
||||
"signature produced by Ed25519SecretKey::sign must verify under its public key"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ed25519_secret_key_sign_rejects_tampered_message() {
|
||||
use ed25519_dalek::{Signature, Verifier};
|
||||
let key = Ed25519SecretKey::generate();
|
||||
let public = key.public();
|
||||
let signature: Signature = key.sign(b"original message");
|
||||
assert!(
|
||||
public.verify(b"tampered message", &signature).is_err(),
|
||||
"signature must not verify against a different message"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ed25519_secret_key_debug_does_not_leak_material() {
|
||||
let key = Ed25519SecretKey::generate();
|
||||
let dbg = format!("{key:?}");
|
||||
assert!(dbg.contains("Ed25519SecretKey"));
|
||||
assert!(!dbg.contains("SigningKey"));
|
||||
let raw = hex::encode(key.as_bytes());
|
||||
assert!(
|
||||
!dbg.contains(&raw),
|
||||
"Debug output must not contain the raw key bytes"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ed25519_secret_key_public_matches_underlying_signing_key() {
|
||||
let key = Ed25519SecretKey::generate();
|
||||
let public = key.public();
|
||||
assert_eq!(public.to_bytes().len(), 32);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1316,4 +1316,291 @@ mod tests {
|
||||
#[cfg(feature = "acme")]
|
||||
assert!(setup.acme_state_handle.is_none());
|
||||
}
|
||||
|
||||
// --- Tier A: directly-callable TLS / rustls helpers -------------------
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn handler_registry_default_is_empty() {
|
||||
let reg = HandlerRegistry::default();
|
||||
assert!(reg.alpn_strings().is_empty());
|
||||
assert!(reg.get(b"alknet/test").is_none());
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "quinn", feature = "iroh"))]
|
||||
#[test]
|
||||
fn handler_registry_debug_lists_alpns_via_default() {
|
||||
let reg = HandlerRegistry::default();
|
||||
let s = format!("{reg:?}");
|
||||
assert!(s.contains("HandlerRegistry"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "iroh")]
|
||||
#[test]
|
||||
fn has_iroh_identity_true_for_raw_key() {
|
||||
let cfg = StaticConfig {
|
||||
listen_addr: None,
|
||||
tls_identity: Some(TlsIdentity::RawKey(crate::config::Ed25519SecretKey::generate())),
|
||||
iroh_relay: None,
|
||||
drain_timeout: Duration::from_millis(10),
|
||||
};
|
||||
assert!(has_iroh_identity(&cfg));
|
||||
}
|
||||
|
||||
#[cfg(feature = "iroh")]
|
||||
#[test]
|
||||
fn has_iroh_identity_false_for_x509() {
|
||||
let cfg = StaticConfig {
|
||||
listen_addr: None,
|
||||
tls_identity: Some(TlsIdentity::X509 {
|
||||
cert: std::path::PathBuf::from("/x.pem"),
|
||||
key: std::path::PathBuf::from("/x.pem"),
|
||||
}),
|
||||
iroh_relay: None,
|
||||
drain_timeout: Duration::from_millis(10),
|
||||
};
|
||||
assert!(!has_iroh_identity(&cfg));
|
||||
}
|
||||
|
||||
#[cfg(feature = "iroh")]
|
||||
#[test]
|
||||
fn has_iroh_identity_false_when_no_identity() {
|
||||
let cfg = StaticConfig {
|
||||
listen_addr: None,
|
||||
tls_identity: None,
|
||||
iroh_relay: None,
|
||||
drain_timeout: Duration::from_millis(10),
|
||||
};
|
||||
assert!(!has_iroh_identity(&cfg));
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn build_rustls_server_config_raw_key_succeeds() {
|
||||
let sk = crate::config::Ed25519SecretKey::generate();
|
||||
let identity = TlsIdentity::RawKey(sk);
|
||||
let alpns = vec![b"alknet/test".to_vec(), b"alknet/call".to_vec()];
|
||||
let config = build_rustls_server_config(&identity, &alpns).expect("raw key config builds");
|
||||
assert_eq!(config.alpn_protocols, alpns);
|
||||
assert_eq!(config.max_early_data_size, u32::MAX);
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn build_rustls_server_config_self_signed_succeeds() {
|
||||
let identity = TlsIdentity::SelfSigned;
|
||||
let alpns = vec![b"alknet/test".to_vec()];
|
||||
let config =
|
||||
build_rustls_server_config(&identity, &alpns).expect("self-signed config builds");
|
||||
assert_eq!(config.alpn_protocols, alpns);
|
||||
assert_eq!(config.max_early_data_size, u32::MAX);
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
#[should_panic(expected = "TlsIdentity::Acme is handled by TlsSetup::new_acme")]
|
||||
fn build_rustls_server_config_acme_is_unreachable() {
|
||||
let identity = TlsIdentity::Acme {
|
||||
domains: vec!["example.com".to_string()],
|
||||
cache_dir: std::path::PathBuf::from("/tmp/alknet-acme-test"),
|
||||
directory: crate::config::AcmeDirectory::Staging,
|
||||
contact: vec!["mailto:dev@example.com".to_string()],
|
||||
};
|
||||
let _ = build_rustls_server_config(&identity, &[]);
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn build_quinn_server_config_from_rustls_succeeds() {
|
||||
let sk = crate::config::Ed25519SecretKey::generate();
|
||||
let rustls_config =
|
||||
build_rustls_server_config(&TlsIdentity::RawKey(sk), &[b"alknet/test".to_vec()])
|
||||
.expect("rustls config builds");
|
||||
let quinn_config =
|
||||
build_quinn_server_config_from_rustls(rustls_config).expect("quinn config converts");
|
||||
let _ = quinn_config;
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn load_private_key_returns_error_when_no_key_present() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let empty = dir.path().join("empty.key");
|
||||
std::fs::write(&empty, b"# no key here\njust a comment\n").unwrap();
|
||||
let err = load_private_key(&empty);
|
||||
assert!(
|
||||
matches!(err, Err(EndpointError::TlsConfig(_))),
|
||||
"empty key file must yield TlsConfig error, got {err:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn load_private_key_returns_error_when_file_missing() {
|
||||
let err = load_private_key(std::path::Path::new("/nonexistent/alknet-coverage/missing.key"));
|
||||
assert!(
|
||||
matches!(err, Err(EndpointError::TlsConfig(_))),
|
||||
"missing key file must yield TlsConfig error, got {err:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn load_cert_chain_returns_error_when_file_missing() {
|
||||
let err = load_cert_chain(std::path::Path::new("/nonexistent/alknet-coverage/missing.pem"));
|
||||
assert!(
|
||||
matches!(err, Err(EndpointError::TlsConfig(_))),
|
||||
"missing cert file must yield TlsConfig error, got {err:?}"
|
||||
);
|
||||
}
|
||||
|
||||
// --- AcceptAnyCertVerifier trait methods ------------------------------
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn accept_any_cert_verifier_offers_and_does_not_require_client_auth() {
|
||||
use rustls::server::danger::ClientCertVerifier;
|
||||
let verifier = AcceptAnyCertVerifier;
|
||||
assert!(verifier.offer_client_auth());
|
||||
assert!(!verifier.client_auth_mandatory());
|
||||
assert!(verifier.root_hint_subjects().is_empty());
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn accept_any_cert_verifier_verifies_any_client_cert() {
|
||||
use rustls::pki_types::{CertificateDer, UnixTime};
|
||||
use rustls::server::danger::ClientCertVerifier;
|
||||
let verifier = AcceptAnyCertVerifier;
|
||||
let cert = CertificateDer::from(b"fake-cert-der".to_vec());
|
||||
let result = verifier.verify_client_cert(&cert, &[], UnixTime::now());
|
||||
assert!(result.is_ok(), "AcceptAnyCertVerifier must accept any client cert");
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn accept_any_cert_verifier_supported_schemes_are_non_empty() {
|
||||
use rustls::server::danger::ClientCertVerifier;
|
||||
let verifier = AcceptAnyCertVerifier;
|
||||
let schemes = verifier.supported_verify_schemes();
|
||||
assert!(!schemes.is_empty(), "must advertise at least one scheme");
|
||||
assert!(schemes.contains(&rustls::SignatureScheme::ED25519));
|
||||
assert!(schemes.contains(&rustls::SignatureScheme::RSA_PSS_SHA256));
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn accept_any_cert_verifier_debug_is_implemented() {
|
||||
let verifier = AcceptAnyCertVerifier;
|
||||
let s = format!("{verifier:?}");
|
||||
assert!(s.contains("AcceptAnyCertVerifier"));
|
||||
}
|
||||
|
||||
// --- Ed25519SigningKey trait impls ------------------------------------
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn ed25519_signing_key_choose_scheme_returns_some_for_ed25519() {
|
||||
use rustls::sign::SigningKey;
|
||||
let sk = crate::config::Ed25519SecretKey::generate();
|
||||
let signing_key = Ed25519SigningKey::new(sk);
|
||||
let signer = signing_key.choose_scheme(&[rustls::SignatureScheme::ED25519]);
|
||||
assert!(signer.is_some(), "must produce a signer when ED25519 is offered");
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn ed25519_signing_key_choose_scheme_returns_none_without_ed25519() {
|
||||
use rustls::sign::SigningKey;
|
||||
let sk = crate::config::Ed25519SecretKey::generate();
|
||||
let signing_key = Ed25519SigningKey::new(sk);
|
||||
let signer = signing_key.choose_scheme(&[rustls::SignatureScheme::RSA_PSS_SHA256]);
|
||||
assert!(
|
||||
signer.is_none(),
|
||||
"must not produce a signer when ED25519 is not offered"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn ed25519_signing_key_algorithm_is_ed25519() {
|
||||
use rustls::sign::SigningKey;
|
||||
let sk = crate::config::Ed25519SecretKey::generate();
|
||||
let signing_key = Ed25519SigningKey::new(sk);
|
||||
assert_eq!(signing_key.algorithm(), rustls::SignatureAlgorithm::ED25519);
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn ed25519_signing_key_public_key_returns_spki() {
|
||||
use rustls::sign::SigningKey;
|
||||
let sk = crate::config::Ed25519SecretKey::generate();
|
||||
let signing_key = Ed25519SigningKey::new(sk);
|
||||
let spki = signing_key.public_key();
|
||||
assert!(spki.is_some(), "public_key must return an SPKI");
|
||||
assert!(!spki.unwrap().as_ref().is_empty(), "SPKI must be non-empty");
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn ed25519_signing_key_signer_signs_message() {
|
||||
use rustls::sign::SigningKey;
|
||||
let sk = crate::config::Ed25519SecretKey::generate();
|
||||
let signing_key = Ed25519SigningKey::new(sk);
|
||||
let signer = signing_key
|
||||
.choose_scheme(&[rustls::SignatureScheme::ED25519])
|
||||
.expect("ED25519 offered");
|
||||
let message = b"alknet coverage signing test";
|
||||
let sig = signer.sign(message).expect("sign must succeed");
|
||||
assert_eq!(sig.len(), 64, "ed25519 signature must be 64 bytes");
|
||||
assert_eq!(signer.scheme(), rustls::SignatureScheme::ED25519);
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn ed25519_signing_key_debug_does_not_leak_material() {
|
||||
let sk = crate::config::Ed25519SecretKey::generate();
|
||||
let signing_key = Ed25519SigningKey::new(sk);
|
||||
let dbg = format!("{signing_key:?}");
|
||||
assert!(dbg.contains("Ed25519SigningKey"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn raw_key_cert_resolver_debug_is_implemented() {
|
||||
let sk = crate::config::Ed25519SecretKey::generate();
|
||||
let resolver = RawKeyCertResolver::new(&sk);
|
||||
let s = format!("{resolver:?}");
|
||||
assert!(s.contains("RawKeyCertResolver"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[tokio::test]
|
||||
async fn debug_for_alknet_endpoint_is_implemented_without_panicking() {
|
||||
let sk = crate::config::Ed25519SecretKey::generate();
|
||||
let static_config = StaticConfig {
|
||||
listen_addr: None,
|
||||
tls_identity: Some(TlsIdentity::RawKey(sk)),
|
||||
iroh_relay: None,
|
||||
drain_timeout: Duration::from_millis(10),
|
||||
};
|
||||
struct NoProvider;
|
||||
impl IdentityProvider for NoProvider {
|
||||
fn resolve_from_fingerprint(&self, _: &str) -> Option<Identity> {
|
||||
None
|
||||
}
|
||||
fn resolve_from_token(&self, _: &AuthToken) -> Option<Identity> {
|
||||
None
|
||||
}
|
||||
}
|
||||
let provider: Arc<dyn IdentityProvider> = Arc::new(NoProvider);
|
||||
let dynamic = Arc::new(ArcSwap::from_pointee(DynamicConfig::default()));
|
||||
let registry = HandlerRegistry::new();
|
||||
let endpoint = AlknetEndpoint::new(&static_config, registry, dynamic, provider)
|
||||
.await
|
||||
.expect("endpoint constructs");
|
||||
let s = format!("{endpoint:?}");
|
||||
assert!(s.contains("AlknetEndpoint"));
|
||||
assert!(s.contains("drain_timeout"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,4 +668,194 @@ mod tests {
|
||||
let e = HandlerError::AuthRequired;
|
||||
assert_eq!(format!("{e}"), "authentication required");
|
||||
}
|
||||
|
||||
// --- HandlerError / StreamError Debug + Display + source ---------------
|
||||
|
||||
#[test]
|
||||
fn handler_error_debug_covers_all_variants() {
|
||||
assert_eq!(
|
||||
format!("{:?}", HandlerError::ConnectionClosed),
|
||||
"HandlerError::ConnectionClosed"
|
||||
);
|
||||
let io_err = io::Error::new(io::ErrorKind::BrokenPipe, "boom");
|
||||
let dbg = format!("{:?}", HandlerError::StreamError(io_err));
|
||||
assert!(dbg.contains("HandlerError::StreamError"));
|
||||
assert_eq!(
|
||||
format!("{:?}", HandlerError::AuthRequired),
|
||||
"HandlerError::AuthRequired"
|
||||
);
|
||||
let inner: Box<dyn std::error::Error + Send + Sync> = "oops".into();
|
||||
let dbg = format!("{:?}", HandlerError::Internal(inner));
|
||||
assert!(dbg.contains("HandlerError::Internal"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handler_error_display_covers_all_variants() {
|
||||
assert_eq!(format!("{}", HandlerError::ConnectionClosed), "connection closed");
|
||||
let io_err = io::Error::new(io::ErrorKind::BrokenPipe, "boom");
|
||||
let s = format!("{}", HandlerError::StreamError(io_err));
|
||||
assert!(s.starts_with("stream error: "));
|
||||
assert_eq!(format!("{}", HandlerError::AuthRequired), "authentication required");
|
||||
let inner: Box<dyn std::error::Error + Send + Sync> = "oops".into();
|
||||
assert_eq!(
|
||||
format!("{}", HandlerError::Internal(inner)),
|
||||
"internal handler error: oops"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handler_error_source_covers_all_variants() {
|
||||
use std::error::Error;
|
||||
assert!(HandlerError::ConnectionClosed.source().is_none());
|
||||
assert!(HandlerError::AuthRequired.source().is_none());
|
||||
let stream_err = HandlerError::StreamError(io::Error::new(io::ErrorKind::BrokenPipe, "boom"));
|
||||
assert!(stream_err.source().is_some(), "StreamError must expose its io::Error as source");
|
||||
let internal_inner: Box<dyn std::error::Error + Send + Sync> = "boom".into();
|
||||
let internal_err = HandlerError::Internal(internal_inner);
|
||||
assert!(internal_err.source().is_some(), "Internal must expose its inner error as source");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_error_debug_covers_all_variants() {
|
||||
assert_eq!(
|
||||
format!("{:?}", StreamError::ConnectionClosed),
|
||||
"StreamError::ConnectionClosed"
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:?}", StreamError::StreamClosed),
|
||||
"StreamError::StreamClosed"
|
||||
);
|
||||
assert_eq!(format!("{:?}", StreamError::Timeout), "StreamError::Timeout");
|
||||
let dbg = format!("{:?}", StreamError::Internal(io::Error::other("x")));
|
||||
assert!(dbg.contains("StreamError::Internal"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_error_display_covers_all_variants() {
|
||||
assert_eq!(format!("{}", StreamError::ConnectionClosed), "connection closed");
|
||||
assert_eq!(format!("{}", StreamError::StreamClosed), "stream closed");
|
||||
assert_eq!(format!("{}", StreamError::Timeout), "stream timed out");
|
||||
assert_eq!(
|
||||
format!("{}", StreamError::Internal(io::Error::other("boom"))),
|
||||
"stream error: boom"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_error_source_covers_all_variants() {
|
||||
use std::error::Error;
|
||||
assert!(StreamError::ConnectionClosed.source().is_none());
|
||||
assert!(StreamError::StreamClosed.source().is_none());
|
||||
assert!(StreamError::Timeout.source().is_none());
|
||||
let internal = StreamError::Internal(io::Error::other("x"));
|
||||
assert!(internal.source().is_some(), "Internal must expose its io::Error as source");
|
||||
}
|
||||
|
||||
// --- map_*_connection_error -------------------------------------------
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn map_quinn_connection_error_timed_out_maps_to_timeout() {
|
||||
assert!(matches!(
|
||||
map_quinn_connection_error(quinn::ConnectionError::TimedOut),
|
||||
StreamError::Timeout
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn map_quinn_connection_error_reset_maps_to_connection_closed() {
|
||||
assert!(matches!(
|
||||
map_quinn_connection_error(quinn::ConnectionError::Reset),
|
||||
StreamError::ConnectionClosed
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn map_quinn_connection_error_application_closed_maps_to_connection_closed() {
|
||||
use bytes::Bytes;
|
||||
let close = quinn::ConnectionError::ApplicationClosed(quinn::ApplicationClose {
|
||||
error_code: quinn::VarInt::from_u32(1),
|
||||
reason: Bytes::new(),
|
||||
});
|
||||
assert!(matches!(
|
||||
map_quinn_connection_error(close),
|
||||
StreamError::ConnectionClosed
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "quinn")]
|
||||
#[test]
|
||||
fn map_quinn_connection_error_other_maps_to_internal() {
|
||||
let other = quinn::ConnectionError::VersionMismatch;
|
||||
match map_quinn_connection_error(other) {
|
||||
StreamError::Internal(e) => assert_eq!(e.kind(), io::ErrorKind::Other),
|
||||
other => panic!("expected StreamError::Internal, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "iroh")]
|
||||
#[test]
|
||||
fn map_iroh_connection_error_timed_out_maps_to_timeout() {
|
||||
assert!(matches!(
|
||||
map_iroh_connection_error(iroh::endpoint::ConnectionError::TimedOut),
|
||||
StreamError::Timeout
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "iroh")]
|
||||
#[test]
|
||||
fn map_iroh_connection_error_reset_maps_to_connection_closed() {
|
||||
assert!(matches!(
|
||||
map_iroh_connection_error(iroh::endpoint::ConnectionError::Reset),
|
||||
StreamError::ConnectionClosed
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "iroh")]
|
||||
#[test]
|
||||
fn map_iroh_connection_error_application_closed_maps_to_connection_closed() {
|
||||
use bytes::Bytes;
|
||||
let close = iroh::endpoint::ConnectionError::ApplicationClosed(
|
||||
iroh::endpoint::ApplicationClose {
|
||||
error_code: iroh::endpoint::VarInt::from_u32(1),
|
||||
reason: Bytes::new(),
|
||||
},
|
||||
);
|
||||
assert!(matches!(
|
||||
map_iroh_connection_error(close),
|
||||
StreamError::ConnectionClosed
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "iroh")]
|
||||
#[test]
|
||||
fn map_iroh_connection_error_other_maps_to_internal() {
|
||||
let other = iroh::endpoint::ConnectionError::VersionMismatch;
|
||||
match map_iroh_connection_error(other) {
|
||||
StreamError::Internal(e) => assert_eq!(e.kind(), io::ErrorKind::Other),
|
||||
other => panic!("expected StreamError::Internal, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
// --- Capabilities zeroize + default -----------------------------------
|
||||
|
||||
#[test]
|
||||
fn capabilities_default_is_empty() {
|
||||
let caps = Capabilities::default();
|
||||
assert!(caps.get("anything").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn capabilities_zeroize_clears_entries() {
|
||||
let mut caps = Capabilities::new()
|
||||
.with_api_key("svc-a", "k1".to_string())
|
||||
.with_http_token("svc-b", "t1".to_string());
|
||||
assert!(caps.get("svc-a").is_some());
|
||||
assert!(caps.get("svc-b").is_some());
|
||||
caps.zeroize();
|
||||
assert!(caps.get("svc-a").is_none());
|
||||
assert!(caps.get("svc-b").is_none());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user