""" Tests for the suggestion closed-loop communication fixes: - Fix 2.1(a): public submission sends an acknowledgement SMS - Fix 2.1(b): status change to acknowledged/closed notifies the suggester """ from unittest.mock import patch from django.contrib.auth.models import Group from django.test import TestCase from django.urls import reverse from apps.accounts.models import User from apps.feedback.models import Feedback, FeedbackStatus, FeedbackType from apps.organizations.models import Hospital class PublicSuggestionAcknowledgementTests(TestCase): """Fix 2.1(a) — public suggestion submit sends an acknowledgement SMS.""" def setUp(self): self.hospital = Hospital.objects.create(name="Ack Test Hospital", code="ACK", status="active") @patch("apps.feedback.views.NotificationService.send_sms") @patch("apps.feedback.tasks.analyze_suggestion_with_ai.delay") @patch("apps.complaints.tasks.notify_staff_new_item.delay") def test_public_suggestion_sends_acknowledgement(self, mock_notify_staff, mock_ai, mock_sms): data = { "contact_name": "Jane Doe", "contact_phone": "0501234567", "message": "Please add more seating in the waiting area.", "hospital": str(self.hospital.id), } response = self.client.post( reverse("feedback:public_suggestion_submit"), data, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) self.assertEqual(response.status_code, 200) result = response.json() self.assertTrue(result["success"]) feedback = Feedback.objects.get() self.assertEqual(feedback.feedback_type, FeedbackType.SUGGESTION) # Acknowledgement SMS fired exactly once to the suggester's phone. mock_sms.assert_called_once() called_phone = mock_sms.call_args.args[0] self.assertEqual(called_phone, "0501234567") class SuggestionStatusChangeNotificationTests(TestCase): """Fix 2.1(b) — moving to ACKNOWLEDGED/CLOSED notifies the suggester.""" def setUp(self): self.hospital = Hospital.objects.create(name="SC Test Hospital", code="SC", status="active") px_group = Group.objects.create(name="PX Employee") self.user = User.objects.create_user(email="px@example.com", password="pass12345") self.user.groups.add(px_group) self.client.force_login(self.user) def _make_suggestion(self, status=FeedbackStatus.REVIEWED, phone="0509876543"): return Feedback.objects.create( hospital=self.hospital, feedback_type=FeedbackType.SUGGESTION, title="Improve signage", message="The signage in the ER is confusing.", status=status, contact_name="Bob", contact_phone=phone, ) @patch("apps.feedback.views.NotificationService.send_sms") def test_acknowledged_notifies_suggester(self, mock_sms): feedback = self._make_suggestion(status=FeedbackStatus.REVIEWED, phone="0509876543") response = self.client.post( reverse("feedback:feedback_change_status", kwargs={"pk": feedback.pk}), {"status": FeedbackStatus.ACKNOWLEDGED.value}, ) self.assertEqual(response.status_code, 302) feedback.refresh_from_db() self.assertEqual(feedback.status, FeedbackStatus.ACKNOWLEDGED.value) mock_sms.assert_called_once() self.assertEqual(mock_sms.call_args.args[0], "0509876543") @patch("apps.feedback.views.NotificationService.send_sms") def test_closed_notifies_suggester(self, mock_sms): feedback = self._make_suggestion(status=FeedbackStatus.REVIEWED, phone="0501112233") response = self.client.post( reverse("feedback:feedback_change_status", kwargs={"pk": feedback.pk}), {"status": FeedbackStatus.CLOSED.value}, ) self.assertEqual(response.status_code, 302) feedback.refresh_from_db() self.assertEqual(feedback.status, FeedbackStatus.CLOSED.value) mock_sms.assert_called_once() @patch("apps.feedback.views.NotificationService.send_sms") def test_no_phone_skips_notification(self, mock_sms): feedback = self._make_suggestion(status=FeedbackStatus.REVIEWED, phone="") self.client.post( reverse("feedback:feedback_change_status", kwargs={"pk": feedback.pk}), {"status": FeedbackStatus.ACKNOWLEDGED.value}, ) mock_sms.assert_not_called() @patch("apps.feedback.views.NotificationService.send_sms") def test_reviewed_does_not_notify(self, mock_sms): # Only acknowledged/closed notify — reviewed is an internal triage step. feedback = self._make_suggestion(status=FeedbackStatus.SUBMITTED, phone="0509998877") self.client.post( reverse("feedback:feedback_change_status", kwargs={"pk": feedback.pk}), {"status": FeedbackStatus.REVIEWED.value}, ) mock_sms.assert_not_called()