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

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