From: 志宇 Date: Tue, 23 Jun 2026 00:14:49 +0000 (+0000) Subject: test(core): consolidate `resolve_candidates` tests into one scenario test X-Git-Tag: pre-fold-backup~4 X-Git-Url: http://internal-gitweb-vhost/-script/src/example_cli/database/encode/struct.EncoderStringWriter.html?a=commitdiff_plain;h=0ecd7687aa8a199bdd33eb81cd2128f27b0e457e;p=bdk test(core): consolidate `resolve_candidates` tests into one scenario test Replace the six per-outcome tests with a single `Case`-table test that runs each scenario through a fresh `BlockQueries`, asserting both the `BlockCandidateResolution` and the outstanding heights afterward. Co-Authored-By: Claude Opus 4.8 (1M context) --- diff --git a/crates/core/src/block_queries.rs b/crates/core/src/block_queries.rs index 52c2a3f2..b6eb0416 100644 --- a/crates/core/src/block_queries.rs +++ b/crates/core/src/block_queries.rs @@ -65,8 +65,9 @@ impl BlockQueries { /// Resolves a set of candidate blocks against the blocks fetched so far. /// /// Each candidate is a pair of an opaque payload and a [`BlockId`]. A candidate is *in-chain* - /// when the block resolved at that `BlockId`'s height is present and its hash matches; the first - /// such candidate's payload is returned as [`Confirmed`](BlockCandidateResolution::Confirmed). + /// when the block resolved at that `BlockId`'s height is present and its hash matches; the + /// first such candidate's payload is returned as + /// [`Confirmed`](BlockCandidateResolution::Confirmed). /// /// When no candidate is confirmed, the result tells the caller how to make progress: /// - [`Query`](BlockCandidateResolution::Query) — heights that still need fetching. These come @@ -154,74 +155,89 @@ mod test { } } - fn candidates( - ids: [BlockId; N], - ) -> impl Iterator + Clone { - ids.into_iter().map(|id| (id, id)) - } - - #[test] - fn confirmed_when_block_resolved_and_matches() { - let mut q = BlockQueries::::new(); - q.request([5].into_iter()); - q.resolve(5, Some(bhash(5))); - - let cands = candidates([id(5, 5)]); - let res = q.resolve_candidates(cands); - assert_eq!(res, BlockCandidateResolution::Confirmed(id(5, 5))); - } - #[test] - fn confirmed_picks_matching_candidate_among_many() { - let mut q = BlockQueries::::new(); - q.request([4, 5].into_iter()); - q.resolve(4, Some(bhash(99))); // height 4 resolved but mismatched - q.resolve(5, Some(bhash(5))); // height 5 matches - - let cands = candidates([id(4, 4), id(5, 5)]); - let res = q.resolve_candidates(cands); - assert_eq!(res, BlockCandidateResolution::Confirmed(id(5, 5))); - } - - #[test] - fn query_returns_unrequested_heights() { - let mut q = BlockQueries::::new(); - let cands = candidates([id(7, 7)]); - let res = q.resolve_candidates(cands); - assert_eq!(res, BlockCandidateResolution::Query(vec![7])); - // The request is recorded, so it is now reported as outstanding. - assert!(q.unresolved().eq([7])); - } - - #[test] - fn awaiting_when_heights_in_flight() { - let mut q = BlockQueries::::new(); - q.request([7].into_iter()); // requested but not resolved - - let cands = candidates([id(7, 7)]); - let res = q.resolve_candidates(cands); - assert_eq!(res, BlockCandidateResolution::Awaiting); - } - - #[test] - fn not_confirmed_when_resolved_and_mismatched() { - let mut q = BlockQueries::::new(); - q.request([7].into_iter()); - q.resolve(7, Some(bhash(99))); // present but wrong hash - - let cands = candidates([id(7, 7)]); - let res = q.resolve_candidates(cands); - assert_eq!(res, BlockCandidateResolution::NotConfirmed); - } + fn resolve_candidates_scenarios() { + struct Case { + name: &'static str, + /// Pre-populate the `BlockQueries` to set up the scenario. + setup: fn(&mut BlockQueries), + candidates: Vec, + expected: BlockCandidateResolution, + /// Heights still outstanding after the call. + expected_unresolved: Vec, + } - #[test] - fn not_confirmed_when_block_absent() { - let mut q = BlockQueries::::new(); - q.request([7].into_iter()); - q.resolve(7, None); // chain source has no block at this height - - let cands = candidates([id(7, 7)]); - let res = q.resolve_candidates(cands); - assert_eq!(res, BlockCandidateResolution::NotConfirmed); + let cases = [ + Case { + name: "confirmed when the block is resolved and its hash matches", + setup: |q| { + q.request([5].into_iter()); + q.resolve(5, Some(bhash(5))); + }, + candidates: vec![id(5, 5)], + expected: BlockCandidateResolution::Confirmed(id(5, 5)), + expected_unresolved: vec![], + }, + Case { + name: "confirmed picks the matching candidate among several", + setup: |q| { + q.request([4, 5].into_iter()); + q.resolve(4, Some(bhash(99))); // resolved but mismatched + q.resolve(5, Some(bhash(5))); // matches + }, + candidates: vec![id(4, 4), id(5, 5)], + expected: BlockCandidateResolution::Confirmed(id(5, 5)), + expected_unresolved: vec![], + }, + Case { + name: "query for a height not yet requested", + setup: |_q| {}, + candidates: vec![id(7, 7)], + expected: BlockCandidateResolution::Query(vec![7]), + expected_unresolved: vec![7], // the call records the request + }, + Case { + name: "awaiting when a requested height is still in-flight", + setup: |q| { + q.request([7].into_iter()); + }, + candidates: vec![id(7, 7)], + expected: BlockCandidateResolution::Awaiting, + expected_unresolved: vec![7], + }, + Case { + name: "not confirmed when the block is resolved but mismatched", + setup: |q| { + q.request([7].into_iter()); + q.resolve(7, Some(bhash(99))); + }, + candidates: vec![id(7, 7)], + expected: BlockCandidateResolution::NotConfirmed, + expected_unresolved: vec![], + }, + Case { + name: "not confirmed when the block is absent", + setup: |q| { + q.request([7].into_iter()); + q.resolve(7, None); + }, + candidates: vec![id(7, 7)], + expected: BlockCandidateResolution::NotConfirmed, + expected_unresolved: vec![], + }, + ]; + + for case in cases { + let mut q = BlockQueries::::new(); + (case.setup)(&mut q); + + let res = q.resolve_candidates(case.candidates.iter().map(|id| (*id, *id))); + assert_eq!(res, case.expected, "scenario: {}", case.name); + assert!( + q.unresolved().eq(case.expected_unresolved.iter().copied()), + "scenario: {} (unresolved mismatch)", + case.name + ); + } } }