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:
2026-06-25 05:43:59 +00:00
parent 32dcc05658
commit 011db05a52
6 changed files with 841 additions and 3 deletions

View File

@@ -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);
}
}

View File

@@ -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"));
}
}

View File

@@ -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());
}
}