Files
spiele-redaktion/tests/test_benutzerverwaltung.py
Flo Hartmann 96a37e878b Phase 1: Kern mit Plugin-System, Auth/Rollen, Migrations, Plugin-Stubs
- redaktionskern (src/): schlanker Kern — App-Fabrik, Plugin-Loader
  (Entry-Points + plugins/-Verzeichnis), Migrations-Laufzeit
  (schema_migrations pro Plugin, SQLite-/Postgres-portabel),
  Auth mit Argon2id + Session-Cookies, Rollen admin/redakteur/rezensent,
  Benutzerverwaltung für Admins
- plugins/: 8 ladbare Stubs (neuheiten, dedup, planung, archiv,
  erinnerung, benachrichtigung, audit-log, export) nach Plugin-Vertrag
- Frontend: Jinja2 + Tailwind (CDN) + HTMX + Alpine.js, UI deutsch
- Tests: 28 pytest-Fälle (Loader, Lifecycle, Entry-Points, Migrations-
  Idempotenz, Login/Logout, Rollen-Zugriff, Benutzerverwaltung)
- Docker/Podman: Compose (Traefik-Labels) + Dockerfile (uv)
- README.md mit Setup-Anleitung
2026-08-21 18:35:09 +00:00

138 lines
4.3 KiB
Python

"""Tests: Benutzerverwaltung des Admins (anlegen, bearbeiten, Schutzzonen)."""
from __future__ import annotations
from fastapi.testclient import TestClient
from sqlalchemy import select
from redaktionskern.auth.models import User
from tests.conftest import melde_an
def zweiter_client(client) -> TestClient:
"""Frische Sitzung (eigene Cookies) auf derselben App."""
return TestClient(client.app)
def _liste_enthält(html: str, username: str) -> bool:
return f">{username}<" in html
def test_admin_legt_benutzer_an(app, client):
melde_an(client)
antwort = client.post(
"/admin/benutzer/neu",
data={
"username": "clara",
"display_name": "Clara Klar",
"role": "rezensent",
"password": "geheim-1234",
},
follow_redirects=False,
)
assert antwort.status_code == 303
liste = client.get("/admin/benutzer").text
assert _liste_enthält(liste, "clara")
# Der neue Benutzer kann sich anmelden.
melde_an(zweiter_client(client), "clara", "geheim-1234")
def test_doppelter_benutzername_wird_abgelehnt(client):
melde_an(client)
daten = {"username": "doppel", "display_name": "", "role": "rezensent",
"password": "geheim-1234"}
erste = client.post("/admin/benutzer/neu", data=daten, follow_redirects=False)
assert erste.status_code == 303
zweite = client.post("/admin/benutzer/neu", data=daten)
assert zweite.status_code == 400
assert "bereits vergeben" in zweite.text
def test_zu_kurzes_passwort_wird_abgelehnt(client):
melde_an(client)
antwort = client.post(
"/admin/benutzer/neu",
data={"username": "kurzer", "display_name": "", "role": "rezensent",
"password": "kurz"},
)
assert antwort.status_code == 400
assert "mindestens 8 Zeichen" in antwort.text
def test_bearbeiten_rolle_status_passwort(app, client):
melde_an(client)
client.post(
"/admin/benutzer/neu",
data={"username": "benno", "display_name": "Benno B.", "role": "rezensent",
"password": "geheim-1234"},
follow_redirects=False,
)
with app.state.session_factory() as db:
benno = db.scalar(select(User).where(User.username == "benno"))
benno_id = benno.id
antwort = client.post(
f"/admin/benutzer/{benno_id}",
data={"display_name": "Benno Bearbeitet", "role": "redakteur",
"active": "on", "password": ""},
follow_redirects=False,
)
assert antwort.status_code == 303
with app.state.session_factory() as db:
benno = db.get(User, benno_id)
assert benno.role == "redakteur"
assert benno.display_name == "Benno Bearbeitet"
assert benno.active is True
def test_admin_kann_sich_selbst_nicht_entmachten(client, app):
"""Eigene Rolle/Status sind geschützt — kein Aussperren möglich."""
with app.state.session_factory() as db:
admin = db.scalar(select(User).where(User.username == "admin"))
admin_id = admin.id
melde_an(client)
antwort = client.post(
f"/admin/benutzer/{admin_id}",
data={"display_name": "Administration", "role": "rezensent", "active": ""},
)
assert antwort.status_code == 400
assert "nicht ändern" in antwort.text
with app.state.session_factory() as db:
admin = db.get(User, admin_id)
assert admin.role == "admin"
assert admin.active is True
def test_neuer_benutzer_kann_sich_anmelden_nach_deaktivierung_nicht_mehr(app, client):
melde_an(client)
client.post(
"/admin/benutzer/neu",
data={"username": "dora", "display_name": "Dora D.", "role": "rezensent",
"password": "geheim-1234"},
follow_redirects=False,
)
with app.state.session_factory() as db:
dora = db.scalar(select(User).where(User.username == "dora"))
dora_id = dora.id
client.post(
f"/admin/benutzer/{dora_id}",
data={"display_name": "Dora D.", "role": "rezensent", "active": "",
"password": ""},
follow_redirects=False,
)
zweiter = zweiter_client(client)
antwort = zweiter.post(
"/login",
data={"username": "dora", "password": "geheim-1234"},
follow_redirects=False,
)
assert antwort.status_code == 200
assert "deaktiviert" in antwort.text