"""US-001: stare chips extinsa (JSON versionat, D4) + actiuni add/remove unificate. Contract nou /form-chips: - Stare curenta: UN SINGUR hidden `chips_state` = JSON {"v":1,"items":[{"cod","op","den","exclus"}]}. - chips_action=add + chips_pick (codul ales) + chips_target_index optional: cu tinta -> leaga codul de operatia de la acel index; fara tinta -> chip liber. chips_pick=__NEDECLARAT__ + tinta valida -> item exclus=1, fara cod_prestatie. - chips_action=remove + chips_remove_index: chip legat de operatie -> revine warning (cod_prestatie="", exclus=0); chip liber -> iese din lista. - Actiunile vechi (add_flat/add_extra/remove_flat/chips_add_cod_{i}/chips_add_cod_flat ca actiuni) sunt ELIMINATE. """ from __future__ import annotations import json import os import re import tempfile import pytest from starlette.testclient import TestClient # --------------------------------------------------------------------------- # # Fixtures # # --------------------------------------------------------------------------- # @pytest.fixture() def client(monkeypatch): tmp = tempfile.mkdtemp() monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "chips.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() 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", 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 _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 _state(items: list[dict]) -> str: """Construieste JSON-ul hidden chips_state din itemi scurti {cod,op,den,exclus}.""" return json.dumps({"v": 1, "items": items}, ensure_ascii=False) def _login_seed(client, email: str) -> None: _create_account_user(email) _login(client, email) # --------------------------------------------------------------------------- # # add cu tinta -> leaga codul de operatia respectiva # # --------------------------------------------------------------------------- # def test_add_cu_tinta_leaga_de_operatie(client): _login_seed(client, "add.tinta@test.com") _seed_cod("OE-1", "Schimb ulei") csrf = _csrf(client) state = _state([ {"cod": "", "op": "Op-A", "den": "Schimb ulei motor", "exclus": 0}, ]) resp = client.post("/form-chips", data={ "csrf_token": csrf, "chips_state": state, "chips_action": "add", "chips_target_index": "0", "chips_pick": "OE-1", }) assert resp.status_code == 200, resp.text[:400] html = resp.text assert "OE-1" in html m = re.search(r'name="chips_state"\s+value=\'([^\']*)\'', html) assert m, f"hidden chips_state lipsa din raspuns: {html[:400]}" payload = json.loads(m.group(1).replace(""", '"')) assert payload["v"] == 1 items = payload["items"] assert len(items) == 1 assert items[0]["cod"] == "OE-1" assert items[0]["op"] == "Op-A" assert items[0]["exclus"] == 0 # --------------------------------------------------------------------------- # # add fara tinta -> chip liber # # --------------------------------------------------------------------------- # def test_add_fara_tinta_adauga_cod_liber(client): _login_seed(client, "add.liber@test.com") _seed_cod("FRN1", "Sistem de franare") csrf = _csrf(client) resp = client.post("/form-chips", data={ "csrf_token": csrf, "chips_state": _state([]), "chips_action": "add", "chips_pick": "FRN1", }) assert resp.status_code == 200, resp.text[:400] m = re.search(r'name="chips_state"\s+value=\'([^\']*)\'', resp.text) assert m payload = json.loads(m.group(1).replace(""", '"')) items = payload["items"] assert len(items) == 1 assert items[0]["cod"] == "FRN1" assert items[0]["op"] == "" # --------------------------------------------------------------------------- # # add __NEDECLARAT__ cu tinta -> marcheaza exclus # # --------------------------------------------------------------------------- # def test_add_nedeclarat_marcheaza_exclus(client): _login_seed(client, "add.ned@test.com") csrf = _csrf(client) state = _state([ {"cod": "", "op": "OP-ITP", "den": "Inspectie tehnica", "exclus": 0}, ]) resp = client.post("/form-chips", data={ "csrf_token": csrf, "chips_state": state, "chips_action": "add", "chips_target_index": "0", "chips_pick": "__NEDECLARAT__", }) assert resp.status_code == 200, resp.text[:400] m = re.search(r'name="chips_state"\s+value=\'([^\']*)\'', resp.text) assert m payload = json.loads(m.group(1).replace(""", '"')) items = payload["items"] assert len(items) == 1 assert items[0]["exclus"] == 1 assert items[0]["cod"] == "", f"item exclus nu trebuie sa aiba cod_prestatie: {items[0]}" assert items[0]["op"] == "OP-ITP" assert "nu se va declara" in resp.text.lower() def test_add_nedeclarat_fara_tinta_da_eroare(client): """Sentinelul __NEDECLARAT__ fara tinta valida -> chips_error explicit, fara modificare.""" _login_seed(client, "add.ned.notinta@test.com") csrf = _csrf(client) resp = client.post("/form-chips", data={ "csrf_token": csrf, "chips_state": _state([]), "chips_action": "add", "chips_pick": "__NEDECLARAT__", # fara chips_target_index }) assert resp.status_code == 200, resp.text[:400] assert "chips-extra-error" in resp.text, ( f"Eroare vizibila lipsa pentru NEDECLARAT fara tinta: {resp.text[:400]}" ) m = re.search(r'name="chips_state"\s+value=\'([^\']*)\'', resp.text) assert m payload = json.loads(m.group(1).replace(""", '"')) assert payload["items"] == [], "NEDECLARAT fara tinta nu trebuie sa modifice starea" def test_add_nedeclarat_tinta_deja_mapata_da_eroare(client): """Tinta trebuie sa fie o operatie NEMAPATA — index spre operatie deja rezolvata -> eroare.""" _login_seed(client, "add.ned.mapata@test.com") _seed_cod("OE-1", "Schimb ulei") csrf = _csrf(client) state = _state([ {"cod": "OE-1", "op": "Op-A", "den": "Schimb ulei", "exclus": 0}, ]) resp = client.post("/form-chips", data={ "csrf_token": csrf, "chips_state": state, "chips_action": "add", "chips_target_index": "0", "chips_pick": "__NEDECLARAT__", }) assert resp.status_code == 200 assert "chips-extra-error" in resp.text m = re.search(r'name="chips_state"\s+value=\'([^\']*)\'', resp.text) payload = json.loads(m.group(1).replace(""", '"')) assert payload["items"][0]["cod"] == "OE-1", "Tinta invalida nu trebuie sa strice starea existenta" # --------------------------------------------------------------------------- # # remove -> readuce operatia nemapata (warning) # # --------------------------------------------------------------------------- # def test_remove_cod_readuce_operatia_nemapata(client): _login_seed(client, "remove.readuce@test.com") csrf = _csrf(client) state = _state([ {"cod": "OE-1", "op": "Op-A", "den": "Schimb ulei", "exclus": 0}, ]) resp = client.post("/form-chips", data={ "csrf_token": csrf, "chips_state": state, "chips_action": "remove", "chips_remove_index": "0", }) assert resp.status_code == 200, resp.text[:400] m = re.search(r'name="chips_state"\s+value=\'([^\']*)\'', resp.text) assert m payload = json.loads(m.group(1).replace(""", '"')) items = payload["items"] assert len(items) == 1, "Operatia trebuie sa RAMANA (doar codul dispare)" assert items[0]["cod"] == "" assert items[0]["op"] == "Op-A" assert items[0]["exclus"] == 0 def test_remove_pe_nedeclarat_readuce_warning(client): """x pe un chip Nedeclarat readuce operatia la warning (nu se sterge operatia).""" _login_seed(client, "remove.nedeclarat@test.com") csrf = _csrf(client) state = _state([ {"cod": "", "op": "OP-ITP", "den": "Inspectie", "exclus": 1}, ]) resp = client.post("/form-chips", data={ "csrf_token": csrf, "chips_state": state, "chips_action": "remove", "chips_remove_index": "0", }) assert resp.status_code == 200 m = re.search(r'name="chips_state"\s+value=\'([^\']*)\'', resp.text) payload = json.loads(m.group(1).replace(""", '"')) items = payload["items"] assert len(items) == 1 assert items[0]["exclus"] == 0 assert items[0]["cod"] == "" assert items[0]["op"] == "OP-ITP" def test_remove_cod_liber_il_sterge_complet(client): """Chip liber (fara operatie sursa) e eliminat integral din lista la remove.""" _login_seed(client, "remove.liber@test.com") csrf = _csrf(client) state = _state([ {"cod": "FRN1", "op": "", "den": "", "exclus": 0}, ]) resp = client.post("/form-chips", data={ "csrf_token": csrf, "chips_state": state, "chips_action": "remove", "chips_remove_index": "0", }) assert resp.status_code == 200 m = re.search(r'name="chips_state"\s+value=\'([^\']*)\'', resp.text) payload = json.loads(m.group(1).replace(""", '"')) assert payload["items"] == [] # --------------------------------------------------------------------------- # # dedup (operatie, cod) # # --------------------------------------------------------------------------- # def test_dedup_operatie_cod(client): """Acelasi cod liber nu se adauga de doua ori (pereche op='',cod deja existenta).""" _login_seed(client, "dedup.op.cod@test.com") _seed_cod("OE-1", "Schimb ulei") csrf = _csrf(client) state = _state([ {"cod": "OE-1", "op": "", "den": "", "exclus": 0}, ]) resp = client.post("/form-chips", data={ "csrf_token": csrf, "chips_state": state, "chips_action": "add", "chips_pick": "OE-1", }) assert resp.status_code == 200 assert "chips-extra-error" in resp.text or "deja in lista" in resp.text m = re.search(r'name="chips_state"\s+value=\'([^\']*)\'', resp.text) payload = json.loads(m.group(1).replace(""", '"')) assert len(payload["items"]) == 1, f"Dedup a esuat, itemi: {payload['items']}" # --------------------------------------------------------------------------- # # actiuni vechi eliminate # # --------------------------------------------------------------------------- # @pytest.mark.parametrize("actiune", ["add_flat", "add_extra", "remove_flat"]) def test_actiuni_vechi_eliminate_sunt_no_op(client, actiune): """Actiunile eliminate nu mai au ramura dedicata — cad prin fara sa schimbe starea.""" _login_seed(client, f"vechi.{actiune}@test.com") _seed_cod("OE-1", "Schimb ulei") csrf = _csrf(client) state = _state([{"cod": "OE-1", "op": "Op-A", "den": "Schimb ulei", "exclus": 0}]) resp = client.post("/form-chips", data={ "csrf_token": csrf, "chips_state": state, "chips_action": actiune, "chips_add_cod_flat": "FRN1", "chips_remove_cod": "OE-1", }) assert resp.status_code == 200 m = re.search(r'name="chips_state"\s+value=\'([^\']*)\'', resp.text) payload = json.loads(m.group(1).replace(""", '"')) assert payload["items"] == [{"cod": "OE-1", "op": "Op-A", "den": "Schimb ulei", "exclus": 0}] # --------------------------------------------------------------------------- # # chips_state prezent mereu, inclusiv gol # # --------------------------------------------------------------------------- # def test_chips_state_gol_ramane_prezent_in_raspuns(client): _login_seed(client, "state.gol@test.com") csrf = _csrf(client) resp = client.post("/form-chips", data={ "csrf_token": csrf, "chips_state": _state([]), "chips_action": "", }) assert resp.status_code == 200 m = re.search(r'name="chips_state"\s+value=\'([^\']*)\'', resp.text) assert m, "hidden chips_state trebuie sa fie mereu prezent, chiar cu lista goala" payload = json.loads(m.group(1).replace(""", '"')) assert payload == {"v": 1, "items": []} # --------------------------------------------------------------------------- # # JSON invalid -> eroare vizibila, nu stare goala silentioasa # # --------------------------------------------------------------------------- # def test_chips_state_json_invalid_da_eroare_vizibila(client): _login_seed(client, "state.corupt@test.com") csrf = _csrf(client) resp = client.post("/form-chips", data={ "csrf_token": csrf, "chips_state": "{not valid json", "chips_action": "", }) assert resp.status_code == 200 assert "chips-extra-error" in resp.text or "invalid" in resp.text.lower(), ( f"JSON corupt trebuie sa dea eroare vizibila: {resp.text[:400]}" ) # --------------------------------------------------------------------------- # # compat tranzitorie: chips_state lipsa -> fallback pe listele vechi # # --------------------------------------------------------------------------- # def test_compat_fallback_pe_liste_vechi_cand_chips_state_lipseste(client): _login_seed(client, "compat.fallback@test.com") _seed_cod("OE-1", "Schimb ulei") csrf = _csrf(client) resp = client.post("/form-chips", data={ "csrf_token": csrf, # FARA chips_state -> fallback pe listele vechi "cod_prestatie": [""], "chip_op_service": ["Op-A"], "chip_denumire": ["Schimb ulei motor"], "chips_action": "add", "chips_target_index": "0", "chips_pick": "OE-1", }) assert resp.status_code == 200, resp.text[:400] m = re.search(r'name="chips_state"\s+value=\'([^\']*)\'', resp.text) assert m payload = json.loads(m.group(1).replace(""", '"')) assert payload["items"][0]["cod"] == "OE-1" assert payload["items"][0]["exclus"] == 0 # =========================================================================== # # US-002: template unificat -- un container .chips, un select, fara op-row # # =========================================================================== # def _post_chips(client, csrf, items, **extra): data = { "csrf_token": csrf, "chips_state": _state(items), "chips_action": "", } data.update(extra) return client.post("/form-chips", data=data) def test_render_un_singur_container_chips(client): """UN singur container `.chips` -- indiferent de mixul de tipuri de chip.""" _login_seed(client, "un.container@test.com") _seed_cod("OE-1", "Schimb ulei") csrf = _csrf(client) items = [ {"cod": "OE-1", "op": "Op-A", "den": "Schimb ulei", "exclus": 0}, {"cod": "", "op": "Op-B", "den": "Vulcanizare", "exclus": 0}, {"cod": "FRN1", "op": "", "den": "", "exclus": 0}, {"cod": "", "op": "Op-C", "den": "ITP anual", "exclus": 1}, ] _seed_cod("FRN1", "Sistem de franare") resp = _post_chips(client, csrf, items) assert resp.status_code == 200, resp.text[:400] html = resp.text assert html.count('class="chips"') == 1, ( f"Trebuie sa existe UN SINGUR container .chips, gasit {html.count('class=\"chips\"')}: {html[:800]}" ) def test_render_fara_op_row_si_fara_butoane_adauga(client): """Fara .op-row, fara butoane Adauga/+, fara actiunile vechi in markup.""" _login_seed(client, "fara.oprow@test.com") _seed_cod("OE-1", "Schimb ulei") csrf = _csrf(client) items = [ {"cod": "OE-1", "op": "Op-A", "den": "Schimb ulei", "exclus": 0}, {"cod": "", "op": "Op-B", "den": "Vulcanizare", "exclus": 0}, ] resp = _post_chips(client, csrf, items) assert resp.status_code == 200, resp.text[:400] html = resp.text assert "op-row" not in html assert "Adauga" not in html assert "add_flat" not in html assert "chips_add_cod_" not in html def test_chip_warning_pentru_operatie_nemapata(client): """Operatie nemapata -> chip warning `warn OP-X . fara cod` cu title complet.""" _login_seed(client, "chip.warning@test.com") csrf = _csrf(client) items = [ {"cod": "", "op": "OP-ITP", "den": "Inspectie tehnica periodica", "exclus": 0}, ] resp = _post_chips(client, csrf, items) assert resp.status_code == 200, resp.text[:400] html = resp.text assert "OP-ITP" in html assert "fara cod" in html assert 'title="OP-ITP' in html def test_select_are_hx_disabled_elt_this(client): """Selectul unic are hx-disabled-elt='this' EXPLICIT (capcana 5.22 -- nu mostenit de la form).""" _login_seed(client, "select.disabled@test.com") _seed_cod("OE-1", "Schimb ulei") csrf = _csrf(client) resp = _post_chips(client, csrf, []) assert resp.status_code == 200, resp.text[:400] html = resp.text m = re.search(r']*name="chips_pick"[^>]*>', html) assert m, f"selectul chips_pick lipseste: {html[:600]}" select_tag = m.group(0) assert 'id="chips-picker"' in select_tag assert 'hx-trigger="change"' in select_tag assert 'hx-post="/form-chips"' in select_tag assert 'hx-target="#chips-section"' in select_tag assert 'hx-swap="outerHTML"' in select_tag assert 'hx-include="closest form"' in select_tag assert 'hx-disabled-elt="this"' in select_tag, ( f"hx-disabled-elt='this' lipseste de pe select (capcana 5.22): {select_tag}" ) def test_mod_plat_fara_operatii_select_doar_coduri_libere(client): """Mod plat (zero operatii din import): doar chips libere + select placeholder generic.""" _login_seed(client, "mod.plat@test.com") _seed_cod("OE-1", "Schimb ulei") csrf = _csrf(client) items = [ {"cod": "OE-1", "op": "", "den": "", "exclus": 0}, ] resp = _post_chips(client, csrf, items) assert resp.status_code == 200, resp.text[:400] html = resp.text assert "⚠" not in html, "Mod plat nu trebuie sa aiba niciun chip warning" m = re.search(r']*name="chips_pick"[^>]*>\s*', html) assert m, f"select cu optiune placeholder lipseste: {html[:600]}" assert "adauga cod RAR" in m.group(1) def test_has_r_odo_pastrat_dupa_actiuni(client): """has_r_odo / data-has-r-odo + chip-warn pe R-ODO/I-ODO raman neschimbate.""" _login_seed(client, "r.odo@test.com") _seed_cod("R-ODO", "Odometru initial") csrf = _csrf(client) items = [ {"cod": "R-ODO", "op": "", "den": "", "exclus": 0}, ] resp = _post_chips(client, csrf, items) assert resp.status_code == 200, resp.text[:400] html = resp.text assert 'data-has-r-odo="true"' in html assert "chip-warn" in html assert "R-ODO" in html def test_chip_nedeclarat_are_x_functional(client): """Item exclus=1 -> chip Nedeclarat (line-through) cu buton de sters functional (remove -> warning).""" _login_seed(client, "nedeclarat.x@test.com") csrf = _csrf(client) items = [ {"cod": "", "op": "OP-ITP", "den": "Inspectie", "exclus": 1}, ] resp = _post_chips(client, csrf, items) assert resp.status_code == 200, resp.text[:400] html = resp.text assert "Nedeclarat" in html assert 'hx-vals=\'{"chips_action":"remove","chips_remove_index":0}\'' in html def test_chips_submission_id_ecouat(client): """chips_submission_id trimis in form e ecouat ca hidden in raspunsul /form-chips.""" _login_seed(client, "sub.id.ecou@test.com") csrf = _csrf(client) resp = _post_chips(client, csrf, [], chips_submission_id="4242") assert resp.status_code == 200, resp.text[:400] assert re.search(r'name="chips_submission_id"\s+value="4242"', resp.text), ( f"chips_submission_id nu a fost ecouat: {resp.text[:600]}" ) # =========================================================================== # # US-003: sugestii fuzzy/k-NN + optiunea "Nu se declara la RAR" in select # # =========================================================================== # def _extrage_select(html: str) -> str: """Extrage markup-ul complet al .""" m = re.search(r']*name="chips_pick".*?', html, re.DOTALL) assert m, f"select chips_pick lipseste din raspuns: {html[:600]}" return m.group(0) def test_select_are_optgroup_sugestii_pt_tinta(client): """Cu tinta activa (operatie nemapata): optgroup 'Sugestii' + __NEDECLARAT__ + nomenclator.""" _login_seed(client, "sugestii.tinta@test.com") _seed_cod("OE-1", "Schimb ulei motor") _seed_cod("FRN1", "Sistem de franare") csrf = _csrf(client) items = [ {"cod": "", "op": "Op-A", "den": "Schimb ulei motor", "exclus": 0}, ] resp = _post_chips(client, csrf, items) assert resp.status_code == 200, resp.text[:400] select_html = _extrage_select(resp.text) assert '' in select_html, ( f"optgroup Sugestii lipseste cu tinta activa: {select_html[:600]}" ) assert "OE-1" in select_html.split('')[1].split("")[0], ( "sugestia fuzzy pentru 'Schimb ulei motor' trebuie sa contina OE-1" ) assert "__NEDECLARAT__" in select_html assert "Nu se declara la RAR" in select_html # Nomenclatorul complet ramane dupa Sugestii/NEDECLARAT. assert "FRN1" in select_html def test_select_are_nedeclarat_doar_cu_tinta(client): """Optiunea __NEDECLARAT__ apare DOAR cand exista o tinta (operatie nemapata) activa.""" _login_seed(client, "nedeclarat.doar.tinta@test.com") _seed_cod("OE-1", "Schimb ulei") csrf = _csrf(client) # Cu tinta (operatie nemapata) -> NEDECLARAT prezent. resp_cu_tinta = _post_chips(client, csrf, [ {"cod": "", "op": "Op-A", "den": "Schimb ulei", "exclus": 0}, ]) assert "__NEDECLARAT__" in _extrage_select(resp_cu_tinta.text) # Fara tinta (doar cod liber, nicio operatie nemapata) -> NEDECLARAT absent. resp_fara_tinta = _post_chips(client, csrf, [ {"cod": "OE-1", "op": "", "den": "", "exclus": 0}, ]) assert "__NEDECLARAT__" not in _extrage_select(resp_fara_tinta.text) def test_select_fara_tinta_fara_sugestii(client): """Fara tinta: selectul listeaza DOAR nomenclatorul (fara optgroup Sugestii, fara NEDECLARAT).""" _login_seed(client, "fara.tinta.fara.sugestii@test.com") _seed_cod("OE-1", "Schimb ulei motor") csrf = _csrf(client) items = [ {"cod": "OE-1", "op": "", "den": "", "exclus": 0}, ] resp = _post_chips(client, csrf, items) assert resp.status_code == 200, resp.text[:400] select_html = _extrage_select(resp.text) assert " select DOAR cu nomenclatorul (fara optgroup), fara eroare 500 si fara blocare (degradare gratioasa, GAP inchis din PRD 5.23).""" _login_seed(client, "sugestii.indisponibile@test.com") _seed_cod("OE-1", "Schimb ulei motor") csrf = _csrf(client) from app.web import routes as _routes def _boom(*args, **kwargs): raise RuntimeError("motor de sugestii indisponibil (simulat)") monkeypatch.setattr(_routes, "suggest_codes", _boom) monkeypatch.setattr(_routes, "enrich_suggestions", _boom) items = [ {"cod": "", "op": "Op-A", "den": "Schimb ulei motor", "exclus": 0}, ] resp = _post_chips(client, csrf, items) assert resp.status_code == 200, resp.text[:400] select_html = _extrage_select(resp.text) assert " 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 test_randare_initiala_are_tinta_si_optgroup(client): """Deschiderea INITIALA a modalului de detaliu (needs_mapping, operatie nemapata), FARA niciun /form-chips, trebuie sa aiba deja is-target pe chip-ul warning, placeholder-ul D2 "Cod pentru OP-X — alege" si optgroup-ul Sugestii + NEDECLARAT.""" acct = _login_seed_ret(client, "randare.initiala@test.com") _seed_cod("OE-1", "Schimb ulei motor") _seed_cod("FRN1", "Sistem de franare") op = "Schimb ulei motor" sid = _insert_submission(acct, status="needs_mapping", payload={ "vin": "WVWZZZ1JZXW0SC001", "nr_inmatriculare": "B100AAA", "data_prestatie": "2026-06-10", "odometru_final": "50000", "prestatii": [{"cod_op_service": op, "denumire": op}], }) resp = client.get(f"/_fragments/trimitere/{sid}") assert resp.status_code == 200, resp.text[:400] html = resp.text assert "is-target" in html, ( f"chip-ul warning al primei operatii nemapate trebuie sa fie tinta la deschiderea initiala: {html[:2000]}" ) assert f"Cod pentru {op} — alege" in html, ( f"placeholder-ul D2 trebuie sa numeasca operatia tinta la deschiderea initiala: {html[:2000]}" ) select_html = _extrage_select(html) assert '' in select_html, ( f"optgroup Sugestii trebuie sa apara la deschiderea initiala (fara /form-chips): {select_html[:600]}" ) assert "OE-1" in select_html.split('')[1].split("")[0], ( "sugestia fuzzy pentru 'Schimb ulei motor' trebuie sa contina OE-1 chiar la randarea initiala" ) assert "__NEDECLARAT__" in select_html def _login_seed_ret(client, email: str) -> int: """Ca `_login_seed`, dar intoarce account_id (necesar pentru inserarea directa a submission-ului).""" acct = _create_account_user(email) _login(client, email) return acct