From 046006a633d686711da18a9507a3f7f4ef416bf6 Mon Sep 17 00:00:00 2001 From: Gagan Trivedi Date: Wed, 15 Apr 2026 14:57:46 +0530 Subject: [PATCH] feat: add has_approvals() helper to ChangeRequest model Returns True if at least one reviewer has approved the CR. Prerequisite for the serializer-level guard in flagsmith-workflows that will block post-approval edits of a Change Request's content (#7056). --- api/features/workflows/core/models.py | 3 ++ .../core/test_unit_workflows_models.py | 36 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/api/features/workflows/core/models.py b/api/features/workflows/core/models.py index 3d33f48e2f0b..ae0e980d360b 100644 --- a/api/features/workflows/core/models.py +++ b/api/features/workflows/core/models.py @@ -153,6 +153,9 @@ def is_approved(self) -> bool: return self.is_approved_via_environment() return self.is_approved_via_project() + def has_approvals(self) -> bool: + return self.approvals.filter(approved_at__isnull=False).exists() + def is_approved_via_project(self) -> bool: return self.project.minimum_change_request_approvals is None or ( self.approvals.filter(approved_at__isnull=False).count() diff --git a/api/tests/unit/features/workflows/core/test_unit_workflows_models.py b/api/tests/unit/features/workflows/core/test_unit_workflows_models.py index 501ec89d6185..bc6f9032eb0c 100644 --- a/api/tests/unit/features/workflows/core/test_unit_workflows_models.py +++ b/api/tests/unit/features/workflows/core/test_unit_workflows_models.py @@ -141,6 +141,42 @@ def test_change_request_is_approved__minimum_approvals_is_none__returns_true( # assert result is True +def test_change_request_has_approvals__approved_approval__returns_true( + change_request_no_required_approvals: ChangeRequest, +) -> None: + # Given + approver = FFAdminUser.objects.create(email="approver@example.com") + ChangeRequestApproval.objects.create( + user=approver, + change_request=change_request_no_required_approvals, + approved_at=timezone.now(), + ) + + # When + result = change_request_no_required_approvals.has_approvals() + + # Then + assert result is True + + +def test_change_request_has_approvals__pending_approval_only__returns_false( + change_request_no_required_approvals: ChangeRequest, +) -> None: + # Given + assignee = FFAdminUser.objects.create(email="assignee@example.com") + ChangeRequestApproval.objects.create( + user=assignee, + change_request=change_request_no_required_approvals, + approved_at=None, + ) + + # When + result = change_request_no_required_approvals.has_approvals() + + # Then + assert result is False + + def test_change_request_commit__not_approved__raises_exception( # type: ignore[no-untyped-def] change_request_1_required_approvals, ):