feat(5.12): modal editare + cont obligatoriu la import; design.md + PRD 5.13 revizuit (/autoplan)
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>
This commit is contained in:
203
tests/test_signup.py
Normal file
203
tests/test_signup.py
Normal file
@@ -0,0 +1,203 @@
|
||||
"""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"])
|
||||
Reference in New Issue
Block a user