diff --git a/src/sentry/incidents/endpoints/serializers/workflow_engine_detector.py b/src/sentry/incidents/endpoints/serializers/workflow_engine_detector.py index 050a016f30cf0d..e7ffaeda45778d 100644 --- a/src/sentry/incidents/endpoints/serializers/workflow_engine_detector.py +++ b/src/sentry/incidents/endpoints/serializers/workflow_engine_detector.py @@ -242,7 +242,7 @@ def get_attrs( detector_trigger_data_conditions = DataCondition.objects.filter( condition_group__in=detector_workflow_condition_group_ids, condition_result__in=[DetectorPriorityLevel.HIGH, DetectorPriorityLevel.MEDIUM], - ) + ).order_by("-condition_result") workflow_dcg_ids = DataConditionGroup.objects.filter( workflowdataconditiongroup__workflow__in=Subquery( DetectorWorkflow.objects.filter(detector__in=detector_ids).values_list( diff --git a/tests/sentry/incidents/endpoints/test_organization_alert_rule_details.py b/tests/sentry/incidents/endpoints/test_organization_alert_rule_details.py index 404caf44ccb7c0..c14d2712672815 100644 --- a/tests/sentry/incidents/endpoints/test_organization_alert_rule_details.py +++ b/tests/sentry/incidents/endpoints/test_organization_alert_rule_details.py @@ -63,6 +63,7 @@ from sentry.testutils.abstract import Abstract from sentry.testutils.helpers.datetime import freeze_time from sentry.testutils.helpers.features import with_feature +from sentry.testutils.helpers.serializer_parity import assert_serializer_parity from sentry.testutils.outbox import outbox_runner from sentry.testutils.silo import assume_test_silo_mode from sentry.testutils.skips import requires_snuba @@ -369,6 +370,21 @@ def test_workflow_engine_serializer_matches_old_serializer(self) -> None: self.assert_alert_detail_results_match(old_data, new_data) + @with_feature("organizations:incidents") + @freeze_time("2024-12-11 03:21:34") + def test_workflow_engine_trigger_order_matches_legacy(self) -> None: + """The frontend uses array position to determine critical vs warning. + Verify the workflow engine returns triggers in the same order as legacy.""" + self.create_team(organization=self.organization, members=[self.user]) + self.login_as(self.user) + + old_resp = self.get_success_response(self.organization.slug, self.alert_rule.id) + + with self.feature("organizations:workflow-engine-rule-serializers"): + new_resp = self.get_success_response(self.organization.slug, self.alert_rule.id) + + assert_serializer_parity(old=old_resp.data, new=new_resp.data) + @with_feature("organizations:incidents") @freeze_time("2024-12-11 03:21:34") def test_workflow_engine_serializer_snoozed_alert_rule(self) -> None: diff --git a/tests/sentry/incidents/endpoints/test_organization_alert_rule_index.py b/tests/sentry/incidents/endpoints/test_organization_alert_rule_index.py index 83741624b32825..7093a13b4c6eef 100644 --- a/tests/sentry/incidents/endpoints/test_organization_alert_rule_index.py +++ b/tests/sentry/incidents/endpoints/test_organization_alert_rule_index.py @@ -391,17 +391,9 @@ def test_workflow_engine_serializer_matches_old_serializer(self) -> None: assert len(new_data) == len(old_data) for old_rule, new_rule in zip(old_data, new_data): - old_sorted = { - **old_rule, - "triggers": sorted(old_rule.get("triggers", []), key=lambda t: t.get("label", "")), - } - new_sorted = { - **new_rule, - "triggers": sorted(new_rule.get("triggers", []), key=lambda t: t.get("label", "")), - } assert_serializer_parity( - old=old_sorted, - new=new_sorted, + old=old_rule, + new=new_rule, known_differences={ # resolveThreshold: Old serializer checked AlertRule.resolve_threshold for None, # but workflow engine always creates a resolve condition during migration. diff --git a/tests/sentry/incidents/serializers/test_workflow_engine_detector.py b/tests/sentry/incidents/serializers/test_workflow_engine_detector.py index 72374aef2a427e..f2295756599a9f 100644 --- a/tests/sentry/incidents/serializers/test_workflow_engine_detector.py +++ b/tests/sentry/incidents/serializers/test_workflow_engine_detector.py @@ -28,19 +28,12 @@ class TestDetectorSerializer(TestWorkflowEngineSerializer): def setUp(self) -> None: super().setUp() - @staticmethod - def _sort_triggers(data: dict[str, Any]) -> dict[str, Any]: - """Sort triggers by id so ordering doesn't affect comparison.""" - result = data.copy() - result["triggers"] = sorted(result.get("triggers", []), key=lambda t: t["id"]) - return result - def test_simple(self) -> None: self.add_warning_trigger() serialized_detector = serialize( self.detector, self.user, WorkflowEngineDetectorSerializer() ) - assert self._sort_triggers(serialized_detector) == self._sort_triggers(self.expected) + assert serialized_detector == self.expected def test_latest_incident(self) -> None: self.add_warning_trigger() @@ -188,7 +181,7 @@ def test_sentry_app(self, mock_sentry_app_components_preparer: Any) -> None: self.user, WorkflowEngineDetectorSerializer(prepare_component_fields=True), ) - assert self._sort_triggers(serialized_detector) == self._sort_triggers(sentry_app_expected) + assert serialized_detector == sentry_app_expected def test_snooze_enabled_detector(self) -> None: serialized = serialize(self.detector, self.user, WorkflowEngineDetectorSerializer())