"""Teste TDD pentru US-007 (PRD 5.15): formular editare slim. RED -> implementare -> GREEN. AC-uri verificate: - Un singur camp VIN (fara "Confirma VIN"). - Textarea obs (Observatii) prezent in formular. - Chips multi-select prestatii cu hidden inputs name="cod_prestatie". - Endpoint /form-chips re-randeaza sectiunea chips (add/remove). - Acelasi _form_editare.html in ambele modale (trimitere detaliu + editare preview). - Reveal dinamic odometru initial cand chips contin R-ODO/I-ODO. """ from __future__ import annotations import json import os import re import tempfile from pathlib import Path import pytest from starlette.testclient import TestClient TEMPLATES_DIR = Path(__file__).resolve().parent.parent / "app" / "web" / "templates" # --------------------------------------------------------------------------- # # Fixtures # # --------------------------------------------------------------------------- # @pytest.fixture() def client(monkeypatch): tmp = tempfile.mkdtemp() monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "slim_form.db")) monkeypatch.setenv("AUTOPASS_WEB_AUTH_REQUIRED", "true") from app.config import get_settings get_settings.cache_clear() from app.web import ratelimit ratelimit._hits.clear() from app.main import app with TestClient(app, follow_redirects=False) as c: yield c ratelimit._hits.clear() get_settings.cache_clear() # --------------------------------------------------------------------------- # # Helpere # # --------------------------------------------------------------------------- # def _create_account_user(email: str, password: str = "parolasecreta10"): from app.accounts import create_account from app.users import create_user from app.db import get_connection conn = get_connection() try: acct_id = create_account(conn, "Service Test", active=True) create_user(conn, acct_id, email, password) return acct_id finally: conn.close() def _login(client, email: str, password: str = "parolasecreta10") -> None: resp = client.get("/login") m = re.search(r'name="csrf_token"\s+value="([^"]+)"', resp.text) or \ re.search(r'value="([^"]+)"\s+name="csrf_token"', resp.text) assert m, "csrf_token nu gasit in login" resp = client.post( "/login", data={"email": email, "parola": password, "csrf_token": m.group(1)}, ) assert resp.status_code == 303 def _csrf(client) -> str: resp = client.get("/?tab=coada") m = re.search(r'name="csrf_token"\s+value="([^"]+)"', resp.text) assert m, "csrf_token nu gasit in dashboard" return m.group(1) def _insert(acct: int, *, status: str, payload: dict) -> int: from app.db import get_connection conn = get_connection() try: cur = conn.execute( "INSERT INTO submissions (idempotency_key, account_id, status, payload_json) " "VALUES (?, ?, ?, ?)", (f"k-{os.urandom(6).hex()}", acct, status, json.dumps(payload)), ) conn.commit() return int(cur.lastrowid) finally: conn.close() def _seed_cod(cod: str, denumire: str = "Prestatie test") -> None: from app.db import get_connection conn = get_connection() try: conn.execute( "INSERT OR REPLACE INTO nomenclator_rar (cod_prestatie, nume_prestatie) VALUES (?, ?)", (cod, denumire), ) conn.commit() finally: conn.close() def _payload_needs_data_cu_cod(vin: str = "WVWZZZ1JZXW0US007A") -> dict: """Payload needs_data: cod RAR setat, dar odometru_final gol.""" return { "vin": vin, "nr_inmatriculare": "B200AA", "data_prestatie": "2026-06-20", "odometru_final": "", # gol -> needs_data "prestatii": [{"cod_prestatie": "OE-1", "cod_op_service": "Op-A", "denumire": "Schimb ulei"}], } def _payload_cu_ops(vin: str, ops: list[tuple]) -> dict: """Payload cu prestatii avand cod_op_service (needs_mapping).""" return { "vin": vin, "nr_inmatriculare": "B100AAA", "data_prestatie": "2026-06-10", "odometru_final": "50000", "prestatii": [ {"cod_op_service": op, "denumire": den} for op, den in ops ], } def _payload_cu_r_odo(vin: str = "WVWZZZ1JZXW0RODO1") -> dict: """Payload needs_data cu R-ODO in chips — declanseaza reveal odometru initial.""" return { "vin": vin, "nr_inmatriculare": "B300RO", "data_prestatie": "2026-06-20", "odometru_final": "39000", # odometru_initial ABSENT -> needs_data cand R-ODO "prestatii": [{"cod_prestatie": "R-ODO", "cod_op_service": "", "denumire": "Revizie odometru"}], } # --------------------------------------------------------------------------- # # Test 1: UN SINGUR camp VIN (fara "Confirma VIN") # # --------------------------------------------------------------------------- # def test_un_singur_vin(client): """US-007 AC1: formularul slim are UN SINGUR input name='vin'. Fara camp 'Confirma VIN' — PRD si contractul RAR cer un singur VIN. RED: daca ar exista doua campuri VIN sau un camp 'confirma_vin', testul pica. """ acct = _create_account_user("vin.unic@test.com") _login(client, "vin.unic@test.com") _seed_cod("OE-1") sid = _insert(acct, status="needs_data", payload=_payload_needs_data_cu_cod()) resp = client.get(f"/_fragments/trimitere/{sid}") assert resp.status_code == 200, resp.text[:300] html = resp.text # Exact un singur input cu name="vin" vin_inputs = re.findall(r']+name="vin"[^>]*>', html) assert len(vin_inputs) == 1, ( f"Trebuie exact UN input name='vin', gasit {len(vin_inputs)}: {vin_inputs}" ) # Fara camp "Confirma VIN" sau "confirma_vin" assert "confirma_vin" not in html.lower(), ( "Formular NU trebuie sa aiba camp 'confirma_vin' (VIN unic per contract RAR)" ) assert "confirma vin" not in html.lower(), ( "Formular NU trebuie sa afiseze eticheta 'confirma vin'" ) # --------------------------------------------------------------------------- # # Test 2: Camp Observatii (textarea name="obs") # # --------------------------------------------------------------------------- # def test_camp_observatii_prezent(client): """US-007 AC2: formularul are textarea name='obs' pentru Observatii (US-005). RED: obs nu e inca in _form_editare.html (US-005 adauga backend-ul, US-007 adauga UI-ul). """ acct = _create_account_user("obs.forma@test.com") _login(client, "obs.forma@test.com") _seed_cod("OE-1") sid = _insert(acct, status="needs_data", payload=_payload_needs_data_cu_cod()) resp = client.get(f"/_fragments/trimitere/{sid}") assert resp.status_code == 200 html = resp.text # textarea cu name="obs" trebuie sa existe has_textarea_obs = bool( re.search(r']+name="obs"', html) or re.search(r']*name=["\']obs["\']', html) ) assert has_textarea_obs, ( "Formularul trebuie sa contina