Catalog central pur app/errors.py ca sursa unica cod->{problema,fix},
consumat de API+UI+worker. Aditiv (field/message pastrate la octet) +
rar_error stocat superset. Scope: fluxul de declarare; login/signup/CSRF
neatinse. labels.parse_erori degradeaza gratios; UI progresiv AA light+dark.
631 teste.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
251 lines
8.7 KiB
Python
251 lines
8.7 KiB
Python
"""Teste T3 — validare de domeniu prezentari (app.validation).
|
|
|
|
Un test per regula din plan.md sect. 2 / contract. Validarea normalizeaza
|
|
(strip/upper) si intoarce erori structurate {field, message}; nu ridica exceptii.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import timedelta
|
|
|
|
import pytest
|
|
|
|
from app.models import PrezentareIn
|
|
from app.validation import today_bucuresti, validate_prezentare
|
|
|
|
|
|
def _base(**overrides) -> dict:
|
|
"""Prezentare valida ca dict (normalizat prin PrezentareIn), cu suprascrieri."""
|
|
data = {
|
|
"vin": "WVWZZZ1KZAW000123",
|
|
"nr_inmatriculare": "B999TST",
|
|
"data_prestatie": "2026-06-15",
|
|
"odometru_final": "123456",
|
|
"odometru_initial": None,
|
|
"prestatii": [{"cod_prestatie": "OE-1"}],
|
|
"sistem_reparat": "null",
|
|
}
|
|
data.update(overrides)
|
|
return PrezentareIn(**data).model_dump()
|
|
|
|
|
|
def _fields(errors: list[dict]) -> set[str]:
|
|
return {e["field"] for e in errors}
|
|
|
|
|
|
def test_prezentare_valida_fara_erori():
|
|
assert validate_prezentare(_base()) == []
|
|
|
|
|
|
def test_normalizare_strip_upper():
|
|
c = _base(vin=" wvwzzz1kzaw000123 ", nr_inmatriculare=" b999tst ")
|
|
assert c["vin"] == "WVWZZZ1KZAW000123"
|
|
assert c["nr_inmatriculare"] == "B999TST"
|
|
assert validate_prezentare(c) == []
|
|
|
|
|
|
@pytest.mark.parametrize("vin", [
|
|
"WVWZZ1KZAW000123", # 16 caractere
|
|
"WVWZZZ1KZAW0001234", # 18 caractere
|
|
"WVWZZZ1OZAW000123", # contine O
|
|
"WVWZZZ1IZAW000123", # contine I
|
|
"WVWZZZ1QZAW000123", # contine Q
|
|
"WVW ZZ1KZAW00012", # spatiu
|
|
])
|
|
def test_vin_invalid(vin):
|
|
errors = validate_prezentare(_base(vin=vin))
|
|
assert "vin" in _fields(errors)
|
|
|
|
|
|
def test_nrinmatriculare_prea_lung():
|
|
errors = validate_prezentare(_base(nr_inmatriculare="ABCDEFGHIJK")) # 11
|
|
assert "nr_inmatriculare" in _fields(errors)
|
|
|
|
|
|
def test_nrinmatriculare_caracter_special():
|
|
errors = validate_prezentare(_base(nr_inmatriculare="B-99"))
|
|
assert "nr_inmatriculare" in _fields(errors)
|
|
|
|
|
|
def test_data_prea_veche():
|
|
errors = validate_prezentare(_base(data_prestatie="2024-11-30"))
|
|
assert "data_prestatie" in _fields(errors)
|
|
|
|
|
|
def test_data_la_limita_inferioara_ok():
|
|
assert validate_prezentare(_base(data_prestatie="2024-12-01")) == []
|
|
|
|
|
|
def test_data_in_viitor():
|
|
maine = (today_bucuresti() + timedelta(days=1)).isoformat()
|
|
errors = validate_prezentare(_base(data_prestatie=maine))
|
|
assert "data_prestatie" in _fields(errors)
|
|
|
|
|
|
def test_data_azi_ok():
|
|
azi = today_bucuresti().isoformat()
|
|
assert validate_prezentare(_base(data_prestatie=azi)) == []
|
|
|
|
|
|
def test_data_format_invalid():
|
|
errors = validate_prezentare(_base(data_prestatie="15-06-2026"))
|
|
assert "data_prestatie" in _fields(errors)
|
|
|
|
|
|
def test_odometru_final_nenumeric():
|
|
errors = validate_prezentare(_base(odometru_final="abc"))
|
|
assert "odometru_final" in _fields(errors)
|
|
|
|
|
|
def test_rodo_fara_odometru_initial():
|
|
errors = validate_prezentare(_base(prestatii=[{"cod_prestatie": "R-ODO"}]))
|
|
assert "odometru_initial" in _fields(errors)
|
|
|
|
|
|
def test_iodo_fara_odometru_initial():
|
|
errors = validate_prezentare(_base(prestatii=[{"cod_prestatie": "I-ODO"}]))
|
|
assert "odometru_initial" in _fields(errors)
|
|
|
|
|
|
def test_rodo_cu_odometru_initial_ok():
|
|
c = _base(prestatii=[{"cod_prestatie": "R-ODO"}], odometru_initial="100000")
|
|
assert validate_prezentare(c) == []
|
|
|
|
|
|
def test_odometru_initial_mai_mare_decat_final():
|
|
c = _base(prestatii=[{"cod_prestatie": "R-ODO"}], odometru_initial="200000", odometru_final="100000")
|
|
assert "odometru_initial" in _fields(errors := validate_prezentare(c))
|
|
assert any("<=" in e["message"] for e in errors)
|
|
|
|
|
|
def test_prestatii_goale():
|
|
errors = validate_prezentare(_base(prestatii=[]))
|
|
assert "prestatii" in _fields(errors)
|
|
|
|
|
|
def test_b64image_invalid():
|
|
errors = validate_prezentare(_base(b64_image="@@@not-base64@@@"))
|
|
assert "b64_image" in _fields(errors)
|
|
|
|
|
|
def test_b64image_valid_ok():
|
|
import base64
|
|
good = base64.b64encode(b"poza odometru").decode()
|
|
assert validate_prezentare(_base(b64_image=good)) == []
|
|
|
|
|
|
def test_erori_multiple_cumulate():
|
|
errors = validate_prezentare(_base(vin="BAD", nr_inmatriculare="X-Y", data_prestatie="2024-01-01"))
|
|
assert {"vin", "nr_inmatriculare", "data_prestatie"} <= _fields(errors)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# US-002: 3 niveluri — coduri stabile, forma aditiva, back-compat
|
|
# ---------------------------------------------------------------------------
|
|
|
|
from app import errors as err_mod # noqa: E402
|
|
|
|
|
|
def test_vin_invalid_are_3niveluri():
|
|
"""VIN cu O/I/Q => eroarea are cod, problema, fix, field, message neschimbat."""
|
|
errs = validate_prezentare(_base(vin="WVWZZZ1OZAW000123"))
|
|
vin_errs = [e for e in errs if e.get("field") == "vin"]
|
|
assert vin_errs, "Trebuie cel putin o eroare cu field==vin"
|
|
e = vin_errs[0]
|
|
assert e["cod"] == "VIN_FORMAT"
|
|
assert e["problema"], "problema trebuie sa fie ne-goala"
|
|
assert e["fix"], "fix trebuie sa fie ne-gol"
|
|
assert e["field"] == "vin"
|
|
# message trebuie sa fie mesajul existent (back-compat)
|
|
assert "17" in e["message"] or "O, I, Q" in e["message"]
|
|
|
|
|
|
def test_data_prea_veche_cod():
|
|
errs = validate_prezentare(_base(data_prestatie="2024-11-30"))
|
|
dp = [e for e in errs if e.get("field") == "data_prestatie"]
|
|
assert dp
|
|
assert dp[0]["cod"] == "DATA_PREA_VECHE"
|
|
|
|
|
|
def test_data_viitor_cod():
|
|
from datetime import timedelta
|
|
maine = (today_bucuresti() + timedelta(days=1)).isoformat()
|
|
errs = validate_prezentare(_base(data_prestatie=maine))
|
|
dp = [e for e in errs if e.get("field") == "data_prestatie"]
|
|
assert dp
|
|
assert dp[0]["cod"] == "DATA_VIITOR"
|
|
|
|
|
|
def test_data_format_cod():
|
|
errs = validate_prezentare(_base(data_prestatie="15-06-2026"))
|
|
dp = [e for e in errs if e.get("field") == "data_prestatie"]
|
|
assert dp
|
|
assert dp[0]["cod"] == "DATA_FORMAT"
|
|
|
|
|
|
def test_odometru_initial_lipsa_cod():
|
|
errs = validate_prezentare(_base(prestatii=[{"cod_prestatie": "R-ODO"}]))
|
|
oi = [e for e in errs if e.get("field") == "odometru_initial"]
|
|
assert oi
|
|
assert oi[0]["cod"] == "ODOMETRU_INITIAL_LIPSA"
|
|
|
|
|
|
def test_odometru_ordine_cod():
|
|
c = _base(prestatii=[{"cod_prestatie": "R-ODO"}], odometru_initial="200000", odometru_final="100000")
|
|
errs = validate_prezentare(c)
|
|
oi = [e for e in errs if e.get("field") == "odometru_initial" and e.get("cod") == "ODOMETRU_INITIAL_ORDINE"]
|
|
assert oi, "Trebuie eroare cu cod ODOMETRU_INITIAL_ORDINE"
|
|
|
|
|
|
def test_prestatii_goale_cod():
|
|
errs = validate_prezentare(_base(prestatii=[]))
|
|
pr = [e for e in errs if e.get("field") == "prestatii"]
|
|
assert pr
|
|
assert pr[0]["cod"] == "PRESTATII_GOALE"
|
|
|
|
|
|
def test_b64_invalid_cod():
|
|
errs = validate_prezentare(_base(b64_image="@@@not-base64@@@"))
|
|
b = [e for e in errs if e.get("field") == "b64_image"]
|
|
assert b
|
|
assert b[0]["cod"] == "B64_INVALID"
|
|
|
|
|
|
def test_back_compat_field_message():
|
|
"""Fiecare eroare are inca field + message (forma veche)."""
|
|
errs = validate_prezentare(_base(
|
|
vin="BAD",
|
|
nr_inmatriculare="X-Y",
|
|
data_prestatie="2024-01-01",
|
|
odometru_final="abc",
|
|
prestatii=[],
|
|
))
|
|
for e in errs:
|
|
assert "field" in e, f"Lipseste 'field' in {e}"
|
|
assert "message" in e, f"Lipseste 'message' in {e}"
|
|
assert e["message"], f"'message' gol in {e}"
|
|
|
|
|
|
def test_toate_codurile_in_catalog():
|
|
"""Fiecare cod emis de validate_prezentare exista in errors.CATALOG."""
|
|
from datetime import timedelta
|
|
|
|
cazuri = [
|
|
_base(vin="WVWZZZ1OZAW000123"), # VIN_FORMAT
|
|
_base(nr_inmatriculare="X-Y"), # NR_INMATRICULARE_FORMAT
|
|
_base(data_prestatie="15-06-2026"), # DATA_FORMAT
|
|
_base(data_prestatie="2024-11-30"), # DATA_PREA_VECHE
|
|
_base(data_prestatie=(today_bucuresti() + timedelta(days=1)).isoformat()), # DATA_VIITOR
|
|
_base(odometru_final="abc"), # ODOMETRU_FINAL_FORMAT
|
|
_base(prestatii=[{"cod_prestatie": "R-ODO"}]), # ODOMETRU_INITIAL_LIPSA
|
|
_base(prestatii=[{"cod_prestatie": "R-ODO"}], odometru_initial="abc"), # ODOMETRU_INITIAL_FORMAT
|
|
_base(prestatii=[{"cod_prestatie": "R-ODO"}], odometru_initial="200000", odometru_final="100000"), # ODOMETRU_INITIAL_ORDINE
|
|
_base(prestatii=[]), # PRESTATII_GOALE
|
|
_base(b64_image="@@@not-base64@@@"), # B64_INVALID
|
|
]
|
|
for caz in cazuri:
|
|
errs = validate_prezentare(caz)
|
|
for e in errs:
|
|
if "cod" in e:
|
|
assert e["cod"] in err_mod.CATALOG, f"Cod {e['cod']!r} absent din CATALOG"
|