Imago/tests/test_services.py
Bruno Charest cc99fea20a
Some checks failed
CI / Lint & Format (push) Has been cancelled
CI / Tests (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
Add comprehensive test suite for image processing and related services
- 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.
2026-02-24 11:22:10 -05:00

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"]