"""Test LIVE end-to-end pe RAR test (opt-in) — mapare inline -> worker -> FINALIZATA. Reproduce automat proba live manuala din 5.7: un submission `needs_mapping` (cod operatie nemapat) -> mapare inline din panoul de detaliu (ruta web reala, sesiune + CSRF) -> `queued` -> worker-ul REAL (login RAR test, `postPrezentare`) -> `sent` cu `idPrezentare` -> verificare INDEPENDENTA in lista de finalizate RAR (regresia de aur). SKIP implicit (nu ruleaza in suita normala / CI). Se activeaza doar cu: AUTOPASS_LIVE_RAR=1 + settings.xml cu creds reale python3 -m pytest tests/test_live_rar.py -q ATENTIE: atinge endpoint-ul real RAR de test -> creeaza o prezentare `FINALIZATA` (terminala la RAR, fara anulare prin API). Folosim VIN unic per rulare ca verificarea in finalizate sa fie fara ambiguitate (nu se ciocneste de rulari anterioare). """ from __future__ import annotations import json import os import re import tempfile import pytest from starlette.testclient import TestClient LIVE = os.environ.get("AUTOPASS_LIVE_RAR") == "1" pytestmark = [ pytest.mark.live, pytest.mark.skipif(not LIVE, reason="test live RAR dezactivat (seteaza AUTOPASS_LIVE_RAR=1)"), ] DATA_PRESTATIE = "2025-01-15" # in [2024-12-01, azi]; fix, ca sa nu depinda de ceas ODOMETRU = "154000" def _vin_unic() -> str: """VIN valid (17 car., A-HJ-NPR-Z0-9), unic per rulare. hex -> 0-9A-F (fara I/O/Q).""" return ("WAUZ" + os.urandom(7).hex().upper())[:17] def _create_account_user(email: str, name: str = "Live Probe", password: str = "parolasecreta10") -> int: 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, name, active=True) create_user(conn, acct_id, email, password) return acct_id finally: conn.close() def _insert_needs_mapping(acct: int, *, op: str, vin: str) -> int: """Submission `needs_mapping` cu o prestatie nemapata (cod_prestatie null) + continut valid.""" from app.db import get_connection payload = { "vin": vin, "nr_inmatriculare": "B123XYZ", "data_prestatie": DATA_PRESTATIE, "odometru_final": ODOMETRU, "prestatii": [{"cod_prestatie": None, "cod_op_service": op, "denumire": op}], } conn = get_connection() try: cur = conn.execute( "INSERT INTO submissions (idempotency_key, account_id, status, payload_json, rar_error) " "VALUES (?, ?, 'needs_mapping', ?, ?)", (f"live-{os.urandom(6).hex()}", acct, json.dumps(payload), json.dumps({"unmapped": [{"cod_op_service": op}]})), ) conn.commit() return int(cur.lastrowid or 0) finally: conn.close() def _login(client: TestClient, email: str, password: str = "parolasecreta10") -> None: resp = client.get("/login") m = re.search(r'name="csrf_token"\s+value="([^"]+)"', resp.text) assert m, "token CSRF negasit pe /login" resp = client.post("/login", data={"email": email, "parola": password, "csrf_token": m.group(1)}) assert resp.status_code == 303 def _csrf(client: TestClient) -> str: resp = client.get("/?tab=acasa") m = re.search(r'name="csrf_token"\s+value="([^"]+)"', resp.text) assert m, "token CSRF negasit pe dashboard" return m.group(1) @pytest.fixture() def env(monkeypatch): """Mediu test: DB temporar, RAR test, worker cu creds si send activat.""" tmp = tempfile.mkdtemp() monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "live.db")) monkeypatch.setenv("AUTOPASS_RAR_ENV", "test") monkeypatch.setenv("AUTOPASS_WEB_AUTH_REQUIRED", "true") monkeypatch.setenv("AUTOPASS_WORKER_USE_TEST_CREDS", "true") monkeypatch.setenv("AUTOPASS_WORKER_SEND_ENABLED", "true") from app.config import get_settings get_settings.cache_clear() from app.db import init_db init_db() # schema + seed nomenclator (inainte de orice insert; lifespan ar veni prea tarziu) from app.web import ratelimit ratelimit._hits.clear() yield ratelimit._hits.clear() get_settings.cache_clear() def test_live_mapare_inline_pana_la_finalizata(env): """Lant complet pe RAR test: needs_mapping -> mapare inline web -> queued -> worker -> sent -> finalizate.""" from app.config import get_settings, load_test_credentials creds = load_test_credentials() if not creds: pytest.skip("settings.xml fara creds reale") op = "OP-LIVE-TEST" vin = _vin_unic() # --- 1. ingestie: un rand blocat needs_mapping (cod operatie nemapat) --- acct = _create_account_user("live@test.local") sid = _insert_needs_mapping(acct, op=op, vin=vin) # --- 2. mapare inline din panoul de detaliu (ruta web reala, sesiune + CSRF) -> queued --- from app.main import app with TestClient(app, follow_redirects=False) as client: _login(client, "live@test.local") # panoul de detaliu expune selectorul de cod RAR pe operatia nemapata det = client.get(f"/_fragments/trimitere/{sid}") assert det.status_code == 200 assert "Mapeaza codul operatiei" in det.text csrf = _csrf(client) resp = client.post(f"/trimitere/{sid}/mapeaza", data={ "cod_op_service": op, "cod_prestatie": "OE-1", "auto_send": "true", "csrf_token": csrf, }) assert resp.status_code == 200, resp.text assert resp.headers.get("HX-Trigger") == "trimiteriChanged" from app.db import get_connection conn = get_connection() try: row = conn.execute("SELECT status, payload_json FROM submissions WHERE id=?", (sid,)).fetchone() assert row["status"] == "queued", f"asteptat queued dupa mapare, e {row['status']}" assert json.loads(row["payload_json"])["prestatii"][0]["cod_prestatie"] == "OE-1" # --- 3. worker REAL: login RAR test + claim + postPrezentare -> sent --- from app.rar_client import RarClient from app.worker.__main__ import claim_one, process_one settings = get_settings() rar = RarClient(settings) token = rar.login(creds["email"], creds["password"]) assert token, "login RAR test esuat" claimed = claim_one(conn) assert claimed is not None and claimed["id"] == sid, "claim_one nu a preluat randul queued" stare = process_one(conn, settings, rar, token, claimed) assert stare == "sent", f"asteptat sent, worker a intors {stare}" sent = conn.execute( "SELECT status, id_prezentare FROM submissions WHERE id=?", (sid,) ).fetchone() assert sent["status"] == "sent" id_prezentare = sent["id_prezentare"] assert id_prezentare, "RAR nu a intors idPrezentare" # --- 4. verificare INDEPENDENTA: prezentarea apare in finalizate RAR (match pe VIN) --- from app.reconcile import match_finalizata finalizate = rar.get_finalizate(token) gasit = match_finalizata( finalizate, vin=vin, data_prestatie=DATA_PRESTATIE, odometru_final=ODOMETRU, ) assert gasit == id_prezentare, ( f"prezentarea {id_prezentare} (VIN {vin}) nu se regaseste in finalizate RAR" ) finally: conn.close()