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