""" Unit Tests — PDFService Tests: - Returns None for a visit that doesn't exist - Returns bytes for a valid visit - Output starts with PDF magic bytes (%PDF) - PDF size is reasonable (>1KB) - Works correctly with no checklists / no photos - Works with checklists and photos present """ import pytest from sqlalchemy.ext.asyncio import AsyncSession from models.visit_model import Visit from models.checklist_model import Checklist from models.photo_model import Photo from services.pdf_service import PDFService PDF_MAGIC = b"%PDF" @pytest.mark.asyncio class TestPDFService: async def test_returns_none_for_missing_visit( self, async_session: AsyncSession ): import uuid svc = PDFService() result = await svc.generate_visit_report(async_session, uuid.uuid4()) assert result is None async def test_returns_bytes_for_valid_visit( self, async_session: AsyncSession, sample_visit: Visit ): svc = PDFService() result = await svc.generate_visit_report(async_session, sample_visit.id) assert result is not None assert isinstance(result, bytes) async def test_output_is_valid_pdf( self, async_session: AsyncSession, sample_visit: Visit ): """PDF must start with the %PDF magic header.""" 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_has_reasonable_size( self, async_session: AsyncSession, sample_visit: Visit ): """PDF should be at least 1KB (basic structure is there).""" svc = PDFService() result = await svc.generate_visit_report(async_session, sample_visit.id) assert result is not None assert len(result) > 1024 # > 1 KB async def test_pdf_with_no_checklists_no_photos( self, async_session: AsyncSession, sample_visit: Visit ): """Visit with no related data should still produce a valid PDF.""" 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_checklists( self, async_session: AsyncSession, sample_visit: Visit, sample_checklist: Checklist, ): """Checklist data should be included — PDF size increases.""" svc = PDFService() result = await svc.generate_visit_report(async_session, sample_visit.id) assert result is not None assert len(result) > 1024 async def test_pdf_with_photos( self, async_session: AsyncSession, sample_visit: Visit, sample_photo: Photo, ): """Photo metadata table should be included — PDF still valid.""" 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_all_data( self, async_session: AsyncSession, sample_visit: Visit, ): """Full visit with multiple checklists and photos.""" # Add 3 checklists for i, checked in enumerate([True, False, True]): async_session.add(Checklist( visit_id=sample_visit.id, item_name=f"Step {i+1}", is_checked=checked, notes=f"Note for step {i+1}" if checked else None, )) # Add 2 photos for j in range(2): async_session.add(Photo( visit_id=sample_visit.id, file_path=f"/uploads/photo_{j}.jpg", file_name=f"photo_{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) > 2048 # Richer content = larger PDF