1
1
from pathlib import Path
2
- from typing import Any , Optional
2
+ from typing import Any
3
3
from unittest import mock
4
4
from unittest .mock import MagicMock , call , patch
5
5
6
6
import operatorcert .entrypoints .check_permissions as check_permissions
7
7
import pytest
8
- from github import UnknownObjectException
8
+ from github import GithubException , UnknownObjectException
9
9
from operatorcert .operator_repo import Repo as OperatorRepo
10
10
from tests .operator_repo import bundle_files , create_files
11
11
@@ -133,20 +133,41 @@ def _mock_label(name: str) -> MagicMock:
133
133
134
134
135
135
@pytest .mark .parametrize (
136
- "is_org_member, is_partner, permission_partner, permission_community, permission_partner_called, permission_community_called, expected_result" ,
136
+ "is_org_member, is_partner, owner_can_write, permission_partner, permission_community, permission_partner_called, permission_community_called, expected_result" ,
137
137
[
138
- pytest .param (True , False , False , False , False , False , True , id = "org member" ),
139
138
pytest .param (
140
- False , True , True , False , True , False , True , id = "partner - approved "
139
+ True , False , False , False , False , False , False , True , id = "org member "
141
140
),
142
141
pytest .param (
143
- False , True , False , False , True , False , False , id = "partner - denied "
142
+ False , True , False , True , False , True , False , True , id = "partner - approved "
144
143
),
145
144
pytest .param (
146
- False , False , False , True , False , True , True , id = "community - approved "
145
+ False , True , False , False , False , True , False , False , id = "partner - denied "
147
146
),
148
147
pytest .param (
149
- False , False , False , False , False , True , False , id = "community - denied"
148
+ False , False , True , False , False , False , True , True , id = "owner - approved"
149
+ ),
150
+ pytest .param (
151
+ False ,
152
+ False ,
153
+ False ,
154
+ False ,
155
+ True ,
156
+ False ,
157
+ True ,
158
+ True ,
159
+ id = "community - approved" ,
160
+ ),
161
+ pytest .param (
162
+ False ,
163
+ False ,
164
+ False ,
165
+ False ,
166
+ False ,
167
+ False ,
168
+ True ,
169
+ False ,
170
+ id = "community - denied" ,
150
171
),
151
172
],
152
173
)
@@ -158,14 +179,17 @@ def _mock_label(name: str) -> MagicMock:
158
179
)
159
180
@patch ("operatorcert.entrypoints.check_permissions.OperatorReview.is_partner" )
160
181
@patch ("operatorcert.entrypoints.check_permissions.OperatorReview.is_org_member" )
182
+ @patch ("operatorcert.entrypoints.check_permissions.OperatorReview.pr_owner_can_write" )
161
183
def test_OperatorReview_check_permissions (
184
+ mock_pr_owner_can_write : MagicMock ,
162
185
mock_is_org_member : MagicMock ,
163
186
mock_is_partner : MagicMock ,
164
187
mock_check_permission_for_partner : MagicMock ,
165
188
mock_check_permission_for_community : MagicMock ,
166
189
review_community : check_permissions .OperatorReview ,
167
190
is_org_member : bool ,
168
191
is_partner : bool ,
192
+ owner_can_write : bool ,
169
193
permission_partner : bool ,
170
194
permission_community : bool ,
171
195
permission_partner_called : bool ,
@@ -174,6 +198,7 @@ def test_OperatorReview_check_permissions(
174
198
) -> None :
175
199
mock_is_org_member .return_value = is_org_member
176
200
mock_is_partner .return_value = is_partner
201
+ mock_pr_owner_can_write .return_value = owner_can_write
177
202
mock_check_permission_for_partner .return_value = permission_partner
178
203
mock_check_permission_for_community .return_value = permission_community
179
204
assert review_community .check_permissions () == expected_result
@@ -216,6 +241,29 @@ def test_OperatorReview_is_org_member(
216
241
assert review_community .is_org_member () == False
217
242
218
243
244
+ @patch ("operatorcert.entrypoints.check_permissions.Github" )
245
+ @patch ("operatorcert.entrypoints.check_permissions.Auth.Token" )
246
+ def test_OperatorReview_pr_owner_can_write (
247
+ mock_token : MagicMock ,
248
+ mock_github : MagicMock ,
249
+ review_community : check_permissions .OperatorReview ,
250
+ ) -> None :
251
+ mock_repo = MagicMock ()
252
+ mock_github .return_value .get_repo .return_value = mock_repo
253
+
254
+ # User can write to the repository
255
+ mock_repo .get_collaborator_permission .return_value = "write"
256
+ assert review_community .pr_owner_can_write () == True
257
+
258
+ # User cannot write to the repository
259
+ mock_repo .get_collaborator_permission .return_value = "read"
260
+ assert review_community .pr_owner_can_write () == False
261
+
262
+ # Cannot get collaborator permission
263
+ mock_repo .get_collaborator_permission .side_effect = GithubException (404 , "" , {})
264
+ assert review_community .pr_owner_can_write () == False
265
+
266
+
219
267
@pytest .mark .parametrize (
220
268
["project" , "valid" ],
221
269
[
@@ -331,10 +379,13 @@ def test_OperatorReview_request_review_from_maintainers(
331
379
[
332
380
"gh" ,
333
381
"pr" ,
334
- "edit " ,
382
+ "comment " ,
335
383
review_community .pull_request_url ,
336
- "--add-reviewer" ,
337
- "maintainer1,maintainer2" ,
384
+ "--body" ,
385
+ "This PR requires a review from repository maintainers.\n "
386
+ f"@maintainer1, @maintainer2: please review the PR and approve it with an "
387
+ "`approved` label if the pipeline is still running or merge the PR "
388
+ "directly after review if the pipeline already passed successfully." ,
338
389
]
339
390
)
340
391
@@ -446,8 +497,27 @@ def test_check_permissions(
446
497
)
447
498
448
499
500
+ @patch ("operatorcert.entrypoints.check_permissions.Github" )
501
+ @patch ("operatorcert.entrypoints.check_permissions.Auth.Token" )
502
+ @patch ("operatorcert.entrypoints.check_permissions.add_labels_to_pull_request" )
503
+ def test_add_label_approved (
504
+ mock_add_labels : MagicMock ,
505
+ mock_token : MagicMock ,
506
+ mock_github : MagicMock ,
507
+ ) -> None :
508
+ mock_repo = MagicMock ()
509
+ mock_github .return_value .get_repo .return_value = mock_repo
510
+ mock_pull = MagicMock ()
511
+ mock_repo .get_pull .return_value = mock_pull
512
+
513
+ check_permissions .add_label_approved ("https://github.com/my-org/repo-123/pulls/1" )
514
+ mock_github .return_value .get_repo .assert_has_calls ([call ("my-org/repo-123" )])
515
+ mock_repo .get_pull .assert_called_once_with (1 )
516
+ mock_add_labels .assert_called_once_with (mock_pull , ["approved" ])
517
+
518
+
449
519
@patch ("operatorcert.entrypoints.check_permissions.json.dump" )
450
- @patch ("operatorcert.entrypoints.check_permissions.run_command " )
520
+ @patch ("operatorcert.entrypoints.check_permissions.add_label_approved " )
451
521
@patch ("operatorcert.entrypoints.check_permissions.check_permissions" )
452
522
@patch ("operatorcert.entrypoints.check_permissions.OperatorRepo" )
453
523
@patch ("operatorcert.entrypoints.check_permissions.setup_logger" )
@@ -457,7 +527,7 @@ def test_main(
457
527
mock_setup_logger : MagicMock ,
458
528
mock_operator_repo : MagicMock ,
459
529
mock_check_permissions : MagicMock ,
460
- mock_run_command : MagicMock ,
530
+ mock_add_label_approved : MagicMock ,
461
531
mock_json_dump : MagicMock ,
462
532
monkeypatch : Any ,
463
533
tmp_path : Path ,
@@ -480,9 +550,7 @@ def test_main(
480
550
check_permissions .main ()
481
551
482
552
mock_check_permissions .assert_called_once_with (base_repo , head_repo , args )
483
- mock_run_command .assert_called_once_with (
484
- ["gh" , "pr" , "review" , args .pull_request_url , "--approve" ]
485
- )
553
+ mock_add_label_approved .assert_called_once_with (args .pull_request_url )
486
554
487
555
expected_output = {
488
556
"approved" : mock_check_permissions .return_value ,
0 commit comments