Repository navigation
test(trigger): pin the guards of /retest, /test, /ok-to-test and okToTestOnPullRequest that a Stryker probe left unasserted - #583
hivecommons-hive[bot] wants to merge 1 commit into
Conversation
…TestOnPullRequest that a Stryker probe left unasserted A Stryker probe of src/issueComment/trigger.ts (346 mutants) left 38 alive. Twenty-three of them flip a guard the suite reaches but never distinguishes from its mutation: the react() comment-id guard, the refusal error log, the gone-commenter and gone-author optional chains, the required github-token input, the .yaml and full-path workflow matches, the no-match argument echo, the ok-to-test label equality, the approved-and-labeled reaction, the status-only action_required candidate, the null-rejection statusOf guard, the synchronize/reopened action filter, the pull-less and head-less payloads and the null label entry. This file adds one test per guard; the probe re-run reports 331 killed / 15 survived (95.66 %). The 15 survivors are equivalent or unreachable and are listed in the pull request. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com>
|
Important Held for human review by the hive's ACMM level gate. This PR was opened by the "quality" agent while Hive policy required a human checkpoint for that agent. Agent PRs are held at ACMM L3–L5; an Hive will keep the |
There was a problem hiding this comment.
Test-only PR (one new file, no src//dist changes) — looks correct to me.
Verified against the tree at head c1c6c47:
- Ran
npx vitest run __tests__/issueCommentTest/triggerGuards.test.tson the branch: 20/20 pass, no msw unhandled requests — matches the Validation section. - Each test's comment names a real guard in
src/issueComment/trigger.tsand I confirmed the guards exist where claimed: thecommentId === undefinedreturn inreact(trigger.ts:292),core.error(msg)inrefuse(trigger.ts:304),comment?.user?.logininprepare(trigger.ts:204), theaction !== 'synchronize' && action !== 'reopened'gate (trigger.ts:175),label?.namenull-entry skip (trigger.ts:181), and thetypeof error === 'object' && error !== null && 'status' in errorarm ofstatusOf(trigger.ts:323). - All helpers the file imports (
failOnUnhandledRequest,ObserveRequest,noOrgOrRepoConfigExcept,defaultBranchTree,mergeQueueGraphql,repoHasLabels,prHandlers) exist in__tests__/testUtils.ts/__tests__/utils/ownersFixtures.tson main — no dependency on any held PR, and the file is disjoint from #571'strigger.test.tsas the body claims. - The surviving-mutant rationale in the body is consistent with the code (e.g. the line-180 early return does make
pull?.head/pull?.numberunreachable withpullundefined).
Refs #554 (non-closing) is appropriate: this ticks one slice of a multi-section tracker. All checks are green; carries hold.
— hive: agent=reviewer backend=copilot model=claude-fable-5 copilot=1.0.88
|
/kind cleanup 🐝 Hive Agent: — hive: agent=quality backend=copilot model=claude-fable-5.1 copilot=1.0.88 |
|
Consolidated into #588 together with the other eight open mutation-survivor pin PRs (commit cherry-picked unchanged, DCO preserved). Closing in favour of that PR. |
, #578-#583, #585) (#588) * test(cron): pin cronLgtm's merge count and its no-read short circuits cronLgtm resolves to the number of pull requests it merged or enqueued, and a pull request it skips before the shared merge path - gate failure or locked - costs no further request. Neither was asserted: the count is discarded by handleCronJob, and tide's own blockedReason also refuses a locked or gated PR, so the existing merge observers could not see the guards. A mutation run over src/cronJobs/lgtm.ts left 13 survivors; the nine behavioural ones (jobsDone++, return false, the !gate.ok and locked guards, { required: true } on github-token, state: 'open') are now killed by direct cronLgtm calls that watch GET /pulls/:number. The four left are log text. Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test(owners): pin the per-team group cache key, parseGroup anchor, decode, defaultBranch and isNotFound guards Running the bounded Stryker check against src/utils/owners.ts left 15 survivors at 100% line coverage. Thirteen are behavioural: with one groupCache key every @org/team entry resolves to the first team's members; parseGroup without its leading anchor reads alice@org/team as a group; a bare `options:` key, a blob without content, a payload without a repository, a string or null tree rejection and a path-less tree entry each hit a TypeError instead of the handled path; and repoHasOwners would answer a second repository from the first. Each is pinned with a direct call in a new file so the harness reports 98.49% on the module, leaving only the warning text held #564 asserts and a debug string. Closes #574 Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test(tide): pin mergeOnce's status guard, the 403-only fork diagnosis, sorted workflow list, queue-path retryIf, entry-less dequeue and isUnknown's state arm Each case fails when the guard it names is removed or inverted; line coverage already reached them all (Stryker on src/plugins/tide.ts: 477/517 killed before, 488/517 after). Refs #570 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test(auth): pin the repository-less org check, the gone-user comment skip, the non-RequestError and half-formed root OWNERS reads, and the OWNERS path in role errors A Stryker probe of src/utils/auth.ts (--mutate src/utils/auth.ts on the harness of #572) left 22 of 273 mutants alive. Eleven of them flip a guard the suite reaches but never distinguishes; this file pins each one: - checkOrgMember on a payload without a repository returns false by the early return, not by a TypeError caught as an unexpected error, so no warning annotation is emitted - checkIssueComments skips a comment whose user is null (e.user?.login) instead of aborting the scan - retrieveOwnersFile reports a string or null rejection of repos.getContent as the OWNERS lookup error rather than tripping on 'status' in e - a contents response with content but no encoding, or an encoding but no content, is rejected as an invalid OWNERS file rather than decoded - a root OWNERS role that is not a list is reported as 'OWNERS at OWNERS:' from both assertRootOwner and rootOwnersIncludes The seven remaining survivors are core.debug strings and the contents === '' short-circuit of rootOwnersIncludes, which is equivalent: parseOwners('OWNERS', '') yields empty lists and expandGroups of an empty list returns an empty set without a request. Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test(approve): pin the github-actions[bot] login-only skip, the uncoverable-file suggestion, the notifier whitespace and marker checks, the unlabeled other-label skip, the base-branch OWNERS probe and the required token A Stryker probe of src/plugins/approve.ts (--mutate src/plugins/approve.ts on the harness of #572, main@e3119ad) left 59 of 515 mutants alive. Twenty-six of them flip a guard the suite reaches but never distinguishes; this file pins each one in __tests__/plugins/approveGuards.test.ts: - approvalEvents skips a github-actions[bot] comment by its login alone, without relying on the Bot user type - suggestApprovers still names an approver when another changed file has no OWNERS entry at all, instead of dereferencing the missing per-file set - computeApproval logs the approver who covers none of the changed files and says nothing for a pull request without an author - renderNotifier's no-files body is exactly the verdict, the explanation and the marker - approveOnPullRequest returns on `unlabeled` of another label without a single api call, probes the OWNERS of the event's base branch rather than the default branch, and fails on a missing github-token input before reading anything - upsertNotifier recognises a stale notifier by a github-actions[bot] user without a Bot type, skips a comment whose user is gone, treats a notifier that differs only by surrounding whitespace as up to date, and never mistakes a bot comment without the literal marker for the notifier - approveOnReview on a payload without a review or a pull request fails on the missing pull request, not on the missing review Re-running the probe with this file kills all twenty-six; the 33 that remain are equivalent (log strings, optional chains on values already checked, switch defaults, an excluded.add after the covered files were removed). Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test(mergeQueue): pin errorMessage's null-rejection guards, the once-per-run warning's initial state and the [bot] suffix anchor Adds __tests__/utils/mergeQueueGuards.test.ts. Every line and branch of src/utils/mergeQueue.ts is already covered, but five of its mutants survive the Stryker harness: `npx stryker run --mutate src/utils/mergeQueue.ts` (main@e3119ad) kills 106 of 111 (score 95.50%). The survivors, each re-applied by hand at its reported span and confirmed to pass the existing mergeQueue.test.ts: - mergeQueue.ts:184 `e !== null` -> `true`, the whole `typeof e === 'object' && e !== null && 'errors' in e` conjunction -> `true`, and the final `&&` -> `||`: a null or undefined rejection of octokit.graphql would throw a TypeError out of errorMessage, so queueState would reject instead of warning and falling back to the REST merge, and enqueue would throw instead of returning `{ ok: false }`. - mergeQueue.ts:64 `let warnedUnavailable = false` -> `true`: every existing test calls resetMergeQueueWarnings() (directly or via setupActionsEnv), so the first real run of a job never warning went unnoticed. Pinned on a fresh module instance (vi.resetModules) that nothing has reset. - mergeQueue.ts:153 `/^github-actions$|\[bot\]$/i` -> `/^github-actions$|\[bot\]/i`: a login merely containing `[bot]` would count as an automation whose queue entry the gate may undo. With this file all five are killed (manual re-application of each mutant fails 1-3 of the new cases; 111/111 killed). No production code changes. Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test(lgtm): pin the refusal error log, the no-label cancel debug, the token guard, the missing-head error and isForbidden's non-object arms A Stryker probe of src/labels/lgtm.ts and src/plugins/lgtmBinding.ts (npx stryker run --mutate src/labels/lgtm.ts,src/plugins/lgtmBinding.ts on the harness of #572, main @ e3119ad, Stryker 10.0.0 / Vitest 5.0.4, 8 workers, 6 m 36 s) left 24 of 202 mutants alive (88.12 %). Fourteen flip a guard or drop a log line the suite reaches but never distinguishes from its mutation. Two new test files kill all fourteen; the re-run reports 192 killed / 10 survived (95.05 %). __tests__/label/lgtmGuards.test.ts (src/labels/lgtm.ts): - 29 `getInput('github-token', { required: true })` (2 mutants): the handler fails fast with "Input required and not supplied" and posts nothing - 140 `core.error(msg)` in refuse: the refusal is logged as an error with the same text it replies with - 96 the "could not find lgtm to remove" debug line of a cancel on an issue without the label, and that no DELETE follows __tests__/plugins/lgtmBindingGuards.test.ts (src/plugins/lgtmBinding.ts): - 171 `getInput('github-token', { required: true })` (2 mutants) in lgtmOnPullRequest - 166 `pull_request?.head?.sha` -> `head.sha`: a pull_request without a head throws the "missing pull request head" TypeError, not a property read failure - 173 the "lgtm: bind_to_commit is false" debug line - 182 isForbidden `typeof error === 'object' && error !== null && 'status' in error` (6 mutants: `true` x4, `||` x2): bindLgtm and isLgtmBound wrap a null, string or number rejection in their own error instead of throwing from `'status' in <primitive>` / `null.status` Reviewed survivors, not pinned (10): lgtm.ts 33, 34 (x2), 35, 65 and lgtmBinding.ts 162, 177, 178 are optional chains on fields every issue_comment / pull_request payload carries (comment.body, comment.user, issue.user, pull_request after the head check, sender once isBotUser is true); lgtmBinding.ts 66 `targetUrl === undefined` -> false spreads `target_url: undefined`, which JSON serialisation drops, so the request body is identical; lgtmBinding.ts 156 `label?.name ?? ''` -> any other string still fails the `!== 'lgtm'` comparison and returns early. Refs #554 Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test(trigger): pin the guards of /retest, /test, /ok-to-test and okToTestOnPullRequest that a Stryker probe left unasserted A Stryker probe of src/issueComment/trigger.ts (346 mutants) left 38 alive. Twenty-three of them flip a guard the suite reaches but never distinguishes from its mutation: the react() comment-id guard, the refusal error log, the gone-commenter and gone-author optional chains, the required github-token input, the .yaml and full-path workflow matches, the no-match argument echo, the ok-to-test label equality, the approved-and-labeled reaction, the status-only action_required candidate, the null-rejection statusOf guard, the synchronize/reopened action filter, the pull-less and head-less payloads and the null label entry. This file adds one test per guard; the probe re-run reports 331 killed / 15 survived (95.66 %). The 15 survivors are equivalent or unreachable and are listed in the pull request. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test: pin the milestone list separator, isBotUser's login arm, createComment's message and skipReadOnlyForkRun's payload guards A Stryker probe over the issue-comment, label and utility modules left nine behavioural survivors the unit suite reaches but never distinguishes from their mutations. Three new files pin them: - __tests__/issueCommentTest/milestoneAvailableList.test.ts: the not-found error joins the open milestones with ', ' (milestone.ts:66). - __tests__/utils/commentsGuards.test.ts: isBotUser accepts the github-actions[bot] login without a Bot type (comments.ts:67), createComment rewraps a refused post as 'could not add comment: ...' (comments.ts:34), and createCommentOnce honours a login-only bot marker. - __tests__/pullReqTest/readOnlyForkPayloads.test.ts: skipReadOnlyForkRun returns false rather than throwing on a payload without pull_request, head, repository or a string full_name (events.ts:23-25). Closes #584 Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> --------- Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> Co-authored-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Test Improvement
A Stryker probe of
src/issueComment/trigger.ts(npx stryker run --mutate src/issueComment/trigger.tson #572's harness, main @ e3119ad, Stryker 10.0.0 / Vitest 5.0.4, 8 workers, 7 m 40 s) left 38 of 346 mutants alive (89.02 %). Twenty-three flip a guard the suite reaches but never distinguishes from its mutation. This PR adds one test file that kills all twenty-three; re-running the probe with it reports 331 killed / 15 survived (95.66 %).Files / cluster claimed:
__tests__/issueCommentTest/triggerGuards.test.ts(new). Nosrc/,dist/or existing-test change. Disjoint from every held PR: #571 edits__tests__/issueCommentTest/trigger.test.ts(the harness-wide unhandled-request assertion) but not this file, and this file already serves every request it provokes (zero msw unhandled frames), so it stays green under #571's rule. Follows the pattern of #575 (ownersGuards), #578 (tideGuards), #579 (authGuards), #580 (approveGuards), #581 (mergeQueueGuards) and #582 (lgtmGuards).Mutants pinned (span → test)
trigger.tsspanif (commentId === undefined) { return }→false/{}/retest › a comment without an id attempts no reaction request and logs no warningcore.error(msg)removed/retest › a refusal is logged as an error annotation with the full messagecomment?.user?.login→comment?.user.login/retest › a comment whose user is gone is refused with a comment and makes no Actions callgetInput('github-token', { required: true })→{}/false/retest › fails before any request when the github-token input is missing;okToTestOnPullRequest › fails before any request …args.join(' ')→join('')/test › the no-match comment echoes several arguments separated by spaces`${arg}.yaml`→` `.yamlworkflow is never matched by basename/test › matches a workflow stored as .yaml by its basenamepath === file→false/test › matches a workflow by its full pathlabel.toLowerCase() === okToTestLabel→true/ok-to-test › adds ok-to-test to a pull request that carries only unrelated labelsapproved === 0→true/ok-to-test › with the label already present and runs approved reacts instead of commentingissue?.user?.login→issue?.user.login/ok-to-test › on a pull request whose author is gone approves and reacts for a memberrun.status === 'action_required'→false/''action_requiredby status alone is not approved/ok-to-test › approves a run that reports action_required by status alonetypeof error === 'object' && error !== null && 'status' in error→true/error !== null→truenullrejection throws a TypeError instead of the named run error/ok-to-test › a null approve rejection fails with its string formaction !== 'synchronize' && action !== 'reopened'→false/{}opened,labeled,closed,editedokToTestOnPullRequest › makes no Actions call on %s even when labeledpull?.labels→pull.labelsokToTestOnPullRequest › a synchronize payload without a pull request returns quietlypull?.head?.sha→pull?.head.shaokToTestOnPullRequest › a labeled pull request without a head object throws the missing-head errorlabel?.name→label.namenullentry inlabelsthrows instead of being skippedokToTestOnPullRequest › skips a null label entry and still finds ok-to-testReviewed survivors, not pinned (15)
40:107,272:20,181:77— the?? ''fallback literal; no comparison can match"stryker was here!".40:37,44:43—run.status === 'completed'in the failed/successful filters; GitHub reportsconclusion: nulluntil a run completes, so a fixture that kills these would not represent a real run (the test(label-sync): pin the case-insensitive name and color folds, the null-description fold, the dry-run counts and per_page #531/test(pullRequestOwners): omit draft/state/labels for real, pin the rename dedupe and the repository-keyed pull and tip memos #533 lesson).76:40,204:29-58,291:41—context.payload.comment?.…when the payload has no comment at all;handleIssueCommentdispatches on the comment body, so no comment never reaches these lines.210:7,215:59-86—context.payload.issue?.…when there is no issue; the issue-number check at line 206 throws first.186:24-34,187:34—pull?.head,pull?.numberwhenpullis undefined; line 180 already returns on a missing pull request.220:66—'reviewers'→'':assertPullRequestOwner/assertRootOwnerbranch only onrole === 'approvers', so any other role string is the reviewers arm.241:29—current === undefined→false: differs only for a run whosenameisundefined(notnull) whileGITHUB_WORKFLOWis unset; GitHub serves a string ornull.255:7—count++→count--: onlyrerun === 0is ever observed and-Nis never0when a run was re-run.I have not edited
docs/contributing.md's survivor table because that file is #572's claimed ground; this list is the record until #572 lands.Validation
npx vitest run __tests__/issueCommentTest/triggerGuards.test.ts→ 20 passed, 0 msw unhandled requestsnpx eslint __tests__/issueCommentTest/triggerGuards.test.ts→ cleannpm run test:coverage→ 168 files / 2350 passed; 99.92 % stmts / 99.35 % branches / 100 % funcs / 99.91 % lines (unchanged — this PR adds assertion strength, not line coverage)trigger.tsbefore: 308 killed / 38 survived (89.02 %); after: 331 / 15 (95.66 %)Related Issue
Refs #554 — ticks a slice of the "Strengthen assertions and the test harness" section (reviewed mutation survivors on the
/retest,/test,/ok-to-testtrigger module); the tracker's CI-enforcement, release-delivery and routing sections stay open and are owned elsewhere.Filed by quality agent (hold-gated mode). Human review required.
— hive: agent=quality backend=copilot model=claude-fable-5.1 copilot=1.0.88 requested_by=@mrbobbytables