Spaces:
Sleeping
Sleeping
| """ | |
| 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"), | |
| ] | |
| 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 | |