8cd5bfe821
Avec prevent_initial_call=True, le callback ne s'exécutait pas lors de la chaîne initiale (_generate_csrf_token → csrf-token), laissant le champ csrf_token vide au premier chargement direct de /connexion → erreur 400. Ajoute des tests comportementaux avec CSRF activé (comme en production) et un test architectural qui vérifie que le callback reste appelable initialement. Corrige aussi les assertions de redirection post-login (/compte/abonnement). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
102 lines
3.1 KiB
Python
102 lines
3.1 KiB
Python
from werkzeug.security import generate_password_hash
|
|
|
|
from src.auth import db
|
|
|
|
|
|
def _make_verified_user(email="a@b.c", password="password12"):
|
|
db.init_schema()
|
|
uid = db.create_user(email, generate_password_hash(password))
|
|
db.set_email_verified(uid)
|
|
return uid
|
|
|
|
|
|
def test_login_success(client, users_db_path):
|
|
_make_verified_user()
|
|
resp = client.post(
|
|
"/auth/login",
|
|
data={"email": "a@b.c", "password": "password12"},
|
|
)
|
|
assert resp.status_code == 302
|
|
assert resp.headers["Location"].endswith("/compte/abonnement")
|
|
|
|
|
|
def test_login_wrong_password(client, users_db_path):
|
|
_make_verified_user()
|
|
resp = client.post(
|
|
"/auth/login", data={"email": "a@b.c", "password": "wrong-password"}
|
|
)
|
|
assert "error=invalid_credentials" in resp.headers["Location"]
|
|
|
|
|
|
def test_login_unknown_email_same_error(client, users_db_path):
|
|
db.init_schema()
|
|
resp = client.post(
|
|
"/auth/login",
|
|
data={"email": "inexistant@example.com", "password": "x" * 12},
|
|
)
|
|
assert "error=invalid_credentials" in resp.headers["Location"]
|
|
|
|
|
|
def test_login_unverified_user(client, users_db_path):
|
|
db.init_schema()
|
|
db.create_user("a@b.c", generate_password_hash("password12"))
|
|
resp = client.post("/auth/login", data={"email": "a@b.c", "password": "password12"})
|
|
assert "error=email_not_verified" in resp.headers["Location"]
|
|
|
|
|
|
def test_login_respects_safe_next(client, users_db_path):
|
|
_make_verified_user()
|
|
resp = client.post(
|
|
"/auth/login",
|
|
data={"email": "a@b.c", "password": "password12", "next": "/tableau"},
|
|
)
|
|
assert resp.headers["Location"].endswith("/tableau")
|
|
|
|
|
|
def test_login_rejects_absolute_next(client, users_db_path):
|
|
_make_verified_user()
|
|
resp = client.post(
|
|
"/auth/login",
|
|
data={
|
|
"email": "a@b.c",
|
|
"password": "password12",
|
|
"next": "https://evil.com",
|
|
},
|
|
)
|
|
assert resp.headers["Location"].endswith("/compte/abonnement")
|
|
|
|
|
|
def test_logout_clears_session(client, users_db_path):
|
|
_make_verified_user()
|
|
client.post("/auth/login", data={"email": "a@b.c", "password": "password12"})
|
|
resp = client.post("/auth/logout")
|
|
assert resp.status_code == 302
|
|
assert resp.headers["Location"].endswith("/")
|
|
|
|
|
|
# --- Tests CSRF (protection active, comme en production) ---
|
|
|
|
|
|
def test_login_rejects_missing_csrf_token(csrf_client):
|
|
"""POST /auth/login sans token CSRF → 400.
|
|
|
|
Régression : avec prevent_initial_call=True sur _fill_csrf_inputs, le token
|
|
n'était pas injecté dans le formulaire lors du chargement initial de /connexion.
|
|
"""
|
|
resp = csrf_client.post(
|
|
"/auth/login",
|
|
data={"email": "a@b.c", "password": "password12"},
|
|
)
|
|
assert resp.status_code == 400
|
|
|
|
|
|
def test_login_accepts_valid_csrf_token(csrf_client, users_db_path):
|
|
"""POST /auth/login avec token CSRF valide → 302."""
|
|
_make_verified_user()
|
|
token = csrf_client.get("/_test/csrf").data.decode()
|
|
resp = csrf_client.post(
|
|
"/auth/login",
|
|
data={"email": "a@b.c", "password": "password12", "csrf_token": token},
|
|
)
|
|
assert resp.status_code == 302
|