US-004: rezolva_rar_env (cerere>default cont>ancora globala) + MediuIndisponibil + cod RAR_MEDIU_INDISPONIBIL. US-005: camp rar_env pe POST /v1/prezentari + /valideaza (Literal), echo in SubmissionResult/ValidareResult/GET, build_key + INSERT env-aware. US-006: AccountSessions re-cheiat (account_id, rar_env); RarClient base_url per env; creds din slotul env; purge + recover_orphans scoped pe env (E1/1a, 1b/E6); claim_one propaga rar_env (1c/E8); keepalive pe ancora globala (M2). US-009: selector mediu la import (>=2 medii), eticheta la 1, banner la 0; commit seteaza rar_env pe submissions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
360 lines
13 KiB
Python
360 lines
13 KiB
Python
"""Teste US-009 (PRD 5.20) — Import web: selector mediu RAR conditionat de disponibilitate.
|
|
|
|
Verifica:
|
|
- La 0 medii: banner avertisment non-blocant (upload functioneaza, commit foloseste ancora globala).
|
|
- La 1 mediu: eticheta statica, fara selector; submissions primesc acel mediu.
|
|
- La 2 medii: selector vizibil pre-bifat pe default-ul contului.
|
|
- La commit: toate submission-urile lotului primesc rar_env ales (sau fallback ancora globala).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import csv
|
|
import io
|
|
import os
|
|
import re
|
|
import tempfile
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# Fixture client cu DB izolat #
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
@pytest.fixture()
|
|
def client(monkeypatch):
|
|
tmp = tempfile.mkdtemp()
|
|
monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "rar_env_test.db"))
|
|
monkeypatch.setenv("AUTOPASS_WEB_AUTH_REQUIRED", "false")
|
|
monkeypatch.setenv("AUTOPASS_RAR_ENV", "test") # ancora globala = test
|
|
from app.config import get_settings
|
|
get_settings.cache_clear()
|
|
from app.crypto import reset_cache
|
|
reset_cache()
|
|
from app.main import app
|
|
with TestClient(app) as c:
|
|
yield c
|
|
get_settings.cache_clear()
|
|
reset_cache()
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# Utilitare #
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
def _csv_bytes(rows: list[dict], sep: str = ";") -> bytes:
|
|
buf = io.StringIO()
|
|
writer = csv.DictWriter(buf, fieldnames=list(rows[0].keys()), delimiter=sep)
|
|
writer.writeheader()
|
|
writer.writerows(rows)
|
|
return buf.getvalue().encode("utf-8")
|
|
|
|
|
|
def _seed_nomenclator_si_mapare(client: TestClient) -> None:
|
|
"""Semeaza nomenclatorul si o mapare pentru randuri ok."""
|
|
from app.db import get_connection
|
|
conn = get_connection()
|
|
try:
|
|
conn.execute(
|
|
"INSERT OR REPLACE INTO nomenclator_rar (cod_prestatie, nume_prestatie) VALUES (?,?)",
|
|
("R-FRANE", "Reparatie frane"),
|
|
)
|
|
conn.execute(
|
|
"INSERT OR IGNORE INTO operations_mapping "
|
|
"(account_id, cod_op_service, cod_prestatie, auto_send) VALUES (1,?,?,1)",
|
|
("OP-FRANE", "R-FRANE"),
|
|
)
|
|
conn.commit()
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def _configureaza_un_mediu(client: TestClient, env: str = "test") -> None:
|
|
"""Activeaza un singur mediu RAR pe contul 1 (simulate creds disponibile)."""
|
|
from app.db import get_connection
|
|
from app.crypto import encrypt_creds
|
|
conn = get_connection()
|
|
try:
|
|
fake_creds = encrypt_creds({"email": "test@rar.ro", "password": "pass"})
|
|
if env == "test":
|
|
conn.execute(
|
|
"UPDATE accounts SET rar_test_enabled=1, rar_creds_test_enc=?, "
|
|
"rar_prod_enabled=0, rar_creds_prod_enc=NULL, rar_env_default='test' WHERE id=1",
|
|
(fake_creds,),
|
|
)
|
|
else:
|
|
conn.execute(
|
|
"UPDATE accounts SET rar_prod_enabled=1, rar_creds_prod_enc=?, "
|
|
"rar_test_enabled=0, rar_creds_test_enc=NULL, rar_env_default='prod' WHERE id=1",
|
|
(fake_creds,),
|
|
)
|
|
conn.commit()
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def _configureaza_doua_medii(client: TestClient, default_env: str = "test") -> None:
|
|
"""Activeaza ambele medii RAR pe contul 1."""
|
|
from app.db import get_connection
|
|
from app.crypto import encrypt_creds
|
|
conn = get_connection()
|
|
try:
|
|
fake_test = encrypt_creds({"email": "test@rar.ro", "password": "pass_test"})
|
|
fake_prod = encrypt_creds({"email": "prod@rar.ro", "password": "pass_prod"})
|
|
conn.execute(
|
|
"UPDATE accounts SET "
|
|
"rar_test_enabled=1, rar_creds_test_enc=?, "
|
|
"rar_prod_enabled=1, rar_creds_prod_enc=?, "
|
|
"rar_env_default=? WHERE id=1",
|
|
(fake_test, fake_prod, default_env),
|
|
)
|
|
conn.commit()
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
_ROWS_OK = [
|
|
{
|
|
"VIN": "WVWZZZ1KZAW009001",
|
|
"Nr": "B009TST",
|
|
"Data": "2026-06-15",
|
|
"KM": "77000",
|
|
"Operatie": "OP-FRANE",
|
|
},
|
|
]
|
|
|
|
|
|
def _upload_si_mapare(client: TestClient, rows: list[dict]) -> int:
|
|
"""Upload CSV si seteaza mapare coloane. Intoarce import_id."""
|
|
data = _csv_bytes(rows)
|
|
r = client.post(
|
|
"/_import/upload",
|
|
files={"file": ("test.csv", io.BytesIO(data), "text/csv")},
|
|
)
|
|
assert r.status_code == 200, r.text
|
|
m = re.search(r"/_import/(\d+)/", r.text)
|
|
assert m, f"import_id negasit in raspunsul de upload: {r.text[:400]}"
|
|
iid = int(m.group(1))
|
|
|
|
# Seteaza maparea daca nu e deja
|
|
if f"/_import/{iid}/mapare-coloane" in r.text or "mapare-coloane" in r.text.lower():
|
|
r2 = client.post(
|
|
f"/_import/{iid}/mapare-coloane",
|
|
data={
|
|
"colname": ["VIN", "Nr", "Data", "KM", "Operatie"],
|
|
"canon": ["vin", "nr_inmatriculare", "data_prestatie", "odometru_final", "operatie"],
|
|
"format_data": "YYYY-MM-DD",
|
|
},
|
|
)
|
|
assert r2.status_code == 200, r2.text
|
|
|
|
return iid
|
|
|
|
|
|
def _get_preview(client: TestClient, iid: int) -> str:
|
|
rp = client.get(f"/_import/{iid}/preview")
|
|
assert rp.status_code == 200, rp.text
|
|
return rp.text
|
|
|
|
|
|
def _commit(client: TestClient, iid: int, n_ok: int, rar_env: str | None = None) -> object:
|
|
data = {
|
|
"csrf_token": "",
|
|
"n_confirmat": str(n_ok),
|
|
"confirmed_by": "test@us009.ro",
|
|
}
|
|
if rar_env:
|
|
data["rar_env"] = rar_env
|
|
return client.post(f"/_import/{iid}/confirma", data=data)
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# Tests #
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
def test_selector_ascuns_la_un_mediu(client):
|
|
"""La 1 mediu disponibil: nu apare selector; apare eticheta statica cu mediul."""
|
|
_seed_nomenclator_si_mapare(client)
|
|
_configureaza_un_mediu(client, env="test")
|
|
|
|
# GET fragment/import: verifica ca nu exista selector si apare eticheta
|
|
r = client.get("/_fragments/import")
|
|
assert r.status_code == 200, r.text
|
|
html = r.text
|
|
|
|
# Eticheta statica "Testare" trebuie sa fie prezenta
|
|
assert "Testare" in html, "Eticheta mediu 'Testare' lipseste la 1 mediu disponibil"
|
|
|
|
# Selectorul nu trebuie sa apara (input cu name=rar_env hidden, dar fara <select>)
|
|
assert "<select" not in html or 'name="rar_env"' not in html or "rar-env-select" not in html, (
|
|
"Selector mediu RAR nu trebuie sa apara la 1 mediu disponibil"
|
|
)
|
|
|
|
|
|
def test_selector_prezent_si_prebifat_la_doua(client):
|
|
"""La 2 medii disponibile: selectorul apare si e pre-bifat pe default-ul contului."""
|
|
_seed_nomenclator_si_mapare(client)
|
|
_configureaza_doua_medii(client, default_env="test")
|
|
|
|
r = client.get("/_fragments/import")
|
|
assert r.status_code == 200, r.text
|
|
html = r.text
|
|
|
|
# Selectorul trebuie sa apara
|
|
assert "rar-env-select" in html, "Selectorul mediu RAR lipseste la 2 medii disponibile"
|
|
assert 'name="rar_env"' in html, 'Atribut name="rar_env" lipsa din selector'
|
|
|
|
# Default pre-selectat = "test" (default contului)
|
|
# Optiunea Testare trebuie sa fie selectata
|
|
assert 'value="test"' in html and "selected" in html, (
|
|
"Optiunea Testare nu e pre-selectata (default cont = test)"
|
|
)
|
|
|
|
|
|
def test_banner_avertisment_la_zero_medii(client):
|
|
"""La 0 medii configurate: apare un banner de avertisment (non-blocant)."""
|
|
# Contul 1 implicit nu are medii configurate
|
|
r = client.get("/_fragments/import")
|
|
assert r.status_code == 200, r.text
|
|
html = r.text
|
|
|
|
# Banner avertisment sau link catre configurare credentiale
|
|
assert "mediu" in html.lower() or "configureaza" in html.lower() or "credentiale" in html.lower(), (
|
|
"Bannerul de avertisment pentru 0 medii lipseste din pagina de upload"
|
|
)
|
|
|
|
# Upload-ul NU e blocat: formularul de upload trebuie sa fie prezent
|
|
assert "upload-form" in html, (
|
|
"Formularul de upload lipseste — la 0 medii upload-ul nu trebuie blocat"
|
|
)
|
|
|
|
|
|
def test_commit_seteaza_env_pe_submissions(client):
|
|
"""La commit: submissions primesc rar_env ales (fallback la ancora globala pt 0 medii)."""
|
|
_seed_nomenclator_si_mapare(client)
|
|
# Contul 1 fara medii configurate -> ancora globala = "test"
|
|
|
|
iid = _upload_si_mapare(client, _ROWS_OK)
|
|
preview_html = _get_preview(client, iid)
|
|
|
|
m_ok = re.search(r'id="n-confirmat"[^>]*?value="(\d+)"', preview_html)
|
|
n_ok = int(m_ok.group(1)) if m_ok else 1
|
|
|
|
r = _commit(client, iid, n_ok)
|
|
assert r.status_code == 200, r.text
|
|
assert any(kw in r.text.lower() for kw in ("coada", "prezenta", "trimiter")), (
|
|
"Mesajul de succes lipseste din raspunsul de commit"
|
|
)
|
|
|
|
# Verifica ca submission-ul are rar_env setat (fallback "test" via ancora globala)
|
|
from app.db import get_connection
|
|
conn = get_connection()
|
|
try:
|
|
sub = conn.execute(
|
|
"SELECT rar_env FROM submissions WHERE account_id=1 ORDER BY id DESC LIMIT 1"
|
|
).fetchone()
|
|
finally:
|
|
conn.close()
|
|
|
|
assert sub is not None, "Niciun submission gasit dupa commit"
|
|
assert sub["rar_env"] in ("test", "prod"), f"rar_env invalid: {sub['rar_env']!r}"
|
|
# Cu AUTOPASS_RAR_ENV=test si 0 medii configurate, expect "test"
|
|
assert sub["rar_env"] == "test", (
|
|
f"Expected rar_env='test' (ancora globala) dar primit {sub['rar_env']!r}"
|
|
)
|
|
|
|
|
|
def test_commit_cu_un_mediu_seteaza_acel_mediu(client):
|
|
"""La commit cu 1 mediu configurat: submission primeste mediul respectiv."""
|
|
_seed_nomenclator_si_mapare(client)
|
|
_configureaza_un_mediu(client, env="test")
|
|
|
|
iid = _upload_si_mapare(client, _ROWS_OK)
|
|
preview_html = _get_preview(client, iid)
|
|
|
|
m_ok = re.search(r'id="n-confirmat"[^>]*?value="(\d+)"', preview_html)
|
|
n_ok = int(m_ok.group(1)) if m_ok else 1
|
|
|
|
r = _commit(client, iid, n_ok)
|
|
assert r.status_code == 200, r.text
|
|
|
|
from app.db import get_connection
|
|
conn = get_connection()
|
|
try:
|
|
sub = conn.execute(
|
|
"SELECT rar_env FROM submissions WHERE account_id=1 ORDER BY id DESC LIMIT 1"
|
|
).fetchone()
|
|
finally:
|
|
conn.close()
|
|
|
|
assert sub is not None
|
|
assert sub["rar_env"] == "test", (
|
|
f"Expected rar_env='test' (singurul mediu disponibil) dar primit {sub['rar_env']!r}"
|
|
)
|
|
|
|
|
|
def test_commit_cu_doua_medii_respecta_alegerea(client):
|
|
"""La 2 medii: commit cu rar_env explicit seteaza mediul ales pe submissions."""
|
|
_seed_nomenclator_si_mapare(client)
|
|
_configureaza_doua_medii(client, default_env="test")
|
|
|
|
iid = _upload_si_mapare(client, _ROWS_OK)
|
|
preview_html = _get_preview(client, iid)
|
|
|
|
m_ok = re.search(r'id="n-confirmat"[^>]*?value="(\d+)"', preview_html)
|
|
n_ok = int(m_ok.group(1)) if m_ok else 1
|
|
|
|
# Commit explicit pe "prod"
|
|
r = _commit(client, iid, n_ok, rar_env="prod")
|
|
assert r.status_code == 200, r.text
|
|
|
|
from app.db import get_connection
|
|
conn = get_connection()
|
|
try:
|
|
sub = conn.execute(
|
|
"SELECT rar_env FROM submissions WHERE account_id=1 ORDER BY id DESC LIMIT 1"
|
|
).fetchone()
|
|
finally:
|
|
conn.close()
|
|
|
|
assert sub is not None
|
|
assert sub["rar_env"] == "prod", (
|
|
f"Expected rar_env='prod' (ales explicit) dar primit {sub['rar_env']!r}"
|
|
)
|
|
|
|
|
|
def test_badge_mediu_in_preview(client):
|
|
"""Preview-ul afiseaza badge-ul cu mediul tinta (US-009, F9/F10)."""
|
|
_seed_nomenclator_si_mapare(client)
|
|
_configureaza_un_mediu(client, env="test")
|
|
|
|
iid = _upload_si_mapare(client, _ROWS_OK)
|
|
|
|
r = client.get(f"/_import/{iid}/preview")
|
|
assert r.status_code == 200, r.text
|
|
html = r.text
|
|
|
|
# Badge cu mediul trebuie sa fie prezent in HTML
|
|
assert "Testare" in html or "PRODUCTIE" in html or "rar_env" in html, (
|
|
"Badge-ul de mediu RAR lipseste din preview"
|
|
)
|
|
|
|
|
|
def test_rar_env_in_confirm_form(client):
|
|
"""Preview-ul contine un field hidden rar_env in formularul de confirmare."""
|
|
_seed_nomenclator_si_mapare(client)
|
|
_configureaza_un_mediu(client, env="test")
|
|
|
|
iid = _upload_si_mapare(client, _ROWS_OK)
|
|
|
|
r = client.get(f"/_import/{iid}/preview")
|
|
assert r.status_code == 200, r.text
|
|
html = r.text
|
|
|
|
# Formularul de confirmare trebuie sa contina rar_env ca hidden field
|
|
assert 'name="rar_env"' in html, (
|
|
"Campul hidden 'rar_env' lipseste din formularul de confirmare preview"
|
|
)
|