@@ -43,6 +43,7 @@ import { createInstallationToken } from "../../src/github/app";
4343import { fetchLiveCiAggregate , refreshInstallationHealthForInstallation } from "../../src/github/backfill" ;
4444import {
4545 actionParams ,
46+ clearInstallationHealthRefreshCooldownForTest ,
4647 clearWritePermissionDenialCooldownForTest ,
4748 executeAgentMaintenanceActions ,
4849 executeIssueMaintenanceActions ,
@@ -93,6 +94,7 @@ describe("executeAgentMaintenanceActions (#778 gate stack)", () => {
9394 liveHeadSha : args . expectedHeadSha ?? null ,
9495 liveState : "open" ,
9596 } ) ) ;
97+ clearInstallationHealthRefreshCooldownForTest ( ) ;
9698 clearWritePermissionDenialCooldownForTest ( ) ;
9799 resetMetrics ( ) ;
98100 } ) ;
@@ -309,7 +311,7 @@ describe("executeAgentMaintenanceActions (#778 gate stack)", () => {
309311 it ( "LIVE merge is denied when live CI has since turned failing (#2128)" , async ( ) => {
310312 const env = createTestEnv ( { } ) ;
311313 vi . mocked ( fetchLiveCiAggregate ) . mockResolvedValueOnce ( { ciState : "failed" , hasPending : false , hasVisiblePending : false , failingDetails : [ ] , nonRequiredFailingDetails : [ ] , ciCompletenessWarning : null } ) ;
312- const outcomes = await executeAgentMaintenanceActions ( env , ctx ( ) , [ merge ] ) ;
314+ const outcomes = await executeAgentMaintenanceActions ( env , ctx ( { installationId : 127 } ) , [ merge ] ) ;
313315 expect ( outcomes [ 0 ] ?. outcome ) . toBe ( "denied" ) ;
314316 expect ( outcomes [ 0 ] ?. detail ) . toContain ( "live CI is no longer passing (now: failed)" ) ;
315317 expect ( mergePullRequest ) . not . toHaveBeenCalled ( ) ;
@@ -730,6 +732,26 @@ describe("executeAgentMaintenanceActions (#778 gate stack)", () => {
730732 expect ( ( await auditFor ( env , "merge" ) ) ?. outcome ) . toBe ( "error" ) ;
731733 } ) ;
732734
735+ it ( "REGRESSION: a generic GitHub 403 merge rejection does not immediately pin merge_blocked_sha" , async ( ) => {
736+ const env = createTestEnv ( { } ) ;
737+ await upsertPullRequestFromGitHub ( env , "owner/repo" , { number : 7 , title : "PR" , state : "open" , user : { login : "c" } , head : { sha : "sha7" } , labels : [ ] , body : "" } ) ;
738+ vi . mocked ( mergePullRequest ) . mockRejectedValueOnce ( Object . assign ( new Error ( "Resource not accessible by integration" ) , { status : 403 } ) ) ;
739+
740+ const outcomes = await executeAgentMaintenanceActions ( env , ctx ( ) , [ merge ] ) ;
741+
742+ expect ( outcomes [ 0 ] ) . toMatchObject ( { actionClass : "merge" , outcome : "error" } ) ;
743+ const row = await env . DB . prepare (
744+ "select merge_attempt_count as mergeAttemptCount, merge_blocked_sha as mergeBlockedSha, merge_blocked_reason as mergeBlockedReason from pull_requests where repo_full_name = ? and number = ?" ,
745+ )
746+ . bind ( "owner/repo" , 7 )
747+ . first < { mergeAttemptCount : number ; mergeBlockedSha : string | null ; mergeBlockedReason : string | null } > ( ) ;
748+ expect ( row ) . toEqual ( { mergeAttemptCount : 1 , mergeBlockedSha : null , mergeBlockedReason : null } ) ;
749+ const blocked = await env . DB . prepare ( "select count(*) as count from audit_events where event_type = ?" )
750+ . bind ( "agent.action.merge_blocked" )
751+ . first < { count : number } > ( ) ;
752+ expect ( blocked ?. count ) . toBe ( 0 ) ;
753+ } ) ;
754+
733755 it ( "opportunistically refreshes installation health when a PR-write mutation fails with a 403 (#2265)" , async ( ) => {
734756 const env = createTestEnv ( { } ) ;
735757 vi . mocked ( closePullRequest ) . mockRejectedValueOnce ( Object . assign ( new Error ( "Resource not accessible by integration" ) , { status : 403 } ) ) ;
0 commit comments