from django.test import TestCase from django.contrib.auth import get_user_model from rest_framework.test import APIClient from rest_framework import status from apps.profiles.models import Institution from apps.tickets.models import Ticket, TicketMessage, TicketStatus, TicketPriority, TicketCategory User = get_user_model() class Phase9TicketsAPITests(TestCase): """ Automated test suite for Phase 9: Official Ticketing - Ticket Submission with Tracking Code Generation - Ticket List & Filtering - Ticket Detail & Message History - User Reply & Official Astan Response Flow - Ticket Closure & Satisfaction Rating """ def setUp(self): self.client = APIClient() self.user = User.objects.create_user( email="representative@beirutcenter.org", password="Password123!", fullname="Sheikh Ali Ammar", user_type=User.UserType.INSTITUTION_ADMIN, ) self.staff_user = User.objects.create_user( email="officer@astan.ir", password="AdminPassword123!", fullname="Mohammad Hosseini", user_type=User.UserType.SUPER_ADMIN, is_staff=True, ) self.institution = Institution.objects.create( name="Imam Ali Cultural Center Beirut", country="Lebanon", city="Beirut", type=Institution.InstitutionType.CULTURAL_CENTER, ) def test_ticket_creation_and_auto_tracking_code(self): self.client.force_authenticate(user=self.user) payload = { "subject": "Request for specialized cultural books and exhibition materials", "category": TicketCategory.CULTURAL_AFFAIRS, "priority": TicketPriority.HIGH, "institution": self.institution.id, "content": "We are organizing the annual Razavi Cultural Week in Beirut and request books in Arabic.", } response = self.client.post("/api/v1/tickets/", payload, format="json") self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertTrue(response.data["tracking_code"].startswith("TK-RAZAVI-")) self.assertEqual(response.data["status"], TicketStatus.OPEN) self.assertEqual(len(response.data["messages"]), 1) self.assertEqual(response.data["messages"][0]["content"], payload["content"]) def test_ticket_listing_and_filtering(self): self.client.force_authenticate(user=self.user) # Create two tickets t1 = Ticket.objects.create( user=self.user, subject="Theological inquiry", category=TicketCategory.RELIGIOUS_INQUIRY, priority=TicketPriority.MEDIUM, status=TicketStatus.OPEN, ) t2 = Ticket.objects.create( user=self.user, subject="Financial grant clarification", category=TicketCategory.FINANCIAL_LEGAL, priority=TicketPriority.URGENT, status=TicketStatus.IN_PROGRESS, ) response = self.client.get("/api/v1/tickets/") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.data), 2) # Filter by priority res_filter = self.client.get(f"/api/v1/tickets/?priority={TicketPriority.URGENT}") self.assertEqual(res_filter.status_code, status.HTTP_200_OK) self.assertEqual(len(res_filter.data), 1) self.assertEqual(res_filter.data[0]["id"], t2.id) def test_ticket_dialogue_reply_and_official_response(self): ticket = Ticket.objects.create( user=self.user, subject="Cooperation Inquiry", category=TicketCategory.INTER_CENTER_COOPERATION, status=TicketStatus.OPEN, ) # 1. Staff replies with official response self.client.force_authenticate(user=self.staff_user) reply_payload = { "content": "Your request has been approved by the Department of International Cultural Affairs.", "department_label": "International Cultural Affairs Desk", } response = self.client.post(f"/api/v1/tickets/{ticket.tracking_code}/reply/", reply_payload, format="json") self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertTrue(response.data["is_official_response"]) self.assertEqual(response.data["department_label"], "International Cultural Affairs Desk") # Ticket status should automatically transition to 'answered' ticket.refresh_from_db() self.assertEqual(ticket.status, TicketStatus.ANSWERED) self.assertEqual(ticket.assigned_expert, self.staff_user) # 2. User replies back self.client.force_authenticate(user=self.user) user_reply = {"content": "Thank you for the prompt official confirmation."} res_user = self.client.post(f"/api/v1/tickets/{ticket.tracking_code}/reply/", user_reply, format="json") self.assertEqual(res_user.status_code, status.HTTP_201_CREATED) self.assertFalse(res_user.data["is_official_response"]) ticket.refresh_from_db() self.assertEqual(ticket.status, TicketStatus.IN_PROGRESS) def test_ticket_close_and_rating(self): ticket = Ticket.objects.create( user=self.user, subject="General questions", status=TicketStatus.ANSWERED, ) self.client.force_authenticate(user=self.user) # 1. Close ticket close_res = self.client.post(f"/api/v1/tickets/{ticket.id}/close/") self.assertEqual(close_res.status_code, status.HTTP_200_OK) self.assertEqual(close_res.data["status"], TicketStatus.CLOSED) self.assertIsNotNone(close_res.data["closed_at"]) # 2. Submit rating rate_payload = { "rating": 5, "feedback": "Outstanding speed and support from Astan officers.", } rate_res = self.client.post(f"/api/v1/tickets/{ticket.id}/rate/", rate_payload, format="json") self.assertEqual(rate_res.status_code, status.HTTP_200_OK) self.assertEqual(rate_res.data["satisfaction_rating"], 5) self.assertEqual(rate_res.data["feedback_comment"], rate_payload["feedback"])