Files
stundenfuchs/tests/test_legal_and_support.py
T
maddin 9794362f39
CI / checks (push) Has been cancelled
chore: initialize public repository
2026-03-22 12:55:55 +00:00

215 lines
7.5 KiB
Python

from datetime import datetime, timedelta, timezone
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 SupportTicket
def _csrf_from_html(html: str) -> str:
match = re.search(r'name="csrf_token" value="([^"]+)"', html)
assert match is not None
return match.group(1)
def _started_at_from_html(html: str) -> str:
match = re.search(r'name="started_at" value="([^"]+)"', html)
assert match is not None
return match.group(1)
def _build_admin_app(db_url: str) -> object:
return create_app(
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,
BOOTSTRAP_ADMIN_EMAIL="admin@example.com",
)
)
def test_public_footer_and_legal_pages_render(app):
with TestClient(app) as client:
response = client.get("/login")
assert response.status_code == 200
assert 'href="/kontakt"' in response.text
assert 'href="/impressum"' in response.text
assert 'href="/datenschutz"' in response.text
impressum = client.get("/impressum")
assert impressum.status_code == 200
assert "Impressum" in impressum.text
privacy = client.get("/datenschutz")
assert privacy.status_code == 200
assert "Datenschutz" in privacy.text
def test_contact_form_creates_ticket(monkeypatch, app):
import app.main as main_module
base_time = datetime(2026, 3, 22, 12, 0, tzinfo=timezone.utc)
monkeypatch.setattr(main_module, "utc_now", lambda: base_time)
with TestClient(app) as client:
form = client.get("/kontakt")
assert form.status_code == 200
csrf = _csrf_from_html(form.text)
started_at = _started_at_from_html(form.text)
monkeypatch.setattr(main_module, "utc_now", lambda: base_time + timedelta(seconds=5))
submit = client.post(
"/kontakt",
data={
"csrf_token": csrf,
"started_at": started_at,
"website": "",
"category": "feature",
"name": "Max Beispiel",
"email": "max@example.com",
"subject": "Bitte Monatsfilter erweitern",
"message": "Ich wünsche mir eine bessere Filterung in der Monatsansicht.",
},
follow_redirects=False,
)
assert submit.status_code == 303
assert submit.headers["location"] == "/kontakt?msg=sent"
with Session(get_engine()) as db:
tickets = db.execute(select(SupportTicket)).scalars().all()
assert len(tickets) == 1
assert tickets[0].category == "feature"
assert tickets[0].status == "open"
assert tickets[0].subject == "Bitte Monatsfilter erweitern"
def test_contact_form_honeypot_blocks_submission(monkeypatch, app):
import app.main as main_module
base_time = datetime(2026, 3, 22, 12, 0, tzinfo=timezone.utc)
monkeypatch.setattr(main_module, "utc_now", lambda: base_time)
with TestClient(app) as client:
form = client.get("/kontakt")
csrf = _csrf_from_html(form.text)
started_at = _started_at_from_html(form.text)
monkeypatch.setattr(main_module, "utc_now", lambda: base_time + timedelta(seconds=5))
submit = client.post(
"/kontakt",
data={
"csrf_token": csrf,
"started_at": started_at,
"website": "spam",
"category": "problem",
"name": "",
"email": "spam@example.com",
"subject": "Spamversuch",
"message": "Das sollte blockiert werden.",
},
)
assert submit.status_code == 429
assert "nicht versendet" in submit.text
with Session(get_engine()) as db:
tickets = db.execute(select(SupportTicket)).scalars().all()
assert tickets == []
def test_admin_can_manage_legal_content_and_tickets(tmp_path, monkeypatch):
import app.main as main_module
db_path = tmp_path / "legal-support.db"
app = _build_admin_app(f"sqlite:///{db_path}")
base_time = datetime(2026, 3, 22, 12, 0, tzinfo=timezone.utc)
monkeypatch.setattr(main_module, "utc_now", lambda: base_time)
with TestClient(app) as admin_client:
register = admin_client.post(
"/auth/register",
json={"email": "admin@example.com", "password": "verystrongPass123"},
)
assert register.status_code == 200
csrf = register.json()["csrf_token"]
update_legal = admin_client.post(
"/settings/admin/site-content",
data={
"csrf_token": csrf,
"impressum_markdown": "# Impressum\n\n**Stage Test**",
"privacy_markdown": "# Datenschutz\n\nBitte Datenschutz beachten.",
},
follow_redirects=False,
)
assert update_legal.status_code == 303
assert update_legal.headers["location"] == "/settings?tab=admin&msg=site_content_updated"
impressum = admin_client.get("/impressum")
assert impressum.status_code == 200
assert "Stage Test" in impressum.text
form = admin_client.get("/kontakt")
started_at = _started_at_from_html(form.text)
monkeypatch.setattr(main_module, "utc_now", lambda: base_time + timedelta(seconds=5))
submit = admin_client.post(
"/kontakt",
data={
"csrf_token": csrf,
"started_at": started_at,
"website": "",
"category": "problem",
"name": "Admin Test",
"email": "admin@example.com",
"subject": "Ticket bitte schließen",
"message": "Dieses Ticket wird direkt im Adminbereich geschlossen.",
},
follow_redirects=False,
)
assert submit.status_code == 303
with Session(get_engine()) as db:
ticket = db.execute(select(SupportTicket).where(SupportTicket.subject == "Ticket bitte schließen")).scalar_one()
ticket_id = ticket.id
with TestClient(app) as admin_client:
login = admin_client.post(
"/login",
data={
"email": "admin@example.com",
"password": "verystrongPass123",
"csrf_token": _csrf_from_html(admin_client.get("/login").text),
},
follow_redirects=False,
)
assert login.status_code == 303
settings_page = admin_client.get("/settings?tab=admin")
settings_csrf = _csrf_from_html(settings_page.text)
update_ticket = admin_client.post(
f"/settings/admin/tickets/{ticket_id}",
data={
"csrf_token": settings_csrf,
"status": "closed",
"admin_notes": "Geschlossen im Test",
},
follow_redirects=False,
)
assert update_ticket.status_code == 303
assert update_ticket.headers["location"] == "/settings?tab=admin&msg=ticket_updated"
with Session(get_engine()) as db:
ticket = db.get(SupportTicket, ticket_id)
assert ticket is not None
assert ticket.status == "closed"
assert ticket.admin_notes == "Geschlossen im Test"
assert ticket.closed_at is not None