@@ -13,6 +13,13 @@ use crate::types::MergeCoordinatorError;
1313
1414const RETRY_DELAYS_SECS : & [ u64 ] = & [ 1 , 2 , 4 ] ;
1515
16+ /// Maximum number of open PRs fetched per `list_open_prs` call.
17+ ///
18+ /// Gitea's hard cap is 50 when no explicit limit is set; the API accepts up
19+ /// to 300. Using 300 ensures PRs beyond position 50 are not silently skipped
20+ /// by the evaluation loop (issue #2850).
21+ const OPEN_PRS_LIMIT : u32 = 300 ;
22+
1623/// Minimal Gitea API client. Caller supplies the API token via env or
1724/// secure storage; it is never written to logs.
1825pub struct GiteaClient {
@@ -57,8 +64,8 @@ impl GiteaClient {
5764 repo : & str ,
5865 ) -> Result < Vec < PrSummary > , MergeCoordinatorError > {
5966 let url = format ! (
60- "{}/api/v1/repos/{}/{}/pulls?state=open&limit=50 " ,
61- self . base_url, owner, repo
67+ "{}/api/v1/repos/{}/{}/pulls?state=open&limit={} " ,
68+ self . base_url, owner, repo, OPEN_PRS_LIMIT
6269 ) ;
6370 let resp = self . get_with_retry ( & url) . await ?;
6471 let prs = resp
@@ -202,4 +209,38 @@ mod tests {
202209 fn retry_delays_are_one_two_four_seconds ( ) {
203210 assert_eq ! ( RETRY_DELAYS_SECS , & [ 1u64 , 2 , 4 ] ) ;
204211 }
212+
213+ #[ test]
214+ fn open_prs_limit_exceeds_gitea_default_of_50 ( ) {
215+ assert ! (
216+ OPEN_PRS_LIMIT > 50 ,
217+ "OPEN_PRS_LIMIT must exceed 50 so PRs beyond position 50 are not silently dropped"
218+ ) ;
219+ }
220+
221+ #[ test]
222+ fn open_prs_limit_within_gitea_max_page_size ( ) {
223+ assert ! (
224+ OPEN_PRS_LIMIT <= 300 ,
225+ "Gitea max page size is 300; limit must not exceed it"
226+ ) ;
227+ }
228+
229+ #[ test]
230+ fn pr_summary_vec_of_51_items_deserialises ( ) {
231+ // Construct JSON array with 51 items to verify no artificial truncation
232+ // happens at the deserialization layer.
233+ let items: String = ( 1u64 ..=51 )
234+ . map ( |n| format ! ( r#"{{"number":{n},"title":"PR {n}","state":"open"}}"# ) )
235+ . collect :: < Vec < _ > > ( )
236+ . join ( "," ) ;
237+ let json = format ! ( "[{items}]" ) ;
238+ let prs: Vec < PrSummary > = serde_json:: from_str ( & json) . unwrap ( ) ;
239+ assert_eq ! (
240+ prs. len( ) ,
241+ 51 ,
242+ "all 51 PRs must be present after deserialisation"
243+ ) ;
244+ assert_eq ! ( prs[ 50 ] . number, 51 , "PR at position 51 must be present" ) ;
245+ }
205246}
0 commit comments