"""Tests for the suggestion workflow realignment (optional implemented-notice SMS).""" from unittest.mock import patch from django.contrib.auth.models import Group import pytest from django.test import TestCase from django.urls import reverse from apps.accounts.models import User from apps.organizations.models import Hospital from apps.feedback.models import Feedback, FeedbackStatus, FeedbackType def _ensure_group(name): """Get or create a role group (pytest runs with --nomigrations).""" grp, _ = Group.objects.get_or_create(name=name) return grp @pytest.mark.django_db class TestSuggestionImplementedNotice(TestCase): """Closing a suggestion with implemented_notice=True sends the high-value SMS. Uses self.client because feedback_change_status calls messages.success/error (needs MessageMiddleware) and redirects (302) — no template rendering. """ def setUp(self): self.hospital = Hospital.objects.create(name="Test Hospital", code="TH09") # Use PX Employee (not PX Admin) so HospitalSelectionMiddleware uses # user.hospital directly instead of requiring session['selected_hospital_id']. _ensure_group("PX Employee") self.user = User.objects.create_user( username="pxsug", email="pxsug@test", password="x", hospital=self.hospital ) self.user.groups.add(Group.objects.get(name="PX Employee")) self.client.force_login(self.user) self.feedback = Feedback.objects.create( hospital=self.hospital, feedback_type=FeedbackType.SUGGESTION, status=FeedbackStatus.REVIEWED, contact_phone="+966500000000", contact_name="Patient", message="Add online check-in", ) @patch("apps.feedback.views.NotificationService.send_sms") def test_close_with_implemented_notice_sends_implemented_sms(self, mock_sms): """When implemented_notice=on and status=closed, SMS body mentions 'implemented'.""" response = self.client.post( reverse("feedback:feedback_change_status", kwargs={"pk": self.feedback.pk}), data={"status": "closed", "implemented_notice": "on"}, ) assert response.status_code == 302, f"expected redirect, got {response.status_code}" self.feedback.refresh_from_db() assert self.feedback.status == FeedbackStatus.CLOSED assert mock_sms.called, "send_sms should have been called" last_call_body = mock_sms.call_args[0][1] assert "implemented" in last_call_body.lower(), \ f"Expected 'implemented' in SMS body, got: {last_call_body}" @patch("apps.feedback.views.NotificationService.send_sms") def test_close_without_implemented_notice_sends_generic_sms(self, mock_sms): """Without the flag, the generic closed message fires (no 'implemented').""" response = self.client.post( reverse("feedback:feedback_change_status", kwargs={"pk": self.feedback.pk}), data={"status": "closed"}, ) assert response.status_code == 302 self.feedback.refresh_from_db() assert self.feedback.status == FeedbackStatus.CLOSED assert mock_sms.called last_call_body = mock_sms.call_args[0][1] assert "implemented" not in last_call_body.lower(), \ f"Did not expect 'implemented' in generic SMS, got: {last_call_body}" @patch("apps.feedback.views.NotificationService.send_sms") def test_implemented_notice_ignored_when_status_not_closed(self, mock_sms): """The flag has no effect when transitioning to a non-closed status (e.g. acknowledged).""" response = self.client.post( reverse("feedback:feedback_change_status", kwargs={"pk": self.feedback.pk}), data={"status": "acknowledged", "implemented_notice": "on"}, ) assert response.status_code == 302 self.feedback.refresh_from_db() assert self.feedback.status == FeedbackStatus.ACKNOWLEDGED # SMS fires for acknowledged too (existing behavior), but must NOT say 'implemented'. if mock_sms.called: last_call_body = mock_sms.call_args[0][1] assert "implemented" not in last_call_body.lower()