Files
rar-autopass/tests/test_dashboard.py
Claude Agent 4a1d28749a feat(web): dashboard ergonomic cu tab-uri, stepper import si microcopy uman (3.4)
Reorganizeaza interfata web pe trei principii, fara a atinge backend-ul de
trimitere (worker, mapping, idempotency, masina de stari neatinse):

- US-001 app/web/labels.py: modul pur stari tehnice -> text uman + clasa CSS
- US-002 bara status /_fragments/status: microcopy uman, defalcare blocate, scoped cont
- US-003 shell 6 tab-uri (Acasa/Import/Coada/Mapari/Cont/Nomenclator): deep-link
  ?tab=, panou activ randat server-side, fragmente inactive lazy, ARIA real
- US-004 stepper import 4 pasi (pur vizual; hx-target + csrf pastrate)
- US-005 Acasa onboarding checklist auto-bifat + colaps + empty states prietenoase

Reparat in cursul VERIFY/CLOSE: izolare teste (reset ratelimit._hits in fixturi),
regresie avertisment "cont in asteptare de activare" (re-introdus in bara status),
culori hardcodate -> variabile paleta. 434 teste pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 22:26:10 +00:00

122 lines
4.4 KiB
Python

"""Teste dashboard + audit CSV: nomenclator browser, stare RAR, export CSV."""
from __future__ import annotations
import csv
import io
import os
import tempfile
import pytest
from fastapi.testclient import TestClient
@pytest.fixture()
def client(monkeypatch):
tmp = tempfile.mkdtemp()
monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "t.db"))
# Comportament in mod dev (fallback cont 1, fara login/CSRF); auth web e
# default ON in prod — testat separat in test_web_*.
monkeypatch.setenv("AUTOPASS_WEB_AUTH_REQUIRED", "false")
from app.config import get_settings
get_settings.cache_clear()
from app.main import app
with TestClient(app) as c:
yield c
get_settings.cache_clear()
def _body(**over):
prez = {
"vin": "WVWZZZ1KZAW000123",
"nr_inmatriculare": "B999TST",
"data_prestatie": "2026-06-15",
"odometru_final": "123456",
"prestatii": [{"cod_prestatie": "OE-1"}],
}
prez.update(over)
return {"rar_credentials": {"email": "x@y.ro", "password": "s"}, "prezentari": [prez]}
# --------------------------------------------------------------------------- #
# Dashboard render + fragmente #
# --------------------------------------------------------------------------- #
def test_dashboard_renders_with_rar_state(client):
r = client.get("/")
assert r.status_code == 200
# Dupa US-003 bara de status e incarcata via HTMX (hx-trigger=load, every 15s)
assert "/_fragments/status" in r.text, "Dashboard-ul trebuie sa referenceze fragmentul de status"
# Fragmentul de status contine starea worker (eticheta umana, nu "worker oprit" brut)
rs = client.get("/_fragments/status")
assert rs.status_code == 200
# eticheta_worker(False) => "Trimitere automata: oprita" → fragmentul afiseaza "oprita"
assert "oprita" in rs.text or "Trimitere automata" in rs.text
# Tab-ul Nomenclator e accesat via /_fragments/nomenclator
rn = client.get("/_fragments/nomenclator")
assert rn.status_code == 200
assert "Nomenclator" in rn.text or "Cod" in rn.text or "OE-1" in rn.text
def test_nomenclator_fragment_lists_seed(client):
r = client.get("/_fragments/nomenclator")
assert r.status_code == 200
# seed fallback are 18 coduri; OE-1 + R-ODO trebuie sa apara
assert "OE-1" in r.text
assert "R-ODO" in r.text
def test_submissions_fragment_empty_state(client):
r = client.get("/_fragments/submissions")
assert r.status_code == 200
# US-005: empty state prietenos cu indemn la Import (nu mesajul tehnic vechi)
assert "Nicio trimitere" in r.text or "incepe cu Import" in r.text or "?tab=import" in r.text
# --------------------------------------------------------------------------- #
# Audit CSV export #
# --------------------------------------------------------------------------- #
def test_audit_export_sent_only(client):
# un submission queued (validare ok) + unul needs_data
client.post("/v1/prezentari", json=_body())
client.post("/v1/prezentari", json=_body(vin="BAD"))
# status implicit = sent -> niciun rand (nimic trimis inca), doar header
r = client.get("/v1/audit/export")
assert r.status_code == 200
assert r.headers["content-type"].startswith("text/csv")
assert "attachment" in r.headers["content-disposition"]
rows = list(csv.DictReader(io.StringIO(r.text)))
assert rows == []
# status=all -> ambele, cu coloane-cheie populate, fara b64_image
r = client.get("/v1/audit/export?status=all")
rows = list(csv.DictReader(io.StringIO(r.text)))
assert len(rows) == 2
assert "vin" in rows[0]
assert "b64_image" not in rows[0]
vins = {row["vin"] for row in rows}
assert "WVWZZZ1KZAW000123" in vins
# prestatii = coduri concatenate
assert any(row["prestatii"] == "OE-1" for row in rows)
def test_audit_export_marks_sent_after_update(client):
client.post("/v1/prezentari", json=_body())
# marcam manual sent (worker ar face asta dupa postPrezentare reusit)
from app.db import get_connection
conn = get_connection()
try:
conn.execute("UPDATE submissions SET status='sent', id_prezentare=68514 WHERE id=1")
finally:
conn.close()
rows = list(csv.DictReader(io.StringIO(client.get("/v1/audit/export").text)))
assert len(rows) == 1
assert rows[0]["status"] == "sent"
assert rows[0]["id_prezentare"] == "68514"