@@ -416,6 +416,40 @@ describe("fetchIssues", () => {
416416 expect ( pushNotification ) . not . toHaveBeenCalled ( ) ;
417417 } ) ;
418418
419+ it ( "extracts partial data from GraphqlResponseError and stops pagination" , async ( ) => {
420+ vi . mocked ( pushNotification ) . mockClear ( ) ;
421+
422+ // Simulate a GraphqlResponseError: has .data with valid nodes + errors
423+ const partialError = Object . assign ( new Error ( "Some nodes failed to resolve" ) , {
424+ data : {
425+ search : {
426+ issueCount : 5 ,
427+ pageInfo : { hasNextPage : true , endCursor : "cursor-partial" } ,
428+ nodes : [ { ...graphqlIssueNode , databaseId : 42 } , null ] ,
429+ } ,
430+ rateLimit : { remaining : 4990 , resetAt : new Date ( Date . now ( ) + 3600000 ) . toISOString ( ) } ,
431+ } ,
432+ } ) ;
433+ const octokit = makeIssueOctokit ( async ( ) => {
434+ throw partialError ;
435+ } ) ;
436+
437+ const result = await fetchIssues (
438+ octokit as unknown as ReturnType < typeof import ( "../../src/app/services/github" ) . getClient > ,
439+ [ testRepo ] ,
440+ "octocat"
441+ ) ;
442+
443+ // Valid node from partial data is returned
444+ expect ( result . issues . length ) . toBe ( 1 ) ;
445+ expect ( result . issues [ 0 ] . id ) . toBe ( 42 ) ;
446+ // Error is recorded
447+ expect ( result . errors . length ) . toBe ( 1 ) ;
448+ expect ( result . errors [ 0 ] . message ) . toContain ( "Some nodes failed to resolve" ) ;
449+ // Only 1 graphql call — did NOT try to paginate after partial error
450+ expect ( octokit . graphql ) . toHaveBeenCalledTimes ( 1 ) ;
451+ } ) ;
452+
419453 it ( "throws when octokit is null" , async ( ) => {
420454 await expect ( fetchIssues ( null , [ testRepo ] , "octocat" ) ) . rejects . toThrow (
421455 "No GitHub client available"
@@ -691,6 +725,39 @@ describe("fetchPullRequests", () => {
691725 expect ( pushNotification ) . not . toHaveBeenCalled ( ) ;
692726 } ) ;
693727
728+ it ( "extracts partial PR data from GraphqlResponseError and stops pagination" , async ( ) => {
729+ vi . mocked ( pushNotification ) . mockClear ( ) ;
730+
731+ const partialError = Object . assign ( new Error ( "Partial node resolution failure" ) , {
732+ data : {
733+ search : {
734+ issueCount : 3 ,
735+ pageInfo : { hasNextPage : true , endCursor : "cursor-partial" } ,
736+ nodes : [ { ...graphqlPRNode , databaseId : 77 } ] ,
737+ } ,
738+ rateLimit : { remaining : 4990 , resetAt : new Date ( Date . now ( ) + 3600000 ) . toISOString ( ) } ,
739+ } ,
740+ } ) ;
741+ const octokit = makePROctokit ( async ( _query , variables ) => {
742+ const q = ( variables as Record < string , unknown > ) . q as string ;
743+ if ( q . includes ( "involves:" ) ) throw partialError ;
744+ return makeGraphqlPRResponse ( [ ] ) ;
745+ } ) ;
746+
747+ const result = await fetchPullRequests (
748+ octokit as unknown as ReturnType < typeof import ( "../../src/app/services/github" ) . getClient > ,
749+ [ testRepo ] ,
750+ "octocat"
751+ ) ;
752+
753+ expect ( result . pullRequests . length ) . toBe ( 1 ) ;
754+ expect ( result . pullRequests [ 0 ] . id ) . toBe ( 77 ) ;
755+ expect ( result . errors . length ) . toBe ( 1 ) ;
756+ expect ( result . errors [ 0 ] . message ) . toContain ( "Partial node resolution failure" ) ;
757+ // involves query: 1 call (threw partial, stopped). review-requested: 1 call = 2 total
758+ expect ( octokit . graphql ) . toHaveBeenCalledTimes ( 2 ) ;
759+ } ) ;
760+
694761 it ( "caps at 1000 PRs and warns via pushNotification" , async ( ) => {
695762 vi . mocked ( pushNotification ) . mockClear ( ) ;
696763
0 commit comments