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
This commit is contained in:
137
tests/test_benutzerverwaltung.py
Normal file
137
tests/test_benutzerverwaltung.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user