- Implement tests for database generator to ensure proper session handling. - Create tests for EXIF extraction and conversion functions. - Add tests for image-related endpoints, ensuring proper data retrieval and isolation between clients. - Develop tests for OCR functionality, including language detection and text extraction. - Introduce tests for the image processing pipeline, covering success and failure scenarios. - Validate rate limiting functionality and ensure independent counters for different clients. - Implement scraper tests to verify HTML content fetching and error handling. - Add unit tests for various services, including storage and filename generation. - Establish worker entry point for ARQ to handle background image processing tasks.
122 lines
4.2 KiB
Python
122 lines
4.2 KiB
Python
"""
|
|
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"]
|