Repository navigation
[quality] milestone.ts, comments.ts and events.ts: nine behavioural Stryker survivors (join separator, isBotUser login arm, createComment message, skipReadOnlyForkRun payload guards) #584
Description
Finding
A Stryker probe of the issue-comment, label and utility modules no previous probe had covered (npx stryker run --mutate over src/issueComment/{assign,unassign,cc,uncc,lock,milestone,retitle,close,reopen}.ts, src/labels/{hold,fixed}.ts, src/utils/{labelCatalog,comments,pullRequestOwners,events}.ts, using the harness of #572, main @ e3119ad, Stryker 10.0.0 / Vitest 5.0.4, concurrency 10, 21 m 33 s) reports 79 survivors of 643 (87.71 %). labelCatalog.ts and pullRequestOwners.ts are mutation-tight (0 survivors). Most survivors are the familiar equivalent shapes: { required: true } on a mocked core.getInput, optional chains on payload fields every issue_comment event carries, debug/log strings, and lock.ts:58 / fixed.ts:65 / hold.ts:73, whose mutations change nothing on the wire (JSON.stringify drops lock_reason: undefined; removeLabels([]) issues no request).
Nine survivors flip a guard or user-facing text that the unit suite reaches but never distinguishes from its mutation:
| site | mutant | what it means |
|---|---|---|
src/issueComment/milestone.ts:66 |
titles.join(', ') → titles.join('') |
the "Available milestones" error runs the titles together; the only not-found test has a single milestone in its fixture |
src/utils/comments.ts:34 |
`could not add comment: ${e}` → ` ` |
createComment's rewrapped error message is asserted only through dist/index.js (bundle tests), which the mutation run cannot reach |
src/utils/comments.ts:67 |
user?.login === 'github-actions[bot]' → false and 'github-actions[bot]' → '' |
the login-only arm of isBotUser: a comment whose user lacks type: 'Bot' would be re-posted by createCommentOnce |
src/utils/events.ts:23 (×2), :24, :25 |
pull_request?.head → pull_request.head, head?.repo → head.repo, repository?.full_name → repository.full_name, typeof base !== 'string' → false |
skipReadOnlyForkRun would throw TypeError instead of returning false on a payload missing pull_request, head, or repository.full_name |
Not filed here: retitle.ts:34 (title === '' guard, 3 mutants) survives only because the /retitle test's unserved GET /collaborators is swallowed by checkCollaborator's fail-closed catch; #571 instruments retitle.test.ts with failTestOnUnhandledRequests, which kills it, so it is left to that PR.
Recommendation
Add unit pins in three new files (no existing test or src/ file edited, disjoint from every held PR):
-
__tests__/issueCommentTest/milestoneAvailableList.test.ts— three open milestones, assertAvailable milestones: v1, v2, backlog -
__tests__/utils/commentsGuards.test.ts—isBotUsertable (login-only, type-only, human, prefix-only, empty, null, undefined);createComment403 rejects with/^could not add comment: HttpError: …/;createCommentOnceskips a marker comment whose user has only thegithub-actions[bot]login -
__tests__/pullReqTest/readOnlyForkPayloads.test.ts—skipReadOnlyForkRunreturnsfalsewithout throwing for nopull_request, nohead, norepository, nofull_name, non-stringfull_name
Re-running the probe on those lines with the pins in place reports milestone 15/15 killed, events 28/28 killed, comments 31/33 killed (the two left are the equivalent ?? '' fallback at :58 and the debug string at :59).
Evidence
- Unit coverage (
npx vitest run --coverage, main @ e3119ad, 167 files / 2330 tests): 99.91 % lines, 99.35 % branches, 100 % functions; every site above is 100 % line-covered, so this is a mutation finding, not a coverage gap. - E2E (
coverage-e2e/lcov.infofromnpm run test:coverage:e2eat the same revision): 100 % lines, 99.46 % branches; the four residual e2e branches (sweep.ts:80/85/208,auth.ts:15-17,config.ts:618,approve.ts:504) are claimed by test(sweep): pin the delayed-reviewers OWNERS guard on a base branch without OWNERS files (sweep.ts:80) #561, test(sweep): pin the delayed-reviewers catch of sweep in its Error and String(e) arms (sweep.ts:85) #565, test(sweep): pin the reviewer-key fallbacks of delayed blunderbuss on a bare pull listing (sweep.ts:208) #562, test(bundle): drive getErrorDetails' status-less and message-less object folds (auth.ts:15-17) through dist/index.js #553, test(config): pin normalizeBlunderbuss' non-Error parseDuration arm and v8-ignore it for the bundle #563, test(approve): pin the github-actions[bot] login-only skip, the uncoverable-file suggestion, the notifier whitespace/marker checks, the unlabeled skip, the base-branch OWNERS probe and the required token #580. - Mutation: Stryker
reports/mutation/report.jsonfrom the command above (local run, not a CI artifact).
Priority
- Impact: medium (an unguarded
skipReadOnlyForkRunwould fail every forkpull_requestrun on a malformed payload; a mis-detected bot comment would spam explanations) - Effort: low
Filed by quality agent (hold-gated mode)
🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5q9t | SHA: e3119ad
— hive: agent=quality backend=copilot model=claude-fable-5.1 copilot=1.0.88
Activity
github-actions commentedon Oct 26, 2026
Please add a kind label with
/kind feature,/kind failing-testor/kind cleanup.test: pin the Stryker survivors of nine modules (consolidates cncf#573,