22
33import pytest
44
5+ from app .config import GitHubIssuesSettings
56from app .database .models import Roles
7+ from app .services .github_issues import GitHubAPIError
68from app .services .github_roles import (
79 GitHubPermissionLookupError ,
810 GitHubRepositoryPermissionClient ,
911 GitHubRepositoryRoleResolver ,
1012 GITHUB_API_VERSION ,
1113 map_github_repository_role ,
14+ resolve_github_repository_role ,
1215)
1316
1417
@@ -32,14 +35,23 @@ def is_contributor(self, username: str) -> bool:
3235
3336
3437class FakeResponse :
35- def __init__ (self , status_code : int , payload : list | dict ):
38+ def __init__ (self , status_code : int , payload : list | dict , text : str = "" ):
3639 self .status_code = status_code
3740 self .payload = payload
41+ self .text = text
3842
3943 def json (self ):
4044 return self .payload
4145
4246
47+ class InvalidJsonResponse (FakeResponse ):
48+ def __init__ (self ):
49+ super ().__init__ (200 , {}, text = "not-json" )
50+
51+ def json (self ):
52+ raise ValueError ("invalid json" )
53+
54+
4355class RecordingSession :
4456 def __init__ (self , response : FakeResponse ):
4557 self .response = response
@@ -87,6 +99,21 @@ def test_repository_permission_client_defaults_to_openml_upload_testing(monkeypa
8799 )
88100
89101
102+ def test_repository_permission_client_uses_configured_repository ():
103+ session = RecordingSession (FakeResponse (200 , {"role_name" : "maintain" }))
104+ client = GitHubRepositoryPermissionClient (
105+ session ,
106+ owner = "openml" ,
107+ repo = "expert-checks" ,
108+ )
109+
110+ assert client .get_repository_permission ("octocat" ) == {"role_name" : "maintain" }
111+ assert session .requests [0 ]["url" ] == (
112+ "https://api.github.com/repos/openml/expert-checks"
113+ "/collaborators/octocat/permission"
114+ )
115+
116+
90117def test_repository_permission_client_applies_auth_header_when_token_provided ():
91118 session = RecordingSession (FakeResponse (200 , {"role_name" : "maintain" }))
92119 # We pass session explicitly to the client even with a token for testing
@@ -107,6 +134,34 @@ def test_repository_permission_client_wraps_http_errors_as_lookup_failures():
107134 raise AssertionError ("Expected GitHubPermissionLookupError" )
108135
109136
137+ def test_repository_permission_client_maps_404_to_no_permission ():
138+ session = RecordingSession (FakeResponse (404 , {"message" : "Not Found" }))
139+ client = GitHubRepositoryPermissionClient (session )
140+
141+ assert client .get_repository_permission ("octocat" ) == {
142+ "permission" : "none" ,
143+ "role_name" : "none" ,
144+ }
145+
146+
147+ def test_repository_permission_client_wraps_non_200_responses ():
148+ session = RecordingSession (
149+ FakeResponse (500 , {"message" : "Server Error" }, text = "server error" )
150+ )
151+ client = GitHubRepositoryPermissionClient (session )
152+
153+ with pytest .raises (GitHubPermissionLookupError , match = "GitHub returned 500" ):
154+ client .get_repository_permission ("octocat" )
155+
156+
157+ def test_repository_permission_client_wraps_invalid_json_responses ():
158+ session = RecordingSession (InvalidJsonResponse ())
159+ client = GitHubRepositoryPermissionClient (session )
160+
161+ with pytest .raises (GitHubPermissionLookupError , match = "invalid JSON" ):
162+ client .get_repository_permission ("octocat" )
163+
164+
110165@pytest .mark .parametrize (
111166 "permission_payload" ,
112167 [
@@ -169,3 +224,81 @@ def test_permission_lookup_failure_falls_back_to_user_and_logs(caplog):
169224
170225 assert role == Roles .USER
171226 assert "GitHub repository permission lookup failed" in caplog .text
227+
228+
229+ def test_resolve_role_uses_configured_permission_repository (monkeypatch ):
230+ class RecordingPermissionClient :
231+ def __init__ (
232+ self ,
233+ session = None ,
234+ * ,
235+ token = None ,
236+ owner = None ,
237+ repo = None ,
238+ ):
239+ self .session = session
240+ self .token = token
241+ self .owner = owner
242+ self .repo = repo
243+ created_clients .append (self )
244+
245+ def get_repository_permission (self , username : str ) -> dict [str , object ]:
246+ return {"role_name" : "maintain" }
247+
248+ created_clients : list [RecordingPermissionClient ] = []
249+
250+ monkeypatch .setattr (
251+ "app.services.github_roles.GitHubRepositoryPermissionClient" ,
252+ RecordingPermissionClient ,
253+ )
254+ monkeypatch .setattr (
255+ "app.services.github_issues.get_installation_token" ,
256+ lambda _settings : "gh-app-token" ,
257+ )
258+ settings = GitHubIssuesSettings (
259+ app_id = 123 ,
260+ install_id = 456 ,
261+ private_key = "test-key" ,
262+ owner = "issue-owner" ,
263+ repo = "issue-repo" ,
264+ permission_owner = "permission-owner" ,
265+ permission_repo = "permission-repo" ,
266+ )
267+
268+ role = resolve_github_repository_role (
269+ "octocat" , session = object (), settings = settings
270+ )
271+
272+ assert role == Roles .EXPERT
273+ assert len (created_clients ) == 1
274+ created_client = created_clients [0 ]
275+ assert created_client .session is None
276+ assert created_client .token == "gh-app-token"
277+ assert created_client .owner == "permission-owner"
278+ assert created_client .repo == "permission-repo"
279+
280+
281+ def test_resolve_role_falls_back_to_user_when_app_token_lookup_fails (monkeypatch ):
282+ session = RecordingSession (FakeResponse (200 , {"role_name" : "maintain" }))
283+
284+ def fail_token_lookup (_settings ):
285+ raise GitHubAPIError ("token unavailable" )
286+
287+ monkeypatch .setattr (
288+ "app.services.github_issues.get_installation_token" ,
289+ fail_token_lookup ,
290+ )
291+ settings = GitHubIssuesSettings (
292+ app_id = 123 ,
293+ install_id = 456 ,
294+ private_key = "test-key" ,
295+ owner = "issue-owner" ,
296+ repo = "issue-repo" ,
297+ permission_owner = "permission-owner" ,
298+ permission_repo = "permission-repo" ,
299+ )
300+
301+ role = resolve_github_repository_role ("octocat" , session = session , settings = settings )
302+
303+ assert role == Roles .USER
304+ assert session .requests == []
0 commit comments