feat(errors): erori pe 3 niveluri (problema+cauza+fix) pe API si UI (PRD 5.4)

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>
This commit is contained in:
Claude Agent
2026-06-23 10:28:09 +00:00
parent b48501d8e4
commit 14e1c463f0
25 changed files with 2440 additions and 44 deletions

View File

@@ -137,3 +137,114 @@ def test_b64image_valid_ok():
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"