Skip to content

test(codec): make the CODEC tests that cannot fail fail - #774

Merged
nh13 merged 1 commit into
mainfrom
771/nhomer/assert-codec-tests-that-cannot-fail
Aug 17, 2026
Merged

nh13 merged 1 commit into
mainfrom
771/nhomer/assert-codec-tests-that-cannot-fail

Conversation

@nh13

@nh13 nh13 commented Aug 15, 2026 •

Copy link
Copy Markdown
Member

Closes #771.

Ten CODEC tests asserted something the code cannot violate, or were satisfied before the code under test ran. Each now asserts what its name claims, and every new assertion was verified by removing the behavior it covers and observing the failure. The perturbation table is below; nothing here rests on inspection alone.

Base branch

Based on #770 (769/nhomer/assert-codec-consensus-bases), which is itself based on #768. Several of these tests need a family that actually consenses — test_not_emit_consensus_chimeric_pair's control assertion, codec_uncapped_family_contributes_every_read's depth tags — which is only true once #768 stores the fixture's reverse read in reference orientation. This cannot stand alone.

The two confirmed defects

test_not_emit_consensus_chimeric_pair needed a fixture, not an assertion. It built its pair at starts 100/135 with 30M each, so R1 spanned 100-129 and R2 spanned 135-164 and the two never overlapped. duplex_length came out negative and phase 4 rejected the family as InsufficientOverlap — long after phase 2 had already dropped it for the reason the test is named for. Deleting both set_ref_id calls left it green.

The pair now overlaps (R1 at 1, R2 at 11, both 30M), a control asserts that the same family does consense while both reads are on one chromosome, and the rejection reason is asserted rather than only the absence of output.

Worth knowing for anyone perturbing this: the cross-chromosome rejection is doubly guarded, and removing either guard alone is undetectable. is_primary_fr_pair_raw compares the two records' ref_ids, and the is_fr_pair_raw it then calls on the reverse record compares that record's ref_id against its mate_ref_id. Removing only the first leaves the test passing; removing both makes the family emit a consensus and the test fail.

test_check_overlap_phase_indel_mismatch discarded its result under // Result depends on whether the boundaries land in indels - just verify it runs. It does not depend: over the overlap 100-129, R1's 30M gives query offsets 1 and 30 while R2's 15M5D15M gives 1 and 25, so the phases are 0 and 5 and the answer is deterministically false. It is asserted, the comment is replaced with that arithmetic, and a 30M control pins that the false is attributable to the deletion rather than to a lookup that returned None — which also yields false.

Per test

Test What it should prove What it now asserts
test_not_emit_consensus_chimeric_pair A cross-chromosomal pair is rejected by the cross-chromosome check Overlapping fixture; a control that consenses; NotPrimaryFrPair: 2 and no other reason
test_check_overlap_phase_indel_mismatch A one-sided indel puts the strands out of phase false, plus a 30M control that is true
test_clip_overlap_failed_counted_and_labeled The reject is counted and labeled ClipOverlapFailed, not the indel reason Drives the production consensus_length < ss.bases.len() branch end to end
codec_downsampling_retains_lowest_hashing_pairs The cap keeps the lowest-hashing templates, not the first N Both strands capped independently through keep_indices_for_infos; R2's list is reversed, so the same three names come back at different positions
codec_uncapped_family_contributes_every_read (was test_downsample_pairs_no_limit) With no cap, every read contributes aD/bD of 10 for a ten-template family, against a capped control of 3
test_clear_rejected_reads It empties a populated buffer, and touches nothing else Buffer filled by a rejected family first; buffer emptied; statistics survive
test_codec_statistics_tracking The counters report what the caller did One consensing family and one rejected one; all three counters driven off zero separately
test_mask_end_qualities --outer-bases-qual masks the ends and only the ends Ends exactly at the masked quality; every interior position above it
test_build_clipped_info_clip_from_start_reverse A three-base start clip advances the position by three adjusted_pos == 103
test_reverse_complement_ss_depths_errors_reversed Bases are reversed and complemented Non-palindromic AACG → CGTT

Two of these needed more than a stronger assertion.

test_clip_overlap_failed_counted_and_labeled was not reaching its subject at all. Its only interaction with the caller was a direct reject_records_count(2, ClipOverlapFailed), so its stated regression guard — that the reject had not leaked into IndelErrorBetweenStrands — could not fire: nothing in the test could insert the other reason.

Its own doc comment claimed the production branch was unreachable from a synthetic fixture. That turns out to be wrong, and the mechanism is worth recording. The branch fires when the consensus is shorter than a single strand, and the overlap clipper measures the mate overhang in soft-clip unclipped coordinates. A query-only operation is therefore invisible to it: R2 at 20I30M carries twenty query bases the clipper does not see and does not clip, the consensus length comes out at 40 in reference space, and R2's own strand consensus is 50. Putting the insertion ahead of the overlap keeps check_overlap_phase_raw satisfied, so the earlier IndelErrorBetweenStrands gate does not fire first. The CIGAR is degenerate rather than aligner-shaped, and deliberately so — a soft-clip-built fixture is normalised by the clipper to exactly consensus_length == ss.bases.len(), one short of the branch. That degeneracy is what the reject exists for.

codec_downsampling_retains_lowest_hashing_pairs' R2 claim was a property of the test harness. The #[cfg(test)] downsample_pairs wrapper derived one index vector from R1 and mapped it over R2, so "R2 must keep the same templates as R1" held by construction. Both strands now go through keep_indices_for_infos — the function cap_infos_to_lowest_ranking, and therefore consensus_reads_raw, calls — each capped from its own list. R2's list is built in the reverse of R1's, so the R2 arm now carries the "lowest-hashing, not first-N" half of the claim: an order-dependent rule would keep {q0, q1, q2} from R1 and {q9, q8, q7} from R2, and the two arms would disagree.

One qualification on the issue's wording there. The old assertion did not state the opposite of production for this fixture — production caps each strand on its own ranks, and mates share a read name and therefore a rank, so two independent caps over identically-named lists do land on the same templates. What was wrong was presenting that as the rule: the rule is per-strand independence, and codec_caps_each_strand_from_its_own_filtered_set is the test where the two strands deliberately retain different templates. The old assertion was unfalsifiable and its comment was misleading; it was not asserting a falsehood.

Deletions

downsample_pairs (the #[cfg(test)] wrapper) and test_downsample_pairs are removed. Once the two tests above go through production, the only remaining caller of the wrapper was test_downsample_pairs, whose assertion (ds_r1s.len() == 2) is strictly weaker than codec_downsampling_retains_lowest_hashing_pairs' on the same rule — and the cap's boundary behaviour is separately pinned by test_select_lowest_ranking's at_cap_keeps_everything / below_cap_keeps_everything cases. No coverage is lost.

This is the only non-test edit, along with the two doc comments that referred to the wrapper.

Non-vacuity

Every strengthened assertion was verified against a perturbation of the behavior it pins. Each perturbation was also run against the pre-change tree, so the last column is measured rather than asserted.

Perturbation Fails now Old test caught it
Delete the chimeric mutation from the fixture not_emit_consensus_chimeric_pair no
Remove both same-reference checks (is_primary_fr_pair_raw and is_fr_pair_raw) not_emit_consensus_chimeric_pair — (new coverage)
Remove only is_primary_fr_pair_raw's same-reference check nothing — the second guard still holds —
Overlap-phase check hardcoded to true check_overlap_phase_indel_mismatch no (result discarded)
Overlap-phase check hardcoded to false check_overlap_phase_indel_mismatch (control) no (result discarded)
ClipOverlapFailed site relabeled IndelErrorBetweenStrands — the pre-fix behavior clip_overlap_failed_counted_and_labeled no
An uncapped run silently caps at 3 codec_uncapped_family_contributes_every_read no
Name ranking replaced by input order codec_downsampling_retains_lowest_hashing_pairs yes
clear_rejected_reads body emptied clear_rejected_reads no
clear_rejected_reads delegates to clear() clear_rejected_reads no
total_input_reads never accumulated codec_statistics_tracking no
consensus_reads_generated never incremented codec_statistics_tracking no
reads_filtered never accumulated codec_statistics_tracking no
Outer-bases mask applied to every position mask_end_qualities no
Outer-bases mask assigns 4 instead of 5 mask_end_qualities no
Start-clip advances the position by one too many build_clipped_info_clip_from_start_reverse no
reverse_complement_ss reverses without complementing reverse_complement_ss_depths_errors_reversed yes
reverse_complement_ss complements without reversing reverse_complement_ss_depths_errors_reversed yes
reverse_complement_ss leaves bases untouched reverse_complement_ss_depths_errors_reversed no
Zero clip substitutes a different one-op CIGAR build_clipped_info_zero_clip_preserves_all no
Shorter pad target repads instead of returning unchanged pad_consensus_shorter_target no

Sixteen of these were undetected by the test that owns them.

Two corrections to the issue while doing this. ACGT does separate reverse-only from complement-only — both give TGCA, which the old assertion rejected. What it could not catch is the function being a no-op, and that is the case that was untested. Relatedly, "no non-palindromic test anywhere in the crate" was true of direct tests only: the crate-wide blast radius of a no-op reverse_complement_ss is 13 tests, 12 of which come from #768's and #770's end-to-end base assertions. Every other unconfirmed claim in the issue held exactly as written.

Sibling sweep

Two more of the same shape, adjacent to tests already being fixed, folded in here rather than filed:

  • test_build_clipped_info_zero_clip_preserves_all asserted the CIGAR had one op, not which op. It now compares against 4M.
  • test_pad_consensus_shorter_target asserted a length where the claim in its own comment is "returned unchanged". It now compares bases, quals, depths and errors.

Still weaker than the contract, not changed here

Listed rather than fixed, to keep the diff on the issue's set:

  • test_to_source_read_*_over_clip (four assertions across two tests) check bases.is_empty() / quals.is_empty() after over-clipping. That is the right claim, but it is also what an unconditional Vec::new() returns; neither test pins that a partial clip keeps the right remainder.
  • test_codec_caller_creation and test_vanilla_to_single_strand_negative_strand echo their own constructor arguments.
  • test_consensus_reads_typed_disagreement_count's assert!(err.is_duplex_disagreement()) is implied by the matches! on the line above it.

Verification

cargo ci-fmt, cargo ci-lint, cargo ci-test (7494 passed), cargo ci-doctest — all green.

Local review

CodeRabbit's automatic reviews are paused on this repo, so a local sweep was run against .coderabbit.yaml's test instruction — "flag assertions weaker than the contract". It caught this PR committing the same defect it fixes: test_reverse_complement_ss_depths_errors_reversed had gained two assert_ne!s (!= "GCAA", != "TTGC") that are implied by the assert_eq!(rc.bases, b"CGTT") above them and cannot fail. They are gone; the reasoning they encoded lives in the doc comment. The sweep also flagged that test_downsample_pairs_no_limit no longer named anything that exists once the wrapper was removed — hence the rename — and produced the two sibling fixes above, which are the "check the file for its siblings and list them all" clause applied to build_clipped_info and pad_consensus.

Risk: command output changes: none; unsafe changes: none, and no CLAUDE.md allowlist update applies; memory bounds, queue capacities, and thread/backpressure policies: none.

  • Strengthen CODEC tests so they execute production paths and assert the intended behavior.
  • Cover chimeric-pair rejection, overlap-phase indels, clip-overlap rejection labels, downsampling, rejected-read clearing, statistics, quality masking, clipping, and reverse complementation.
  • Correct and expand fixtures for reference-oriented bases, CIGAR variants, saturation behavior, and fgbio oracle results.
  • Remove the obsolete test-only downsample_pairs wrapper and its test.
  • Validate assertions through behavior perturbations.
  • Update related documentation.

@nh13
nh13 deployed to github-actions August 15, 2026 02:14 — with GitHub Actions Active
@coderabbitai

coderabbitai Bot commented Aug 15, 2026 •

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3260a474-339c-4561-9ee4-9997afa741df

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3cf9ed04-4975-4f4e-a360-d3f1407531fb

📥 Commits

Reviewing files that changed from the base of the PR and between 4884585 and f7883c3.

📒 Files selected for processing (1)
  • crates/fgumi-consensus/src/codec_caller.rs

Included review availability: 0 reviews are currently available. Based on recent review activity, included reviews refill at 1 per hour.


Walkthrough

The PR removes a test-only downsampling wrapper and expands CODEC tests to exercise production rejection, consensus transformation, downsampling, statistics, and rejected-read handling paths.

Changes

CODEC production-path tests

Layer / File(s) Summary
Production rejection paths
crates/fgumi-consensus/src/codec_caller.rs
Tests use real overlapping and indel-containing pairs to validate ClipOverlapFailed, NotPrimaryFrPair, and pair-level rejection accounting.
Consensus transformations
crates/fgumi-consensus/src/codec_caller.rs
Tests assert exact quality masking, clipping, CIGAR, indel-phase, padding, and reverse-complement behavior.
Production downsampling
crates/fgumi-consensus/src/codec_caller.rs
The test-only downsample_pairs wrapper is removed. Tests cover production ranking, input ordering, uncapped families, and per-strand caps.
Statistics and rejected-read state
crates/fgumi-consensus/src/codec_caller.rs
Tests validate input, generated, filtered, and rejection counters. Rejected-read clearing is tested with populated buffers and preserved statistics.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: ⚪ Minimal · up to f7883

This change strengthens CODEC test coverage and removes an obsolete test-only wrapper; no actionable merge-blocking risk remains at the current head after normal checks and review.

Possibly related issues

  • Issue 769 — Covers the broader effort to strengthen CODEC consensus tests for base, clipping, orientation, masking, and rejection behavior.

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address all coding objectives in issue #771, including production-path coverage, corrected fixtures, and stronger assertions.
Out of Scope Changes check ✅ Passed The wrapper removal, sibling test updates, fixtures, and documentation changes support the linked issue objectives.
Title check ✅ Passed The title uses valid Conventional Commit syntax and accurately describes the strengthened CODEC tests.

Comment @coderabbitai help to get the list of available commands.

@nh13

nh13 commented Aug 15, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai pause

@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews paused.

@codecov

codecov Bot commented Aug 15, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.28%. Comparing base (d3cce70) to head (06a81c6).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #774      +/-   ##
==========================================
- Coverage   94.30%   94.28%   -0.02%     
==========================================
  Files         186      186              
  Lines      112508   112550      +42     
==========================================
+ Hits       106098   106116      +18     
- Misses       6410     6434      +24     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@nh13
nh13 force-pushed the 769/nhomer/assert-codec-consensus-bases branch from 54f5f10 to 4884585 Compare August 16, 2026 03:45
@nh13
nh13 force-pushed the 771/nhomer/assert-codec-tests-that-cannot-fail branch from ab217bf to f7883c3 Compare August 16, 2026 04:20
@nh13
nh13 deployed to github-actions August 16, 2026 04:20 — with GitHub Actions Active
@nh13 nh13 added hygiene fgumi codec rust Pull requests that update rust code labels Aug 16, 2026
@nh13

nh13 commented Aug 17, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 17, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13
nh13 force-pushed the 769/nhomer/assert-codec-consensus-bases branch from 4884585 to 477dbbe Compare August 17, 2026 02:10
Base automatically changed from 769/nhomer/assert-codec-consensus-bases to main August 17, 2026 02:11
Ten CODEC tests either asserted something the code cannot violate, or were
satisfied before the code under test ran. Each now asserts what its name
claims, and each new assertion was verified by removing the behavior it covers
and observing the failure.

Two needed a fixture change rather than a stronger assertion:

- `test_not_emit_consensus_chimeric_pair` built its pair at starts 100 and 135
  with 30M each, so the reads never overlapped and the family was rejected for
  insufficient overlap in phase 4 -- long before the cross-chromosome check in
  phase 2 was consulted. The pair now overlaps, a control asserts it consenses
  while on one chromosome, and the rejection reason is asserted.
- `test_clip_overlap_failed_counted_and_labeled` hand-called
  `reject_records_count`, so its guard against the mislabeling it is named for
  could not fire. It now drives the production site end to end.

`test_check_overlap_phase_indel_mismatch` discarded its result under a comment
claiming the outcome was indeterminate. It is deterministic; it is asserted,
with a control that attributes it to the indel.

The rest: `codec_downsampling_retains_lowest_hashing_pairs` and
`test_downsample_pairs_no_limit` both ran against a `#[cfg(test)]`
`downsample_pairs` wrapper rather than production -- the first's R2 claim was a
property of the wrapper, the second returned before any production code. Both
now go through the production cap; the wrapper and `test_downsample_pairs`,
which only exercised it, are removed. `test_clear_rejected_reads` cleared an
already-empty buffer, `test_codec_statistics_tracking` fed no records and
asserted `Default`, `test_mask_end_qualities` asserted an upper bound where the
code assigns an exact value and never looked at the interior,
`test_build_clipped_info_clip_from_start_reverse` asserted `> 100` for an exact
103, and `test_reverse_complement_ss_depths_errors_reversed` used the
palindrome `ACGT`, so it passed for a no-op.

Two siblings found in the same sweep are fixed alongside:
`test_build_clipped_info_zero_clip_preserves_all` asserted the CIGAR had one op
rather than which op, and `test_pad_consensus_shorter_target` asserted a length
where the claim is "returned unchanged".

No production code changes; the only non-test edit removes the unused
`#[cfg(test)]` wrapper and the doc references to it.

Closes #771.
@nh13
nh13 force-pushed the 771/nhomer/assert-codec-tests-that-cannot-fail branch from f7883c3 to 06a81c6 Compare August 17, 2026 02:32
@nh13
nh13 deployed to github-actions August 17, 2026 02:32 — with GitHub Actions Active
@nh13
nh13 merged commit 257dedf into main Aug 17, 2026
14 checks passed
@nh13
nh13 deleted the 771/nhomer/assert-codec-tests-that-cannot-fail branch August 17, 2026 02:33
@nh13 nh13 mentioned this pull request Aug 17, 2026

This branch was successfully deployed

1 active deployment
github-actions — 06a81c66 Deployed Aug 17, 2026 by nh13 via coverage #3603
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fgumi codec hygiene rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CODEC tests with assertions that cannot fail, including one that never reaches its subject

1 participant