"""US-003: Reguli text active la ingestie (API + import + corectie web) si la re-rezolvarea blocajelor (`reresolve_account`). Verifica ca o regula text salvata in prealabil rezolva o operatie fara mapare exacta pe TOATE caile de ingestie, in loc sa o lase `needs_mapping`, si ca la re-rezolvare un rand `needs_mapping` care acum da match pe o regula se deblocheaza. Codul rezolvat din regula respecta validarea fata de nomenclator (US-002): folosim `OE-2`, cod valid din seed-ul nomenclatorului. """ from __future__ import annotations import io import json import os import re import tempfile import openpyxl import pytest from fastapi.testclient import TestClient # --------------------------------------------------------------------------- # # Fixtures # # --------------------------------------------------------------------------- # @pytest.fixture() def api_env(monkeypatch): """Client API + get_connection, DB temporara izolata (fara web-auth).""" tmp = tempfile.mkdtemp() monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "rr.db")) from app.config import get_settings get_settings.cache_clear() from app.db import get_connection from app.main import app with TestClient(app) as c: yield c, get_connection get_settings.cache_clear() @pytest.fixture() def web_env(monkeypatch): """Client web (auth pornit) + get_connection, DB temporara izolata.""" tmp = tempfile.mkdtemp() monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "rrw.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.db import get_connection from app.main import app with TestClient(app, follow_redirects=False) as c: yield c, get_connection ratelimit._hits.clear() get_settings.cache_clear() # --------------------------------------------------------------------------- # # Helpere # # --------------------------------------------------------------------------- # def _seed_text_rule(get_connection, account_id, pattern, cod, auto_send=True): from app.mapping import save_text_rule conn = get_connection() try: save_text_rule(conn, account_id, pattern, cod, auto_send=auto_send) conn.commit() finally: conn.close() def _make_xlsx(rows): wb = openpyxl.Workbook() ws = wb.active ws.title = "Sheet1" for row in rows: ws.append(row) buf = io.BytesIO() wb.save(buf) return buf.getvalue() def _row_status_cod(get_connection, sub_id): conn = get_connection() try: r = conn.execute( "SELECT status, payload_json FROM submissions WHERE id=?", (sub_id,) ).fetchone() payload = json.loads(r["payload_json"]) if r["payload_json"] else {} cod = (payload.get("prestatii") or [{}])[0].get("cod_prestatie") return r["status"], cod finally: conn.close() # --------------------------------------------------------------------------- # # 1. Ingestie API (router.py -> classify_prezentare) # # --------------------------------------------------------------------------- # def test_ingestie_api_aplica_regula_text(api_env): """POST /v1/prezentari cu operatie fara mapare exacta dar match pe regula text -> queued (cod din regula), nu needs_mapping.""" client, get_connection = api_env _seed_text_rule(get_connection, 1, "verificare", "OE-2") body = { "rar_credentials": {"email": "x@y.ro", "password": "s"}, "prezentari": [{ "vin": "WVWZZZ1KZAW000123", "nr_inmatriculare": "B999TST", "data_prestatie": "2026-06-15", "odometru_final": "123456", "prestatii": [{"cod_op_service": "Verificare frane", "denumire": "Verificare frane"}], }], } r = client.post("/v1/prezentari", json=body) assert r.status_code == 200, r.text res = r.json()["results"][0] assert res["status"] == "queued", res assert not res.get("nemapate") status, cod = _row_status_cod(get_connection, res["submission_id"]) assert status == "queued" assert cod == "OE-2" # --------------------------------------------------------------------------- # # 2. Ingestie import (import_router.py preview + commit) # # --------------------------------------------------------------------------- # def test_ingestie_import_aplica_regula_text(api_env): """Import xlsx cu operatie fara mapare exacta dar match pe regula text: preview o marcheaza 'ok' si commit o pune 'queued' (cod din regula).""" client, get_connection = api_env _seed_text_rule(get_connection, 1, "verificare", "OE-2") header = ["VIN", "Nr inmatriculare", "Data prestatie", "Odometru final", "Operatie"] row = ["WVWZZZ1KZAW001111", "B100TST", "2026-06-15", "123456", "Verificare frane"] data = _make_xlsx([header, row]) r = client.post( "/v1/import", files={"file": ("t.xlsx", io.BytesIO(data), "application/octet-stream")}, ) assert r.status_code == 200, r.text import_id = r.json()["import_id"] rc = client.post(f"/v1/import/{import_id}/column-mapping", json={"json_mapare": { "VIN": "vin", "Nr inmatriculare": "nr_inmatriculare", "Data prestatie": "data_prestatie", "Odometru final": "odometru_final", "Operatie": "operatie", }}) assert rc.status_code == 200, rc.text rp = client.get(f"/v1/import/{import_id}/preview") assert rp.status_code == 200, rp.text assert rp.json()["summary"].get("ok", 0) == 1, rp.json()["summary"] rcommit = client.post(f"/v1/import/{import_id}/commit", json={ "n_confirmat": 1, "reviewed_rows": [], }) assert rcommit.status_code == 200, rcommit.text assert rcommit.json()["enqueued"] == 1 sub_id = rcommit.json()["submissions"][0]["submission_id"] status, cod = _row_status_cod(get_connection, sub_id) assert status == "queued" assert cod == "OE-2" # --------------------------------------------------------------------------- # # 3. Corectie web (routes.py -> post_corectie_trimitere) # # --------------------------------------------------------------------------- # def _create_account_user(get_connection, email, name="Service", password="parolasecreta10"): from app.accounts import create_account from app.users import create_user conn = get_connection() try: acct_id = create_account(conn, name, active=True) create_user(conn, acct_id, email, password) return acct_id finally: conn.close() def _login(client, email, password="parolasecreta10"): resp = client.get("/login") m = re.search(r'name="csrf_token"\s+value="([^"]+)"', resp.text) assert m resp = client.post("/login", data={"email": email, "parola": password, "csrf_token": m.group(1)}) assert resp.status_code == 303 def _csrf(client): resp = client.get("/?tab=acasa") m = re.search(r'name="csrf_token"\s+value="([^"]+)"', resp.text) assert m return m.group(1) def _insert_needs_mapping(get_connection, acct, op, denumire=None, batch_id=None): conn = get_connection() try: payload = { "vin": "WVWZZZ1KZAW000123", "nr_inmatriculare": "B123ABC", "data_prestatie": "2026-06-10", "odometru_final": "159004", "prestatii": [{"cod_prestatie": None, "cod_op_service": op, "denumire": denumire or op}], } k = f"k-{os.urandom(6).hex()}" cur = conn.execute( "INSERT INTO submissions (idempotency_key, account_id, status, payload_json, rar_error, batch_id) " "VALUES (?, ?, 'needs_mapping', ?, ?, ?)", (k, acct, json.dumps(payload), json.dumps({"unmapped": [{"cod_op_service": op}]}), batch_id), ) conn.commit() return int(cur.lastrowid) finally: conn.close() def test_corectie_web_aplica_regula_text(web_env): """POST /trimitere/{id}/corecteaza pe un needs_mapping a carui operatie acum da match pe o regula text -> randul intra 'queued' (cod din regula).""" client, get_connection = web_env acct = _create_account_user(get_connection, "cor@test.com") sid = _insert_needs_mapping(get_connection, acct, op="Verificare frane") _seed_text_rule(get_connection, acct, "verificare", "OE-2") _login(client, "cor@test.com") csrf = _csrf(client) resp = client.post(f"/trimitere/{sid}/corecteaza", data={"csrf_token": csrf}) assert resp.status_code == 200, resp.text status, cod = _row_status_cod(get_connection, sid) assert status == "queued" assert cod == "OE-2" # --------------------------------------------------------------------------- # # 4. Re-rezolvare blocaje (mapping.reresolve_account) # # --------------------------------------------------------------------------- # def test_salvare_regula_rerezolva_blocate(api_env): """Dupa salvarea unei reguli noi, reresolve_account deblocheaza randurile needs_mapping care acum dau match (acelasi mecanism ca la save_mapping).""" client, get_connection = api_env sid = _insert_needs_mapping(get_connection, 1, op="Verificare frane") _seed_text_rule(get_connection, 1, "verificare", "OE-2") from app.mapping import reresolve_account conn = get_connection() try: stats = reresolve_account(conn, 1) conn.commit() finally: conn.close() assert stats["requeued"] == 1, stats status, cod = _row_status_cod(get_connection, sid) assert status == "queued" assert cod == "OE-2"