Skip to content

Commit 902ba78

Browse files
fix(code-review): Add more checks to the code review checkrun (#105236)
Add additional toggle checks to the code review checkrun
1 parent 5df9ed2 commit 902ba78

File tree

2 files changed

+70
-28
lines changed

2 files changed

+70
-28
lines changed

src/sentry/seer/code_review/webhooks.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from pydantic import ValidationError
2020

2121
from sentry import features
22+
from sentry.constants import ENABLE_PR_REVIEW_TEST_GENERATION_DEFAULT, HIDE_AI_FEATURES_DEFAULT
2223
from sentry.models.organization import Organization
2324
from sentry.utils import metrics
2425

@@ -91,7 +92,23 @@ def _should_handle_github_check_run_event(organization: Organization, action: st
9192
"""
9293
Determine if the GitHub check_run event should be handled.
9394
"""
94-
if not features.has("organizations:code-review-beta", organization=organization):
95+
if action != GitHubCheckRunAction.REREQUESTED:
9596
return False
9697

97-
return action == GitHubCheckRunAction.REREQUESTED
98+
if not features.has("organizations:gen-ai-features", organization):
99+
return False
100+
101+
hide_ai_features = organization.get_option("sentry:hide_ai_features", HIDE_AI_FEATURES_DEFAULT)
102+
if hide_ai_features:
103+
return False
104+
105+
pr_review_test_generation_enabled = bool(
106+
organization.get_option(
107+
"sentry:enable_pr_review_test_generation",
108+
ENABLE_PR_REVIEW_TEST_GENERATION_DEFAULT,
109+
)
110+
)
111+
if not pr_review_test_generation_enabled:
112+
return False
113+
114+
return features.has("organizations:code-review-beta", organization)

tests/sentry/seer/code_review/test_webhooks.py

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
class CheckRunEventWebhookTest(GitHubWebhookTestCase):
1515
"""Integration tests for GitHub check_run webhook events."""
1616

17+
def _enable_code_review(self) -> None:
18+
"""Enable all required options for code review to work."""
19+
self.organization.update_option("sentry:enable_pr_review_test_generation", True)
20+
1721
def _send_check_run_event(self, event_data: bytes | str) -> HttpResponseBase:
1822
"""Helper to send check_run event with Pydantic validation."""
1923
self.event_dict = (
@@ -22,7 +26,6 @@ def _send_check_run_event(self, event_data: bytes | str) -> HttpResponseBase:
2226
repo_id = int(self.event_dict["repository"]["id"])
2327

2428
integration = self.create_github_integration()
25-
# Create a repository that matches the fixture's repository ID
2629
self.create_repo(
2730
project=self.project,
2831
provider="integrations:github",
@@ -34,20 +37,20 @@ def _send_check_run_event(self, event_data: bytes | str) -> HttpResponseBase:
3437
return response
3538

3639
@patch("sentry.seer.code_review.webhooks.process_github_webhook_event")
37-
@with_feature({"organizations:code-review-beta"})
40+
@with_feature({"organizations:gen-ai-features", "organizations:code-review-beta"})
3841
def test_base_case(self, mock_task: MagicMock) -> None:
3942
"""Test that rerequested action enqueues task with correct parameters."""
40-
with self.options({"coding_workflows.code_review.github.check_run.rerun.enabled": True}):
41-
self._send_check_run_event(CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE)
42-
43-
mock_task.delay.assert_called_once()
44-
call_kwargs = mock_task.delay.call_args[1]
45-
assert call_kwargs["original_run_id"] == self.event_dict["check_run"]["external_id"]
46-
assert call_kwargs["organization_id"] == self.organization.id
47-
assert call_kwargs["action"] == "rerequested"
48-
assert call_kwargs["html_url"] == self.event_dict["check_run"]["html_url"]
49-
assert "enqueued_at_str" in call_kwargs
50-
assert isinstance(call_kwargs["enqueued_at_str"], str)
43+
self._enable_code_review()
44+
self._send_check_run_event(CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE)
45+
46+
mock_task.delay.assert_called_once()
47+
call_kwargs = mock_task.delay.call_args[1]
48+
assert call_kwargs["original_run_id"] == self.event_dict["check_run"]["external_id"]
49+
assert call_kwargs["organization_id"] == self.organization.id
50+
assert call_kwargs["action"] == "rerequested"
51+
assert call_kwargs["html_url"] == self.event_dict["check_run"]["html_url"]
52+
assert "enqueued_at_str" in call_kwargs
53+
assert isinstance(call_kwargs["enqueued_at_str"], str)
5154

5255
@patch("sentry.seer.code_review.webhooks.process_github_webhook_event")
5356
def test_check_run_skips_when_ai_features_disabled(self, mock_task: MagicMock) -> None:
@@ -56,57 +59,55 @@ def test_check_run_skips_when_ai_features_disabled(self, mock_task: MagicMock) -
5659
mock_task.delay.assert_not_called()
5760

5861
@patch("sentry.seer.code_review.webhooks.process_github_webhook_event")
59-
@with_feature({"organizations:code-review-beta"})
62+
@with_feature({"organizations:gen-ai-features", "organizations:code-review-beta"})
6063
def test_check_run_fails_when_action_missing(self, mock_task: MagicMock) -> None:
6164
"""Test that missing action field is handled gracefully without KeyError."""
65+
self._enable_code_review()
6266
event_without_action = orjson.loads(CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE)
6367
del event_without_action["action"]
6468

6569
with patch("sentry.seer.code_review.webhooks.logger") as mock_logger:
6670
self._send_check_run_event(orjson.dumps(event_without_action))
67-
6871
mock_task.delay.assert_not_called()
6972
mock_logger.error.assert_called_once_with("github.webhook.check_run.missing-action")
7073

7174
@patch("sentry.seer.code_review.webhooks.process_github_webhook_event")
72-
@with_feature({"organizations:code-review-beta"})
75+
@with_feature({"organizations:gen-ai-features", "organizations:code-review-beta"})
7376
def test_check_run_fails_when_external_id_missing(self, mock_task: MagicMock) -> None:
7477
"""Test that missing external_id is handled gracefully."""
78+
self._enable_code_review()
7579
event_without_external_id = orjson.loads(CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE)
7680
del event_without_external_id["check_run"]["external_id"]
7781

7882
with patch("sentry.seer.code_review.webhooks.logger") as mock_logger:
7983
self._send_check_run_event(orjson.dumps(event_without_external_id))
80-
8184
mock_task.delay.assert_not_called()
82-
# Validation errors are logged with exception
8385
mock_logger.exception.assert_called_once()
8486
assert (
8587
"github.webhook.check_run.invalid-payload" in mock_logger.exception.call_args[0][0]
8688
)
8789

8890
@patch("sentry.seer.code_review.webhooks.process_github_webhook_event")
89-
@with_feature({"organizations:code-review-beta"})
91+
@with_feature({"organizations:gen-ai-features", "organizations:code-review-beta"})
9092
def test_check_run_fails_when_external_id_not_numeric(self, mock_task: MagicMock) -> None:
9193
"""Test that non-numeric external_id is handled gracefully."""
94+
self._enable_code_review()
9295
event_with_invalid_external_id = orjson.loads(CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE)
9396
event_with_invalid_external_id["check_run"]["external_id"] = "not-a-number"
9497

9598
with patch("sentry.seer.code_review.webhooks.logger") as mock_logger:
9699
self._send_check_run_event(orjson.dumps(event_with_invalid_external_id))
97-
98100
mock_task.delay.assert_not_called()
99-
# ValueError raised for non-numeric external_id
100101
mock_logger.exception.assert_called_once()
101102
assert (
102103
"github.webhook.check_run.invalid-payload" in mock_logger.exception.call_args[0][0]
103104
)
104105

105106
@patch("sentry.seer.code_review.webhooks.process_github_webhook_event")
106-
@with_feature({"organizations:code-review-beta"})
107+
@with_feature({"organizations:gen-ai-features", "organizations:code-review-beta"})
107108
def test_check_run_enqueues_task_for_processing(self, mock_task: MagicMock) -> None:
108109
"""Test that webhook successfully enqueues task for async processing."""
109-
110+
self._enable_code_review()
110111
self._send_check_run_event(CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE)
111112

112113
mock_task.delay.assert_called_once()
@@ -116,8 +117,32 @@ def test_check_run_enqueues_task_for_processing(self, mock_task: MagicMock) -> N
116117

117118
def test_check_run_without_integration_returns_204(self) -> None:
118119
"""Test that check_run events without integration return 204."""
119-
# Don't create an integration, just send the event
120120
response = self.send_github_webhook_event("check_run", CHECK_RUN_COMPLETED_EVENT_EXAMPLE)
121-
122-
# Should still return 204 even without integration
123121
assert response.status_code == 204
122+
123+
@patch("sentry.seer.code_review.webhooks.process_github_webhook_event")
124+
@with_feature({"organizations:gen-ai-features"})
125+
def test_check_run_skips_when_code_review_beta_flag_disabled(
126+
self, mock_task: MagicMock
127+
) -> None:
128+
"""Test that task is not enqueued when code-review-beta flag is off."""
129+
self._enable_code_review()
130+
self._send_check_run_event(CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE)
131+
mock_task.delay.assert_not_called()
132+
133+
@patch("sentry.seer.code_review.webhooks.process_github_webhook_event")
134+
@with_feature({"organizations:gen-ai-features"})
135+
def test_check_run_skips_when_pr_review_option_disabled(self, mock_task: MagicMock) -> None:
136+
"""Test that task is not enqueued when pr_review option is off."""
137+
self.organization.update_option("sentry:enable_pr_review_test_generation", False)
138+
self._send_check_run_event(CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE)
139+
mock_task.delay.assert_not_called()
140+
141+
@patch("sentry.seer.code_review.webhooks.process_github_webhook_event")
142+
@with_feature({"organizations:gen-ai-features", "organizations:code-review-beta"})
143+
def test_check_run_skips_when_hide_ai_features_enabled(self, mock_task: MagicMock) -> None:
144+
"""Test that task is not enqueued when hide_ai_features option is True."""
145+
self._enable_code_review()
146+
self.organization.update_option("sentry:hide_ai_features", True)
147+
self._send_check_run_event(CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE)
148+
mock_task.delay.assert_not_called()

0 commit comments

Comments
 (0)