Files
2024-02-15 17:01:50 +00:00

752 lines
30 KiB
Python

from datetime import datetime, timedelta, timezone
from unittest.mock import patch
import pytest
import responses
from django.utils import timezone as django_timezone
from sentry.integrations.github.integration import GitHubIntegrationProvider
from sentry.models.commit import Commit
from sentry.models.group import Group
from sentry.models.groupowner import GroupOwner, GroupOwnerType
from sentry.models.options.organization_option import OrganizationOption
from sentry.models.project import Project
from sentry.models.pullrequest import (
CommentType,
PullRequest,
PullRequestComment,
PullRequestCommit,
)
from sentry.models.repository import Repository
from sentry.shared_integrations.exceptions import ApiError
from sentry.tasks.commit_context import DEBOUNCE_PR_COMMENT_CACHE_KEY
from sentry.tasks.integrations.github.pr_comment import (
format_comment,
get_comment_contents,
get_top_5_issues_by_count,
github_comment_reactions,
github_comment_workflow,
pr_to_issue_query,
)
from sentry.tasks.integrations.github.utils import PullRequestIssue
from sentry.testutils.cases import IntegrationTestCase, SnubaTestCase, TestCase
from sentry.testutils.helpers.datetime import before_now, freeze_time, iso_format
from sentry.testutils.silo import region_silo_test
from sentry.testutils.skips import requires_snuba
from sentry.utils.cache import cache
pytestmark = [requires_snuba]
class GithubCommentTestCase(IntegrationTestCase):
provider = GitHubIntegrationProvider
base_url = "https://api.github.com"
def setUp(self):
super().setUp()
self.another_integration = self.create_integration(
organization=self.organization, external_id="1", provider="gitlab"
)
self.another_org_user = self.create_user("foo@localhost")
self.another_organization = self.create_organization(
name="Foobar", owner=self.another_org_user
)
self.another_team = self.create_team(organization=self.organization, name="Mariachi Band")
self.another_org_project = self.create_project(
organization=self.another_organization, teams=[self.another_team], name="Bengal"
)
self.another_org_integration = self.create_integration(
organization=self.another_organization, external_id="1", provider="github"
)
self.user_to_commit_author_map = {
self.user: self.create_commit_author(project=self.project, user=self.user),
self.another_org_user: self.create_commit_author(
project=self.another_org_project, user=self.another_org_user
),
}
self.gh_repo = self.create_repo(
name="getsentry/sentry",
provider="integrations:github",
integration_id=self.integration.id,
project=self.project,
url="https://github.com/getsentry/sentry",
)
self.not_gh_repo = self.create_repo(
name="getsentry/santry",
provider="integrations:gitlab",
integration_id=self.another_integration.id,
project=self.project,
url="https://gitlab.com/getsentry/santry",
)
self.another_org_repo = self.create_repo(
name="getsentry/sentree",
provider="integrations:github",
integration_id=self.another_org_integration.id,
project=self.another_org_project,
url="https://github.com/getsentry/sentree",
)
self.pr_key = 1
self.commit_sha = 1
self.fingerprint = 1
patch("sentry.integrations.github.client.get_jwt", return_value=b"jwt_token_1").start()
def add_commit_to_repo(self, repo, user, project):
if user not in self.user_to_commit_author_map:
self.user_to_commit_author_map[user] = self.create_commit_author(
project=repo.project, user=user
)
commit = self.create_commit(
project=project,
repo=repo,
author=self.user_to_commit_author_map[user],
key=str(self.commit_sha),
message=str(self.commit_sha),
)
self.commit_sha += 1
return commit
def add_pr_to_commit(self, commit: Commit, date_added=None):
if date_added is None:
date_added = iso_format(before_now(minutes=1))
pr = PullRequest.objects.create(
organization_id=commit.organization_id,
repository_id=commit.repository_id,
key=str(self.pr_key),
author=commit.author,
message="foo",
title="bar",
merge_commit_sha=commit.key,
date_added=date_added,
)
self.pr_key += 1
self.add_branch_commit_to_pr(commit, pr)
return pr
def add_branch_commit_to_pr(self, commit: Commit, pr: PullRequest):
pr_commit = PullRequestCommit.objects.create(pull_request=pr, commit=commit)
return pr_commit
def add_groupowner_to_commit(self, commit: Commit, project, user):
event = self.store_event(
data={
"message": f"issue {self.fingerprint}",
"culprit": f"issue{self.fingerprint}",
"fingerprint": [f"issue{self.fingerprint}"],
},
project_id=project.id,
)
self.fingerprint += 1
groupowner = GroupOwner.objects.create(
group=event.group,
user_id=user.id,
project=project,
organization_id=commit.organization_id,
type=GroupOwnerType.SUSPECT_COMMIT.value,
context={"commitId": commit.id},
)
return groupowner
def create_pr_issues(self, gh_repo=None):
if gh_repo is None:
gh_repo = self.gh_repo
commit_1 = self.add_commit_to_repo(gh_repo, self.user, self.project)
pr = self.add_pr_to_commit(commit_1)
self.add_groupowner_to_commit(commit_1, self.project, self.user)
self.add_groupowner_to_commit(commit_1, self.another_org_project, self.another_org_user)
return pr
@region_silo_test
class TestPrToIssueQuery(GithubCommentTestCase):
def test_simple(self):
"""one pr with one issue"""
commit = self.add_commit_to_repo(self.gh_repo, self.user, self.project)
pr = self.add_pr_to_commit(commit)
groupowner = self.add_groupowner_to_commit(commit, self.project, self.user)
results = pr_to_issue_query(pr.id)
assert results[0] == (self.gh_repo.id, pr.key, self.organization.id, [groupowner.group_id])
def test_multiple_issues(self):
"""one pr with multiple issues"""
commit = self.add_commit_to_repo(self.gh_repo, self.user, self.project)
pr = self.add_pr_to_commit(commit)
groupowner_1 = self.add_groupowner_to_commit(commit, self.project, self.user)
groupowner_2 = self.add_groupowner_to_commit(commit, self.project, self.user)
groupowner_3 = self.add_groupowner_to_commit(commit, self.project, self.user)
results = pr_to_issue_query(pr.id)
assert results[0][0:3] == (self.gh_repo.id, pr.key, self.organization.id)
assert (
groupowner_1.group_id in results[0][3]
and groupowner_2.group_id in results[0][3]
and groupowner_3.group_id in results[0][3]
)
def test_multiple_prs(self):
"""multiple eligible PRs with one issue each"""
commit_1 = self.add_commit_to_repo(self.gh_repo, self.user, self.project)
commit_2 = self.add_commit_to_repo(self.gh_repo, self.user, self.project)
pr_1 = self.add_pr_to_commit(commit_1)
pr_2 = self.add_pr_to_commit(commit_2)
groupowner_1 = self.add_groupowner_to_commit(commit_1, self.project, self.user)
groupowner_2 = self.add_groupowner_to_commit(commit_2, self.project, self.user)
results = pr_to_issue_query(pr_1.id)
assert results[0] == (
self.gh_repo.id,
pr_1.key,
self.organization.id,
[groupowner_1.group_id],
)
results = pr_to_issue_query(pr_2.id)
assert results[0] == (
self.gh_repo.id,
pr_2.key,
self.organization.id,
[groupowner_2.group_id],
)
def test_multiple_commits(self):
"""Multiple eligible commits with one issue each"""
commit_1 = self.add_commit_to_repo(self.gh_repo, self.user, self.project)
commit_2 = self.add_commit_to_repo(self.gh_repo, self.user, self.project)
pr = self.add_pr_to_commit(commit_1)
self.add_branch_commit_to_pr(commit_2, pr)
groupowner_1 = self.add_groupowner_to_commit(commit_1, self.project, self.user)
groupowner_2 = self.add_groupowner_to_commit(commit_2, self.project, self.user)
results = pr_to_issue_query(pr.id)
assert results[0] == (
self.gh_repo.id,
pr.key,
self.organization.id,
[groupowner_1.group_id, groupowner_2.group_id],
)
@region_silo_test
class TestTop5IssuesByCount(TestCase, SnubaTestCase):
def test_simple(self):
group1 = [
self.store_event(
{"fingerprint": ["group-1"], "timestamp": iso_format(before_now(days=1))},
project_id=self.project.id,
)
for _ in range(3)
][0].group.id
group2 = [
self.store_event(
{"fingerprint": ["group-2"], "timestamp": iso_format(before_now(days=1))},
project_id=self.project.id,
)
for _ in range(6)
][0].group.id
group3 = [
self.store_event(
{"fingerprint": ["group-3"], "timestamp": iso_format(before_now(days=1))},
project_id=self.project.id,
)
for _ in range(4)
][0].group.id
res = get_top_5_issues_by_count([group1, group2, group3], self.project)
assert [issue["group_id"] for issue in res] == [group2, group3, group1]
def test_over_5_issues(self):
issue_ids = [
self.store_event(
{"fingerprint": [f"group-{idx}"], "timestamp": iso_format(before_now(days=1))},
project_id=self.project.id,
).group.id
for idx in range(6)
]
res = get_top_5_issues_by_count(issue_ids, self.project)
assert len(res) == 5
@region_silo_test
class TestCommentBuilderQueries(GithubCommentTestCase):
def test_simple(self):
ev1 = self.store_event(
data={"message": "issue 1", "culprit": "issue1", "fingerprint": ["group-1"]},
project_id=self.project.id,
)
assert ev1.group is not None
ev2 = self.store_event(
data={"message": "issue 2", "culprit": "issue2", "fingerprint": ["group-2"]},
project_id=self.project.id,
)
assert ev2.group is not None
ev3 = self.store_event(
data={"message": "issue 3", "culprit": "issue3", "fingerprint": ["group-3"]},
project_id=self.project.id,
)
assert ev3.group is not None
comment_contents = get_comment_contents([ev1.group.id, ev2.group.id, ev3.group.id])
assert (
PullRequestIssue(
title="issue 1",
subtitle="issue1",
url=f"http://testserver/organizations/{self.organization.slug}/issues/{ev1.group.id}/",
)
in comment_contents
)
assert (
PullRequestIssue(
title="issue 2",
subtitle="issue2",
url=f"http://testserver/organizations/{self.organization.slug}/issues/{ev2.group.id}/",
)
in comment_contents
)
assert (
PullRequestIssue(
title="issue 3",
subtitle="issue3",
url=f"http://testserver/organizations/{self.organization.slug}/issues/{ev3.group.id}/",
)
in comment_contents
)
@region_silo_test
class TestFormatComment(TestCase):
def test_format_comment(self):
issues = [
PullRequestIssue(
title="TypeError",
subtitle="sentry.tasks.derive_code_mappings.derive_code_mappings",
url="https://sentry.sentry.io/issues/",
),
PullRequestIssue(
title="KafkaException",
subtitle="query_subscription_consumer_process_message",
url="https://sentry.sentry.io/stats/",
),
]
formatted_comment = format_comment(issues)
expected_comment = "## Suspect Issues\nThis pull request was deployed and Sentry observed the following issues:\n\n- ‼️ **TypeError** `sentry.tasks.derive_code_mappings.derive_code_m...` [View Issue](https://sentry.sentry.io/issues/?referrer=github-pr-bot)\n- ‼️ **KafkaException** `query_subscription_consumer_process_message` [View Issue](https://sentry.sentry.io/stats/?referrer=github-pr-bot)\n\n<sub>Did you find this useful? React with a 👍 or 👎</sub>"
assert formatted_comment == expected_comment
@region_silo_test
class TestCommentWorkflow(GithubCommentTestCase):
def setUp(self):
super().setUp()
self.user_id = "user_1"
self.app_id = "app_1"
self.pr = self.create_pr_issues()
self.cache_key = DEBOUNCE_PR_COMMENT_CACHE_KEY(self.pr.id)
@patch("sentry.tasks.integrations.github.pr_comment.get_top_5_issues_by_count")
@patch("sentry.tasks.integrations.github.utils.metrics")
@responses.activate
def test_comment_workflow(self, mock_metrics, mock_issues):
groups = [g.id for g in Group.objects.all()]
titles = [g.title for g in Group.objects.all()]
culprits = [g.culprit for g in Group.objects.all()]
mock_issues.return_value = [{"group_id": id, "event_count": 10} for id in groups]
responses.add(
responses.POST,
self.base_url + "/repos/getsentry/sentry/issues/1/comments",
json={"id": 1},
headers={"X-Ratelimit-Limit": "60", "X-Ratelimit-Remaining": "59"},
)
github_comment_workflow(self.pr.id, self.project.id)
assert (
responses.calls[0].request.body
== f'{{"body": "## Suspect Issues\\nThis pull request was deployed and Sentry observed the following issues:\\n\\n- \\u203c\\ufe0f **{titles[0]}** `{culprits[0]}` [View Issue](http://testserver/organizations/foo/issues/{groups[0]}/?referrer=github-pr-bot)\\n- \\u203c\\ufe0f **{titles[1]}** `{culprits[1]}` [View Issue](http://testserver/organizations/foobar/issues/{groups[1]}/?referrer=github-pr-bot)\\n\\n<sub>Did you find this useful? React with a \\ud83d\\udc4d or \\ud83d\\udc4e</sub>"}}'.encode()
)
pull_request_comment_query = PullRequestComment.objects.all()
assert len(pull_request_comment_query) == 1
assert pull_request_comment_query[0].external_id == 1
assert pull_request_comment_query[0].comment_type == CommentType.MERGED_PR
mock_metrics.incr.assert_called_with("github_pr_comment.comment_created")
@patch("sentry.tasks.integrations.github.pr_comment.get_top_5_issues_by_count")
@patch("sentry.tasks.integrations.github.utils.metrics")
@responses.activate
@freeze_time(datetime(2023, 6, 8, 0, 0, 0, tzinfo=timezone.utc))
def test_comment_workflow_updates_comment(self, mock_metrics, mock_issues):
groups = [g.id for g in Group.objects.all()]
mock_issues.return_value = [{"group_id": id, "event_count": 10} for id in groups]
pull_request_comment = PullRequestComment.objects.create(
external_id=1,
pull_request_id=self.pr.id,
created_at=django_timezone.now() - timedelta(hours=1),
updated_at=django_timezone.now() - timedelta(hours=1),
group_ids=[1, 2, 3, 4],
)
# An Open PR comment should not affect the rest of the test as the filter should ignore it.
PullRequestComment.objects.create(
external_id=2,
pull_request_id=self.pr.id,
created_at=django_timezone.now() - timedelta(hours=1),
updated_at=django_timezone.now() - timedelta(hours=1),
group_ids=[],
comment_type=CommentType.OPEN_PR,
)
responses.add(
responses.PATCH,
self.base_url + "/repos/getsentry/sentry/issues/comments/1",
json={"id": 1},
headers={"X-Ratelimit-Limit": "60", "X-Ratelimit-Remaining": "59"},
)
github_comment_workflow(self.pr.id, self.project.id)
assert (
responses.calls[0].request.body
== f'{{"body": "## Suspect Issues\\nThis pull request was deployed and Sentry observed the following issues:\\n\\n- \\u203c\\ufe0f **issue 1** `issue1` [View Issue](http://testserver/organizations/foo/issues/{groups[0]}/?referrer=github-pr-bot)\\n- \\u203c\\ufe0f **issue 2** `issue2` [View Issue](http://testserver/organizations/foobar/issues/{groups[1]}/?referrer=github-pr-bot)\\n\\n<sub>Did you find this useful? React with a \\ud83d\\udc4d or \\ud83d\\udc4e</sub>"}}'.encode()
)
pull_request_comment.refresh_from_db()
assert pull_request_comment.group_ids == [g.id for g in Group.objects.all()]
assert pull_request_comment.updated_at == django_timezone.now()
mock_metrics.incr.assert_called_with("github_pr_comment.comment_updated")
@patch("sentry.tasks.integrations.github.pr_comment.get_top_5_issues_by_count")
@patch("sentry.tasks.integrations.github.pr_comment.metrics")
@responses.activate
def test_comment_workflow_api_error(self, mock_metrics, mock_issues):
cache.set(self.cache_key, True, timedelta(minutes=5).total_seconds())
mock_issues.return_value = [
{"group_id": g.id, "event_count": 10} for g in Group.objects.all()
]
responses.add(
responses.POST,
self.base_url + "/repos/getsentry/sentry/issues/1/comments",
status=400,
json={"id": 1},
)
responses.add(
responses.POST,
self.base_url + "/repos/getsentry/sentry/issues/2/comments",
status=400,
json={
"message": "Unable to create comment because issue is locked.",
"documentation_url": "https://docs.github.com/articles/locking-conversations/",
},
)
responses.add(
responses.POST,
self.base_url + "/repos/getsentry/sentry/issues/3/comments",
status=400,
json={
"message": "API rate limit exceeded",
"documentation_url": "https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting",
},
)
with pytest.raises(ApiError):
github_comment_workflow(self.pr.id, self.project.id)
assert cache.get(self.cache_key) is None
mock_metrics.incr.assert_called_with("github_pr_comment.api_error")
pr_2 = self.create_pr_issues()
cache_key = DEBOUNCE_PR_COMMENT_CACHE_KEY(pr_2.id)
cache.set(cache_key, True, timedelta(minutes=5).total_seconds())
# does not raise ApiError for locked issue
github_comment_workflow(pr_2.id, self.project.id)
assert cache.get(cache_key) is None
mock_metrics.incr.assert_called_with(
"github_pr_comment.error", tags={"type": "issue_locked_error"}
)
pr_3 = self.create_pr_issues()
cache_key = DEBOUNCE_PR_COMMENT_CACHE_KEY(pr_3.id)
cache.set(cache_key, True, timedelta(minutes=5).total_seconds())
# does not raise ApiError for rate limited error
github_comment_workflow(pr_3.id, self.project.id)
assert cache.get(cache_key) is None
mock_metrics.incr.assert_called_with(
"github_pr_comment.error", tags={"type": "rate_limited_error"}
)
@patch(
"sentry.tasks.integrations.github.pr_comment.pr_to_issue_query",
return_value=[(0, 0, 0, [])],
)
@patch("sentry.tasks.integrations.github.pr_comment.get_top_5_issues_by_count")
@patch("sentry.tasks.integrations.github.pr_comment.metrics")
def test_comment_workflow_missing_org(self, mock_metrics, mock_issues, mock_issue_query):
# Organization.DoesNotExist should trigger the cache to release the key
cache.set(self.cache_key, True, timedelta(minutes=5).total_seconds())
github_comment_workflow(self.pr.id, self.project.id)
assert not mock_issues.called
assert cache.get(self.cache_key) is None
mock_metrics.incr.assert_called_with(
"github_pr_comment.error", tags={"type": "missing_org"}
)
@patch("sentry.tasks.integrations.github.pr_comment.get_top_5_issues_by_count")
def test_comment_workflow_missing_org_option(self, mock_issues):
OrganizationOption.objects.set_value(
organization=self.organization, key="sentry:github_pr_bot", value=False
)
github_comment_workflow(self.pr.id, self.project.id)
assert not mock_issues.called
@patch("sentry.tasks.integrations.github.pr_comment.get_top_5_issues_by_count")
@patch("sentry.models.Project.objects.get_from_cache")
@patch("sentry.tasks.integrations.github.pr_comment.metrics")
def test_comment_workflow_missing_project(self, mock_metrics, mock_project, mock_issues):
# Project.DoesNotExist should trigger the cache to release the key
cache.set(self.cache_key, True, timedelta(minutes=5).total_seconds())
mock_project.side_effect = Project.DoesNotExist
github_comment_workflow(self.pr.id, self.project.id)
assert not mock_issues.called
assert cache.get(self.cache_key) is None
mock_metrics.incr.assert_called_with(
"github_pr_comment.error", tags={"type": "missing_project"}
)
@patch(
"sentry.tasks.integrations.github.pr_comment.get_top_5_issues_by_count",
)
@patch("sentry.models.Repository.objects")
@patch("sentry.tasks.integrations.github.pr_comment.format_comment")
@patch("sentry.tasks.integrations.github.pr_comment.metrics")
def test_comment_workflow_missing_repo(
self, mock_metrics, mock_format_comment, mock_repository, mock_issues
):
# Repository.DoesNotExist should trigger the cache to release the key
cache.set(self.cache_key, True, timedelta(minutes=5).total_seconds())
mock_repository.get.side_effect = Repository.DoesNotExist
github_comment_workflow(self.pr.id, self.project.id)
mock_issues.return_value = [
{"group_id": g.id, "event_count": 10} for g in Group.objects.all()
]
assert mock_issues.called
assert not mock_format_comment.called
assert cache.get(self.cache_key) is None
mock_metrics.incr.assert_called_with(
"github_pr_comment.error", tags={"type": "missing_repo"}
)
@patch(
"sentry.tasks.integrations.github.pr_comment.get_top_5_issues_by_count",
)
@patch("sentry.tasks.integrations.github.pr_comment.format_comment")
@patch("sentry.tasks.integrations.github.pr_comment.metrics")
def test_comment_workflow_missing_integration(
self, mock_metrics, mock_format_comment, mock_issues
):
# missing integration should trigger the cache to release the key
cache.set(self.cache_key, True, timedelta(minutes=5).total_seconds())
# invalid integration id
self.gh_repo.integration_id = 0
self.gh_repo.save()
mock_issues.return_value = [
{"group_id": g.id, "event_count": 10} for g in Group.objects.all()
]
github_comment_workflow(self.pr.id, self.project.id)
assert mock_issues.called
assert not mock_format_comment.called
assert cache.get(self.cache_key) is None
mock_metrics.incr.assert_called_with(
"github_pr_comment.error", tags={"type": "missing_integration"}
)
@region_silo_test
class TestCommentReactionsTask(GithubCommentTestCase):
base_url = "https://api.github.com"
def setUp(self):
super().setUp()
self.user_id = "user_1"
self.app_id = "app_1"
self.pr = self.create_pr_issues()
self.comment = PullRequestComment.objects.create(
external_id="2",
pull_request=self.pr,
created_at=django_timezone.now(),
updated_at=django_timezone.now(),
group_ids=[4, 5],
)
self.expired_pr = self.create_pr_issues()
self.expired_pr.date_added = django_timezone.now() - timedelta(days=35)
self.expired_pr.save()
self.comment_reactions = {
"reactions": {
"url": "abcdef",
"hooray": 1,
"+1": 2,
"-1": 0,
}
}
@patch("sentry.tasks.integrations.github.pr_comment.metrics")
@responses.activate
def test_comment_reactions_task(self, mock_metrics):
old_comment = PullRequestComment.objects.create(
external_id="1",
pull_request=self.expired_pr,
created_at=django_timezone.now() - timedelta(days=35),
updated_at=django_timezone.now() - timedelta(days=35),
group_ids=[1, 2, 3],
)
responses.add(
responses.GET,
self.base_url + "/repos/getsentry/sentry/issues/comments/2",
json=self.comment_reactions,
)
github_comment_reactions()
old_comment.refresh_from_db()
assert old_comment.reactions is None
self.comment.refresh_from_db()
stored_reactions = self.comment_reactions["reactions"]
del stored_reactions["url"]
assert self.comment.reactions == stored_reactions
mock_metrics.incr.assert_called_with("github_pr_comment.comment_reactions.success")
@patch("sentry.tasks.integrations.github.pr_comment.metrics")
@responses.activate
def test_comment_reactions_task_missing_repo(self, mock_metrics):
self.gh_repo.delete()
responses.add(
responses.GET,
self.base_url + "/repos/getsentry/sentry/issues/comments/2",
status=400,
json={},
)
github_comment_reactions()
self.comment.refresh_from_db()
assert self.comment.reactions is None
mock_metrics.incr.assert_called_with("github_pr_comment.comment_reactions.missing_repo")
@patch("sentry.tasks.integrations.github.pr_comment.metrics")
@responses.activate
def test_comment_reactions_task_missing_integration(self, mock_metrics):
# invalid integration id
self.gh_repo.integration_id = 0
self.gh_repo.save()
responses.add(
responses.GET,
self.base_url + "/repos/getsentry/sentry/issues/comments/2",
status=400,
json={},
)
github_comment_reactions()
self.comment.refresh_from_db()
assert self.comment.reactions is None
mock_metrics.incr.assert_called_with(
"github_pr_comment.comment_reactions.missing_integration"
)
@patch("sentry.tasks.integrations.github.pr_comment.metrics")
@responses.activate
def test_comment_reactions_task_api_error_one(self, mock_metrics):
gh_repo = self.create_repo(
name="getsentry/santry",
provider="integrations:github",
integration_id=self.integration.id,
project=self.project,
url="https://github.com/getsentry/santry",
)
no_error_comment = PullRequestComment.objects.create(
external_id="3",
pull_request=self.create_pr_issues(gh_repo=gh_repo),
created_at=django_timezone.now(),
updated_at=django_timezone.now(),
group_ids=[7, 8],
)
responses.add(
responses.GET,
self.base_url + "/repos/getsentry/sentry/issues/comments/2",
status=400,
json={},
)
responses.add(
responses.GET,
self.base_url + "/repos/getsentry/santry/issues/comments/3",
json=self.comment_reactions,
)
github_comment_reactions()
self.comment.refresh_from_db()
assert self.comment.reactions is None
no_error_comment.refresh_from_db()
stored_reactions = self.comment_reactions["reactions"]
del stored_reactions["url"]
assert no_error_comment.reactions == stored_reactions
# assert the last metric emitted is a success
mock_metrics.incr.assert_called_with("github_pr_comment.comment_reactions.success")
@patch("sentry.tasks.integrations.github.pr_comment.metrics")
@responses.activate
def test_comment_reactions_task_api_error_rate_limited(self, mock_metrics):
responses.add(
responses.GET,
self.base_url + "/repos/getsentry/sentry/issues/comments/2",
status=400,
json={
"message": "API rate limit exceeded",
"documentation_url": "https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting",
},
)
github_comment_reactions()
self.comment.refresh_from_db()
assert self.comment.reactions is None
mock_metrics.incr.assert_called_with(
"github_pr_comment.comment_reactions.rate_limited_error"
)
@patch("sentry.tasks.integrations.github.pr_comment.metrics")
@responses.activate
def test_comment_reactions_task_api_error_404(self, mock_metrics):
responses.add(
responses.GET,
self.base_url + "/repos/getsentry/sentry/issues/comments/2",
status=404,
json={},
)
github_comment_reactions()
self.comment.refresh_from_db()
assert self.comment.reactions is None
mock_metrics.incr.assert_called_with("github_pr_comment.comment_reactions.not_found_error")