- Config ruff dans pyproject.toml : règles E/W/F/I/UP/B, whitelist des appels d'injection FastAPI (Depends/File/Form/Query) pour B008, B904 ignoré (traduction volontaire des exceptions en réponses HTTP) - Auto-fixes : tri des imports, suppression d'imports inutilisés, annotations PEP 604, f-strings sans placeholder, modes open redondants - Suppression de variables inutilisées (config.reset_setting, parser locataires) ruff check . : All checks passed ; 61 tests OK Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
127 lines
4.2 KiB
Python
127 lines
4.2 KiB
Python
"""Tests de DatabaseService.save_document (logique métier centrale)."""
|
|
|
|
from datetime import date
|
|
|
|
import pytest
|
|
|
|
from plesna_gerance.database.models import (
|
|
Depense,
|
|
Document,
|
|
Immeuble,
|
|
Locataire,
|
|
Lot,
|
|
Revenu,
|
|
Tag,
|
|
)
|
|
from plesna_gerance.database.service import DatabaseService, DuplicateDocumentError
|
|
|
|
|
|
def test_save_document_creates_full_graph(db_session, sample_data):
|
|
service = DatabaseService(db_session)
|
|
doc = service.save_document(data=sample_data, source_file="cr.pdf")
|
|
|
|
assert doc.id is not None
|
|
assert doc.reference == "REF001"
|
|
assert doc.date == date(2024, 1, 15)
|
|
assert doc.solde_montant == 100.0
|
|
|
|
# Immeuble / lot / locataire créés
|
|
assert db_session.query(Immeuble).filter_by(code="IMM1").count() == 1
|
|
assert db_session.query(Lot).filter_by(numero="001").count() == 1
|
|
assert db_session.query(Locataire).filter_by(nom="DUPONT").count() == 1
|
|
|
|
# Revenu et dépense rattachés
|
|
revenus = db_session.query(Revenu).all()
|
|
assert len(revenus) == 1 and revenus[0].loyers == 500.0
|
|
depenses = db_session.query(Depense).all()
|
|
assert len(depenses) == 1 and depenses[0].debit == 50.0
|
|
|
|
|
|
def test_save_document_requires_reference_and_date(db_session):
|
|
service = DatabaseService(db_session)
|
|
with pytest.raises(ValueError):
|
|
service.save_document(data={"metadata": {"document": {}}})
|
|
|
|
|
|
def test_save_document_duplicate_raises(db_session, sample_data):
|
|
service = DatabaseService(db_session)
|
|
service.save_document(data=sample_data)
|
|
with pytest.raises(DuplicateDocumentError):
|
|
service.save_document(data=sample_data)
|
|
|
|
|
|
def test_save_document_overwrite_replaces(db_session, sample_data):
|
|
service = DatabaseService(db_session)
|
|
first = service.save_document(data=sample_data)
|
|
first_id = first.id
|
|
|
|
# Réécriture : l'ancien document est supprimé puis recréé (cascade incluse).
|
|
# NB : SQLite peut réutiliser le même rowid, on ne compare donc pas les ids.
|
|
assert first_id is not None
|
|
service.save_document(data=sample_data, overwrite=True)
|
|
|
|
assert db_session.query(Document).count() == 1
|
|
assert db_session.query(Revenu).count() == 1
|
|
assert db_session.query(Depense).count() == 1
|
|
|
|
|
|
def test_save_document_reuses_immeuble(db_session, sample_data):
|
|
service = DatabaseService(db_session)
|
|
service.save_document(data=sample_data)
|
|
|
|
# Deuxième document, même immeuble, référence différente
|
|
data2 = {**sample_data}
|
|
data2["metadata"] = {
|
|
**sample_data["metadata"],
|
|
"document": {**sample_data["metadata"]["document"], "reference": "REF002"},
|
|
}
|
|
service.save_document(data=data2)
|
|
|
|
assert db_session.query(Immeuble).filter_by(code="IMM1").count() == 1
|
|
assert db_session.query(Document).count() == 2
|
|
|
|
|
|
def test_save_document_assigns_tags(db_session, sample_data):
|
|
service = DatabaseService(db_session)
|
|
tag = db_session.query(Tag).first()
|
|
assert tag is not None
|
|
|
|
doc = service.save_document(
|
|
data=sample_data,
|
|
depenses_tags=[{"index": 0, "tag_id": tag.id}],
|
|
)
|
|
|
|
depense = db_session.query(Depense).filter_by(document_id=doc.id).first()
|
|
assert depense.tag_id == tag.id
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"raw,expected",
|
|
[
|
|
(100.0, 100.0),
|
|
(100, 100.0),
|
|
("1 234,56", 1234.56), # string française -> parsée
|
|
(None, None),
|
|
({}, None), # type inattendu -> None, jamais stocké tel quel
|
|
(True, None), # bool exclu
|
|
],
|
|
)
|
|
def test_normalize_amount(raw, expected):
|
|
assert DatabaseService._normalize_amount(raw) == expected
|
|
|
|
|
|
def test_save_document_solde_string_is_normalized(db_session, sample_data):
|
|
sample_data["metadata"]["solde"]["montant"] = "1 234,56"
|
|
service = DatabaseService(db_session)
|
|
doc = service.save_document(data=sample_data)
|
|
assert isinstance(doc.solde_montant, float)
|
|
assert doc.solde_montant == pytest.approx(1234.56)
|
|
|
|
|
|
def test_check_duplicate(db_session, sample_data):
|
|
service = DatabaseService(db_session)
|
|
assert service.check_duplicate("REF001", date(2024, 1, 15)) is None
|
|
service.save_document(data=sample_data)
|
|
found = service.check_duplicate("REF001", date(2024, 1, 15))
|
|
assert found is not None and found.reference == "REF001"
|