@@ -12,24 +12,27 @@ const makeRawCommit = (message: string) => ({
1212 commit : { message, author : { date : "2026-04-01T12:00:00Z" } } ,
1313} ) ;
1414
15+ // Helper to create a Response with a Link header for pagination
16+ const pagedResponse = ( commits : unknown [ ] , nextUrl ?: string ) => {
17+ const headers : Record < string , string > = { } ;
18+ if ( nextUrl ) headers [ "link" ] = `<${ nextUrl } >; rel="next"` ;
19+ return new Response ( JSON . stringify ( commits ) , { status : 200 , headers } ) ;
20+ } ;
21+
1522describe ( "fetchCommitMessages" , ( ) => {
1623 beforeEach ( ( ) => {
1724 vi . restoreAllMocks ( ) ;
1825 } ) ;
1926
2027 it ( "fetches commit messages for multiple repos" , async ( ) => {
2128 vi . spyOn ( globalThis , "fetch" )
22- . mockResolvedValueOnce (
23- new Response ( JSON . stringify ( [
24- makeRawCommit ( "feat: add login" ) ,
25- makeRawCommit ( "fix: typo in header" ) ,
26- ] ) , { status : 200 } ) ,
27- )
28- . mockResolvedValueOnce (
29- new Response ( JSON . stringify ( [
30- makeRawCommit ( "chore: update deps" ) ,
31- ] ) , { status : 200 } ) ,
32- ) ;
29+ . mockResolvedValueOnce ( pagedResponse ( [
30+ makeRawCommit ( "feat: add login" ) ,
31+ makeRawCommit ( "fix: typo in header" ) ,
32+ ] ) )
33+ . mockResolvedValueOnce ( pagedResponse ( [
34+ makeRawCommit ( "chore: update deps" ) ,
35+ ] ) ) ;
3336
3437 const result = await fetchCommitMessages ( "token" , "user" , [ "org/repo-a" , "org/repo-b" ] , range ) ;
3538
@@ -38,11 +41,24 @@ describe("fetchCommitMessages", () => {
3841 expect ( result [ 1 ] ) . toEqual ( { repo : "org/repo-b" , messages : [ "chore: update deps" ] } ) ;
3942 } ) ;
4043
44+ it ( "paginates through multiple pages" , async ( ) => {
45+ const page1 = Array . from ( { length : 100 } , ( _ , i ) => makeRawCommit ( `page1-${ i } ` ) ) ;
46+ const page2 = Array . from ( { length : 50 } , ( _ , i ) => makeRawCommit ( `page2-${ i } ` ) ) ;
47+
48+ vi . spyOn ( globalThis , "fetch" )
49+ . mockResolvedValueOnce ( pagedResponse ( page1 , "https://api.github.com/repos/org/repo/commits?page=2" ) )
50+ . mockResolvedValueOnce ( pagedResponse ( page2 ) ) ;
51+
52+ const result = await fetchCommitMessages ( "token" , "user" , [ "org/repo" ] , range ) ;
53+
54+ expect ( result [ 0 ] . messages ) . toHaveLength ( 150 ) ;
55+ expect ( result [ 0 ] . messages [ 0 ] ) . toBe ( "page1-0" ) ;
56+ expect ( result [ 0 ] . messages [ 100 ] ) . toBe ( "page2-0" ) ;
57+ } ) ;
58+
4159 it ( "extracts only the first line of multi-line commit messages" , async ( ) => {
4260 vi . spyOn ( globalThis , "fetch" ) . mockResolvedValueOnce (
43- new Response ( JSON . stringify ( [
44- makeRawCommit ( "feat: new feature\n\nLong description here\nMore details" ) ,
45- ] ) , { status : 200 } ) ,
61+ pagedResponse ( [ makeRawCommit ( "feat: new feature\n\nLong description here\nMore details" ) ] ) ,
4662 ) ;
4763
4864 const result = await fetchCommitMessages ( "token" , "user" , [ "org/repo" ] , range ) ;
@@ -53,7 +69,7 @@ describe("fetchCommitMessages", () => {
5369 it ( "truncates long commit messages to 200 characters" , async ( ) => {
5470 const longMessage = "a" . repeat ( 300 ) ;
5571 vi . spyOn ( globalThis , "fetch" ) . mockResolvedValueOnce (
56- new Response ( JSON . stringify ( [ makeRawCommit ( longMessage ) ] ) , { status : 200 } ) ,
72+ pagedResponse ( [ makeRawCommit ( longMessage ) ] ) ,
5773 ) ;
5874
5975 const result = await fetchCommitMessages ( "token" , "user" , [ "org/repo" ] , range ) ;
@@ -63,9 +79,7 @@ describe("fetchCommitMessages", () => {
6379 } ) ;
6480
6581 it ( "skips repos with no commits" , async ( ) => {
66- vi . spyOn ( globalThis , "fetch" ) . mockResolvedValueOnce (
67- new Response ( JSON . stringify ( [ ] ) , { status : 200 } ) ,
68- ) ;
82+ vi . spyOn ( globalThis , "fetch" ) . mockResolvedValueOnce ( pagedResponse ( [ ] ) ) ;
6983
7084 const result = await fetchCommitMessages ( "token" , "user" , [ "org/empty" ] , range ) ;
7185
@@ -92,32 +106,6 @@ describe("fetchCommitMessages", () => {
92106 expect ( result ) . toHaveLength ( 0 ) ;
93107 } ) ;
94108
95- it ( "respects per_page limit from API (10 per repo)" , async ( ) => {
96- // API honors per_page=10, so even if repo has more commits, only 10 are returned
97- const commits = Array . from ( { length : 10 } , ( _ , i ) => makeRawCommit ( `commit ${ i } ` ) ) ;
98- vi . spyOn ( globalThis , "fetch" ) . mockResolvedValueOnce (
99- new Response ( JSON . stringify ( commits ) , { status : 200 } ) ,
100- ) ;
101-
102- const result = await fetchCommitMessages ( "token" , "user" , [ "org/busy" ] , range ) ;
103-
104- expect ( result [ 0 ] . messages . length ) . toBe ( 10 ) ;
105- } ) ;
106-
107- it ( "caps total messages at 50 across all repos" , async ( ) => {
108- // 6 repos each returning 10 commits = 60 total, should be capped at 50
109- const commits = Array . from ( { length : 10 } , ( _ , i ) => makeRawCommit ( `msg ${ i } ` ) ) ;
110- vi . spyOn ( globalThis , "fetch" ) . mockImplementation (
111- ( ) => Promise . resolve ( new Response ( JSON . stringify ( commits ) , { status : 200 } ) ) ,
112- ) ;
113-
114- const repos = Array . from ( { length : 6 } , ( _ , i ) => `org/repo-${ i } ` ) ;
115- const result = await fetchCommitMessages ( "token" , "user" , repos , range ) ;
116-
117- const totalMsgs = result . reduce ( ( sum , r ) => sum + r . messages . length , 0 ) ;
118- expect ( totalMsgs ) . toBeLessThanOrEqual ( 50 ) ;
119- } ) ;
120-
121109 it ( "returns empty array for empty repos list" , async ( ) => {
122110 const result = await fetchCommitMessages ( "token" , "user" , [ ] , range ) ;
123111
@@ -129,9 +117,7 @@ describe("fetchCommitMessages", () => {
129117 . mockResolvedValueOnce (
130118 new Response ( "" , { status : 429 , headers : { "retry-after" : "0" } } ) ,
131119 )
132- . mockResolvedValueOnce (
133- new Response ( JSON . stringify ( [ makeRawCommit ( "after retry" ) ] ) , { status : 200 } ) ,
134- ) ;
120+ . mockResolvedValueOnce ( pagedResponse ( [ makeRawCommit ( "after retry" ) ] ) ) ;
135121
136122 const result = await fetchCommitMessages ( "token" , "user" , [ "org/repo" ] , range ) ;
137123
0 commit comments