-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
feat(gh-comments): Queuing Logic #50865
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 11 commits
9151fa3
629fa50
8b88835
fd48476
6be211d
56efbb1
5ccba4b
21f4b3d
17fa05b
abff1e7
5bd3151
6a834ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,23 @@ | ||
from datetime import timedelta | ||
from unittest.mock import patch | ||
from unittest.mock import Mock, patch | ||
|
||
import pytest | ||
from celery.exceptions import MaxRetriesExceededError | ||
from django.utils import timezone | ||
|
||
from sentry.models import Repository | ||
from sentry.models import PullRequest, PullRequestComment, Repository | ||
from sentry.models.commit import Commit | ||
from sentry.models.groupowner import GroupOwner, GroupOwnerType | ||
from sentry.shared_integrations.exceptions.base import ApiError | ||
from sentry.tasks.commit_context import process_commit_context | ||
from sentry.testutils import TestCase | ||
from sentry.testutils.helpers import with_feature | ||
from sentry.testutils.helpers.datetime import before_now, iso_format | ||
from sentry.testutils.silo import region_silo_test | ||
from sentry.utils.committers import get_frame_paths | ||
|
||
|
||
@region_silo_test(stable=True) | ||
class TestCommitContext(TestCase): | ||
class TestCommitContextMixin(TestCase): | ||
def setUp(self): | ||
self.project = self.create_project() | ||
self.repo = Repository.objects.create( | ||
|
@@ -72,6 +72,9 @@ def setUp(self): | |
project_id=self.project.id, | ||
) | ||
|
||
|
||
@region_silo_test(stable=True) | ||
class TestCommitContext(TestCommitContextMixin): | ||
@patch( | ||
"sentry.integrations.github.GitHubIntegration.get_commit_context", | ||
return_value={ | ||
|
@@ -408,3 +411,157 @@ def after_return(self, status, retval, task_id, args, kwargs, einfo): | |
) | ||
|
||
assert mock_suspect_commits.called | ||
|
||
|
||
@region_silo_test(stable=True) | ||
@patch( | ||
"sentry.integrations.github.GitHubIntegration.get_commit_context", | ||
Mock( | ||
return_value={ | ||
"commitId": "asdfwreqr", | ||
"committedDate": "2023-02-14T11:11Z", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't it better to add a date() here so you make sure in tests the commit is always recent? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the commit date isn't important in this case. I'd rather just mock a response once here and have the object be fixed. |
||
"commitMessage": "placeholder commit message", | ||
"commitAuthorName": "", | ||
"commitAuthorEmail": "admin@localhost", | ||
} | ||
), | ||
) | ||
@patch("sentry.tasks.integrations.github.pr_comment.comment_workflow.delay") | ||
class TestGHCommentQueuing(TestCommitContextMixin): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does putting all the patches before the class mean that all the patches are applied to the tests inside the class? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yup! Saves on code repetition. I think you can override it too if you have one or two tests that want a different patch |
||
def setUp(self): | ||
super().setUp() | ||
self.pull_request = PullRequest.objects.create( | ||
organization_id=self.organization.id, | ||
repository_id=self.repo.id, | ||
key="99", | ||
author=self.commit.author, | ||
message="foo", | ||
title="bar", | ||
merge_commit_sha=self.commit.key, | ||
date_added=iso_format(before_now(days=1)), | ||
) | ||
self.repo.provider = "integrations:github" | ||
self.repo.save() | ||
self.pull_request_comment = PullRequestComment.objects.create( | ||
pull_request=self.pull_request, | ||
external_id=1, | ||
created_at=iso_format(before_now(days=1)), | ||
updated_at=iso_format(before_now(days=1)), | ||
group_ids=[], | ||
) | ||
|
||
@with_feature("organizations:pr-comment-bot") | ||
def test_gh_comment_not_github(self, mock_comment_workflow): | ||
"""Non github repos shouldn't be commented on""" | ||
self.repo.provider = "integrations:gitlab" | ||
self.repo.save() | ||
with self.tasks(): | ||
event_frames = get_frame_paths(self.event) | ||
process_commit_context( | ||
event_id=self.event.event_id, | ||
event_platform=self.event.platform, | ||
event_frames=event_frames, | ||
group_id=self.event.group_id, | ||
project_id=self.event.project_id, | ||
) | ||
assert not mock_comment_workflow.called | ||
|
||
def test_gh_comment_feature_flag(self, mock_comment_workflow): | ||
"""No comments on org with feature flag disabled""" | ||
with self.tasks(): | ||
event_frames = get_frame_paths(self.event) | ||
process_commit_context( | ||
event_id=self.event.event_id, | ||
event_platform=self.event.platform, | ||
event_frames=event_frames, | ||
group_id=self.event.group_id, | ||
project_id=self.event.project_id, | ||
) | ||
assert not mock_comment_workflow.called | ||
|
||
@with_feature("organizations:pr-comment-bot") | ||
def test_gh_comment_no_pr(self, mock_comment_workflow): | ||
"""No comments on suspect commit with no pr""" | ||
self.pull_request.delete() | ||
with self.tasks(): | ||
event_frames = get_frame_paths(self.event) | ||
process_commit_context( | ||
event_id=self.event.event_id, | ||
event_platform=self.event.platform, | ||
event_frames=event_frames, | ||
group_id=self.event.group_id, | ||
project_id=self.event.project_id, | ||
) | ||
assert not mock_comment_workflow.called | ||
|
||
@with_feature("organizations:pr-comment-bot") | ||
def test_gh_comment_org_settings(self, mock_comment_workflow): | ||
"""No comments on org who disabled feature""" | ||
# TODO(Cathy or Aniket): implement once the toggle is merged | ||
pass | ||
|
||
@with_feature("organizations:pr-comment-bot") | ||
def test_gh_comment_pr_too_old(self, mock_comment_workflow): | ||
"""No comment on pr that's older than 30 days""" | ||
self.pull_request.date_added = iso_format(before_now(days=31)) | ||
self.pull_request.save() | ||
|
||
with self.tasks(): | ||
event_frames = get_frame_paths(self.event) | ||
process_commit_context( | ||
event_id=self.event.event_id, | ||
event_platform=self.event.platform, | ||
event_frames=event_frames, | ||
group_id=self.event.group_id, | ||
project_id=self.event.project_id, | ||
) | ||
assert not mock_comment_workflow.called | ||
|
||
@with_feature("organizations:pr-comment-bot") | ||
def test_gh_comment_repeat_issue(self, mock_comment_workflow): | ||
"""No comment on a pr that has a comment with the issue in the same pr list""" | ||
self.pull_request_comment.group_ids.append(self.event.group_id) | ||
self.pull_request_comment.save() | ||
|
||
with self.tasks(): | ||
event_frames = get_frame_paths(self.event) | ||
process_commit_context( | ||
event_id=self.event.event_id, | ||
event_platform=self.event.platform, | ||
event_frames=event_frames, | ||
group_id=self.event.group_id, | ||
project_id=self.event.project_id, | ||
) | ||
assert not mock_comment_workflow.called | ||
|
||
@with_feature("organizations:pr-comment-bot") | ||
def test_gh_comment_create_queued(self, mock_comment_workflow): | ||
"""Task queued if no prior comment exists""" | ||
self.pull_request_comment.delete() | ||
|
||
with self.tasks(): | ||
event_frames = get_frame_paths(self.event) | ||
process_commit_context( | ||
event_id=self.event.event_id, | ||
event_platform=self.event.platform, | ||
event_frames=event_frames, | ||
group_id=self.event.group_id, | ||
project_id=self.event.project_id, | ||
) | ||
assert mock_comment_workflow.called | ||
|
||
@with_feature("organizations:pr-comment-bot") | ||
def test_gh_comment_update_queue(self, mock_comment_workflow): | ||
"""Task queued if new issue for prior comment""" | ||
|
||
with self.tasks(): | ||
assert not GroupOwner.objects.filter(group=self.event.group).exists() | ||
event_frames = get_frame_paths(self.event) | ||
process_commit_context( | ||
event_id=self.event.event_id, | ||
event_platform=self.event.platform, | ||
event_frames=event_frames, | ||
group_id=self.event.group_id, | ||
project_id=self.event.project_id, | ||
) | ||
assert mock_comment_workflow.called |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would pr ever include more than one object? and if so why do we choose pr[0] over the others?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pr
is technically a query so we might want to call itpr_query
. we could also useget()
butget()
throws an error if the item doesn't exist or if multiple items exist that match the criteriaThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
although we should only have one match so we could use
get()