Repository navigation
test(mutation): pin the milestone list separator, isBotUser's login arm, createComment's message and skipReadOnlyForkRun's payload guards - #585
Conversation
…Comment'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>
|
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.
Read the three new test files against the sources they pin; this looks correct to me.
isBotUsertable matchessrc/utils/comments.ts:67-69exactly:user?.type === 'Bot' || user?.login === 'github-actions[bot]'makes{login:'github-actions[bot]'}with no/Usertype a bot, the baregithub-actionsprefix and{}/null/undefinednot. The 403 rewrap assertion matches thecould not add comment: ${e}wrap atcomments.ts:34.skipReadOnlyForkRunpayload cases matchsrc/utils/events.ts:23-25: any non-stringhead/basefull_name (including the numeric42) falls into thetypeof !== 'string'arm, returnsfalsewith no notice; the control case (differing names) returnstruewith one notice. Mock state across cases is safe —vitest.config.mjs:12-14setsclearMocks/mockReset/restoreMocks.- The milestone assertion matches
src/issueComment/milestone.ts:66-68(titles.join(', ')intoAvailable milestones: …), and theObserveRequeston the PATCH pins that no milestone is set.
Diff adds three test files only — no src/, dist/, or existing test edited, so the body's disjointness claim holds. All helpers used (failOnUnhandledRequest, ObserveRequest, MockContext, setupActionsEnv, mockResponse) exist in __tests__/testUtils.ts. CI is green on head 9221ac2. No findings; fine to land once a maintainer lifts the 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
Pins nine behavioural Stryker survivors reported in #584, found by running #572's harness over the issue-comment, label and utility modules no earlier probe had covered (
main@ e3119ad, 643 mutants, 79 survived; the rest are equivalent, see the issue).Files / cluster claimed — three new test files; no
src/,dist/or existing test is edited, so this is disjoint from every held PR (#571 touchesretitle.test.ts,comments.test.ts,milestone.test.tsandreadOnlyFork.test.tsby one import line each; none of those files change here):__tests__/issueCommentTest/milestoneAvailableList.test.ts/milestone v3against three open milestones fails withAvailable milestones: v1, v2, backlogand issues no PATCH (milestone.ts:66,join(', '))__tests__/utils/commentsGuards.test.tsisBotUsertable —github-actions[bot]login with no type / withtype: 'User'is a bot,github-actionsprefix is not,{}/null/undefinedare not (comments.ts:67);createCommenton a 403 rejects with/^could not add comment: HttpError: Resource not accessible by integration/(comments.ts:34);createCommentOnceskips a marker comment whose user has only the login__tests__/pullReqTest/readOnlyForkPayloads.test.tsskipReadOnlyForkRunreturnsfalse, emits no notice and does not throw underpull_requestandpull_request_reviewfor a payload with nopull_request, nohead, norepository, nofull_name, or a numericfull_name; the control case with both names present still skips (events.ts:23-25)Re-running Stryker on those lines with the pins:
milestone.ts15/15 killed,events.ts28/28 killed,comments.ts31/33 killed (the two survivors are the equivalentbody ?? ''fallback at:58and the debug string at:59).Not pinned here:
retitle.ts:34(title === ''guard) — #571'sfailTestOnUnhandledRequestsonretitle.test.tskills it, so it is left to that PR.Checks run locally
npx vitest run __tests__/issueCommentTest/milestoneAvailableList.test.ts __tests__/utils/commentsGuards.test.ts __tests__/pullReqTest/readOnlyForkPayloads.test.ts→ 3 files, 17 tests passnpx eslinton the three files → cleanRelated Issue
Closes #584
Filed by quality agent (hold-gated mode). Human review required.
— hive: agent=quality backend=copilot model=claude-fable-5.1 copilot=1.0.88