"""Teste US-004 (PRD 3.3): GET/POST /login, POST /logout. TDD: testele se scriu INAINTE de implementarea auth_routes.py; la inceput pica (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, "t.db")) 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() @pytest.fixture() def client_rl(monkeypatch): """Client cu login_rate_max=2 pentru testul de rate-limit.""" tmp = tempfile.mkdtemp() monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "t.db")) monkeypatch.setenv("AUTOPASS_LOGIN_RATE_MAX", "2") 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 _get_csrf(client, url: str) -> str: resp = client.get(url) assert resp.status_code == 200 m = re.search(r'name="csrf_token"\s+value="([^"]+)"', resp.text) if not m: m = re.search(r'value="([^"]+)"\s+name="csrf_token"', resp.text) assert m, f"csrf_token negasit in {url}" return m.group(1) def _create_user(email: str = "test@test.com", password: str = "parolasecreta", active: bool = True): """Creeaza direct un cont + user in DB (fara HTTP).""" 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, "Service Test", active=active) user_id = create_user(conn, acct_id, email, password) return acct_id, user_id finally: conn.close() def test_login_corect_seteaza_sesiune(client): """Credentiale corecte -> 303 redirect la /; sesiunea are account_id.""" _create_user("valid@test.com", "parolasecreta", active=True) token = _get_csrf(client, "/login") resp = client.post("/login", data={ "email": "valid@test.com", "parola": "parolasecreta", "csrf_token": token, }, follow_redirects=False) assert resp.status_code == 303 loc = resp.headers.get("location", "") assert loc in ("/", "http://testserver/"), f"Redirect gresit: {loc}" def test_login_gresit_401_fara_leak(client): """Parola gresita -> 401; mesaj generic fara a dezvalui daca emailul exista.""" _create_user("real@test.com", "parolasecreta", active=True) token = _get_csrf(client, "/login") resp = client.post("/login", data={ "email": "real@test.com", "parola": "gresita", "csrf_token": token, }) assert resp.status_code == 401 text = resp.text.lower() assert "inexistent" not in text, "Raspunsul dezvaluie ca emailul nu exista" assert "nu exista" not in text, "Raspunsul dezvaluie ca emailul nu exista" def test_logout_redirect_login(client): """POST /logout -> 303 redirect la /login.""" _create_user("logout@test.com", "parolasecreta", active=True) token = _get_csrf(client, "/login") client.post("/login", data={ "email": "logout@test.com", "parola": "parolasecreta", "csrf_token": token, }, follow_redirects=False) # Dupa login, sesiunea e reset -> obtine un token CSRF nou token = _get_csrf(client, "/login") resp = client.post("/logout", data={"csrf_token": token}, follow_redirects=False) assert resp.status_code == 303 assert "/login" in resp.headers.get("location", "") def test_login_cont_inactiv_intra(client): """C18: Login pe cont active=0 trebuie sa functioneze (gate-ul e doar pe trimitere).""" _create_user("inactiv@test.com", "parolasecreta", active=False) token = _get_csrf(client, "/login") resp = client.post("/login", data={ "email": "inactiv@test.com", "parola": "parolasecreta", "csrf_token": token, }, follow_redirects=False) assert resp.status_code == 303, ( "Login pe cont inactiv trebuia sa reuseasca (gate-ul e doar la trimitere, nu la login)" ) def test_login_rate_limit_429(client_rl): """Peste login_rate_max incercari (login_rate_max=2) -> 429 la urmatoarea cerere.""" # Doua incercari (permise) for _ in range(2): token = _get_csrf(client_rl, "/login") client_rl.post("/login", data={ "email": "nimeni@test.com", "parola": "parola_gresita", "csrf_token": token, }) # A treia — trebuie 429 token = _get_csrf(client_rl, "/login") resp = client_rl.post("/login", data={ "email": "nimeni@test.com", "parola": "parola_gresita", "csrf_token": token, }) assert resp.status_code == 429, "Peste login_rate_max trebuia 429"