1010import yaml
1111
1212from scripts .reviewer_bot_core import comment_routing_policy
13+ from scripts .reviewer_bot_lib .context import PrCommentAdmission
1314
1415
1516def _load_observer_contract_matrix () -> dict :
@@ -129,6 +130,16 @@ def test_pr_comment_router_normalizes_performed_via_app_without_raw_truthiness()
129130 assert "bool(comment.get('performed_via_github_app'))" not in workflow_text
130131
131132
133+ def _load_pr_comment_router_inline_namespace ():
134+ workflow_text = Path (".github/workflows/reviewer-bot-pr-comment-router.yml" ).read_text (encoding = "utf-8" )
135+ first_def = workflow_text .index ("def _classify_issue_comment_actor(" )
136+ start = workflow_text .rfind ("\n " , 0 , first_def ) + 1
137+ end = workflow_text .index ("\n \n performed_via_github_app =" , start )
138+ namespace = {}
139+ exec (textwrap .dedent (workflow_text [start :end ]), namespace )
140+ return namespace
141+
142+
132143@pytest .mark .parametrize (
133144 ("value" , "expected" ),
134145 [
@@ -172,11 +183,7 @@ def test_pr_comment_router_actor_classifier_matches_core_policy(
172183 installation_id ,
173184 performed_via_app ,
174185):
175- workflow_text = Path (".github/workflows/reviewer-bot-pr-comment-router.yml" ).read_text (encoding = "utf-8" )
176- start = workflow_text .index ("def _classify_issue_comment_actor(" )
177- end = workflow_text .index ("\n def _performed_via_github_app_truth" , start )
178- namespace = {}
179- exec (textwrap .dedent (workflow_text [start :end ]), namespace )
186+ namespace = _load_pr_comment_router_inline_namespace ()
180187
181188 request = SimpleNamespace (
182189 comment_user_type = comment_user_type ,
@@ -195,6 +202,142 @@ def test_pr_comment_router_actor_classifier_matches_core_policy(
195202 ) == comment_routing_policy .classify_issue_comment_actor (request )
196203
197204
205+ @pytest .mark .parametrize (
206+ "scenario" ,
207+ [
208+ {
209+ "name" : "same_repo_trusted_member" ,
210+ "comment_user_type" : "User" ,
211+ "comment_author" : "alice" ,
212+ "comment_sender_type" : "User" ,
213+ "installation_id" : None ,
214+ "performed_via_app" : False ,
215+ "comment_author_association" : "MEMBER" ,
216+ "pr_head_full_name" : "rustfoundation/safety-critical-rust-coding-guidelines" ,
217+ "pr_author" : "carol" ,
218+ "route_outcome" : comment_routing_policy .PrCommentRouterOutcome .TRUSTED_DIRECT ,
219+ },
220+ {
221+ "name" : "same_repo_untrusted_author_association" ,
222+ "comment_user_type" : "User" ,
223+ "comment_author" : "alice" ,
224+ "comment_sender_type" : "User" ,
225+ "installation_id" : None ,
226+ "performed_via_app" : False ,
227+ "comment_author_association" : "contributor" ,
228+ "pr_head_full_name" : "rustfoundation/safety-critical-rust-coding-guidelines" ,
229+ "pr_author" : "carol" ,
230+ "route_outcome" : comment_routing_policy .PrCommentRouterOutcome .TRUSTED_DIRECT ,
231+ },
232+ {
233+ "name" : "cross_repo_deferred" ,
234+ "comment_user_type" : "User" ,
235+ "comment_author" : "alice" ,
236+ "comment_sender_type" : "User" ,
237+ "installation_id" : None ,
238+ "performed_via_app" : False ,
239+ "comment_author_association" : "MEMBER" ,
240+ "pr_head_full_name" : "fork/example" ,
241+ "pr_author" : "carol" ,
242+ "route_outcome" : comment_routing_policy .PrCommentRouterOutcome .TRUSTED_DIRECT ,
243+ },
244+ {
245+ "name" : "dependabot_pr_author_deferred" ,
246+ "comment_user_type" : "User" ,
247+ "comment_author" : "alice" ,
248+ "comment_sender_type" : "User" ,
249+ "installation_id" : None ,
250+ "performed_via_app" : False ,
251+ "comment_author_association" : "OWNER" ,
252+ "pr_head_full_name" : "rustfoundation/safety-critical-rust-coding-guidelines" ,
253+ "pr_author" : "dependabot[bot]" ,
254+ "route_outcome" : comment_routing_policy .PrCommentRouterOutcome .TRUSTED_DIRECT ,
255+ },
256+ {
257+ "name" : "automation_actor_noop" ,
258+ "comment_user_type" : "User" ,
259+ "comment_author" : "alice" ,
260+ "comment_sender_type" : "Organization" ,
261+ "installation_id" : None ,
262+ "performed_via_app" : False ,
263+ "comment_author_association" : "MEMBER" ,
264+ "pr_head_full_name" : "rustfoundation/safety-critical-rust-coding-guidelines" ,
265+ "pr_author" : "carol" ,
266+ "route_outcome" : comment_routing_policy .PrCommentRouterOutcome .TRUSTED_DIRECT ,
267+ },
268+ {
269+ "name" : "pull_request_read_failed" ,
270+ "comment_user_type" : "User" ,
271+ "comment_author" : "alice" ,
272+ "comment_sender_type" : "User" ,
273+ "installation_id" : None ,
274+ "performed_via_app" : False ,
275+ "comment_author_association" : "MEMBER" ,
276+ "pr_head_full_name" : "" ,
277+ "pr_author" : "" ,
278+ "pull_request_read_failed" : True ,
279+ "route_outcome" : comment_routing_policy .PrCommentRouterOutcome .DEFERRED_RECONCILE ,
280+ },
281+ ],
282+ ids = lambda scenario : scenario ["name" ],
283+ )
284+ def test_pr_comment_router_route_outcome_matches_core_policy (scenario ):
285+ repo = "rustfoundation/safety-critical-rust-coding-guidelines"
286+ namespace = _load_pr_comment_router_inline_namespace ()
287+ request = SimpleNamespace (
288+ is_pull_request = True ,
289+ comment_user_type = scenario ["comment_user_type" ],
290+ comment_author = scenario ["comment_author" ],
291+ comment_sender_type = scenario ["comment_sender_type" ],
292+ comment_installation_id = scenario ["installation_id" ],
293+ comment_performed_via_github_app = scenario ["performed_via_app" ],
294+ comment_author_association = scenario ["comment_author_association" ],
295+ )
296+ pr_admission = PrCommentAdmission (
297+ route_outcome = scenario ["route_outcome" ],
298+ declared_trust_class = "pr_trusted_direct" ,
299+ github_repository = repo ,
300+ pr_head_full_name = scenario ["pr_head_full_name" ],
301+ pr_author = scenario ["pr_author" ],
302+ issue_state = "open" ,
303+ issue_labels = (),
304+ comment_author_id = 123 ,
305+ github_run_id = 1 ,
306+ github_run_attempt = 1 ,
307+ )
308+ actor_class = comment_routing_policy .classify_issue_comment_actor (request )
309+ processing_target = comment_routing_policy .classify_pr_comment_processing_target (
310+ request ,
311+ pr_admission ,
312+ actor_class = actor_class ,
313+ is_self_comment = False ,
314+ )
315+ expected = comment_routing_policy .route_issue_comment_trust (
316+ request ,
317+ pr_admission ,
318+ processing_target = processing_target ,
319+ )
320+ workflow_actor_class = namespace ["_classify_issue_comment_actor" ](
321+ scenario ["comment_user_type" ],
322+ scenario ["comment_author" ],
323+ scenario ["comment_sender_type" ],
324+ scenario ["installation_id" ],
325+ scenario ["performed_via_app" ],
326+ )
327+
328+ workflow_route = namespace ["_route_pr_comment_outcome" ](
329+ workflow_actor_class ,
330+ repo ,
331+ scenario ["pr_head_full_name" ],
332+ scenario ["pr_author" ],
333+ scenario ["comment_author_association" ],
334+ pull_request_read_failed = scenario .get ("pull_request_read_failed" , False ),
335+ )
336+
337+ assert workflow_actor_class == actor_class
338+ assert workflow_route == expected .value
339+
340+
198341def test_pr_comment_router_workflow_builds_payload_inline_without_bot_src_root ():
199342 workflow = Path (".github/workflows/reviewer-bot-pr-comment-router.yml" ).read_text (encoding = "utf-8" )
200343 assert "build_pr_comment_observer_payload" not in workflow
0 commit comments