update
This commit is contained in:
@@ -250,6 +250,25 @@ def sitzung(response: Response, sid):
|
||||
return neu
|
||||
|
||||
|
||||
def mitglied(sid):
|
||||
"""Wer keinen Namen gewählt hat, darf zusehen, aber nichts eintragen.
|
||||
|
||||
Vorschläge, Sammlungen, Wertungen und Wünsche gehören einer Person.
|
||||
Ohne Namen hingen sie nur am Cookie: im geteilten Browser schriebe
|
||||
jeder auf dasselbe Konto, und nach dem Löschen der Cookies wäre alles
|
||||
weg. Die Knöpfe blendet die Oberfläche aus — das hier ist der Riegel.
|
||||
|
||||
Der Host braucht dafür ebenfalls einen Namen. Seine eigenen Knöpfe
|
||||
(drehen, eintragen, aufräumen) laufen über andere Routen.
|
||||
"""
|
||||
ich = users.besitzer(sid)
|
||||
if ich is None:
|
||||
raise HTTPException(
|
||||
status_code=403, detail="Dafür musst du oben deinen Namen wählen."
|
||||
)
|
||||
return ich
|
||||
|
||||
|
||||
# Der letzte Dreh, damit eine Seite mitten in der Animation nachziehen kann
|
||||
letzter_dreh = {"ergebnis": None, "start": 0.0}
|
||||
|
||||
@@ -324,6 +343,7 @@ async def vorschlagen(
|
||||
filmabend_sid: str | None = Cookie(None),
|
||||
):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
mitglied(sid)
|
||||
movie = await film_holen(movie_id)
|
||||
# Ein zweiter Vorschlag ersetzt den ersten, statt abgelehnt zu werden
|
||||
eintrag = suggestions.ersetzen(sid, movie, users.name_von(sid))
|
||||
@@ -336,6 +356,7 @@ async def vorschlagen(
|
||||
@app.delete("/api/suggestions")
|
||||
def vorschlag_loeschen(response: Response, filmabend_sid: str | None = Cookie(None)):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
mitglied(sid)
|
||||
entfernt = suggestions.loeschen(sid)
|
||||
if entfernt:
|
||||
events.senden("suggestions")
|
||||
@@ -366,6 +387,7 @@ def wunsch_anlegen(
|
||||
filmabend_sid: str | None = Cookie(None),
|
||||
):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
mitglied(sid)
|
||||
liste = features.anlegen(sid, text)
|
||||
if liste is None:
|
||||
raise HTTPException(status_code=400, detail="Kein Text")
|
||||
@@ -397,6 +419,7 @@ def wunsch_abstimmen(
|
||||
):
|
||||
"""Ein Klick setzt die Stimme, der nächste nimmt sie zurück."""
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
mitglied(sid)
|
||||
steht = features.abstimmen(feature_id, sid)
|
||||
if steht is None:
|
||||
raise HTTPException(status_code=404, detail="Wunsch nicht gefunden")
|
||||
@@ -421,6 +444,7 @@ def wunsch_anmerken(
|
||||
filmabend_sid: str | None = Cookie(None),
|
||||
):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
mitglied(sid)
|
||||
if not features.anmerken(feature_id, sid, text):
|
||||
raise HTTPException(status_code=400, detail="Kein Text oder Wunsch unbekannt")
|
||||
events.senden("features")
|
||||
@@ -451,6 +475,7 @@ async def merken(
|
||||
filmabend_sid: str | None = Cookie(None),
|
||||
):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
mitglied(sid)
|
||||
return wishlist.merken(sid, await film_holen(movie_id))
|
||||
|
||||
|
||||
@@ -461,6 +486,7 @@ def vergessen(
|
||||
filmabend_sid: str | None = Cookie(None),
|
||||
):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
mitglied(sid)
|
||||
return {"removed": wishlist.vergessen(sid, movie_id)}
|
||||
|
||||
|
||||
@@ -554,6 +580,7 @@ def bewerten(
|
||||
filmabend_sid: str | None = Cookie(None),
|
||||
):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
mitglied(sid)
|
||||
stars = max(1, min(watched.MAX_STERNE, stars))
|
||||
# Der Name kommt aus der Runde, nicht aus der Anfrage
|
||||
eintrag = watched.bewerten(watched_id, sid, stars, users.name_von(sid), text)
|
||||
|
||||
@@ -52,14 +52,36 @@ def frische_db(app_start):
|
||||
main.letzter_dreh.update(ergebnis=None, start=0.0)
|
||||
|
||||
|
||||
# Ein Client je Person — jeder hält sein eigenes Cookie
|
||||
def _anmelden(client, name):
|
||||
"""Namen anlegen, falls nötig, und für diesen Client wählen."""
|
||||
antwort = client.post("/api/users", json={"name": name})
|
||||
if antwort.status_code == 200:
|
||||
user_id = antwort.json()["id"]
|
||||
else:
|
||||
runde = client.get("/api/users").json()["items"]
|
||||
user_id = next(u["id"] for u in runde if u["name"] == name)
|
||||
return client.post("/api/users/waehlen", json={"user_id": user_id}).json()["mine"]
|
||||
|
||||
|
||||
# Ein Client je Person — jeder hält sein eigenes Cookie. Beide stehen in
|
||||
# der Runde, denn ohne Namen darf niemand etwas eintragen.
|
||||
@pytest.fixture
|
||||
def client():
|
||||
return TestClient(main.app)
|
||||
c = TestClient(main.app)
|
||||
_anmelden(c, "Anna")
|
||||
return c
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client2():
|
||||
c = TestClient(main.app)
|
||||
_anmelden(c, "Ben")
|
||||
return c
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def zuschauer():
|
||||
"""Ohne gewählten Namen: darf alles lesen, nichts schreiben."""
|
||||
return TestClient(main.app)
|
||||
|
||||
|
||||
@@ -78,14 +100,4 @@ def film():
|
||||
@pytest.fixture
|
||||
def anmelden():
|
||||
"""Client wählt einen Namen; legt ihn an, falls es ihn noch nicht gibt."""
|
||||
|
||||
def waehlen(client, name):
|
||||
antwort = client.post("/api/users", json={"name": name})
|
||||
if antwort.status_code == 200:
|
||||
user_id = antwort.json()["id"]
|
||||
else:
|
||||
runde = client.get("/api/users").json()["items"]
|
||||
user_id = next(u["id"] for u in runde if u["name"] == name)
|
||||
return client.post("/api/users/waehlen", json={"user_id": user_id}).json()["mine"]
|
||||
|
||||
return waehlen
|
||||
return _anmelden
|
||||
|
||||
@@ -39,8 +39,10 @@ def test_namenswechsel_laesst_alte_eintraege_beim_alten_namen(client, anmelden,
|
||||
assert client.get("/api/suggestions").json()["mine"] == als_anna["id"]
|
||||
|
||||
|
||||
def test_ohne_namen_zaehlt_die_sitzung(client, client2, film):
|
||||
vorschlag = client.post("/api/suggestions", json={"movie_id": film(1)}).json()
|
||||
assert vorschlag["name"] is None
|
||||
assert client.get("/api/suggestions").json()["mine"] == vorschlag["id"]
|
||||
assert client2.get("/api/suggestions").json()["mine"] is None
|
||||
def test_abwaehlen_macht_wieder_zum_zuschauer(client, film):
|
||||
client.post("/api/suggestions", json={"movie_id": film(1)})
|
||||
client.post("/api/users/waehlen", json={"user_id": None})
|
||||
assert client.get("/api/users").json()["mine"] is None
|
||||
# Lesen geht weiter, Schreiben nicht mehr
|
||||
assert client.get("/api/suggestions").json()["mine"] is None
|
||||
assert client.post("/api/suggestions", json={"movie_id": film(2)}).status_code == 403
|
||||
|
||||
74
backend/tests/test_zuschauer.py
Normal file
74
backend/tests/test_zuschauer.py
Normal file
@@ -0,0 +1,74 @@
|
||||
"""Der Zuschauer: kein Name gewählt, also nur mitlesen.
|
||||
|
||||
Gedacht für alle, die noch nicht in der Runde stehen. Sie sollen den
|
||||
Katalog, das Rad und die Wertungen der anderen sehen, aber nichts
|
||||
eintragen — sonst hinge ihr Kram allein am Cookie.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
# Jede Zeile: wie die Anfrage aussieht, wenn ein Zuschauer sie stellt
|
||||
SCHREIBEN = [
|
||||
("post", "/api/suggestions", {"movie_id": 1}),
|
||||
("delete", "/api/suggestions", None),
|
||||
("post", "/api/wishlist", {"movie_id": 1}),
|
||||
("delete", "/api/wishlist/1", None),
|
||||
("post", "/api/features", {"text": "Dunkles Design"}),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("methode,pfad,daten", SCHREIBEN)
|
||||
def test_zuschauer_darf_nicht_schreiben(zuschauer, film, methode, pfad, daten):
|
||||
film(1)
|
||||
# DELETE nimmt bei TestClient keinen json-Parameter
|
||||
anfrage = getattr(zuschauer, methode)
|
||||
antwort = anfrage(pfad, json=daten) if daten is not None else anfrage(pfad)
|
||||
assert antwort.status_code == 403
|
||||
|
||||
|
||||
def test_zuschauer_darf_nicht_bewerten(zuschauer, client, film):
|
||||
eintrag = client.post("/api/watched", json={"movie_id": film(1)}).json()
|
||||
antwort = zuschauer.post(
|
||||
f"/api/watched/{eintrag['id']}/rating", json={"stars": 5, "text": "stark"}
|
||||
)
|
||||
assert antwort.status_code == 403
|
||||
assert zuschauer.get("/api/watched").json()[0]["count"] == 0
|
||||
|
||||
|
||||
def test_zuschauer_darf_nicht_abstimmen_oder_ergaenzen(zuschauer, client):
|
||||
wunsch = client.post("/api/features", json={"text": "Dunkles Design"}).json()[0]
|
||||
assert zuschauer.post(f"/api/features/{wunsch['id']}/vote").status_code == 403
|
||||
assert (
|
||||
zuschauer.post(
|
||||
f"/api/features/{wunsch['id']}/notes", json={"text": "dazu"}
|
||||
).status_code
|
||||
== 403
|
||||
)
|
||||
assert zuschauer.get("/api/features").json()[0]["votes"] == 0
|
||||
|
||||
|
||||
def test_zuschauer_darf_alles_lesen(zuschauer, client, film):
|
||||
client.post("/api/suggestions", json={"movie_id": film(1)})
|
||||
client.post("/api/wishlist", json={"movie_id": film(2)})
|
||||
client.post("/api/features", json={"text": "Dunkles Design"})
|
||||
client.post("/api/watched", json={"movie_id": film(3)})
|
||||
|
||||
for pfad in ("/api/movies", "/api/suggestions", "/api/wishlist",
|
||||
"/api/features", "/api/watched", "/api/users", "/api/spin"):
|
||||
assert zuschauer.get(pfad).status_code == 200
|
||||
|
||||
# Fremdes gehört ihm nicht
|
||||
assert zuschauer.get("/api/suggestions").json()["mine"] is None
|
||||
assert zuschauer.get("/api/wishlist").json()[0]["mine"] is False
|
||||
assert zuschauer.get("/api/features").json()[0]["mine"] is False
|
||||
|
||||
|
||||
def test_host_kommt_ohne_namen_an_seine_knoepfe(zuschauer, client, film):
|
||||
"""Den Hostmodus kennt nur der Browser — seine Routen bleiben offen."""
|
||||
client.post("/api/suggestions", json={"movie_id": film(1)})
|
||||
eintrag = zuschauer.post("/api/watched", json={"movie_id": film(2)}).json()
|
||||
assert zuschauer.patch(
|
||||
f"/api/watched/{eintrag['id']}", json={"hidden": True}
|
||||
).status_code == 200
|
||||
assert zuschauer.post("/api/spin").json()["started"] is True
|
||||
assert zuschauer.delete("/api/suggestions/alle").status_code == 200
|
||||
Reference in New Issue
Block a user