""" Tests unitaires — services et endpoints """ import pytest import asyncio from unittest.mock import patch, MagicMock from pathlib import Path # ── Tests EXIF ──────────────────────────────────────────────── def test_exif_missing_file(): from app.services.exif_service import extract_exif result = extract_exif("/tmp/non_existant.jpg") assert result["make"] is None assert result["gps_lat"] is None assert result["raw"] == {} def test_exif_dms_to_decimal(): from app.services.exif_service import _dms_to_decimal # Paris : 48°51'24.12"N → ~48.856700 dms = ((48, 1), (51, 1), (2412, 100)) result = _dms_to_decimal(dms, "N") assert result is not None assert 48.85 < result < 48.86 # Direction Sud = négatif result_s = _dms_to_decimal(dms, "S") assert result_s < 0 # ── Tests OCR ───────────────────────────────────────────────── def test_ocr_disabled(monkeypatch): monkeypatch.setattr("app.services.ocr_service.settings.OCR_ENABLED", False) from app.services.ocr_service import extract_text result = extract_text("/tmp/test.jpg") assert result["has_text"] is False assert result["text"] is None def test_ocr_language_detection(): from app.services.ocr_service import _detect_language fr_text = "le chat est sur le tapis et la maison est grande" en_text = "the cat is on the mat and the house is great" assert _detect_language(fr_text) == "fr" assert _detect_language(en_text) == "en" assert _detect_language("") == "unknown" # ── Tests Storage ───────────────────────────────────────────── def test_generate_filename(): from app.services.storage import _generate_filename filename, uuid = _generate_filename("photo.jpg") assert filename.endswith(".jpg") assert len(uuid) == 36 # UUID format def test_generate_filename_no_extension(): from app.services.storage import _generate_filename filename, uuid = _generate_filename("image") assert filename.endswith(".jpg") # extension par défaut # ── Tests Schémas ───────────────────────────────────────────── def test_image_detail_schema(): from app.schemas import ImageDetail from app.models.image import ProcessingStatus # Simule un objet Image ORM mock_img = MagicMock() mock_img.id = 1 mock_img.uuid = "abc-123" mock_img.original_name = "test.jpg" mock_img.mime_type = "image/jpeg" mock_img.file_size = 1024 mock_img.width = 800 mock_img.height = 600 mock_img.uploaded_at = None mock_img.processing_status = ProcessingStatus.DONE mock_img.thumbnail_path = None mock_img.exif_make = "Canon" mock_img.exif_model = "EOS R5" mock_img.exif_lens = None mock_img.exif_iso = 400 mock_img.exif_aperture = "f/2.8" mock_img.exif_shutter = "1/250" mock_img.exif_focal = "50mm" mock_img.exif_flash = False mock_img.exif_orientation = 1 mock_img.exif_software = None mock_img.exif_taken_at = None mock_img.exif_gps_lat = 48.857 mock_img.exif_gps_lon = 2.295 mock_img.exif_altitude = 35.0 mock_img.exif_raw = {} mock_img.has_gps = True mock_img.ocr_text = "Bonjour monde" mock_img.ocr_language = "fr" mock_img.ocr_confidence = 0.95 mock_img.ocr_has_text = True mock_img.ai_description = "Une belle photo" mock_img.ai_tags = ["nature", "paysage"] mock_img.ai_confidence = 0.92 mock_img.ai_model_used = "gemini-1.5-pro" mock_img.ai_processed_at = None mock_img.ai_prompt_tokens = 500 mock_img.ai_output_tokens = 200 mock_img.processing_error = None mock_img.processing_started_at = None mock_img.processing_done_at = None detail = ImageDetail.from_orm_full(mock_img) assert detail.id == 1 assert detail.exif.camera.make == "Canon" assert detail.exif.gps.has_gps is True assert detail.ocr.has_text is True assert detail.ai.tags == ["nature", "paysage"]