mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
493 lines
20 KiB
Python
493 lines
20 KiB
Python
from unittest.mock import patch
|
|
|
|
from django.utils import timezone
|
|
|
|
from sentry.models.groupowner import GroupOwner, GroupOwnerType
|
|
from sentry.models.grouprelease import GroupRelease
|
|
from sentry.models.repository import Repository
|
|
from sentry.silo import SiloMode
|
|
from sentry.tasks.deletion.hybrid_cloud import schedule_hybrid_cloud_foreign_key_jobs
|
|
from sentry.tasks.groupowner import PREFERRED_GROUP_OWNER_AGE, process_suspect_commits
|
|
from sentry.testutils.cases import TestCase
|
|
from sentry.testutils.helpers import TaskRunner
|
|
from sentry.testutils.helpers.datetime import before_now, iso_format
|
|
from sentry.testutils.outbox import outbox_runner
|
|
from sentry.testutils.silo import assume_test_silo_mode, region_silo_test
|
|
from sentry.testutils.skips import requires_snuba
|
|
from sentry.utils.committers import get_frame_paths, get_serialized_event_file_committers
|
|
|
|
pytestmark = [requires_snuba]
|
|
|
|
|
|
@region_silo_test
|
|
class TestGroupOwners(TestCase):
|
|
def setUp(self):
|
|
self.project = self.create_project()
|
|
self.repo = Repository.objects.create(
|
|
organization_id=self.organization.id, name=self.organization.id
|
|
)
|
|
self.release = self.create_release(project=self.project, version="v1337")
|
|
self.group = self.create_group(
|
|
project=self.project, message="Kaboom!", first_release=self.release
|
|
)
|
|
GroupRelease.objects.create(
|
|
group_id=self.group.id, release_id=self.release.id, project_id=self.project.id
|
|
)
|
|
|
|
self.event = self.store_event(
|
|
data={
|
|
"message": "Kaboom!",
|
|
"platform": "python",
|
|
"timestamp": iso_format(before_now(seconds=10)),
|
|
"stacktrace": {
|
|
"frames": [
|
|
{
|
|
"function": "handle_set_commits",
|
|
"abs_path": "/usr/src/sentry/src/sentry/tasks.py",
|
|
"module": "sentry.tasks",
|
|
"in_app": True,
|
|
"lineno": 30,
|
|
"filename": "sentry/tasks.py",
|
|
},
|
|
{
|
|
"function": "set_commits",
|
|
"abs_path": "/usr/src/sentry/src/sentry/models/release.py",
|
|
"module": "sentry.models.release",
|
|
"in_app": True,
|
|
"lineno": 39,
|
|
"filename": "sentry/models/release.py",
|
|
},
|
|
]
|
|
},
|
|
"tags": {"sentry:release": self.release.version},
|
|
"fingerprint": ["put-me-in-the-control-group"],
|
|
},
|
|
project_id=self.project.id,
|
|
)
|
|
assert self.event.group is not None
|
|
GroupRelease.objects.create(
|
|
group_id=self.event.group.id, project_id=self.project.id, release_id=self.release.id
|
|
)
|
|
|
|
def set_release_commits(self, author_email):
|
|
self.release.set_commits(
|
|
[
|
|
{
|
|
"id": "a" * 40,
|
|
"repository": self.repo.name,
|
|
"author_email": author_email,
|
|
"author_name": "Bob",
|
|
"message": "i fixed a bug",
|
|
"patch_set": [{"path": "src/sentry/models/release.py", "type": "M"}],
|
|
}
|
|
]
|
|
)
|
|
|
|
def test_simple(self):
|
|
self.set_release_commits(self.user.email)
|
|
assert not GroupOwner.objects.filter(group=self.event.group).exists()
|
|
event_frames = get_frame_paths(self.event)
|
|
process_suspect_commits(
|
|
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 GroupOwner.objects.get(
|
|
group=self.event.group,
|
|
project=self.event.project,
|
|
organization=self.event.project.organization,
|
|
type=GroupOwnerType.SUSPECT_COMMIT.value,
|
|
)
|
|
|
|
def test_user_deletion_cascade(self):
|
|
other_user = self.create_user()
|
|
group = self.create_group()
|
|
other_group = self.create_group()
|
|
GroupOwner.objects.create(
|
|
group=group,
|
|
project=group.project,
|
|
organization=group.project.organization,
|
|
type=0,
|
|
user_id=self.user.id,
|
|
)
|
|
GroupOwner.objects.create(
|
|
group=other_group,
|
|
project=other_group.project,
|
|
organization=other_group.project.organization,
|
|
type=0,
|
|
user_id=other_user.id,
|
|
)
|
|
|
|
assert GroupOwner.objects.count() == 2
|
|
with assume_test_silo_mode(SiloMode.CONTROL), outbox_runner():
|
|
self.user.delete()
|
|
assert GroupOwner.objects.count() == 2
|
|
|
|
with TaskRunner():
|
|
schedule_hybrid_cloud_foreign_key_jobs()
|
|
|
|
assert GroupOwner.objects.count() == 1
|
|
|
|
def test_no_matching_user(self):
|
|
self.set_release_commits("[email protected]")
|
|
|
|
result = get_serialized_event_file_committers(self.project, self.event)
|
|
|
|
assert len(result) == 1
|
|
assert "commits" in result[0]
|
|
assert len(result[0]["commits"]) == 1
|
|
assert result[0]["commits"][0]["id"] == "a" * 40
|
|
assert not GroupOwner.objects.filter(group=self.event.group).exists()
|
|
event_frames = get_frame_paths(self.event)
|
|
process_suspect_commits(
|
|
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 GroupOwner.objects.filter(group=self.event.group).exists()
|
|
|
|
def test_delete_old_entries(self):
|
|
# As new events come in associated with new owners, we should delete old ones.
|
|
self.set_release_commits(self.user.email)
|
|
event_frames = get_frame_paths(self.event)
|
|
process_suspect_commits(
|
|
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,
|
|
)
|
|
process_suspect_commits(
|
|
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,
|
|
)
|
|
process_suspect_commits(
|
|
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 GroupOwner.objects.filter(group=self.event.group).count() == 1
|
|
assert GroupOwner.objects.filter(group=self.event.group, user_id=self.user.id).exists()
|
|
event_2 = self.store_event(
|
|
data={
|
|
"message": "BANG!",
|
|
"platform": "python",
|
|
"timestamp": iso_format(before_now(seconds=1)),
|
|
"stacktrace": {
|
|
"frames": [
|
|
{
|
|
"function": "process_suspect_commits",
|
|
"abs_path": "/usr/src/sentry/src/sentry/tasks/groupowner.py",
|
|
"module": "sentry.tasks.groupowner",
|
|
"in_app": True,
|
|
"lineno": 48,
|
|
"filename": "sentry/tasks/groupowner.py",
|
|
},
|
|
]
|
|
},
|
|
"tags": {"sentry:release": self.release.version},
|
|
"fingerprint": ["put-me-in-the-control-group"],
|
|
},
|
|
project_id=self.project.id,
|
|
)
|
|
event_3 = self.store_event(
|
|
data={
|
|
"message": "BOP!",
|
|
"platform": "python",
|
|
"timestamp": iso_format(before_now(seconds=1)),
|
|
"stacktrace": {
|
|
"frames": [
|
|
{
|
|
"function": "process_suspect_commits",
|
|
"abs_path": "/usr/src/sentry/src/sentry/tasks/groupowner.py",
|
|
"module": "sentry.tasks.groupowner",
|
|
"in_app": True,
|
|
"lineno": 48,
|
|
"filename": "sentry/tasks/groupowner.py",
|
|
},
|
|
]
|
|
},
|
|
"tags": {"sentry:release": self.release.version},
|
|
"fingerprint": ["put-me-in-the-control-group"],
|
|
},
|
|
project_id=self.project.id,
|
|
)
|
|
|
|
self.user_2 = self.create_user("[email protected]", is_superuser=True)
|
|
self.create_member(teams=[self.team], user=self.user_2, organization=self.organization)
|
|
self.user_3 = self.create_user("[email protected]", is_superuser=True)
|
|
self.create_member(teams=[self.team], user=self.user_3, organization=self.organization)
|
|
self.release.set_commits(
|
|
[
|
|
{
|
|
"id": "a" * 40,
|
|
"repository": self.repo.name,
|
|
"author_email": self.user_2.email,
|
|
"author_name": "joe",
|
|
"message": "i fixed another bug",
|
|
"patch_set": [{"path": "src/sentry/tasks/groupowner.py", "type": "M"}],
|
|
}
|
|
]
|
|
)
|
|
|
|
assert event_2.group == self.event.group
|
|
assert event_3.group == self.event.group
|
|
|
|
self.set_release_commits(self.user_2.email)
|
|
event_2_frames = get_frame_paths(event_2)
|
|
process_suspect_commits(
|
|
event_id=event_2.event_id,
|
|
event_platform=event_2.platform,
|
|
event_frames=event_2_frames,
|
|
group_id=event_2.group_id,
|
|
project_id=event_2.project_id,
|
|
)
|
|
assert GroupOwner.objects.filter(group=self.event.group).count() == 2
|
|
assert GroupOwner.objects.filter(group=self.event.group, user_id=self.user.id).exists()
|
|
assert GroupOwner.objects.filter(group=event_2.group, user_id=self.user_2.id).exists()
|
|
|
|
self.set_release_commits(self.user_3.email)
|
|
event_3_frames = get_frame_paths(event_3)
|
|
process_suspect_commits(
|
|
event_id=event_3.event_id,
|
|
event_platform=event_3.platform,
|
|
event_frames=event_3_frames,
|
|
group_id=event_3.group_id,
|
|
project_id=event_3.project_id,
|
|
)
|
|
assert GroupOwner.objects.filter(group=self.event.group).count() == 2
|
|
assert GroupOwner.objects.filter(group=self.event.group, user_id=self.user.id).exists()
|
|
assert GroupOwner.objects.filter(group=event_2.group, user_id=self.user_2.id).exists()
|
|
assert not GroupOwner.objects.filter(group=event_2.group, user_id=self.user_3.id).exists()
|
|
|
|
go = GroupOwner.objects.filter(group=event_2.group, user_id=self.user_2.id).first()
|
|
go.date_added = timezone.now() - PREFERRED_GROUP_OWNER_AGE * 2
|
|
go.save()
|
|
|
|
self.set_release_commits(self.user_3.email)
|
|
process_suspect_commits(
|
|
event_id=event_3.event_id,
|
|
event_platform=event_3.platform,
|
|
event_frames=event_3_frames,
|
|
group_id=event_3.group_id,
|
|
project_id=event_3.project_id,
|
|
)
|
|
# Won't be processed because the cache is present and this group has owners
|
|
assert GroupOwner.objects.filter(group=self.event.group).count() == 2
|
|
assert GroupOwner.objects.filter(group=self.event.group, user_id=self.user.id).exists()
|
|
assert not GroupOwner.objects.filter(group=event_2.group, user_id=self.user_2.id).exists()
|
|
assert GroupOwner.objects.filter(group=event_2.group, user_id=self.user_3.id).exists()
|
|
|
|
def test_update_existing_entries(self):
|
|
# As new events come in associated with existing owners, we should update the date_added of that owner.
|
|
self.set_release_commits(self.user.email)
|
|
event_frames = get_frame_paths(self.event)
|
|
process_suspect_commits(
|
|
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,
|
|
)
|
|
go = GroupOwner.objects.get(
|
|
group=self.event.group,
|
|
project=self.event.project,
|
|
organization=self.event.project.organization,
|
|
type=GroupOwnerType.SUSPECT_COMMIT.value,
|
|
)
|
|
|
|
date_added_before_update = go.date_added
|
|
process_suspect_commits(
|
|
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,
|
|
)
|
|
go.refresh_from_db()
|
|
assert go.date_added > date_added_before_update
|
|
assert GroupOwner.objects.filter(group=self.event.group).count() == 1
|
|
assert GroupOwner.objects.get(
|
|
group=self.event.group,
|
|
project=self.event.project,
|
|
organization=self.event.project.organization,
|
|
type=GroupOwnerType.SUSPECT_COMMIT.value,
|
|
)
|
|
|
|
def test_no_release_or_commit(self):
|
|
event_with_no_release = self.store_event(
|
|
data={
|
|
"message": "BOOM!",
|
|
"platform": "python",
|
|
"timestamp": iso_format(before_now(seconds=1)),
|
|
"stacktrace": {
|
|
"frames": [
|
|
{
|
|
"function": "process_suspect_commits",
|
|
"abs_path": "/usr/src/sentry/src/sentry/tasks/groupowner.py",
|
|
"module": "sentry.tasks.groupowner",
|
|
"in_app": True,
|
|
"lineno": 48,
|
|
"filename": "sentry/tasks/groupowner.py",
|
|
},
|
|
]
|
|
},
|
|
"fingerprint": ["i-have-no-release"],
|
|
},
|
|
project_id=self.project.id,
|
|
)
|
|
process_suspect_commits(
|
|
event_with_no_release.event_id,
|
|
event_with_no_release.platform,
|
|
event_with_no_release.data,
|
|
event_with_no_release.group_id,
|
|
event_with_no_release.project_id,
|
|
)
|
|
assert GroupOwner.objects.filter(group=event_with_no_release.group).count() == 0
|
|
|
|
@patch("sentry.tasks.groupowner.get_event_file_committers")
|
|
def test_keep_highest_score(self, patched_committers):
|
|
self.user2 = self.create_user(email="[email protected]")
|
|
self.user3 = self.create_user(email="[email protected]")
|
|
patched_committers.return_value = [
|
|
{
|
|
"commits": [(None, 3)],
|
|
"author": {
|
|
"username": self.user.email,
|
|
"lastLogin": None,
|
|
"isSuperuser": True,
|
|
"isManaged": False,
|
|
"experiments": {},
|
|
"lastActive": timezone.now(),
|
|
"isStaff": True,
|
|
"id": self.user.id,
|
|
"isActive": True,
|
|
"has2fa": False,
|
|
"name": self.user.email,
|
|
"avatarUrl": "https://secure.gravatar.com/avatar/46d229b033af06a191ff2267bca9ae56?s=32&d=mm",
|
|
"dateJoined": timezone.now(),
|
|
"emails": [
|
|
{"is _verified": True, "id": self.user.id, "email": self.user.email}
|
|
],
|
|
"avatar": {"avatarUuid": None, "avatarType": "letter_avatar"},
|
|
"hasPasswordAuth": True,
|
|
"email": self.user.email,
|
|
},
|
|
},
|
|
{
|
|
"commits": [(None, 1)],
|
|
"author": {
|
|
"username": self.user2.email,
|
|
"lastLogin": None,
|
|
"isSuperuser": True,
|
|
"isManaged": False,
|
|
"experiments": {},
|
|
"lastActive": timezone.now(),
|
|
"isStaff": True,
|
|
"id": self.user2.id,
|
|
"isActive": True,
|
|
"has2fa": False,
|
|
"name": self.user2.email,
|
|
"avatarUrl": "https://secure.gravatar.com/avatar/46d229b033af06a191ff2267bca9ae56?s=32&d=mm",
|
|
"dateJoined": timezone.now(),
|
|
"emails": [
|
|
{"is_verified": True, "id": self.user2.id, "email": self.user2.email}
|
|
],
|
|
"avatar": {"avatarUuid": None, "avatarType": "letter_avatar"},
|
|
"hasPasswordAuth": True,
|
|
"email": self.user2.email,
|
|
},
|
|
},
|
|
{
|
|
"commits": [(None, 2)],
|
|
"author": {
|
|
"username": self.user3.email,
|
|
"lastLogin": None,
|
|
"isSuperuser": True,
|
|
"isManaged": False,
|
|
"experiments": {},
|
|
"lastActive": timezone.now(),
|
|
"isStaff": True,
|
|
"id": self.user3.id,
|
|
"isActive": True,
|
|
"has2fa": False,
|
|
"name": self.user3.email,
|
|
"avatarUrl": "https://secure.gravatar.com/avatar/46d229b033af06a191ff2267bca9ae56?s=32&d=mm",
|
|
"dateJoined": timezone.now(),
|
|
"emails": [
|
|
{"is_verified": True, "id": self.user3.id, "email": self.user3.email}
|
|
],
|
|
"avatar": {"avatarUuid": None, "avatarType": "letter_avatar"},
|
|
"hasPasswordAuth": True,
|
|
"email": self.user3.email,
|
|
},
|
|
},
|
|
]
|
|
event_frames = get_frame_paths(self.event)
|
|
process_suspect_commits(
|
|
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,
|
|
)
|
|
# Doesn't use self.user2 due to low score.
|
|
assert GroupOwner.objects.get(user_id=self.user.id)
|
|
assert GroupOwner.objects.get(user_id=self.user3.id)
|
|
assert not GroupOwner.objects.filter(user_id=self.user2.id).exists()
|
|
|
|
@patch("sentry.tasks.groupowner.get_event_file_committers")
|
|
def test_low_suspect_committer_score(self, patched_committers):
|
|
self.user = self.create_user()
|
|
patched_committers.return_value = [
|
|
{
|
|
# score < MIN_COMMIT_SCORE
|
|
"commits": [(None, 1)],
|
|
"author": {
|
|
"id": self.user.id,
|
|
},
|
|
},
|
|
]
|
|
event_frames = get_frame_paths(self.event)
|
|
process_suspect_commits(
|
|
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 GroupOwner.objects.filter(user_id=self.user.id).exists()
|
|
|
|
def test_owners_count(self):
|
|
self.set_release_commits(self.user.email)
|
|
self.user = self.create_user()
|
|
event_frames = get_frame_paths(self.event)
|
|
|
|
process_suspect_commits(
|
|
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,
|
|
)
|
|
|
|
owners = GroupOwner.objects.filter(
|
|
group_id=self.event.group_id,
|
|
project=self.event.project,
|
|
organization_id=self.event.project.organization_id,
|
|
type=GroupOwnerType.SUSPECT_COMMIT.value,
|
|
)
|
|
|
|
assert owners.count() == 1
|