5.12 (livrat): editare in modal a randurilor de preview, cont obligatoriu inainte de import, formular editare extras (_form_editare, _editare_preview_modal), plus suita de teste aferenta (preview edit/compact, mapare op, form editare, signup, admin panel). Design + planificare: - docs/design.md: sistem de design (tokeni, breakpoints, scara control, componente, a11y). - docs/prd/prd-5.12-* si prd-5.13-* (5.13 cu raport /autoplan: CEO+Design+Eng, audit trail). Curatare: sterse PNG-urile de test/mockup temporare din radacina. Nota: implementarea CSS 5.13 (responsive compact + sistem butoane) NU e inca facuta — planul revizuit cere refactorul testelor fragile din test_web_responsive.py INAINTE de CSS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
204 lines
7.1 KiB
Python
204 lines
7.1 KiB
Python
"""Teste US-001 (PRD 5.12): companie/email/CUI obligatorii la signup.
|
|
|
|
TDD strict: testele se scriu INAINTE de implementare (RED), dupa implementare trec (GREEN).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import re
|
|
import tempfile
|
|
|
|
import pytest
|
|
from starlette.testclient import TestClient
|
|
|
|
|
|
@pytest.fixture()
|
|
def client(monkeypatch):
|
|
tmp = tempfile.mkdtemp()
|
|
monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "test_signup_us001.db"))
|
|
monkeypatch.setenv("AUTOPASS_SIGNUP_RATE_MAX", "100")
|
|
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) as c:
|
|
yield c
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def _csrf(html: str) -> str:
|
|
m = re.search(r'name="csrf_token"\s+value="([^"]+)"', html)
|
|
if not m:
|
|
m = re.search(r'value="([^"]+)"\s+name="csrf_token"', html)
|
|
assert m, "csrf_token negasit in HTML"
|
|
return m.group(1)
|
|
|
|
|
|
def test_signup_html_cui_obligatoriu_ui(client):
|
|
"""GET /signup: campul CUI NU contine '(optional)' si are atribut required (US-001 UI)."""
|
|
resp = client.get("/signup")
|
|
assert resp.status_code == 200
|
|
# (a) NU trebuie sa apara textul "(optional)" langa CUI
|
|
assert "(optional)" not in resp.text, "Campul CUI afiseaza '(optional)' — trebuie sa fie obligatoriu"
|
|
# (b) input[name=cui] trebuie sa aiba atribut required
|
|
assert 'name="cui"' in resp.text
|
|
# cautam required pe aceeasi linie cu name="cui" sau intr-un bloc care contine name="cui" required
|
|
import re
|
|
# fie pe aceeasi linie: <input ... name="cui" ... required ...>
|
|
# fie in orice forma cu required si name="cui" in acelasi tag
|
|
cui_input_match = re.search(r'<input[^>]*name="cui"[^>]*>', resp.text)
|
|
assert cui_input_match, "input name='cui' negasit in HTML"
|
|
assert "required" in cui_input_match.group(0), (
|
|
f"input[name='cui'] NU are atribut required: {cui_input_match.group(0)}"
|
|
)
|
|
|
|
|
|
def test_signup_fara_cui_422(client):
|
|
"""POST /signup fara CUI -> 422, formular re-randat cu eroare, fara cont creat."""
|
|
resp = client.get("/signup")
|
|
token = _csrf(resp.text)
|
|
|
|
resp = client.post("/signup", data={
|
|
"name": "Service Fara CUI",
|
|
"cui": "",
|
|
"email": "fara_cui@test.com",
|
|
"parola": "parolasecreta123",
|
|
"csrf_token": token,
|
|
})
|
|
# trebuie sa returneze 422 (sau sa randeze formularul cu eroare)
|
|
assert resp.status_code == 422
|
|
# cheia API nu trebuie sa apara
|
|
assert "rfak_" not in resp.text
|
|
# campul name trebuie sa fie pastrat (form re-render cu valorile existente)
|
|
assert "Service Fara CUI" in resp.text
|
|
|
|
# verifica ca nu s-a creat niciun cont
|
|
from app.db import get_connection
|
|
conn = get_connection()
|
|
try:
|
|
n = conn.execute(
|
|
"SELECT COUNT(*) AS n FROM accounts WHERE name='Service Fara CUI'"
|
|
).fetchone()["n"]
|
|
assert n == 0, "Cont creat desi CUI lipsea"
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def test_signup_scrie_email_pe_account(client):
|
|
"""POST /signup valid -> accounts.email = emailul utilizatorului."""
|
|
resp = client.get("/signup")
|
|
token = _csrf(resp.text)
|
|
|
|
resp = client.post("/signup", data={
|
|
"name": "Service Cu Email",
|
|
"cui": "RO9999001",
|
|
"email": "cu_email@test.com",
|
|
"parola": "parolasecreta123",
|
|
"csrf_token": token,
|
|
})
|
|
assert resp.status_code == 200
|
|
assert "rfak_" in resp.text
|
|
|
|
from app.db import get_connection
|
|
conn = get_connection()
|
|
try:
|
|
acct = conn.execute(
|
|
"SELECT * FROM accounts WHERE name='Service Cu Email'"
|
|
).fetchone()
|
|
assert acct is not None
|
|
# emailul trebuie scris pe cont (normalizat: lower + trim)
|
|
assert acct["email"] == "cu_email@test.com"
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def test_signup_email_duplicat_mesaj_email(client):
|
|
"""POST /signup cu email existent dar CUI nou -> mesaj despre EMAIL, NU despre CUI/firma.
|
|
|
|
Bug: 'email deja folosit' contine 'deja folosit' -> era prins de conditia CUI duplicat
|
|
si afisa gresit 'Aceasta firma (CUI X) e deja inregistrata' (CUI nou, NU cauza reala).
|
|
Fix: verifica intai email-ul, apoi CUI-ul.
|
|
"""
|
|
from tests.conftest import make_test_cui
|
|
|
|
# primul signup cu email E + CUI C1
|
|
resp = client.get("/signup")
|
|
token = _csrf(resp.text)
|
|
resp1 = client.post("/signup", data={
|
|
"name": "Firma Prima SRL",
|
|
"cui": make_test_cui("email-dup-c1"),
|
|
"email": "emaildup@test.com",
|
|
"parola": "parolasecreta123",
|
|
"csrf_token": token,
|
|
})
|
|
assert resp1.status_code == 200
|
|
assert "rfak_" in resp1.text, "Primul signup trebuia sa reuseasca"
|
|
|
|
# al doilea signup cu ACELASI email dar CUI NOU
|
|
resp = client.get("/signup")
|
|
token2 = _csrf(resp.text)
|
|
cui_nou = make_test_cui("email-dup-c2")
|
|
resp2 = client.post("/signup", data={
|
|
"name": "Firma A Doua SRL",
|
|
"cui": cui_nou,
|
|
"email": "emaildup@test.com",
|
|
"parola": "parolasecreta456",
|
|
"csrf_token": token2,
|
|
})
|
|
|
|
assert resp2.status_code in (200, 422)
|
|
assert "rfak_" not in resp2.text, "Nu trebuia creata cheie API la email duplicat"
|
|
|
|
body_lower = resp2.text.lower()
|
|
# mesajul trebuie sa se refere la EMAIL
|
|
assert "email" in body_lower, (
|
|
f"Mesajul de eroare nu mentioneaza 'email': {resp2.text[:500]}"
|
|
)
|
|
# mesajul NU trebuie sa afiseze pattern-ul gresit cu firma si CUI-ul nou
|
|
# (CUI-ul apare legitim si in campul pre-completat al formularului, dar nu in mesajul de eroare)
|
|
wrong_pattern = f"(cui {cui_nou.lower()}) e deja inregistrata"
|
|
assert wrong_pattern not in body_lower, (
|
|
f"Mesajul arata gresit pattern-ul CUI-duplicat desi problema e emailul: {resp2.text[:500]}"
|
|
)
|
|
# nu trebuie sa apara mesajul specific CUI-duplicat
|
|
assert "e deja inregistrata" not in body_lower, (
|
|
f"Mesajul arata 'e deja inregistrata' (mesaj CUI) la eroare de email: {resp2.text[:500]}"
|
|
)
|
|
|
|
|
|
def test_signup_cui_existent_mesaj_prietenos(client):
|
|
"""POST /signup cu CUI existent -> mesaj prietenos, NU mesaj tehnic cu 'activate --account'."""
|
|
resp = client.get("/signup")
|
|
token = _csrf(resp.text)
|
|
|
|
# primul signup
|
|
client.post("/signup", data={
|
|
"name": "Firma Existenta SRL",
|
|
"cui": "RO8888001",
|
|
"email": "firma1@test.com",
|
|
"parola": "parolasecreta123",
|
|
"csrf_token": token,
|
|
})
|
|
|
|
# al doilea signup cu acelasi CUI
|
|
resp = client.get("/signup")
|
|
token2 = _csrf(resp.text)
|
|
resp2 = client.post("/signup", data={
|
|
"name": "Alt Utilizator SRL",
|
|
"cui": "RO8888001",
|
|
"email": "firma2@test.com",
|
|
"parola": "parolasecreta456",
|
|
"csrf_token": token2,
|
|
})
|
|
|
|
assert resp2.status_code in (200, 422)
|
|
# NU trebuie sa apara mesajul tehnic cu referinta la CLI
|
|
assert "activate --account" not in resp2.text
|
|
# trebuie sa apara un mesaj prietenos cu CUI-ul
|
|
assert "RO8888001" in resp2.text
|
|
# trebuie sa contina cuvant cheie de tip "firma" sau "inregistrata"
|
|
body_lower = resp2.text.lower()
|
|
assert any(kw in body_lower for kw in ["firma", "inregistrat", "cont", "acces"])
|