test(audit): pin the suppressed_count zero guard and list_attachments - #89
Merged
Conversation
Two paths in the audit suppression counter had no coverage: deleting
note_suppressed_count's `if n == 0 { return; }` early return, or deleting
the counter call in list_attachments, each left the whole suite green.
That guard is what stops a suppression-free call from merging
ServedFiltered, so without it every bug_comments, summarize_bug and
list_attachments record would claim the response was filtered whether or
not anything was withheld — and verdict is the field an operator filters
on to find the calls where the guard actually did something.
Add three record-level tests. A clean bug_comments call stays served
with a zero count; a list_attachments call whose private metadata the I5
gate dropped records that count over an EMPTY id list, the shape only
that tool produces; and a list_attachments call over two PUBLIC
attachments stays served at zero. The third serves real rows rather than
an empty list on purpose: total == filtered.len() == 2 means the
recorded zero says nothing was withheld, not that nothing existed to
withhold.
The third test covers the call site rather than the guard, which a
guard-only test cannot do. Adding any single unconditional note to the
list_attachments audit block — note_redacted, note_verdict, or
note_suppressed over an empty iterator — makes every call of that tool
record served_filtered, and each of those left the full suite green
before this commit.
Every test here was mutation-proven rather than assumed, since "fails if
the guard is removed" is exactly what a green CI run cannot show:
zero guard deleted -> both zero tests fail on the verdict
counter call deleted -> the private-metadata test fails, served
vs served_filtered
total, not total-filtered -> both attachment tests fail, 3 vs 2 and
served_filtered vs served
note_redacted added -> the public-attachment test fails
note_verdict added -> same
note_suppressed(empty) -> same
guard moved to a bug_comments-only call-site condition -> only the
list_attachments test fails
No production code changes.
Closes #87
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #87.
Two paths in the audit suppression counter had no coverage. Deleting
note_suppressed_count'sif n == 0 { return; }early return, or deleting the counter call inlist_attachments, each left the whole suite green.That guard is what stops a suppression-free call from merging
ServedFiltered. Every call site passestotal - filtered.len(), which is0on the common path — so without it, everybug_comments,summarize_bugandlist_attachmentsrecord would claim the response was filtered whether or not anything was withheld.verdictis the field an operator filters on to find calls where the guard actually did something; collapsing it is the same silent-but-valid defect class as #68.No production code changes. Tests and the DESIGN.md test inventory only.
The three tests
bug_comments_with_nothing_filtered_stays_served_and_counts_zeroservedat count 0list_attachments_counts_the_private_metadata_it_droppedsuppressed_ids— the shape only this tool produceslist_attachments_with_nothing_private_stays_served_and_counts_zeroservedat count 0The third serves real rows rather than an empty list deliberately:
total == filtered.len() == 2, so the recorded zero means nothing was withheld, not nothing existed to withhold. An empty-list fixture would sleep through anote_suppressed_count(total)mutation.It also covers the call site rather than the guard, which a guard-only test cannot do — see the review section.
Mutation evidence
"Fails if the guard is removed" is precisely what a green CI run cannot demonstrate, so every test here was mutation-proven rather than assumed:
list_attachmentscounter call deletedServedvsServedFilteredtotalinstead oftotal - filtered.len()ServedFilteredvsServednote_redacted("attachment.is_private")addednote_verdict(ServedFiltered)addednote_suppressed(empty)addedbug_comments-only call-site conditionlist_attachmentstest failsAdversarial review before opening
Three reviewers attacked the uncommitted diff — test-validity, invariants, and convention lenses. All three returned MERGE-SAFE; the test-validity lens also ran a pre-diff control, confirming the first three mutations were green at HEAD, so this genuinely adds six kills rather than restating existing coverage.
The substantive finding overturned my own call. I had omitted a
list_attachmentszero-path test as redundant, reasoning the zero guard is a single shared early return already killed by test 1. That was half right: anything insidenote_suppressed_countis covered, but adding one plausible line to thelist_attachmentsaudit block —note_redacted("attachment.is_private"), an edit someone would make for good reasons — makes every call of that tool recordserved_filteredwith all 423 tests green. A second reviewer found the mirror image: relocating the zero guard to a call-site condition aroundbug_commentsonly, also green. Both are exactly #87's failure mode at the exact tool #87 named as uncovered, so the test landed here rather than as a follow-up.Also applied: a DESIGN.md wording fix (the clause claimed no record assertion had reached
list_attachments, butevery_routed_tool_writes_exactly_one_record_per_calldoes — the true statement is that none had reached its guard fields), a missing entry in DESIGN.md's list of causes for an absentguard.rule, and an audit-file canary assertion to match the envelope one.Verified under refutation and left alone: both original tests pass for the right reason,
rule == Some("default")is a documented contract literal and not brittle, the new mocks are load-bearing without.expect(1)(breaking either matcher turns the test red), and four adjacent mutations I asked about all die on pre-existing tests. 25 consecutive runs, zero flakes.On the
guard.ruleclearing this PR's fixture exhibits — alist_attachmentscall that drops private metadata recordsrule: Nonethough the default granted the bug — the invariants lens argued it is correct by design:rulenames what decided the record's verdict, and the filtering was decided by a global plus an absent request flag, not by a rule. Writing"default"besideserved_filteredwould assert that rule did the filtering. Separate slots for "what decided access" and "what decided filtering" is a schema change already deferred to #34.Verification
cargo fmt --check, both clippy invocations,cargo test --workspace --all-targets --locked(424 passed, 0 failed) andcargo deny checkall pass — re-run independently, not taken on report.cargo doc --no-deps --workspaceemits the same 4 pre-existing warnings asmain.Follow-up filed
#88 —
summarize_bug'sif !hidden.is_empty()guard can be deleted with the suite green, the twin of thebug_commentshole this PR closes as a side effect.