def test_get_owners_when_codeowners_and_issueowners_exists(self): self.team = self.create_team( organization=self.organization, slug="tiger-team", members=[self.user] ) self.team2 = self.create_team( organization=self.organization, slug="dolphin-team", members=[self.user] ) self.project = self.create_project( organization=self.organization, teams=[self.team, self.team2] ) self.code_mapping = self.create_code_mapping(project=self.project) rule_a = Rule(Matcher("path", "*.py"), [Owner("team", self.team.slug)]) rule_b = Rule(Matcher("path", "src/*"), [Owner("user", self.user.email)]) rule_c = Rule(Matcher("path", "*.py"), [Owner("team", self.team2.slug)]) ProjectOwnership.objects.create( project_id=self.project.id, schema=dump_schema([rule_a, rule_b]), fallthrough=True ) self.create_codeowners( self.project, self.code_mapping, raw="*.py @tiger-team", schema=dump_schema([rule_c]) ) self.assert_ownership_equals( ProjectOwnership.get_owners( self.project.id, {"stacktrace": {"frames": [{"filename": "api/foo.py"}]}} ), ( [ActorTuple(self.team.id, Team), ActorTuple(self.team2.id, Team)], [rule_a, rule_c], ), )
def setUp(self): self.user = self.create_user(email="*****@*****.**", is_active=True) self.user2 = self.create_user(email="*****@*****.**", is_active=True) self.organization = self.create_organization(owner=self.user) self.team = self.create_team(organization=self.organization) self.project = self.create_project(name="Test", teams=[self.team]) OrganizationMemberTeam.objects.create( organizationmember=OrganizationMember.objects.get( user=self.user, organization=self.organization ), team=self.team, ) self.create_member(user=self.user2, organization=self.organization, teams=[self.team]) ProjectOwnership.objects.create( project_id=self.project.id, schema=dump_schema( [ Rule(Matcher("path", "*.py"), [Owner("team", self.team.slug)]), Rule(Matcher("path", "*.jx"), [Owner("user", self.user2.email)]), Rule( Matcher("path", "*.cbl"), [Owner("user", self.user.email), Owner("user", self.user2.email)], ), ] ), fallthrough=True, )
def test_get_autoassign_owners_only_issueowners_exists(self): rule_a = Rule(Matcher("path", "*.py"), [Owner("team", self.team.slug)]) rule_b = Rule(Matcher("path", "src/*"), [Owner("user", self.user.email)]) ProjectOwnership.objects.create( project_id=self.project.id, schema=dump_schema([rule_a, rule_b]), ) # No data matches assert ProjectOwnership.get_autoassign_owners(self.project.id, {}) == (False, [], False) # No autoassignment on match assert ProjectOwnership.get_autoassign_owners( self.project.id, {"stacktrace": { "frames": [{ "filename": "foo.py" }] }}) == (False, [self.team], False) # autoassignment is True owner = ProjectOwnership.objects.get(project_id=self.project.id) owner.auto_assignment = True owner.save() assert ProjectOwnership.get_autoassign_owners( self.project.id, {"stacktrace": { "frames": [{ "filename": "foo.py" }] }}) == (True, [self.team], False)
def test_multiple_owners_order_matters(self): users = [self.user, self.user2, self.user3] rules = [ Rule(Matcher("path", "*.py"), [Owner("user", users[0].email)]), Rule(Matcher("path", "*foo*"), [Owner("user", users[1].email)]), Rule(Matcher("path", "*"), [Owner("user", users[2].email)]), ] rules.reverse() ProjectOwnership.objects.create( project_id=self.project.id, schema=dump_schema(rules), fallthrough=True ) event1 = self.store_event( data={"stacktrace": {"frames": [{"filename": "foo.py"}]}}, project_id=self.project.id ) self.path = reverse( "sentry-api-0-event-owners", kwargs={ "organization_slug": self.organization.slug, "project_slug": self.project.slug, "event_id": event1.event_id, }, ) resp = self.client.get(self.path) assert resp.status_code == 200 assert len(resp.data["owners"]) == 3 assert [o["id"] for o in resp.data["owners"]] == [str(u.id) for u in reversed(users)] assert resp.data["rule"] == Matcher("path", "*") assert len(resp.data["rules"]) == 3
def test_parse_rules(): assert parse_rules(fixture_data) == [ Rule(Matcher("path", "*.js"), [Owner("team", "frontend"), Owner("user", "*****@*****.**")]), Rule(Matcher("url", "http://google.com/*"), [Owner("team", "backend")]), Rule(Matcher("path", "src/sentry/*"), [Owner("user", "*****@*****.**")]), Rule(Matcher("tags.foo", "bar"), [Owner("user", "*****@*****.**")]), Rule(Matcher("tags.foo", "bar baz"), [Owner("user", "*****@*****.**")]), ]
def test_get_autoassign_owners_when_codeowners_and_issueowners_exists( self): self.team = self.create_team(organization=self.organization, slug="tiger-team", members=[self.user]) self.team2 = self.create_team(organization=self.organization, slug="dolphin-team", members=[self.user]) self.project = self.create_project(organization=self.organization, teams=[self.team, self.team2]) self.code_mapping = self.create_code_mapping(project=self.project) rule_a = Rule(Matcher("path", "*.py"), [Owner("team", self.team.slug)]) rule_b = Rule(Matcher("path", "src/*"), [Owner("user", self.user.email)]) rule_c = Rule(Matcher("path", "*.py"), [Owner("team", self.team2.slug)]) ProjectOwnership.objects.create(project_id=self.project.id, schema=dump_schema([rule_a, rule_b]), fallthrough=True) self.create_codeowners(self.project, self.code_mapping, raw="*.py @tiger-team", schema=dump_schema([rule_c])) # No autoassignment on match assert ProjectOwnership.get_autoassign_owners( self.project.id, {"stacktrace": { "frames": [{ "filename": "api/foo.py" }] }}) == (False, [self.team, self.team2], False) # autoassignment is True owner = ProjectOwnership.objects.get(project_id=self.project.id) owner.auto_assignment = True owner.save() assert ProjectOwnership.get_autoassign_owners( self.project.id, {"stacktrace": { "frames": [{ "filename": "api/foo.py" }] }}) == (True, [self.team, self.team2], False) # # more than 2 matches assert ProjectOwnership.get_autoassign_owners( self.project.id, {"stacktrace": { "frames": [{ "filename": "src/foo.py" }] }}) == (True, [self.user, self.team], False)
def test_parse_rules(): assert parse_rules(fixture_data) == [ Rule(Matcher('path', '*.js'), [Owner('team', 'frontend'), Owner('user', '*****@*****.**')]), Rule(Matcher('url', 'http://google.com/*'), [Owner('team', 'backend')]), Rule(Matcher('path', 'src/sentry/*'), [Owner('user', '*****@*****.**')]), ]
def test_get_owners_basic(self): rule_a = Rule(Matcher("path", "*.py"), [Owner("team", self.team.slug)]) rule_b = Rule(Matcher("path", "src/*"), [Owner("user", self.user.email)]) ProjectOwnership.objects.create( project_id=self.project.id, schema=dump_schema([rule_a, rule_b]), fallthrough=True ) # No data matches assert ProjectOwnership.get_owners(self.project.id, {}) == (ProjectOwnership.Everyone, None) # Match only rule_a self.assert_ownership_equals( ProjectOwnership.get_owners( self.project.id, {"stacktrace": {"frames": [{"filename": "foo.py"}]}} ), ([Actor(self.team.id, Team)], [rule_a]), ) # Match only rule_b self.assert_ownership_equals( ProjectOwnership.get_owners( self.project.id, {"stacktrace": {"frames": [{"filename": "src/thing.txt"}]}} ), ([Actor(self.user.id, User)], [rule_b]), ) # Matches both rule_a and rule_b self.assert_ownership_equals( ProjectOwnership.get_owners( self.project.id, {"stacktrace": {"frames": [{"filename": "src/foo.py"}]}} ), ([Actor(self.team.id, Team), Actor(self.user.id, User)], [rule_a, rule_b]), ) assert ProjectOwnership.get_owners( self.project.id, {"stacktrace": {"frames": [{"filename": "xxxx"}]}} ) == (ProjectOwnership.Everyone, None) # When fallthrough = False, we don't implicitly assign to Everyone owner = ProjectOwnership.objects.get(project_id=self.project.id) owner.fallthrough = False owner.save() assert ProjectOwnership.get_owners( self.project.id, {"stacktrace": {"frames": [{"filename": "xxxx"}]}} ) == ([], None) self.assert_ownership_equals( ProjectOwnership.get_owners( self.project.id, {"stacktrace": {"frames": [{"filename": "src/foo.py"}]}} ), ([Actor(self.team.id, Team), Actor(self.user.id, User)], [rule_a, rule_b]), )
def make_ownership(self): rule_a = Rule(Matcher("path", "src/*"), [Owner("user", self.user.email)]) rule_b = Rule(Matcher("path", "tests/*"), [Owner("team", self.team.name)]) rule_c = Rule(Matcher("path", "src/app/*"), [Owner("team", self.team.name)]) ProjectOwnership.objects.create( project_id=self.project.id, schema=dump_schema([rule_a, rule_b, rule_c]), fallthrough=True, auto_assignment=True, )
def make_ownership(self, extra_rules=None): rules = [ Rule(Matcher("path", "src/*"), [Owner("user", self.user.email)]), Rule(Matcher("path", "tests/*"), [Owner("team", self.team.name)]), Rule(Matcher("path", "src/app/*"), [Owner("team", self.team.name)]), ] if extra_rules: rules.extend(extra_rules) ProjectOwnership.objects.create( project_id=self.project.id, schema=dump_schema(rules), fallthrough=True, auto_assignment=True, )
def test_matching_non_existing_owner(self): rule_a = Rule(Matcher("path", "*"), [Owner("user", "*****@*****.**")]) ProjectOwnership.objects.create(project_id=self.project.id, schema=dump_schema([rule_a]), fallthrough=True) event1 = self.store_event( data={"stacktrace": { "frames": [{ "filename": "foo.py" }] }}, project_id=self.project.id) self.path = reverse( "sentry-api-0-event-owners", kwargs={ "organization_slug": self.organization.slug, "project_slug": self.project.slug, "event_id": event1.event_id, }, ) resp = self.client.get(self.path) assert resp.status_code == 200 assert len(resp.data["owners"]) == 0 assert resp.data["rule"] == Matcher(type="path", pattern="*") assert len(resp.data["rules"]) == 1
def test_owner_assignment_extra_groups(self): extra_user = self.create_user() self.create_team_membership(self.team, user=extra_user) self.make_ownership( [Rule(Matcher("path", "src/app/things/in/*"), [Owner("user", extra_user.email)])], ) event = self.store_event( data={ "message": "oh no", "platform": "python", "stacktrace": {"frames": [{"filename": "src/app/things/in/a/path/example2.py"}]}, }, project_id=self.project.id, ) cache_key = write_event_to_cache(event) post_process_group( is_new=False, is_regression=False, is_new_group_environment=False, cache_key=cache_key, group_id=event.group_id, ) assignee = event.group.assignee_set.first() assert assignee.user == extra_user assert assignee.team is None owners = list(GroupOwner.objects.filter(group=event.group)) assert len(owners) == 2 assert set([(extra_user.id, None), (None, self.team.id)]) == { (o.user_id, o.team_id) for o in owners }
def test_one_owner(self): rule_a = Rule(Matcher("path", "*.py"), [Owner("user", self.user.email)]) ProjectOwnership.objects.create(project_id=self.project.id, schema=dump_schema([rule_a]), fallthrough=True) event1 = self.store_event( data={"stacktrace": { "frames": [{ "filename": "foo.py" }] }}, project_id=self.project.id) self.path = reverse( "sentry-api-0-event-owners", kwargs={ "organization_slug": self.organization.slug, "project_slug": self.project.slug, "event_id": event1.event_id, }, ) resp = self.client.get(self.path) assert resp.status_code == 200 assert len(resp.data["owners"]) == 1 assert resp.data["owners"][0]["id"] == six.text_type(self.user.id) assert resp.data["rule"] == Matcher("path", "*.py") assert len(resp.data["rules"]) == 1
def test_get_owners_when_codeowners_exists_and_no_issueowners(self): # This case will never exist bc we create a ProjectOwnership record if none exists when creating a ProjectCodeOwner record. # We have this testcase for potential corrupt data. self.team = self.create_team(organization=self.organization, slug="tiger-team", members=[self.user]) self.code_mapping = self.create_code_mapping(project=self.project) rule_a = Rule(Matcher("path", "*.js"), [Owner("team", self.team.slug)]) self.create_codeowners( self.project, self.code_mapping, raw="*.js @tiger-team", schema=dump_schema([rule_a]), ) self.assert_ownership_equals( ProjectOwnership.get_owners( self.project.id, {"stacktrace": { "frames": [{ "filename": "src/foo.js" }] }}), ( [ActorTuple(self.team.id, Team)], [rule_a], ), )
def test_get_autoassign_owners_only_codeowners_exists(self): # This case will never exist bc we create a ProjectOwnership record if none exists when creating a ProjectCodeOwner record. # We have this testcase for potential corrupt data. self.team = self.create_team(organization=self.organization, slug="tiger-team", members=[self.user]) self.code_mapping = self.create_code_mapping(project=self.project) rule_a = Rule(Matcher("path", "*.js"), [Owner("team", self.team.slug)]) self.create_codeowners( self.project, self.code_mapping, raw="*.js @tiger-team", schema=dump_schema([rule_a]), ) # No data matches assert ProjectOwnership.get_autoassign_owners(self.project.id, {}) == (False, [], False) # No autoassignment on match assert ProjectOwnership.get_autoassign_owners( self.project.id, {"stacktrace": { "frames": [{ "filename": "foo.js" }] }}) == (False, [self.team], True)
def make_ownership(self): rule_a = Rule(Matcher('path', 'src/*'), [ Owner('user', self.user.email), ]) rule_b = Rule(Matcher('path', 'tests/*'), [ Owner('team', self.team.name), ]) rule_c = Rule(Matcher('path', 'src/app/*'), [ Owner('team', self.team.name), ]) ProjectOwnership.objects.create( project_id=self.project.id, schema=dump_schema([rule_a, rule_b, rule_c]), fallthrough=True, auto_assignment=True, )
def test_owner_assignment_when_owners_have_been_unassigned(self): """ Test that ensures that if certain assignees get unassigned, and project rules are changed then the new group assignees should be re-calculated and re-assigned """ # Create rules and check assignees self.make_ownership() event = self.store_event( data={ "message": "oh no", "platform": "python", "stacktrace": { "frames": [{ "filename": "src/app/example.py" }] }, }, project_id=self.project.id, ) cache_key = write_event_to_cache(event) post_process_group( is_new=False, is_regression=False, is_new_group_environment=False, cache_key=cache_key, group_id=event.group_id, ) assignee = event.group.assignee_set.first() assert assignee.user == self.user user_3 = self.create_user() self.create_team_membership(self.team, user=user_3) # De-assign group assignees GroupAssignee.objects.deassign(event.group, self.user) assert event.group.assignee_set.first() is None # Change ProjectOwnership rules rules = [ Rule(Matcher("path", "src/*"), [Owner("user", user_3.email)]), ] self.prj_ownership.schema = dump_schema(rules) self.prj_ownership.save() cache_key = write_event_to_cache(event) post_process_group( is_new=False, is_regression=False, is_new_group_environment=False, cache_key=cache_key, group_id=event.group_id, ) # Group should be re-assigned to the new group owner assignee = event.group.assignee_set.first() assert assignee.user == user_3
def test_dump_schema(): assert dump_schema([Rule(Matcher("path", "*.js"), [Owner("team", "frontend")])]) == { "$version": 1, "rules": [ { "matcher": {"type": "path", "pattern": "*.js"}, "owners": [{"type": "team", "identifier": "frontend"}], } ], }
def setUp(self): self.user_1 = self.create_user(email="*****@*****.**") self.user_2 = self.create_user(email="*****@*****.**") self.user_3 = self.create_user(email="*****@*****.**") self.organization = self.create_organization(name="Padishah Emperor") self.team_1 = self.create_team( organization=self.organization, name="House Atreides", members=[self.user_1, self.user_2], ) self.team_2 = self.create_team( organization=self.organization, name="Bene Gesserit", members=[self.user_1, self.user_3] ) self.project = self.create_project( name="Settle Arrakis", organization=self.organization, teams=[self.team_1, self.team_2] ) self.rule_1 = Rule(Matcher("path", "*.js"), [Owner("team", self.team_1.slug)]) self.rule_2 = Rule(Matcher("path", "*.js"), [Owner("team", self.team_2.slug)]) self.rule_3 = Rule(Matcher("path", "*.js"), [Owner("user", self.user_1.email)])
def test_owners_of_different_types_ordered_correctly(self): owners = [self.user, self.team3, self.user2, self.team2, self.user3, self.team] rules = [ Rule(Matcher("path", "*.py"), [Owner("user", owners[0].email)]), Rule(Matcher("path", "*py"), [Owner("team", owners[1].slug)]), Rule(Matcher("path", "*foo*"), [Owner("user", owners[2].email)]), Rule(Matcher("path", "*y"), [Owner("team", owners[3].slug)]), Rule(Matcher("path", "*"), [Owner("user", owners[4].email)]), Rule(Matcher("path", "*o.py"), [Owner("team", owners[5].slug)]), ] ProjectOwnership.objects.create( project_id=self.project.id, schema=dump_schema(rules), fallthrough=True ) event1 = self.store_event( data={"stacktrace": {"frames": [{"filename": "foo.py"}]}}, project_id=self.project.id ) self.path = reverse( "sentry-api-0-event-owners", kwargs={ "organization_slug": self.organization.slug, "project_slug": self.project.slug, "event_id": event1.event_id, }, ) resp = self.client.get(self.path) assert resp.status_code == 200 assert len(resp.data["owners"]) == 6 assert [o["id"] for o in resp.data["owners"]] == [str(o.id) for o in owners] assert [o["type"] for o in resp.data["owners"]] == ["user", "team"] * 3 assert resp.data["rule"] == Matcher("path", "*.py") assert len(resp.data["rules"]) == 6
def test_abs_path_when_filename_present(self): frame = { "filename": "computer.cpp", "abs_path": "C:\\My\\Path\\computer.cpp", } rule = Rule(Matcher("path", "*My\\Path*"), [Owner("team", self.team.slug)]) ProjectOwnership.objects.create(project_id=self.project.id, schema=dump_schema([rule]), fallthrough=True) assert ProjectOwnership.get_owners( self.project.id, {"stacktrace": { "frames": [frame] }}) == ([ActorTuple(self.team.id, Team)], [rule])
def test_parse_rules(): assert parse_rules(fixture_data) == [ Rule(Matcher("path", "*.js"), [Owner("team", "frontend"), Owner("user", "*****@*****.**")]), Rule(Matcher("url", "http://google.com/*"), [Owner("team", "backend")]), Rule(Matcher("path", "src/sentry/*"), [Owner("user", "*****@*****.**")]), Rule(Matcher("tags.foo", "bar"), [Owner("user", "*****@*****.**")]), Rule(Matcher("tags.foo", "bar baz"), [Owner("user", "*****@*****.**")]), Rule(Matcher("module", "foo.bar"), [Owner("team", "workflow")]), Rule(Matcher("module", "foo bar"), [Owner("user", "*****@*****.**")]), Rule(Matcher("codeowners", "/src/components/"), [Owner("user", "*****@*****.**")]), Rule(Matcher("codeowners", "frontend/*.ts"), [Owner("user", "*****@*****.**")]), ]
def test_load_schema(): assert load_schema({ '$version': 1, 'rules': [{ 'matcher': { 'type': 'path', 'pattern': '*.js', }, 'owners': [{ 'type': 'team', 'identifier': 'frontend', }] }] }) == [Rule(Matcher('path', '*.js'), [Owner('team', 'frontend')])]
def test_get_owners_basic(self): matcher = Matcher('path', '*.py') ProjectOwnership.objects.create( project_id=self.project.id, schema=dump_schema([ Rule(matcher, [ Owner('user', self.user.email), Owner('team', self.team.slug), ]), ]), fallthrough=True, ) # No data matches assert ProjectOwnership.get_owners(self.project.id, {}) == (ProjectOwnership.Everyone, None) assert ProjectOwnership.get_owners(self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'foo.py', }] } }) == ([Actor(self.user.id, User), Actor(self.team.id, Team)], matcher) assert ProjectOwnership.get_owners(self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'xxxx', }] } }) == (ProjectOwnership.Everyone, None) # When fallthrough = False, we don't implicitly assign to Everyone ProjectOwnership.objects.filter( project_id=self.project.id, ).update(fallthrough=False) assert ProjectOwnership.get_owners(self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'xxxx', }] } }) == ([], None)
def test_owner_assignment_existing_owners(self): extra_team = self.create_team() ProjectTeam.objects.create(team=extra_team, project=self.project) self.make_ownership([ Rule(Matcher("path", "src/app/things/in/*"), [Owner("team", extra_team.slug)]) ], ) GroupOwner.objects.create( group=self.group, project=self.project, organization=self.organization, user=self.user, type=GroupOwnerType.OWNERSHIP_RULE.value, ) event = self.store_event( data={ "message": "oh no", "platform": "python", "stacktrace": { "frames": [{ "filename": "src/app/things/in/a/path/example2.py" }] }, }, project_id=self.project.id, ) cache_key = write_event_to_cache(event) post_process_group( is_new=False, is_regression=False, is_new_group_environment=False, cache_key=cache_key, group_id=event.group_id, ) assignee = event.group.assignee_set.first() assert assignee.user is None assert assignee.team == extra_team owners = list(GroupOwner.objects.filter(group=event.group)) assert {(None, extra_team.id), (self.user.id, None)} == {(o.user_id, o.team_id) for o in owners}
def test_team_without_members(self): team = self.create_team() project = self.create_project(teams=[team], fire_project_created=True) ProjectOwnership.objects.create( project_id=project.id, schema=dump_schema([Rule(Matcher("path", "*.cpp"), [Owner("team", team.slug)])]), fallthrough=True, ) rule = project.rule_set.all()[0] records = [ event_to_record(event, (rule,)) for event in self.create_events_from_filenames( project, ["hello.py", "goodbye.py", "hola.py", "adios.py"] ) ] digest = build_digest(project, sort_records(records)) user_ids = [member.user_id for member in team.member_set] assert not user_ids for user_id, user_digest in get_personalized_digests(project.id, digest, user_ids): assert False # no users in this team no digests should be processed
def test_team_without_members(self): team = self.create_team() project = self.create_project(teams=[team], fire_project_created=True) ProjectOwnership.objects.create( project_id=project.id, schema=dump_schema([Rule(Matcher("path", "*.cpp"), [Owner("team", team.slug)])]), fallthrough=True, ) rule = project.rule_set.all()[0] records = [ event_to_record(event, (rule,)) for event in self.create_events_from_filenames( project, ["hello.py", "goodbye.py", "hola.py", "adios.py"] ) ] digest = build_digest(project, sort_records(records))[0] user_ids = [member.user_id for member in team.member_set] assert not user_ids participants_by_provider_by_event = get_participants_by_event(digest, project) assert not { actor for actors in participants_by_provider_by_event.values() for actor in actors } # no users in this team no digests should be processed
def test_team_without_members(self): team = self.create_team() project = self.create_project(teams=[team]) ProjectOwnership.objects.create( project_id=project.id, schema=dump_schema([ Rule(Matcher('path', '*.cpp'), [ Owner('team', team.slug), ]), ]), fallthrough=True, ) rule = project.rule_set.all()[0] records = [ event_to_record(event, (rule, )) for event in self.create_events(timezone.now( ), project, ['hello.py', 'goodbye.py', 'hola.py', 'adios.py']) ] digest = build_digest(project, sort_records(records)) user_ids = [member.user_id for member in team.member_set] assert not user_ids for user_id, user_digest in get_personalized_digests( project.id, digest, user_ids): assert False # no users in this team no digests should be processed
def setUp(self): self.user1 = self.create_user() self.user2 = self.create_user() self.user3 = self.create_user() self.user4 = self.create_user() self.user5 = self.create_user() # this user has no events self.user_ids = [ self.user1.id, self.user2.id, self.user3.id, self.user4.id, self.user5.id ] self.team1 = self.create_team() self.team2 = self.create_team() self.team3 = self.create_team() self.project = self.create_project( teams=[self.team1, self.team2, self.team3]) self.create_member(user=self.user1, organization=self.organization, teams=[self.team1]) self.create_member(user=self.user2, organization=self.organization, teams=[self.team2]) self.create_member(user=self.user3, organization=self.organization, teams=[self.team1, self.team2]) self.create_member(user=self.user4, organization=self.organization, teams=[self.team3]) self.create_member(user=self.user5, organization=self.organization, teams=[self.team3]) start_time = timezone.now() self.team1_events = self.create_events( start_time, self.project, ["hello.py", "goodbye.py", "hola.py", "adios.py"]) self.team2_events = self.create_events( start_time, self.project, ["old.cbl", "retro.cbl", "cool.cbl", "gem.cbl"]) self.user4_events = [ self.create_event( group=self.create_group(project=self.project), data=self.create_event_data("foo.bar", "helloworld.org"), ), self.create_event( group=self.create_group(project=self.project), data=self.create_event_data("bar.foo", "helloworld.org"), ), ] self.team1_matcher = Matcher("path", "*.py") self.team2_matcher = Matcher("path", "*.cbl") self.user4_matcher = Matcher("url", "*.org") ProjectOwnership.objects.create( project_id=self.project.id, schema=dump_schema([ Rule( self.team1_matcher, [ Owner("team", self.team1.slug), Owner("user", self.user3.email) ], ), Rule(self.team2_matcher, [Owner("team", self.team2.slug)]), Rule(self.user4_matcher, [Owner("user", self.user4.email)]), ]), fallthrough=True, )
def test_get_owners_basic(self): rule_a = Rule( Matcher('path', '*.py'), [ Owner('team', self.team.slug), ]) rule_b = Rule( Matcher('path', 'src/*'), [ Owner('user', self.user.email), ]) ProjectOwnership.objects.create( project_id=self.project.id, schema=dump_schema([rule_a, rule_b]), fallthrough=True, ) # No data matches assert ProjectOwnership.get_owners(self.project.id, {}) == (ProjectOwnership.Everyone, None) # Match only rule_a self.assert_ownership_equals(ProjectOwnership.get_owners( self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'foo.py', }] } } ), ([Actor(self.team.id, Team)], [rule_a])) # Match only rule_b self.assert_ownership_equals(ProjectOwnership.get_owners( self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'src/thing.txt', }] } } ), ([Actor(self.user.id, User)], [rule_b])) # Matches both rule_a and rule_b self.assert_ownership_equals(ProjectOwnership.get_owners( self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'src/foo.py', }] } } ), ([Actor(self.user.id, User), Actor(self.team.id, Team)], [rule_a, rule_b])) assert ProjectOwnership.get_owners( self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'xxxx', }] } } ) == (ProjectOwnership.Everyone, None) # When fallthrough = False, we don't implicitly assign to Everyone ProjectOwnership.objects.filter( project_id=self.project.id, ).update(fallthrough=False) assert ProjectOwnership.get_owners( self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'xxxx', }] } } ) == ([], None)