Restore @pytest.mark.unit on TestRootUserGate; guard this file against it recurring - #156
Conversation
…against it recurring TestRootUserGate came out of the #1/#2 add/add merge without its marker: both PRs appended a class after the same trailing @pytest.mark.unit, git hoisted the marker into common context and left it on the staging side's class (TestKillTaskContainers), and the class appended after it was bare. 29 root-gate tests -- including the round-2 blocker fixtures for 4294967296 / 8589934592 / 2147483648 / 999999999999 -- were invisible to `-m unit` while still passing by path, so CI could not see the loss. -m unit -k TestRootUserGate before: 29 deselected after: 29 passed A file-local guard asserts every Test* class in this file carries the marker, and names the class and line when one does not. It is deliberately NOT the suite-wide check the issue floated: 781 of ~890 classes under tests/unit carry no marker and no CI target selects by -m unit, so a suite-wide guard would assert a convention nobody follows. This file does follow it, so keep it honest here. Fixes #128 Claude-Session: https://claude.ai/code/session_01UpRYiFserdBE5ESHn759N4
Self-reviewNo defects found. Verified the parts that could plausibly be wrong. The guard's parser — edge cases checkedThe backward walk from Result: The guard runs under both selection modesIt is itself Verified non-vacuous (restating from the body, since it's the whole point)Stripping the marker again → The scope decision, re-examinedI pushed back on the issue's suggested suite-wide guard because 781/~890 unit classes are unmarked and no CI target selects by Issue associationBody updated to a bare 100/100 in file, |
mwiget
left a comment
There was a problem hiding this comment.
Approving — the fix is right and, unusually, I could check every number in the description.
Verified independently against origin/staging:
- 891
class Test*underbackend/tests/unit/, 781 of them without@pytest.mark.unit(walked the same decorator/blank-line rule your guard uses) — your "781 of ~890" is exact. - No
-m unitselector anywhere inMakefile,.github/,bin/orscripts/— confirmed, so the marker really is unused by CI today. - Reproduced the failure mode: the marker on
TestKillTaskContainerswithTestRootUserGatebare is exactly the add/add hoist signature.
One correction to the description (not to the code): AGENTS.md does exist — it's tracked at the repo root on staging. It carries no marker convention, but docs/DEVELOPMENT.md:211 does: "Markers: @pytest.mark.unit, @pytest.mark.component, @pytest.mark.full". So the convention is documented, it's just unenforced and unread by CI — which leaves your scoping decision intact (781 bare classes and no -m unit consumer is still the argument against a suite-wide guard), but the reasoning should rest on "documented but not enforced", not on "doesn't exist".
The "worth a separate decision" note is the right call — if -m unit is ever wired into CI, that's a marker sweep plus a CI change, not this PR.
| j = i - 1 | ||
| marked = False | ||
| # Walk back over decorators and blank lines to find the marker. | ||
| while j >= 0 and (lines[j].strip().startswith("@") or not lines[j].strip()): |
There was a problem hiding this comment.
Minor, and only a false-alarm risk rather than a miss: the walk-back accepts decorators and blank lines, so a comment between the marker and the class makes an actually-marked class report as bare —
@pytest.mark.unit
# why this class exists
class TestSomething:That's a failing test on correct code, which is the annoying direction for a guard nobody expects to fire. or lines[j].lstrip().startswith("#") in the loop condition covers it. Not worth another CI round on its own.
…ywords Review finding (mwiget): merging #158 as it stood would have closed #94 and #128 -- not because of the regex change, but because the PR's own description DOCUMENTS closing keywords in backticks, and the parser reads body text raw. Reproduced against this branch's real parse step with this PR's body as PR_BODY: Parsed closing-keyword issues: ['94', '128', '7'] #94 and #128 are open with their real fixes unmerged in #157/#156; they would have closed with "Auto-closed by PR #158", wrong issue and wrong PR. The old parser had the same blind spot (it read ['94'] from this body); widening the skip tripled the blast radius on a body that talks about the very forms it now accepts. A parser PR is the right place to close the class, not the instance. Fenced blocks are stripped first (they may contain backticks), then inline spans. Re-ran the full matrix through the real step: every real closing line in plain text still closes; every example in backticks or a fence no longer does; a body with both a real "Fixes #94" and a documented "`Fixes #999`" closes only 94. This PR's own body now yields no issues. Then ran EVERY open PR's actual body through the patched step -- the check I should have done the first time: #156 -> 128 #157 -> 94 #159 -> 154 #160 -> 99 #158 -> (none) #161 -> (none, deliberate Refs #79) #135 -> (none) Each PR closes exactly its own issue and nothing else. Claude-Session: https://claude.ai/code/session_01UpRYiFserdBE5ESHn759N4
Description
TestRootUserGatecame out of the #1/#2 add/add merge without its@pytest.mark.unit: both PRs appended a class after the same trailing marker, git hoisted the marker into common context and left it on thestagingside's class (TestKillTaskContainers), and the class appended after it was bare. 29 root-gate tests — including the round-2 blocker fixtures for4294967296/8589934592/2147483648/999999999999— were invisible to-m unitwhile still passing by path, so CI could not see the loss.Reproduced the issue's own negative control exactly:
Fixes #128
The guard — and why it's narrower than the issue suggested
The issue floats a suite-wide check that every
class Test*undertests/unit/carries the marker, "the suite convention documented inAGENTS.md". I looked before building it, and that convention doesn't exist in this repo: there is noAGENTS.md, 781 of ~890 test classes undertests/unit/carry no marker, and no CI target or Makefile rule selects by-m unit(unit tests run by path, as the issue itself notes). A suite-wide guard would fail 781 classes asserting a rule nobody follows, for a selector nobody uses.What is true is that this file follows the convention — every other class in
test_container_runner.pyis marked, andTestRootUserGatewas the one odd one out, which is exactly the merge-hoist signature. So the guard is file-local:TestEveryClassInThisFileIsMarkedUnitasserts everyTest*class in this file is marked, and names the class and line when one isn't. Verified it bites — stripping the marker again produces:That makes the next add/add merge on this file self-detecting, which is what the issue actually wants, without inventing a repo-wide rule.
Architectural Decision Record (ADR)
Type of Change
Verification & Testing
test_container_runner.py: 100 passed (99 + the guard).-m unit.ruffclean.Environment validation needed
None — test-only change.
Checklist
Worth a separate decision
If you want
-m unitto be a meaningful selector, that's a real (and larger) piece of work: 781 classes to mark, plus a CI change to select by marker. I've stated the numbers so that call can be made with eyes open; it's out of scope here.