66from unittest import mock
77from uuid import uuid4
88
9- from snuba_sdk import Column , Condition , Entity , Function , Op , Query , Request
9+ from snuba_sdk import Column , Condition , DeleteQuery , Entity , Function , Op , Query , Request
1010
1111from sentry import deletions , nodestore
1212from sentry .deletions .defaults .group import update_group_hash_metadata_in_batches
1313from sentry .deletions .tasks .groups import delete_groups_for_project
14- from sentry .issues .grouptype import GroupCategory
14+ from sentry .issues .grouptype import FeedbackGroup , GroupCategory
1515from sentry .issues .issue_occurrence import IssueOccurrence
1616from sentry .models .activity import Activity
1717from sentry .models .eventattachment import EventAttachment
@@ -441,6 +441,67 @@ def select_rows(
441441 def tenant_ids (self ) -> dict [str , str ]:
442442 return {"referrer" : self .referrer , "organization_id" : self .organization .id }
443443
444+ @mock .patch ("sentry.deletions.tasks.nodestore.bulk_snuba_queries" )
445+ def test_simple_issue_platform (self , mock_bulk_snuba_queries : mock .Mock ) -> None :
446+ # Adding this query here to make sure that the cache is not being used
447+ assert self .select_error_events (self .project .id ) is None
448+ assert self .select_issue_platform_events (self .project .id ) is None
449+
450+ # Create initial error event and occurrence related to it; two different groups will exist
451+ event = self .store_event (data = {}, project_id = self .project .id )
452+ # XXX: We need a different way of creating occurrences which will insert into the nodestore
453+ occurrence_event , issue_platform_group = self .create_occurrence (
454+ event , type_id = FeedbackGroup .type_id
455+ )
456+
457+ # Assertions after creation
458+ assert occurrence_event .id != event .event_id
459+ assert event .group_id != issue_platform_group .id
460+ assert event .group .issue_category == GroupCategory .ERROR
461+ assert issue_platform_group .issue_category == GroupCategory .FEEDBACK
462+ assert issue_platform_group .type == FeedbackGroup .type_id
463+
464+ # Assert that the error event has been inserted in the nodestore & Snuba
465+ event_node_id = Event .generate_node_id (event .project_id , event .event_id )
466+ assert nodestore .backend .get (event_node_id )
467+ expected_error = {"event_id" : event .event_id , "group_id" : event .group_id }
468+ assert self .select_error_events (self .project .id ) == expected_error
469+
470+ # Assert that the occurrence event has been inserted in the nodestore & Snuba
471+ expected_occurrence_event = {
472+ "event_id" : occurrence_event .event_id ,
473+ "group_id" : issue_platform_group .id ,
474+ "occurrence_id" : occurrence_event .id ,
475+ }
476+ assert self .select_issue_platform_events (self .project .id ) == expected_occurrence_event
477+
478+ # This will delete the group and the events from the node store and Snuba
479+ with self .tasks ():
480+ delete_groups_for_project (
481+ object_ids = [issue_platform_group .id ],
482+ transaction_id = uuid4 ().hex ,
483+ project_id = self .project .id ,
484+ )
485+
486+ # The original error event and group still exist
487+ assert Group .objects .filter (id = event .group_id ).exists ()
488+ assert nodestore .backend .get (event_node_id )
489+ assert self .select_error_events (self .project .id ) == expected_error
490+
491+ # The Issue Platform group and occurrence have been deleted from Postgres
492+ assert not Group .objects .filter (id = issue_platform_group .id ).exists ()
493+ # assert not nodestore.backend.get(occurrence_node_id)
494+
495+ # Verify that a DELETE query was sent to Snuba with the correct conditions
496+ mock_bulk_snuba_queries .assert_called_once ()
497+ requests = mock_bulk_snuba_queries .call_args [0 ][0 ]
498+ assert len (requests ) == 1
499+ delete_request = requests [0 ]
500+ assert isinstance (delete_request .query , DeleteQuery )
501+ assert delete_request .dataset == "search_issues"
502+ assert delete_request .query .column_conditions ["project_id" ] == [self .project .id ]
503+ assert delete_request .query .column_conditions ["group_id" ] == [issue_platform_group .id ]
504+
444505 @mock .patch ("sentry.deletions.tasks.nodestore.bulk_snuba_queries" )
445506 def test_issue_platform_batching (self , mock_bulk_snuba_queries : mock .Mock ) -> None :
446507 # Patch max_rows_to_delete to a small value for testing
@@ -455,18 +516,17 @@ def test_issue_platform_batching(self, mock_bulk_snuba_queries: mock.Mock) -> No
455516 group4 = self .create_group (project = self .project )
456517
457518 # Set times_seen for each group
458- Group .objects .filter (id = group1 .id ).update (times_seen = 3 , type = GroupCategory . FEEDBACK )
459- Group .objects .filter (id = group2 .id ).update (times_seen = 1 , type = GroupCategory . FEEDBACK )
460- Group .objects .filter (id = group3 .id ).update (times_seen = 3 , type = GroupCategory . FEEDBACK )
461- Group .objects .filter (id = group4 .id ).update (times_seen = 3 , type = GroupCategory . FEEDBACK )
519+ Group .objects .filter (id = group1 .id ).update (times_seen = 3 , type = FeedbackGroup . type_id )
520+ Group .objects .filter (id = group2 .id ).update (times_seen = 1 , type = FeedbackGroup . type_id )
521+ Group .objects .filter (id = group3 .id ).update (times_seen = 3 , type = FeedbackGroup . type_id )
522+ Group .objects .filter (id = group4 .id ).update (times_seen = 3 , type = FeedbackGroup . type_id )
462523
463524 # This will delete the group and the events from the node store and Snuba
464- with self .tasks ():
465- delete_groups_for_project (
466- object_ids = [group1 .id , group2 .id , group3 .id , group4 .id ],
467- transaction_id = uuid4 ().hex ,
468- project_id = self .project .id ,
469- )
525+ delete_groups_for_project (
526+ object_ids = [group1 .id , group2 .id , group3 .id , group4 .id ],
527+ transaction_id = uuid4 ().hex ,
528+ project_id = self .project .id ,
529+ )
470530
471531 assert mock_bulk_snuba_queries .call_count == 1
472532 # There should be two batches with max_rows_to_delete=6
0 commit comments