01f09adf
Make ambiguous-prefix scanner test deterministic
alex emery 2026-04-12 10:39
The previous test opened two issues and hoped their first chars would collide; if they didn't, the test silently degraded to a no-op via an 8-char unambiguous fallback prefix. That made the test a tautology — it only verified `scan_and_link` didn't panic. Open 17 issues so that pigeonhole on the 16 possible hex first-chars guarantees at least one ambiguous one-char prefix exists. Pick that prefix, run the scan, and assert that (a) zero events were emitted and (b) every candidate issue's DAG still consists solely of its IssueOpen event (no spurious IssueCommitLink leaked through). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
diff --git a/tests/sync_test.rs b/tests/sync_test.rs index b91c656..e3e2c77 100644 --- a/tests/sync_test.rs +++ b/tests/sync_test.rs @@ -1360,37 +1360,58 @@ fn commit_link_scan_skips_unknown_prefix_without_error() { } #[test] fn commit_link_scan_skips_ambiguous_prefix_without_error() { fn commit_link_scan_skips_genuinely_ambiguous_prefix() { let cluster = TestCluster::new(); let alice_repo = cluster.alice_repo(); // Open two issues. Their OIDs are random, so we can't guarantee they // share a prefix — instead, use a single-character prefix that is // likely to be ambiguous. If the two OIDs happen to not share a first // char, this test degrades to a no-op but still passes the "no error" // check, which is the important property. let (_, id_a) = open_issue(&alice_repo, &alice(), "a"); let (_, id_b) = open_issue(&alice_repo, &alice(), "b"); // Find a shared character prefix, or fall back to the first char of a. let shared_prefix = if id_a.chars().next() == id_b.chars().next() { id_a[..1].to_string() } else { // No ambiguity possible — use a prefix that matches only a, which // exercises the resolve-success path instead. Test name still holds // because "without error" is the core assertion. id_a[..8].to_string() }; // Open 17 issues. By pigeonhole on the 16 possible hex first-chars, // at least two of the resulting issue IDs must share a first character, // guaranteeing we can construct an ambiguous one-char prefix. let mut ids: Vec<String> = Vec::with_capacity(17); for i in 0..17 { let (_, id) = open_issue(&alice_repo, &alice(), &format!("issue {}", i)); ids.push(id); } // Find a first-char that has at least 2 matching IDs. let mut counts: std::collections::HashMap<char, Vec<&str>> = std::collections::HashMap::new(); for id in &ids { let c = id.chars().next().unwrap(); counts.entry(c).or_default().push(id.as_str()); } let (ambiguous_char, matching_ids) = counts .iter() .find(|(_, v)| v.len() >= 2) .map(|(c, v)| (*c, v.clone())) .expect("pigeonhole guarantees at least one shared first char among 17 hex IDs"); let ambiguous_prefix = ambiguous_char.to_string(); make_commit_with_message( &alice_repo, &format!("Touch\n\nIssue: {}", shared_prefix), &format!("Touch\n\nIssue: {}", ambiguous_prefix), ); let author = git_collab::identity::get_author(&alice_repo).unwrap(); let sk = signing::load_signing_key(&signing::signing_key_dir().unwrap()).unwrap(); // Must not error regardless of whether the prefix ambiguously matched. let _ = commit_link::scan_and_link(&alice_repo, &author, &sk).unwrap(); // Ambiguous prefix must be skipped silently — no event emitted. assert_eq!( commit_link::scan_and_link(&alice_repo, &author, &sk).unwrap(), 0 ); // None of the candidate issues should have grown a commit-link event: // each one still consists only of its IssueOpen event. for id in &matching_ids { let issue_ref = format!("refs/collab/issues/{}", id); let state = IssueState::from_ref_uncached(&alice_repo, &issue_ref, id).unwrap(); assert!( state.linked_commits.is_empty(), "candidate issue {} should not have any linked commits, but has {}", id, state.linked_commits.len() ); } } #[test]