Skip to content

Commit d9e3314

Browse files
committed
fix tests
1 parent 66e863e commit d9e3314

File tree

2 files changed

+67
-86
lines changed

2 files changed

+67
-86
lines changed

fixtures/github.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3548,7 +3548,6 @@
35483548
# Simplified example of a check_run rerequested action event
35493549
# Note: installation.id must match the external_id used in create_github_integration (default: "12345")
35503550
# Note: repository.id must match a Repository created for the organization (use create_repo in tests)
3551-
# Note: check_run events are linked to PRs via check_run.pull_requests - we use user.id as fallback for billing
35523551
CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE = b"""{
35533552
"action": "rerequested",
35543553
"installation": {"id": 12345},
@@ -3559,8 +3558,7 @@
35593558
},
35603559
"check_run": {
35613560
"external_id": "4663713",
3562-
"html_url": "https://github.com/test/repo/runs/4",
3563-
"pull_requests": []
3561+
"html_url": "https://github.com/test/repo/runs/4"
35643562
},
35653563
"user": {
35663564
"id": 12345678,

tests/sentry/seer/code_review/test_webhooks.py

Lines changed: 66 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
)
1515
from sentry.integrations.github.client import GitHubReaction
1616
from sentry.integrations.github.webhook_types import GithubWebhookType
17+
from sentry.integrations.models.integration import Integration
18+
from sentry.models.organizationcontributors import OrganizationContributors
1719
from sentry.seer.code_review.utils import ClientError
1820
from sentry.seer.code_review.webhooks.check_run import GitHubCheckRunAction
1921
from sentry.seer.code_review.webhooks.issue_comment import (
@@ -28,64 +30,57 @@
2830
process_github_webhook_event,
2931
)
3032
from sentry.testutils.cases import TestCase
31-
from sentry.testutils.helpers.features import with_feature
33+
from sentry.testutils.helpers.features import Feature
3234
from sentry.testutils.helpers.github import GitHubWebhookTestCase
3335

3436
CODE_REVIEW_FEATURES = {"organizations:gen-ai-features", "organizations:code-review-beta"}
3537

38+
DEFAULT_PR_AUTHOR_ID = "12345678"
39+
3640

3741
@patch("sentry.seer.code_review.billing.quotas.backend.check_seer_quota", return_value=True)
3842
class GitHubWebhookHelper(GitHubWebhookTestCase):
3943
"""Base class for GitHub webhook integration tests."""
4044

41-
def _enable_code_review(self) -> None:
42-
"""Enable all required options for code review to work."""
43-
self.organization.update_option("sentry:enable_pr_review_test_generation", True)
44-
45-
def _setup_billing(self, integration_id: int, sender_id: str) -> None:
46-
"""Set up billing by creating OrganizationContributors record."""
47-
from sentry.models.organizationcontributors import OrganizationContributors
48-
49-
OrganizationContributors.objects.get_or_create(
50-
organization_id=self.organization.id,
51-
integration_id=integration_id,
52-
external_identifier=sender_id,
53-
)
45+
github_integration: Integration | None = None
5446

5547
def _send_webhook_event(
56-
self, github_event: GithubWebhookType, event_data: bytes | str
48+
self,
49+
github_event: GithubWebhookType,
50+
event_data: bytes | str,
51+
enable_code_review: bool = False,
52+
features: set[str] | None = None,
5753
) -> HttpResponseBase:
58-
"""Helper to send a GitHub webhook event."""
54+
if enable_code_review:
55+
self.organization.update_option("sentry:enable_pr_review_test_generation", True)
56+
self.github_integration = self.create_github_integration()
57+
OrganizationContributors.objects.get_or_create(
58+
organization_id=self.organization.id,
59+
integration_id=self.github_integration.id,
60+
external_identifier=DEFAULT_PR_AUTHOR_ID,
61+
)
62+
5963
self.event_dict = (
6064
orjson.loads(event_data) if isinstance(event_data, (bytes, str)) else event_data
6165
)
6266
repo_id = int(self.event_dict["repository"]["id"])
6367

64-
integration = self.create_github_integration()
68+
integration = self.github_integration or self.create_github_integration()
69+
6570
self.create_repo(
6671
project=self.project,
6772
provider="integrations:github",
6873
external_id=repo_id,
6974
integration_id=integration.id,
7075
)
7176

72-
# Set up billing using PR author ID (matches _get_pr_author_id in handlers.py)
73-
pr_author_id = None
74-
# Check issue.user.id first (for issue comments)
75-
issue_user_id = self.event_dict.get("issue", {}).get("user", {}).get("id")
76-
if issue_user_id is not None:
77-
pr_author_id = str(issue_user_id)
78-
# Then check pull_request.user.id
79-
elif self.event_dict.get("pull_request", {}).get("user", {}).get("id") is not None:
80-
pr_author_id = str(self.event_dict["pull_request"]["user"]["id"])
81-
# Finally check user.id (fallback)
82-
elif self.event_dict.get("user", {}).get("id") is not None:
83-
pr_author_id = str(self.event_dict["user"]["id"])
84-
85-
if pr_author_id is not None:
86-
self._setup_billing(integration.id, pr_author_id)
87-
88-
response = self.send_github_webhook_event(github_event, event_data)
77+
if enable_code_review:
78+
features_to_enable = features if features is not None else CODE_REVIEW_FEATURES
79+
with Feature(features_to_enable):
80+
response = self.send_github_webhook_event(github_event, event_data)
81+
else:
82+
response = self.send_github_webhook_event(github_event, event_data)
83+
8984
assert response.status_code == 204
9085
return response
9186

@@ -94,13 +89,12 @@ class CheckRunEventWebhookTest(GitHubWebhookHelper):
9489
"""Integration tests for GitHub check_run webhook events."""
9590

9691
@patch("sentry.seer.code_review.webhooks.task.process_github_webhook_event")
97-
@with_feature(CODE_REVIEW_FEATURES)
9892
def test_base_case(self, mock_task: MagicMock) -> None:
9993
"""Test that rerequested action enqueues task with correct parameters."""
100-
self._enable_code_review()
10194
self._send_webhook_event(
10295
GithubWebhookType.CHECK_RUN,
10396
CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE,
97+
enable_code_review=True,
10498
)
10599

106100
mock_task.delay.assert_called_once()
@@ -117,44 +111,42 @@ def test_base_case(self, mock_task: MagicMock) -> None:
117111
assert isinstance(call_kwargs["enqueued_at_str"], str)
118112

119113
@patch("sentry.seer.code_review.webhooks.task.process_github_webhook_event")
120-
@with_feature(CODE_REVIEW_FEATURES)
121-
def test_check_run_skips_when_ai_features_disabled(self, mock_task: MagicMock) -> None:
122-
"""Test that the handler returns early when AI features are not enabled (even though the option is enabled)."""
114+
def test_check_run_skips_when_code_review_option_disabled(self, mock_task: MagicMock) -> None:
115+
"""Test that the handler skips when preflight requirements are not met."""
123116
self._send_webhook_event(
124117
GithubWebhookType.CHECK_RUN,
125118
CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE,
119+
enable_code_review=False,
126120
)
127121
mock_task.delay.assert_not_called()
128122

129123
@patch("sentry.seer.code_review.webhooks.task.process_github_webhook_event")
130-
@with_feature(CODE_REVIEW_FEATURES)
131124
def test_check_run_fails_when_action_missing(self, mock_task: MagicMock) -> None:
132125
"""Test that missing action field is handled gracefully without KeyError."""
133-
self._enable_code_review()
134126
event_without_action = orjson.loads(CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE)
135127
del event_without_action["action"]
136128

137129
with patch("sentry.seer.code_review.webhooks.check_run.logger") as mock_logger:
138130
self._send_webhook_event(
139131
GithubWebhookType.CHECK_RUN,
140132
orjson.dumps(event_without_action),
133+
enable_code_review=True,
141134
)
142135
mock_task.delay.assert_not_called()
143136
mock_logger.error.assert_called_once()
144137
assert "github.webhook.check_run.missing-action" in str(mock_logger.error.call_args)
145138

146139
@patch("sentry.seer.code_review.webhooks.task.process_github_webhook_event")
147-
@with_feature(CODE_REVIEW_FEATURES)
148140
def test_check_run_fails_when_external_id_missing(self, mock_task: MagicMock) -> None:
149141
"""Test that missing external_id is handled gracefully."""
150-
self._enable_code_review()
151142
event_without_external_id = orjson.loads(CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE)
152143
del event_without_external_id["check_run"]["external_id"]
153144

154145
with patch("sentry.seer.code_review.webhooks.check_run.logger") as mock_logger:
155146
self._send_webhook_event(
156147
GithubWebhookType.CHECK_RUN,
157148
orjson.dumps(event_without_external_id),
149+
enable_code_review=True,
158150
)
159151
mock_task.delay.assert_not_called()
160152
mock_logger.exception.assert_called_once()
@@ -163,17 +155,16 @@ def test_check_run_fails_when_external_id_missing(self, mock_task: MagicMock) ->
163155
)
164156

165157
@patch("sentry.seer.code_review.webhooks.task.process_github_webhook_event")
166-
@with_feature(CODE_REVIEW_FEATURES)
167158
def test_check_run_fails_when_external_id_not_numeric(self, mock_task: MagicMock) -> None:
168159
"""Test that non-numeric external_id is handled gracefully."""
169-
self._enable_code_review()
170160
event_with_invalid_external_id = orjson.loads(CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE)
171161
event_with_invalid_external_id["check_run"]["external_id"] = "not-a-number"
172162

173163
with patch("sentry.seer.code_review.webhooks.check_run.logger") as mock_logger:
174164
self._send_webhook_event(
175165
GithubWebhookType.CHECK_RUN,
176166
orjson.dumps(event_with_invalid_external_id),
167+
enable_code_review=True,
177168
)
178169
mock_task.delay.assert_not_called()
179170
mock_logger.exception.assert_called_once()
@@ -182,13 +173,12 @@ def test_check_run_fails_when_external_id_not_numeric(self, mock_task: MagicMock
182173
)
183174

184175
@patch("sentry.seer.code_review.webhooks.task.process_github_webhook_event")
185-
@with_feature(CODE_REVIEW_FEATURES)
186176
def test_check_run_enqueues_task_for_processing(self, mock_task: MagicMock) -> None:
187177
"""Test that webhook successfully enqueues task for async processing."""
188-
self._enable_code_review()
189178
self._send_webhook_event(
190179
GithubWebhookType.CHECK_RUN,
191180
CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE,
181+
enable_code_review=True,
192182
)
193183

194184
mock_task.delay.assert_called_once()
@@ -207,30 +197,17 @@ def test_check_run_without_integration_returns_204(self) -> None:
207197
)
208198
assert response.status_code == 204
209199

210-
@patch("sentry.seer.code_review.webhooks.task.process_github_webhook_event")
211-
@with_feature({"organizations:gen-ai-features"})
212-
def test_check_run_runs_when_code_review_beta_flag_disabled_but_pr_review_test_generation_enabled(
213-
self, mock_task: MagicMock
214-
) -> None:
215-
"""Test that task is enqueued when code-review-beta flag is off but pr_review_test_generation is enabled."""
216-
self._enable_code_review()
217-
self._send_webhook_event(
218-
GithubWebhookType.CHECK_RUN,
219-
CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE,
220-
)
221-
mock_task.delay.assert_called_once()
222-
223200
@patch("sentry.seer.code_review.utils.make_seer_request")
224-
@with_feature(CODE_REVIEW_FEATURES)
225201
def test_check_run_skips_when_hide_ai_features_enabled(
226202
self, mock_make_seer_request: MagicMock
227203
) -> None:
228204
"""Test that task is not enqueued when hide_ai_features option is True."""
229-
self._enable_code_review()
205+
# Enable hide_ai_features before sending - preflight will fail legal AI consent check
230206
self.organization.update_option("sentry:hide_ai_features", True)
231207
self._send_webhook_event(
232208
GithubWebhookType.CHECK_RUN,
233209
CHECK_RUN_REREQUESTED_ACTION_EVENT_EXAMPLE,
210+
enable_code_review=True,
234211
)
235212
mock_make_seer_request.assert_not_called()
236213

@@ -723,8 +700,18 @@ def test_false_cases(self) -> None:
723700
class IssueCommentEventWebhookTest(GitHubWebhookHelper):
724701
"""Integration tests for GitHub issue_comment webhook events."""
725702

726-
def _send_issue_comment_event(self, event_data: bytes | str) -> HttpResponseBase:
727-
return self._send_webhook_event(GithubWebhookType.ISSUE_COMMENT, event_data)
703+
def _send_issue_comment_event(
704+
self,
705+
event_data: bytes | str,
706+
enable_code_review: bool = False,
707+
features: set[str] | None = None,
708+
) -> HttpResponseBase:
709+
return self._send_webhook_event(
710+
GithubWebhookType.ISSUE_COMMENT,
711+
event_data,
712+
enable_code_review=enable_code_review,
713+
features=features,
714+
)
728715

729716
def _build_issue_comment_event(
730717
self, comment_body: str, comment_id: int | None = 123456789
@@ -757,72 +744,68 @@ def _build_issue_comment_event(
757744

758745
@patch("sentry.seer.code_review.webhooks.task.schedule_task")
759746
def test_skips_when_code_review_not_enabled(self, mock_schedule: MagicMock) -> None:
747+
"""Test that issue_comment skips when preflight requirements are not met."""
760748
event = self._build_issue_comment_event(f"Please {SENTRY_REVIEW_COMMAND} this PR")
761-
self._send_issue_comment_event(event)
749+
self._send_issue_comment_event(event, enable_code_review=False)
762750
mock_schedule.assert_not_called()
763751

764752
@patch("sentry.seer.code_review.webhooks.task.schedule_task")
765-
@with_feature({"organizations:gen-ai-features", "organizations:code-review-beta"})
766753
def test_skips_when_no_review_command(self, mock_schedule: MagicMock) -> None:
767-
self._enable_code_review()
754+
"""Test that issue_comment skips when the comment doesn't contain the review command."""
768755
event = self._build_issue_comment_event("This is a regular comment without the command")
769-
self._send_issue_comment_event(event)
756+
self._send_issue_comment_event(event, enable_code_review=True)
770757
mock_schedule.assert_not_called()
771758

772759
@patch("sentry.seer.code_review.webhooks.task.schedule_task")
773-
@with_feature({"organizations:gen-ai-features"})
774760
def test_runs_when_code_review_beta_flag_disabled_but_pr_review_test_generation_enabled(
775761
self, mock_schedule: MagicMock
776762
) -> None:
777-
with self.options(
778-
{"organizations:code-review-beta": False, "github.webhook.issue-comment": False}
779-
):
780-
self.organization.update_option("sentry:enable_pr_review_test_generation", True)
763+
"""Test that code review works via legacy option even without the beta feature flag."""
764+
# Only enable gen-ai-features flag, not code-review-beta
765+
with self.options({"github.webhook.issue-comment": False}):
781766
event = self._build_issue_comment_event(f"Please {SENTRY_REVIEW_COMMAND} this PR")
782-
self._send_issue_comment_event(event)
767+
self._send_issue_comment_event(
768+
event,
769+
enable_code_review=True,
770+
features={"organizations:gen-ai-features"},
771+
)
783772
mock_schedule.assert_called_once()
784773

785774
@patch("sentry.seer.code_review.webhooks.task.make_seer_request")
786775
@patch("sentry.integrations.github.client.GitHubApiClient.create_comment_reaction")
787-
@with_feature({"organizations:gen-ai-features", "organizations:code-review-beta"})
788776
def test_adds_reaction_and_forwards_when_valid(
789777
self, mock_create_reaction: MagicMock, mock_seer: MagicMock
790778
) -> None:
791-
self._enable_code_review()
792779
with self.options({"github.webhook.issue-comment": False}):
793780
event = self._build_issue_comment_event(f"Please {SENTRY_REVIEW_COMMAND} this PR")
794781

795782
with self.tasks():
796-
self._send_issue_comment_event(event)
783+
self._send_issue_comment_event(event, enable_code_review=True)
797784

798785
mock_create_reaction.assert_called_once()
799786
mock_seer.assert_called_once()
800787

801788
@patch("sentry.seer.code_review.webhooks.issue_comment._add_eyes_reaction_to_comment")
802789
@patch("sentry.seer.code_review.webhooks.task.schedule_task")
803-
@with_feature({"organizations:gen-ai-features", "organizations:code-review-beta"})
804790
def test_skips_reaction_when_no_comment_id(
805791
self, mock_schedule: MagicMock, mock_reaction: MagicMock
806792
) -> None:
807-
self._enable_code_review()
808793
with self.options({"github.webhook.issue-comment": False}):
809794
event = self._build_issue_comment_event(SENTRY_REVIEW_COMMAND, comment_id=None)
810-
self._send_issue_comment_event(event)
795+
self._send_issue_comment_event(event, enable_code_review=True)
811796

812797
mock_reaction.assert_not_called()
813798
mock_schedule.assert_called_once()
814799

815800
@patch("sentry.seer.code_review.webhooks.issue_comment._add_eyes_reaction_to_comment")
816801
@patch("sentry.seer.code_review.webhooks.task.schedule_task")
817-
@with_feature({"organizations:gen-ai-features", "organizations:code-review-beta"})
818802
def test_skips_processing_when_option_is_true(
819803
self, mock_schedule: MagicMock, mock_reaction: MagicMock
820804
) -> None:
821805
"""Test that when github.webhook.issue-comment option is True (default), no processing occurs."""
822-
self._enable_code_review()
823806
with self.options({"github.webhook.issue-comment": True}):
824807
event = self._build_issue_comment_event(f"Please {SENTRY_REVIEW_COMMAND} this PR")
825-
self._send_issue_comment_event(event)
808+
self._send_issue_comment_event(event, enable_code_review=True)
826809

827810
mock_reaction.assert_not_called()
828811
mock_schedule.assert_not_called()

0 commit comments

Comments
 (0)