mirror of
https://github.com/freemkv/freemkv-keysources.git
synced 2026-10-07 18:13:11 +00:00
KU-K1: tests for answer_depends_on_samples and last_failure_was_transport
KeydbSource::answer_depends_on_samples (KSK1) and MultiSource::answer_depends_on_samples (KSK2, KU3-6 "any(inner)") both default to `true` today, so a keydb-then-online composition would be re-asked per piece instead of once. OnlineSource::last_failure_was_transport (J15) defaults to `false`, so a transient DNS/connect/idle failure would never be retried by `resolve` (J13). Red: these six assertions fail against today's defaults.
This commit is contained in:
@@ -1142,6 +1142,13 @@ mod tests {
|
||||
assert_eq!(KeydbSource::new("/nonexistent/keydb.cfg").label(), "keydb");
|
||||
}
|
||||
|
||||
// KSK1: keyed by disc hash, so `resolve` asks it once per rip, not once per
|
||||
// piece (KU §2.3 step 8, N-KU10).
|
||||
#[test]
|
||||
fn keydb_source_answer_does_not_depend_on_samples() {
|
||||
assert!(!KeydbSource::new("/nonexistent/keydb.cfg").answer_depends_on_samples());
|
||||
}
|
||||
|
||||
// A stamp with no mtime (mtime-less FS) or a future mtime (clock skew) must
|
||||
// not be stuck un-settled forever — it now settles via the inode-identity
|
||||
// fallback once the observation has aged past the granularity.
|
||||
|
||||
+30
@@ -214,6 +214,36 @@ mod tests {
|
||||
assert!(multi.host_certs(None).is_empty());
|
||||
}
|
||||
|
||||
// A source whose `answer_depends_on_samples` is fixed to the given value —
|
||||
// the only behaviour KSK2 exercises.
|
||||
struct FixedDependency(bool);
|
||||
impl KeySource for FixedDependency {
|
||||
fn get_unit_keys(&self, _ctx: &dyn ResolveCtx) -> Result<Vec<UnitKey>, libfreemkv::Error> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
fn answer_depends_on_samples(&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
// KSK2: `MultiSource::answer_depends_on_samples` is `any(inner)`, so nesting
|
||||
// a sample-dependent online source under a sample-independent keydb still
|
||||
// gets the per-piece ask (`resolve` must not treat the pair as keydb-only).
|
||||
#[test]
|
||||
fn multi_source_answer_depends_on_samples_is_any_inner() {
|
||||
let all_independent = MultiSource::new(vec![
|
||||
Box::new(FixedDependency(false)) as Box<dyn KeySource>,
|
||||
Box::new(FixedDependency(false)) as Box<dyn KeySource>,
|
||||
]);
|
||||
assert!(!all_independent.answer_depends_on_samples());
|
||||
|
||||
let keydb_then_online = MultiSource::new(vec![
|
||||
Box::new(FixedDependency(false)) as Box<dyn KeySource>,
|
||||
Box::new(FixedDependency(true)) as Box<dyn KeySource>,
|
||||
]);
|
||||
assert!(keydb_then_online.answer_depends_on_samples());
|
||||
}
|
||||
|
||||
/// The `mkb` generation must reach each inner source so its OWN revocation
|
||||
/// filter can act; a source that drops everything at a given generation is
|
||||
/// honoured by the union.
|
||||
|
||||
@@ -2838,4 +2838,82 @@ mod tests {
|
||||
);
|
||||
assert_eq!(take_last_decode_reachability(), None);
|
||||
}
|
||||
|
||||
// ── KU-K1: `last_failure_was_transport` (J13, J15) ──────────────────────
|
||||
|
||||
#[test]
|
||||
fn last_failure_was_transport_is_false_before_any_query() {
|
||||
let src = OnlineSource::new("https://keyserver.test/keys", "s3cr3t");
|
||||
assert!(!src.last_failure_was_transport());
|
||||
}
|
||||
|
||||
// Per J15/J13: a DNS failure never answers, so it is transport-class and gets retried.
|
||||
#[test]
|
||||
fn last_failure_was_transport_true_after_dns_failure() {
|
||||
let src = source_via("kuk1a.test", T_IDLE, || {
|
||||
Err((GuardFail::Unreachable, "did not resolve".into()))
|
||||
});
|
||||
assert!(src.get_unit_keys(&ctx_with_mkb(0)).is_err());
|
||||
assert!(
|
||||
src.last_failure_was_transport(),
|
||||
"a DNS failure is transport-class"
|
||||
);
|
||||
}
|
||||
|
||||
// A refused connection never answers either — transport-class (J13's "connect").
|
||||
#[test]
|
||||
fn last_failure_was_transport_true_after_connect_refused() {
|
||||
let refused: SocketAddr = ([127, 0, 0, 1], 1).into();
|
||||
let src = source_via("kuk1b.test", T_IDLE, move || Ok(vec![refused]));
|
||||
assert!(src.get_unit_keys(&ctx_with_mkb(0)).is_err());
|
||||
assert!(
|
||||
src.last_failure_was_transport(),
|
||||
"a refused connection is transport-class"
|
||||
);
|
||||
}
|
||||
|
||||
// Stop-design-v5 T16: "no bytes moved" for the idle bound is a transport-class timeout.
|
||||
#[test]
|
||||
fn last_failure_was_transport_true_after_idle_timeout() {
|
||||
let addr = stub_server(Stub::NeverAnswer);
|
||||
let src = source_via("kuk1c.test", T_IDLE, move || Ok(vec![addr]));
|
||||
assert!(src.get_unit_keys(&ctx_with_mkb(0)).is_err());
|
||||
assert!(
|
||||
src.last_failure_was_transport(),
|
||||
"an idle stall is transport-class"
|
||||
);
|
||||
}
|
||||
|
||||
// J15: a 5xx (or any decode reply) IS an answer, so it must never look transport-class —
|
||||
// that would re-ask a source the service already answered ("never call it twice").
|
||||
#[test]
|
||||
fn last_failure_was_transport_false_after_5xx() {
|
||||
let addr = stub_server(Stub::Answer(503));
|
||||
let src = source_via("kuk1d.test", T_IDLE, move || Ok(vec![addr]));
|
||||
assert!(src.get_unit_keys(&ctx_with_mkb(0)).is_err());
|
||||
assert!(
|
||||
!src.last_failure_was_transport(),
|
||||
"the service answered (5xx) — never re-asked"
|
||||
);
|
||||
}
|
||||
|
||||
// J15: "reset it on success" — a later answer must clear a prior transport verdict.
|
||||
#[test]
|
||||
fn last_failure_was_transport_resets_on_success() {
|
||||
let ok_addr = stub_server(Stub::Answer(200));
|
||||
let refused: SocketAddr = ([127, 0, 0, 1], 1).into();
|
||||
let target = Arc::new(Mutex::new(refused));
|
||||
let for_resolve = target.clone();
|
||||
let src = source_via("kuk1e.test", T_IDLE, move || {
|
||||
Ok(vec![*for_resolve.lock().unwrap()])
|
||||
});
|
||||
assert!(src.get_unit_keys(&ctx_with_mkb(0)).is_err());
|
||||
assert!(src.last_failure_was_transport());
|
||||
*target.lock().unwrap() = ok_addr;
|
||||
assert!(src.get_unit_keys(&ctx_with_mkb(0)).is_ok());
|
||||
assert!(
|
||||
!src.last_failure_was_transport(),
|
||||
"a success must reset the flag"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user