Repository navigation
test(owners): pin the fail-closed OWNERS group read when a later members page cannot be fetched (owners.ts:153-162) - #564
Conversation
…ers page cannot be fetched (owners.ts:153-162) Let the real octokit paginator walk a team's Link headers instead of the stubbed paginate the existing group tests use. A page-one member is an approver once every page is read; when page two returns 500 the group contributes nobody, the run warns once naming the team, and the page-one member is denied without a fallback to organization membership. 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 |
|
/kind cleanup 🐝 Hive Agent: — hive: agent=quality backend=copilot model=claude-fable-5.1 |
|
Consolidated into #589 (commit cherry-picked unchanged, author and DCO sign-off preserved). |
, #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>
…solidates #555, #558, #559, #561, #562, #564-#567) (#589) * test: cover paginated OWNERS and review timelines Exercise approval authorization, automatic reviewer selection, and delayed reviewer timing across page boundaries. Hive-Run: #554 Hive-Plan: P2 cross-request contracts Hive-Spec: issue-554#P2-cross-request-contracts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: mrbobbytables <mrbobbytables@users.noreply.github.com> * test(sweep): pin the inclusive due-window bounds and the timeline-read failure of delayed reviewers Add four cases to the 'sweep with blunderbuss.delay' block of __tests__/cronJobTest/sweep.test.ts, exercising requestReviewersIfDue's window checks at the exact instants and 1 ms past them, for created_at and for the last ready_for_review alike, and the failure path when the timeline read itself fails. Each boundary case fails on a one-character mutant of sweep.ts that the existing suite accepted: '>' to '>=' on the created_at early return, '<' to '<=' or '>' to '>=' on the shifted-window check. The failure case covers the catch in lastReadyForReview (previously unit-uncovered) and pins that the error is reported as a blunderbuss failure of that pull request while the other pull requests still get their reviewers. Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test(sweep): pin that a second delayed sweep leaves the first run's requested reviewers alone The delayed-blunderbuss bundle suite modelled a pull that already had reviewers only as a static fixture. This drives dist/index.js through two successive sweeps against a fake whose pull list reflects the reviewer request the first sweep made, and asserts the second run neither re-reads the timeline or reviews nor requests reviewers again. Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test(sweep): pin the delayed-reviewers OWNERS guard on a base branch without OWNERS files Both suites left the false arm of the branchHasOwners guard ahead of requestReviewersIfDue in src/cronJobs/sweep.ts with zero hits: every delayed-blunderbuss case served an OWNERS file on master. Add one unit case and one dist/index.js scenario, each in its own file, that configure blunderbuss.delay, serve an empty master tree and a due pull request, and assert the tree is probed once while the timeline, reviews and requested_reviewers endpoints are never touched. Both fail when the guard is replaced by `|| true`. Fixes #560 Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test(sweep): pin the reviewer-key fallbacks of delayed blunderbuss on a bare pull listing (sweep.ts:208) requestReviewersIfDue reads requested_reviewers ?? [] and requested_teams ?? [] off the pull list entry, but no unit or bundle test listed a delayed-only pull request without those keys, leaving both fallback arms unreached in coverage/ and coverage-e2e/. Serve a due pull with the keys removed on both harnesses and assert reviewers are still requested. Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test(owners): pin the fail-closed OWNERS group read when a later members page cannot be fetched (owners.ts:153-162) Let the real octokit paginator walk a team's Link headers instead of the stubbed paginate the existing group tests use. A page-one member is an approver once every page is read; when page two returns 500 the group contributes nobody, the run warns once naming the team, and the page-one member is denied without a fallback to organization membership. Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test(sweep): pin the delayed-reviewers catch of sweep in its Error and String(e) arms (sweep.ts:85) Adds __tests__/cronJobTest/sweepDelayFailures.test.ts: two due delayed-only pull requests on a base branch with OWNERS, where one fails. A 500 on the reviews read is recorded as that pull request's `blunderbuss:` failure while the other still gets its reviewer request; a non-Error rejection of requestOwnersReviewers is stringified the same way. Before: coverage/lcov.info BRDA:85,5,0,0 / BRDA:85,5,1,0 (neither arm hit by the unit suite). After: both 1; sweep.ts branches 92.3 -> 94.87 %. Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test(sweep): pin the ignore_drafts=false arm of delayed reviewers (sweep.ts:218) With blunderbuss.delay set and ignore_drafts: false, requestReviewersIfDue must not skip drafts and must time every pull request from created_at alone, never reading the timeline. The unit suite only ever ran the delayed sweep with the default ignore_drafts: true, so that arm was reached by the bundle suite alone. Pins it in its own file so it does not collide with the pending edits to the delay block of sweep.test.ts. Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test(sweep): pin the no-catch-up recovery of delayed reviewers after missed schedule runs After the schedule misses runs for longer than sweep.lookback, a pull request whose review clock expired before now - delay - lookback is neither listed, read nor requested by the delayed-reviewers step, and a longer sweep.lookback is what recovers it. Pinned in a unit suite (__tests__/cronJobTest/sweepDelayOutage.test.ts) and against dist/index.js (__tests__/bundle/blunderbussDelayOutage.test.ts); the blunderbuss.delay paragraph of docs/configuration.md now records the expectation. Refs #554 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> * test(harness): fail tide and sweep unit tests on unserved requests Completes the #571 wiring that was deferred while #557 and #558 occupied these files: both servers now answer the merge-queue read with noMergeQueue and assert at the end of each test that no request went unserved; sweep's OWNERS scenarios serve the base-branch tip instead of running through the 'could not read the tip' fallback. Refs #570 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Bob Killen <bkillen@linuxfoundation.org> --------- Signed-off-by: mrbobbytables <mrbobbytables@users.noreply.github.com> Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> Signed-off-by: Bob Killen <bkillen@linuxfoundation.org> Co-authored-by: mrbobbytables <mrbobbytables@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Test Improvement
Adds
__tests__/utils/ownersGroupPagination.test.ts, which reads an OWNERS@codertocat/maintainersgroup through the realoctokit.paginateagainst mswLinkheaders (the existing group tests in__tests__/utils/owners.test.tsstubpaginate;__tests__/bundle/ownersGroups.test.tsserves a single page). It pinsexpandGroups(src/utils/owners.ts:150-165) and the/approveoutcome throughassertAuthorizedByOwnersOrMembership:Linkto page two, so a member listed only there is an approver (and both pages are requested withper_page=100);dave is not an approver for any changed file), exactly one warning is emitted (OWNERS: could not read the members of @codertocat/maintainers: HttpError: Server Error), and both pages were requested — i.e. the read fails closed rather than trusting the members fetched so far;GET /orgs/{org}/members/{login}is made; the unhandled-request guard would reject one).Evidence at
maine3119ad:npm run test:coverage(99.92 / 99.35 / 100 / 99.91) andnpm run test:coverage:e2e(100 / 99.46 / 100 / 100) both already hitowners.ts:160-162, so this is a behavioural contract, not a line-coverage change — per #554's "judge tests by behavior" section.Related Issue
Refs #554 — ticks only the "OWNERS pagination failure: fail a subsequent membership page and assert the warning and fail-closed behavior" box of the P2 cross-request section. Decision taken by hive: the scenario is pinned at the unit layer with a real
Octokitrather than in__tests__/bundle/, because the bundle variant needs thefakeGithubresponse-header support that #555 adds and that file is occupied ground until #555 merges. The tracker's other boxes (paginated OWNERS/timeline in #555, boundaries in #558, successive sweeps in #559, scheduling outage recovery, and the P1 CI/release/harness sections) stay open; the issue states a single test PR must not close it.Disjoint from the open hold-gated PRs: touches only the new file; no change to
fakeGithub.ts,ownersGroups.test.ts,blunderbussDelay*.test.ts,sweep*.test.ts,tide.test.ts,auth.ts, orconfig.ts.Tests
npx vitest run __tests__/utils/ownersGroupPagination.test.ts— 4 passednpx eslint __tests__/utils/ownersGroupPagination.test.ts— cleanFiled by quality agent (hold-gated mode). Human review required.
— hive: agent=quality backend=copilot model=claude-fable-5.1 requested_by=@mrbobbytables