from datetime import datetime from io import BytesIO from unittest.mock import MagicMock, patch from uuid import uuid4 from zipfile import ZipFile from openpyxl import load_workbook from rest_framework import status from rest_framework.test import APITestCase from utils.clients.gooyal_advertising_client.models.ticket_state_enum import TicketStateEnum class GenerateReportTests(APITestCase): def _build_ticket(self, user_uuid=None): ticket = MagicMock() ticket.uuid = uuid4() ticket.user = user_uuid or uuid4() ticket.title = "Login issue" ticket.description = "Cannot login" ticket.state = TicketStateEnum(10) ticket.created_at = datetime(2026, 5, 20, 10, 0) ticket.updated_at = datetime(2026, 5, 21, 10, 0) return ticket def _build_user(self, user_uuid): user = MagicMock() user.uuid = user_uuid user.username = "alice" user.first_name = "Alice" user.last_name = "Doe" user.email = "alice@example.com" user.phone_number = "09120000000" user.address = "Tehran" return user @patch("apps.reports.services.data_sources.ReportDataSources.fetch_users_for_tickets") @patch("apps.reports.services.data_sources.ReportDataSources.fetch_application_tickets") def test_generate_report_returns_zip_with_two_excel_files( self, mock_fetch_tickets, mock_fetch_users, ): ticket = self._build_ticket() user = self._build_user(ticket.user) mock_fetch_tickets.return_value = [ticket] mock_fetch_users.return_value = [user] response = self.client.post("/api/reports/generate/") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response["Content-Type"], "application/zip") self.assertEqual( response["Content-Disposition"], 'attachment; filename="crm-reports.zip"', ) with ZipFile(BytesIO(response.content)) as archive: self.assertEqual( set(archive.namelist()), {"users-report.xlsx", "tickets-report.xlsx"}, ) users_workbook = load_workbook(BytesIO(archive.read("users-report.xlsx"))) users_sheet = users_workbook.active self.assertEqual(users_sheet["A1"].value, "Users Report") self.assertEqual( [cell.value for cell in users_sheet[2]], [ "User UUID", "Username", "First Name", "Last Name", "Email", "Phone Number", "Address", ], ) self.assertEqual( [cell.value for cell in users_sheet[3]], [ str(user.uuid), "alice", "Alice", "Doe", "alice@example.com", "09120000000", "Tehran", ], ) tickets_workbook = load_workbook( BytesIO(archive.read("tickets-report.xlsx")) ) tickets_sheet = tickets_workbook.active self.assertEqual(tickets_sheet["A1"].value, "Tickets Report") self.assertEqual( [cell.value for cell in tickets_sheet[2]], [ "Ticket UUID", "User UUID", "Title", "Description", "State", "Created At", "Updated At", ], ) self.assertEqual( [cell.value for cell in tickets_sheet[3]], [ str(ticket.uuid), str(ticket.user), "Login issue", "Cannot login", "CLOSED", "2026-05-20 10:00:00", "2026-05-21 10:00:00", ], ) @patch("apps.reports.services.data_sources.ReportDataSources.fetch_users_for_tickets") @patch("apps.reports.services.data_sources.ReportDataSources.fetch_application_tickets") def test_generate_report_skips_missing_users( self, mock_fetch_tickets, mock_fetch_users, ): ticket = self._build_ticket() mock_fetch_tickets.return_value = [ticket] mock_fetch_users.return_value = [None] response = self.client.post("/api/reports/generate/") self.assertEqual(response.status_code, status.HTTP_200_OK) with ZipFile(BytesIO(response.content)) as archive: users_workbook = load_workbook(BytesIO(archive.read("users-report.xlsx"))) self.assertIsNone(users_workbook.active["A3"].value)