""" Unit Tests — PDFService with Assessment Data Tests: - PDF generation with assessment items - PDF with GS-specific visit fields (ghost_shopper, visit_type) - PDF with multiple categories (A-E) - PDF includes signature section - PDF binary category D rendering (Ya/Tidak) """ import pytest from sqlalchemy.ext.asyncio import AsyncSession from models.visit_model import Visit from models.assessment_model import Assessment from models.checklist_model import Checklist from models.photo_model import Photo from services.pdf_service import PDFService PDF_MAGIC = b"%PDF" # Standard 21-item assessment template (categories A-E) GS_ITEMS = [ ("A", 1, "Welcoming Kasir (Senyum, Sapa, Salam)", 4.0, "4"), ("A", 2, "Upselling & Cross-selling Menu", 3.0, "3"), ("A", 3, "Konfirmasi pesanan dan penyebutan total harga", 5.0, "5"), ("A", 4, "Waktu penyajian pesanan sesuai standar", 4.0, "4"), ("A", 5, "Ucapan terima kasih dan salam penutup", 4.0, "4"), ("B", 1, "Suhu minuman sesuai standar", 4.0, "4"), ("B", 2, "Rasa minuman sesuai standar", 3.0, "3"), ("B", 3, "Presentasi / tampilan minuman", 5.0, "5"), ("B", 4, "Suhu makanan sesuai standar", 4.0, "4"), ("B", 5, "Rasa dan tampilan makanan", 4.0, "4"), ("C", 1, "Kebersihan meja dan kursi", 4.0, "4"), ("C", 2, "Kebersihan lantai", 3.0, "3"), ("C", 3, "Kebersihan dan kelengkapan toilet", 2.0, "2"), ("C", 4, "Kebersihan area outdoor/teras", 4.0, "4"), ("D", 1, "Kasir memberikan struk belanja", 1.0, "Ya"), ("D", 2, "Staf menggunakan seragam lengkap", 0.0, "Tidak"), ("E", 1, "Kenyamanan suhu ruangan / AC", 4.0, "4"), ("E", 2, "Background music tidak terlalu keras", 5.0, "5"), ("E", 3, "Pencahayaan ruangan memadai", 4.0, "4"), ("E", 4, "Aroma ruangan nyaman", 3.0, "3"), ("E", 5, "Kesan keseluruhan pengalaman", 4.0, "4"), ] @pytest.mark.asyncio class TestPDFWithAssessments: async def _seed_assessments(self, session: AsyncSession, visit: Visit): """Populate a visit with the full 21-item GS assessment.""" for cat, item_no, criteria, score, raw in GS_ITEMS: session.add(Assessment( visit_id=visit.id, category=cat, item_no=item_no, criteria=criteria, score=score, raw_value=raw, )) await session.flush() async def test_pdf_with_assessments_is_valid( self, async_session: AsyncSession, sample_visit: Visit, ): """PDF with assessments should start with PDF magic.""" await self._seed_assessments(async_session, sample_visit) svc = PDFService() result = await svc.generate_visit_report(async_session, sample_visit.id) assert result is not None assert result[:4] == PDF_MAGIC async def test_pdf_with_seeded_assessments( self, async_session: AsyncSession, sample_visit: Visit, ): """PDF after seeding 21 assessment items should still be valid.""" await self._seed_assessments(async_session, sample_visit) svc = PDFService() result = await svc.generate_visit_report(async_session, sample_visit.id) assert result is not None assert result[:4] == PDF_MAGIC assert len(result) > 1024 # Must be at least 1KB async def test_pdf_with_gs_fields_valid( self, async_session: AsyncSession, sample_visit: Visit, ): """Visit with ghost_shopper and visit_type should produce valid PDF.""" # sample_visit already has ghost_shopper="Auditor Test" and visit_type="dine_in" await self._seed_assessments(async_session, sample_visit) svc = PDFService() result = await svc.generate_visit_report(async_session, sample_visit.id) assert result is not None assert result[:4] == PDF_MAGIC assert len(result) > 3000 # Rich content async def test_pdf_full_report_with_all_data( self, async_session: AsyncSession, sample_visit: Visit, ): """Full report: assessments + checklists + photos.""" await self._seed_assessments(async_session, sample_visit) # Add checklists for i, checked in enumerate([True, True, False]): async_session.add(Checklist( visit_id=sample_visit.id, item_name=f"SOP Check {i+1}", is_checked=checked, )) # Add photos for j in range(2): async_session.add(Photo( visit_id=sample_visit.id, file_path=f"/uploads/evidence_{j}.jpg", file_name=f"evidence_{j}.jpg", file_size=51200, mime_type="image/jpeg", )) await async_session.flush() svc = PDFService() result = await svc.generate_visit_report(async_session, sample_visit.id) assert result is not None assert result[:4] == PDF_MAGIC assert len(result) > 5000 # Full report should be substantial async def test_pdf_single_assessment_item( self, async_session: AsyncSession, sample_visit: Visit, sample_assessment: Assessment, ): """PDF with just one assessment item should still be valid.""" svc = PDFService() result = await svc.generate_visit_report(async_session, sample_visit.id) assert result is not None assert result[:4] == PDF_MAGIC