3434from sentry .grouping .grouptype import ErrorGroupType
3535from sentry .issues .action_log import ActionSource , GroupActionActor , action_context_scope
3636from sentry .issues .action_log .types import GroupActionType , GroupActorType
37+ from sentry .issues .derived .gate import GROUP_ACTION_LOG_BACKFILL_COMPLETED_OPTION
3738from sentry .issues .issue_search import parse_search_query
3839from sentry .issues .models .groupactionlogentry import GroupActionLogEntry
3940from sentry .models .activity import Activity
@@ -736,6 +737,7 @@ def test_resolve_in_next_release_ignores_archived_releases(self) -> None:
736737 def test_resolve_in_next_release_activity_from_action_log (self ) -> None :
737738 self .create_release (project = self .project , version = "test@1.0.0.0" )
738739 group = self .create_group (status = GroupStatus .UNRESOLVED )
740+ self .project .update_option (GROUP_ACTION_LOG_BACKFILL_COMPLETED_OPTION , True )
739741 GroupActionLogEntry .objects .create (
740742 group_id = group .id ,
741743 project_id = group .project_id ,
@@ -751,24 +753,34 @@ def test_resolve_in_next_release_activity_from_action_log(self) -> None:
751753 request = _wrap_request (http_request , data = {"status" : "resolvedInNextRelease" })
752754
753755 group_list = get_group_list (self .organization .id , [self .project ], request .GET .getlist ("id" ))
754- with self .feature ("projects:issue-action-log-activity" ):
756+ with self .feature (
757+ ["projects:issue-action-log-write-to-db" , "projects:issue-action-log-activity" ]
758+ ):
755759 response = update_groups (request , group_list )
756760
757761 activity = response .data ["activity" ]
758- assert [entry ["type" ] for entry in activity ] == ["set_resolved" , "first_seen" ]
762+ # the manually logged RESOLVE exists only in GALE, so its presence means
763+ # the action log was served rather than Activity
764+ assert "set_resolved" in [entry ["type" ] for entry in activity ]
759765 assert activity [- 1 ]["id" ] == "0"
760766
761- def test_resolve_in_next_release_no_activity_without_action_log (self ) -> None :
767+ def test_resolve_in_next_release_no_activity_when_action_log_is_empty (self ) -> None :
768+ # A gated project can still read an empty log: the GALE write for this
769+ # resolve goes through an outbox that may not have drained yet.
762770 self .create_release (project = self .project , version = "test@1.0.0.0" )
763771 group = self .create_group (status = GroupStatus .UNRESOLVED )
772+ self .project .update_option (GROUP_ACTION_LOG_BACKFILL_COMPLETED_OPTION , True )
764773
765774 http_request = self .make_request (user = self .user , method = "GET" )
766775 http_request .GET = QueryDict (query_string = f"id={ group .id } " )
767776 request = _wrap_request (http_request , data = {"status" : "resolvedInNextRelease" })
768777
769778 group_list = get_group_list (self .organization .id , [self .project ], request .GET .getlist ("id" ))
770779 with (
771- self .feature ("projects:issue-action-log-activity" ),
780+ self .feature (
781+ ["projects:issue-action-log-write-to-db" , "projects:issue-action-log-activity" ]
782+ ),
783+ patch .object (GroupActionLogEntry .objects , "get_actions_for_group" , return_value = []),
772784 self .assertLogs ("sentry.api.helpers.group_index.update" , level = "INFO" ) as logs ,
773785 ):
774786 response = update_groups (request , group_list )
@@ -778,7 +790,35 @@ def test_resolve_in_next_release_no_activity_without_action_log(self) -> None:
778790 )
779791 assert response is not None
780792 assert "activity" not in response .data
781- assert GroupActionLogEntry .objects .filter (group_id = group .id ).count () == 0
793+
794+ def test_resolve_in_next_release_ignores_action_log_when_not_backfilled (self ) -> None :
795+ # The log covers only part of this project's history, so serving it would
796+ # silently drop everything that predates the rollout. Fall back to Activity.
797+ self .create_release (project = self .project , version = "test@1.0.0.0" )
798+ group = self .create_group (status = GroupStatus .UNRESOLVED )
799+ GroupActionLogEntry .objects .create (
800+ group_id = group .id ,
801+ project_id = group .project_id ,
802+ type = GroupActionType .COMMENT .value ,
803+ actor_type = GroupActorType .USER .value ,
804+ actor_id = self .user .id ,
805+ source = "web" ,
806+ data = {"comment_id" : 123 , "text" : "hello world" },
807+ )
808+
809+ http_request = self .make_request (user = self .user , method = "GET" )
810+ http_request .GET = QueryDict (query_string = f"id={ group .id } " )
811+ request = _wrap_request (http_request , data = {"status" : "resolvedInNextRelease" })
812+
813+ group_list = get_group_list (self .organization .id , [self .project ], request .GET .getlist ("id" ))
814+ with self .feature (
815+ ["projects:issue-action-log-write-to-db" , "projects:issue-action-log-activity" ]
816+ ):
817+ response = update_groups (request , group_list )
818+
819+ # the COMMENT only exists in the log, so its absence means Activity was served
820+ activity = response .data ["activity" ]
821+ assert "note" not in [entry ["type" ] for entry in activity ]
782822
783823
784824class MergeGroupsTest (TestCase ):
0 commit comments