This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
import re
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.config import Settings
|
||||
from app.database import get_engine
|
||||
from app.main import create_app
|
||||
from app.models import EmailServerConfig, User
|
||||
|
||||
|
||||
def _extract_csrf(html: str) -> str:
|
||||
match = re.search(r'name="csrf_token" value="([^"]+)"', html)
|
||||
assert match is not None
|
||||
return match.group(1)
|
||||
|
||||
|
||||
def _build_settings(db_url: str) -> Settings:
|
||||
return Settings(
|
||||
APP_ENV="test",
|
||||
DB_URL=db_url,
|
||||
SESSION_SECRET="test-secret",
|
||||
COOKIE_SECURE=False,
|
||||
COOKIE_SAMESITE="lax",
|
||||
LOGIN_RATE_LIMIT_ATTEMPTS=5,
|
||||
LOGIN_RATE_LIMIT_WINDOW_MINUTES=15,
|
||||
EMAIL_VERIFICATION_REQUIRED=True,
|
||||
)
|
||||
|
||||
|
||||
def test_register_requires_email_verification_with_mail_server(tmp_path, monkeypatch):
|
||||
db_path = tmp_path / "verify.db"
|
||||
app = create_app(_build_settings(f"sqlite:///{db_path}"))
|
||||
|
||||
sent_mails: list[dict[str, str]] = []
|
||||
|
||||
def fake_send_email(*, settings, to_email: str, subject: str, text_body: str) -> None:
|
||||
sent_mails.append({"to": to_email, "subject": subject, "body": text_body})
|
||||
|
||||
monkeypatch.setattr("app.main.send_email", fake_send_email)
|
||||
|
||||
with Session(get_engine()) as db:
|
||||
db.add(
|
||||
EmailServerConfig(
|
||||
smtp_host="smtp.test.local",
|
||||
smtp_port=587,
|
||||
from_email="noreply@test.local",
|
||||
from_name="Stundentracker",
|
||||
use_starttls=True,
|
||||
use_ssl=False,
|
||||
verify_tls=False,
|
||||
registration_mails_enabled=True,
|
||||
password_reset_mails_enabled=True,
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
|
||||
with TestClient(app) as client:
|
||||
register_page = client.get("/register")
|
||||
csrf = _extract_csrf(register_page.text)
|
||||
|
||||
register_submit = client.post(
|
||||
"/register",
|
||||
data={
|
||||
"email": "verify-user@example.com",
|
||||
"password": "strongpasswordVerify1",
|
||||
"csrf_token": csrf,
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert register_submit.status_code == 303
|
||||
assert register_submit.headers["location"] == "/login?msg=email_verification_sent"
|
||||
assert len(sent_mails) == 1
|
||||
assert sent_mails[0]["to"] == "verify-user@example.com"
|
||||
|
||||
login_page = client.get("/login")
|
||||
login_csrf = _extract_csrf(login_page.text)
|
||||
denied_login = client.post(
|
||||
"/login",
|
||||
data={
|
||||
"email": "verify-user@example.com",
|
||||
"password": "strongpasswordVerify1",
|
||||
"csrf_token": login_csrf,
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert denied_login.status_code == 403
|
||||
assert "Bitte zuerst deine E-Mail-Adresse bestätigen" in denied_login.text
|
||||
|
||||
link_match = re.search(r"https?://[^\s]+/verify-email\?token=[^\s]+", sent_mails[0]["body"])
|
||||
assert link_match is not None
|
||||
verify_response = client.get(link_match.group(0), follow_redirects=False)
|
||||
assert verify_response.status_code == 303
|
||||
assert verify_response.headers["location"] == "/login?msg=email_verified"
|
||||
|
||||
login_page_after_verify = client.get("/login")
|
||||
login_csrf_after_verify = _extract_csrf(login_page_after_verify.text)
|
||||
login_after_verify = client.post(
|
||||
"/login",
|
||||
data={
|
||||
"email": "verify-user@example.com",
|
||||
"password": "strongpasswordVerify1",
|
||||
"csrf_token": login_csrf_after_verify,
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert login_after_verify.status_code == 303
|
||||
assert login_after_verify.headers["location"].startswith("/dashboard")
|
||||
|
||||
with Session(get_engine()) as db:
|
||||
verified_user = db.execute(select(User).where(User.email == "verify-user@example.com")).scalar_one()
|
||||
assert verified_user.email_verified is True
|
||||
Reference in New Issue
Block a user