@@ -5,6 +5,8 @@ import assert from 'node:assert/strict';
55import {
66 parseRouterIssueNumber ,
77 parseCacheIssueNumber ,
8+ parseCacheNoStandingReason ,
9+ parseNoStandingReasonFromReport ,
810 resolveStandingIssueNumberForPr ,
911 parseIssueNumberFromBranch ,
1012 assertBranchMatchesIssue ,
@@ -58,6 +60,40 @@ test('parseCacheIssueNumber rejects closed or non-standing cache entries', () =>
5860 ) ;
5961} ) ;
6062
63+ test ( 'parseCacheNoStandingReason exposes queue-empty idle cache state' , ( ) => {
64+ assert . equal (
65+ parseCacheNoStandingReason ( {
66+ state : 'NONE' ,
67+ noStandingReason : 'queue-empty'
68+ } ) ,
69+ 'queue-empty'
70+ ) ;
71+ assert . equal (
72+ parseCacheNoStandingReason ( {
73+ state : 'OPEN' ,
74+ noStandingReason : 'queue-empty'
75+ } ) ,
76+ null
77+ ) ;
78+ } ) ;
79+
80+ test ( 'parseNoStandingReasonFromReport exposes queue-empty from the no-standing artifact' , ( ) => {
81+ assert . equal (
82+ parseNoStandingReasonFromReport ( {
83+ schema : 'standing-priority/no-standing@v1' ,
84+ reason : 'queue-empty'
85+ } ) ,
86+ 'queue-empty'
87+ ) ;
88+ assert . equal (
89+ parseNoStandingReasonFromReport ( {
90+ schema : 'other/schema' ,
91+ reason : 'queue-empty'
92+ } ) ,
93+ null
94+ ) ;
95+ } ) ;
96+
6197test ( 'resolveStandingIssueNumberForPr prefers router over cache' , ( ) => {
6298 const result = resolveStandingIssueNumberForPr ( '/tmp/repo' , {
6399 readJsonFn : ( filePath ) => {
@@ -72,7 +108,7 @@ test('resolveStandingIssueNumberForPr prefers router over cache', () => {
72108 }
73109 } ) ;
74110
75- assert . deepEqual ( result , { issueNumber : 680 , source : 'router' } ) ;
111+ assert . deepEqual ( result , { issueNumber : 680 , source : 'router' , noStandingReason : null } ) ;
76112} ) ;
77113
78114test ( 'resolveStandingIssueNumberForPr treats explicit empty router issue as authoritative' , ( ) => {
@@ -81,15 +117,21 @@ test('resolveStandingIssueNumberForPr treats explicit empty router issue as auth
81117 if ( filePath . endsWith ( 'router.json' ) ) {
82118 return { issue : null } ;
83119 }
120+ if ( filePath . endsWith ( 'no-standing-priority.json' ) ) {
121+ return {
122+ schema : 'standing-priority/no-standing@v1' ,
123+ reason : 'queue-empty'
124+ } ;
125+ }
84126 return {
85127 number : 680 ,
86- state : 'open ' ,
87- labels : [ 'standing-priority' ]
128+ state : 'NONE ' ,
129+ labels : [ ]
88130 } ;
89131 }
90132 } ) ;
91133
92- assert . deepEqual ( result , { issueNumber : null , source : 'router' } ) ;
134+ assert . deepEqual ( result , { issueNumber : null , source : 'router' , noStandingReason : 'queue-empty' } ) ;
93135} ) ;
94136
95137test ( 'resolveStandingIssueNumberForPr falls back to cache when router is unavailable' , ( ) => {
@@ -106,7 +148,27 @@ test('resolveStandingIssueNumberForPr falls back to cache when router is unavail
106148 }
107149 } ) ;
108150
109- assert . deepEqual ( result , { issueNumber : 680 , source : 'cache' } ) ;
151+ assert . deepEqual ( result , { issueNumber : 680 , source : 'cache' , noStandingReason : null } ) ;
152+ } ) ;
153+
154+ test ( 'createPriorityPr refuses to open a priority PR when the standing queue is empty' , ( ) => {
155+ assert . throws (
156+ ( ) =>
157+ createPriorityPr ( {
158+ env : { } ,
159+ getRepoRootFn : ( ) => '/tmp/repo' ,
160+ getCurrentBranchFn : ( ) => 'feature/manual-follow-up' ,
161+ ensureGhCliFn : ( ) => { } ,
162+ resolveUpstreamFn : ( ) => ( { owner : 'upstream-owner' , repo : 'repo' } ) ,
163+ ensureOriginForkFn : ( ) => ( { owner : 'fork-owner' , repo : 'repo' } ) ,
164+ pushBranchFn : ( ) => { } ,
165+ runGhPrCreateFn : ( ) => {
166+ throw new Error ( 'should not create PR' ) ;
167+ } ,
168+ resolveStandingIssueNumberFn : ( ) => ( { issueNumber : null , source : 'router' , noStandingReason : 'queue-empty' } )
169+ } ) ,
170+ / S t a n d i n g - p r i o r i t y q u e u e i s e m p t y / i
171+ ) ;
110172} ) ;
111173
112174test ( 'parseIssueNumberFromBranch extracts issue numbers from issue/* branches' , ( ) => {
0 commit comments