66import requests
77
88from src .github_client import REST_API_VERSION , GitHubClient
9+ from src .http_link_header import next_link_from_header
910
1011
1112class _MemoryCache :
@@ -30,6 +31,49 @@ def test_rest_session_sets_explicit_api_version(self):
3031 client = GitHubClient ()
3132 assert client .session .headers ["X-GitHub-Api-Version" ] == REST_API_VERSION
3233
34+ def test_pagination_uses_linear_link_header_fallback (self , monkeypatch ):
35+ client = GitHubClient ()
36+
37+ class _Page :
38+ def __init__ (self , payload , link_header = "" ):
39+ self ._payload = payload
40+ self .links = {}
41+ self .headers = {"Link" : link_header }
42+
43+ def json (self ):
44+ return self ._payload
45+
46+ pages = iter (
47+ [
48+ _Page (
49+ [{"page" : 1 }],
50+ '<https://api.github.test/items?page=2>; rel="next", '
51+ '<https://api.github.test/items?page=2>; rel="last"' ,
52+ ),
53+ _Page ([{"page" : 2 }]),
54+ ]
55+ )
56+ requested = []
57+
58+ def _request (url , params = None ):
59+ requested .append ((url , params ))
60+ return next (pages )
61+
62+ monkeypatch .setattr (client , "_request" , _request )
63+
64+ assert client ._paginate (
65+ "https://api.github.test/items" , {"per_page" : 100 }
66+ ) == [{"page" : 1 }, {"page" : 2 }]
67+ assert requested == [
68+ ("https://api.github.test/items" , {"per_page" : 100 }),
69+ ("https://api.github.test/items?page=2" , {}),
70+ ]
71+
72+ def test_link_header_fallback_fails_closed_on_large_malformed_input (self ):
73+ malformed = "<" + "<=" * 100_000
74+
75+ assert next_link_from_header (malformed ) is None
76+
3377 def test_repo_list_cache_key_includes_owner_private_scope (self , monkeypatch ):
3478 cache = _MemoryCache ()
3579 client = GitHubClient (token = "secret" , cache = cache )
0 commit comments