update
This commit is contained in:
@@ -12,6 +12,8 @@ _ORDNER = Path(tempfile.mkdtemp(prefix="filmabend-tests-"))
|
||||
os.environ["DB_PATH"] = str(_ORDNER / "test.db")
|
||||
os.environ.setdefault("TMDB_TOKEN", "test")
|
||||
os.environ.setdefault("MINIMAX_KEY", "test")
|
||||
# Der Hostmodus hängt am Schlüssel — in den Tests ein fester
|
||||
os.environ["HOST_SCHLUESSEL"] = "test-host-schluessel"
|
||||
|
||||
import pytest # noqa: E402
|
||||
from fastapi.testclient import TestClient # noqa: E402
|
||||
@@ -85,6 +87,21 @@ def zuschauer():
|
||||
return TestClient(main.app)
|
||||
|
||||
|
||||
HOST_SCHLUESSEL = "test-host-schluessel"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hostmodus():
|
||||
"""Schaltet einen Client per Hostschlüssel frei."""
|
||||
|
||||
def an(client):
|
||||
return client.post(
|
||||
"/api/host", json={"an": True, "schluessel": HOST_SCHLUESSEL}
|
||||
)
|
||||
|
||||
return an
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def film():
|
||||
"""Testfilm direkt in der Datenbank, ohne TMDB. Gibt die id zurück."""
|
||||
|
||||
156
backend/tests/test_gaestebuch.py
Normal file
156
backend/tests/test_gaestebuch.py
Normal file
@@ -0,0 +1,156 @@
|
||||
"""Das Buch der Abende: Wertungen als Momentaufnahme, Kommentare als
|
||||
Diskussion — verschachtelt, so tief man will."""
|
||||
|
||||
|
||||
def abend(client, film, movie_id=1):
|
||||
"""Host trägt einen Film ein, gibt die watched_id zurück."""
|
||||
return client.post("/api/watched", json={"movie_id": film(movie_id)}).json()["id"]
|
||||
|
||||
|
||||
def wertung(client, watched_id, stars=4):
|
||||
client.post(f"/api/watched/{watched_id}/rating", json={"stars": stars})
|
||||
film = next(f for f in client.get("/api/watched").json() if f["id"] == watched_id)
|
||||
return next(r for r in film["ratings"] if r["mine"])
|
||||
|
||||
|
||||
def film_von(client, watched_id):
|
||||
return next(f for f in client.get("/api/watched").json() if f["id"] == watched_id)
|
||||
|
||||
|
||||
def kommentar(client, watched_id, text, parent_id=None):
|
||||
antwort = client.post(
|
||||
f"/api/watched/{watched_id}/notes", json={"text": text, "parent_id": parent_id}
|
||||
)
|
||||
assert antwort.status_code == 200, antwort.text
|
||||
return antwort.json()["id"]
|
||||
|
||||
|
||||
def test_laufende_nummer_zaehlt_vom_ersten_abend(client, film):
|
||||
erster = abend(client, film, 1)
|
||||
client.patch(f"/api/watched/{erster}", json={"watched_at": "2026-01-01"})
|
||||
zweiter = abend(client, film, 2)
|
||||
client.patch(f"/api/watched/{zweiter}", json={"watched_at": "2026-01-08"})
|
||||
|
||||
liste = client.get("/api/watched").json()
|
||||
# Jüngster zuerst, trägt aber die höchste Nummer
|
||||
assert [(f["id"], f["nummer"]) for f in liste] == [(zweiter, 2), (erster, 1)]
|
||||
|
||||
|
||||
def test_katalogdaten_haengen_am_abend(client):
|
||||
from app import db
|
||||
|
||||
with db.connect() as conn:
|
||||
db.upsert(conn, {"id": 5, "title": "Alien", "backdrop_path": "/b.jpg",
|
||||
"images": ["/s1.jpg"], "runtime": 117, "genres": ["Horror"]})
|
||||
watched_id = client.post("/api/watched", json={"movie_id": 5}).json()["id"]
|
||||
f = film_von(client, watched_id)
|
||||
assert f["backdrop_path"] == "/b.jpg"
|
||||
assert f["images"] == ["/s1.jpg"]
|
||||
assert f["runtime"] == 117
|
||||
assert f["genres"] == ["Horror"]
|
||||
|
||||
|
||||
def test_kommentare_bilden_einen_baum(client, client2, film):
|
||||
watched_id = abend(client, film)
|
||||
oben = kommentar(client, watched_id, "War super.")
|
||||
antwort = kommentar(client2, watched_id, "Fand ich nicht.", oben)
|
||||
tiefer = kommentar(client, watched_id, "Doch!", antwort)
|
||||
noch_einer = kommentar(client2, watched_id, "Anderes Thema.")
|
||||
|
||||
notes = film_von(client, watched_id)["notes"]
|
||||
assert [n["text"] for n in notes] == ["War super.", "Anderes Thema."]
|
||||
assert notes[0]["replies"][0]["text"] == "Fand ich nicht."
|
||||
assert notes[0]["replies"][0]["name"] == "Ben"
|
||||
assert notes[0]["replies"][0]["mine"] is False
|
||||
assert notes[0]["replies"][0]["replies"][0]["text"] == "Doch!"
|
||||
assert notes[0]["replies"][0]["replies"][0]["mine"] is True
|
||||
assert notes[1]["replies"] == []
|
||||
assert (oben, antwort, tiefer, noch_einer) == tuple(sorted((oben, antwort, tiefer, noch_einer)))
|
||||
|
||||
|
||||
def test_zehn_ebenen_tief(client, film):
|
||||
watched_id = abend(client, film)
|
||||
vater = None
|
||||
for stufe in range(10):
|
||||
vater = kommentar(client, watched_id, f"Stufe {stufe}", vater)
|
||||
|
||||
knoten = film_von(client, watched_id)["notes"][0]
|
||||
tiefe = 0
|
||||
while knoten["replies"]:
|
||||
knoten = knoten["replies"][0]
|
||||
tiefe += 1
|
||||
assert tiefe == 9
|
||||
assert knoten["text"] == "Stufe 9"
|
||||
|
||||
|
||||
def test_leerer_text_oder_fremder_vater_werden_abgelehnt(client, film):
|
||||
watched_id = abend(client, film, 1)
|
||||
anderer = abend(client, film, 2)
|
||||
dort = kommentar(client, anderer, "woanders")
|
||||
|
||||
assert client.post(f"/api/watched/{watched_id}/notes", json={"text": " "}).status_code == 400
|
||||
# Der Vater gehört zu einem anderen Abend
|
||||
assert client.post(
|
||||
f"/api/watched/{watched_id}/notes", json={"text": "x", "parent_id": dort}
|
||||
).status_code == 400
|
||||
assert client.post(
|
||||
f"/api/watched/{watched_id}/notes", json={"text": "x", "parent_id": 9999}
|
||||
).status_code == 400
|
||||
|
||||
|
||||
def test_wertungen_und_kommentare_sind_unabhaengig(client, film):
|
||||
watched_id = abend(client, film)
|
||||
wertung(client, watched_id, 5)
|
||||
kommentar(client, watched_id, "Diskussion.")
|
||||
f = film_von(client, watched_id)
|
||||
assert f["ratings"][0]["stars"] == 5
|
||||
assert "text" not in f["ratings"][0]
|
||||
assert [n["text"] for n in f["notes"]] == ["Diskussion."]
|
||||
|
||||
|
||||
def test_eigenen_kommentar_loeschen_fremden_nicht(client, client2, film):
|
||||
watched_id = abend(client, film)
|
||||
meiner = kommentar(client, watched_id, "meiner")
|
||||
|
||||
assert client2.delete(f"/api/watched-notes/{meiner}").status_code == 404
|
||||
assert client.delete(f"/api/watched-notes/{meiner}").json()["removed"] == 1
|
||||
assert film_von(client, watched_id)["notes"] == []
|
||||
|
||||
|
||||
def test_loeschen_nimmt_den_ganzen_ast_mit(client, client2, film):
|
||||
"""Eine Antwort ohne ihren Anlass ergäbe keinen Sinn."""
|
||||
watched_id = abend(client, film)
|
||||
oben = kommentar(client, watched_id, "a")
|
||||
mitte = kommentar(client2, watched_id, "b", oben)
|
||||
unten = kommentar(client, watched_id, "c", mitte)
|
||||
daneben = kommentar(client2, watched_id, "d")
|
||||
|
||||
assert client.delete(f"/api/watched-notes/{oben}").json()["removed"] == 3
|
||||
notes = film_von(client, watched_id)["notes"]
|
||||
assert [n["id"] for n in notes] == [daneben]
|
||||
|
||||
|
||||
def test_host_loescht_jeden_kommentar(client, zuschauer, hostmodus, film):
|
||||
watched_id = abend(client, film)
|
||||
note_id = kommentar(client, watched_id, "x")
|
||||
hostmodus(zuschauer)
|
||||
assert zuschauer.delete(f"/api/watched-notes/{note_id}").json()["removed"] == 1
|
||||
|
||||
|
||||
def test_film_loeschen_raeumt_alles(client, client2, film):
|
||||
from app import db
|
||||
|
||||
watched_id = abend(client, film)
|
||||
wertung(client, watched_id, 5)
|
||||
oben = kommentar(client2, watched_id, "frei")
|
||||
kommentar(client, watched_id, "antwort", oben)
|
||||
|
||||
client.delete(f"/api/watched/{watched_id}")
|
||||
with db.connect() as conn:
|
||||
for tabelle in ("ratings", "watched_notes"):
|
||||
assert conn.execute(f"SELECT COUNT(*) FROM {tabelle}").fetchone()[0] == 0, tabelle
|
||||
|
||||
|
||||
def test_zuschauer_darf_nicht_schreiben(zuschauer, client, film):
|
||||
watched_id = abend(client, film)
|
||||
assert zuschauer.post(f"/api/watched/{watched_id}/notes", json={"text": "x"}).status_code == 403
|
||||
185
backend/tests/test_login.py
Normal file
185
backend/tests/test_login.py
Normal file
@@ -0,0 +1,185 @@
|
||||
"""Anmelden per Film.
|
||||
|
||||
Wer einen Anmeldefilm hinterlegt hat, muss ihn beim Namenswechsel in der
|
||||
Suche finden. Ohne Film bleibt der Name offen für alle.
|
||||
"""
|
||||
|
||||
|
||||
def namen(client, name="Anna"):
|
||||
"""Legt den Namen an und gibt seine Kennung zurück."""
|
||||
antwort = client.post("/api/users", json={"name": name})
|
||||
if antwort.status_code == 200:
|
||||
return antwort.json()["id"]
|
||||
runde = client.get("/api/users").json()["items"]
|
||||
return next(u["id"] for u in runde if u["name"] == name)
|
||||
|
||||
|
||||
def test_ohne_film_bleibt_der_name_offen(client2, film):
|
||||
user_id = namen(client2, "Offen")
|
||||
antwort = client2.post("/api/users/waehlen", json={"user_id": user_id})
|
||||
assert antwort.status_code == 200
|
||||
assert antwort.json()["mine"]["geschuetzt"] is False
|
||||
|
||||
|
||||
def test_falscher_film_kommt_nicht_durch(client, client2, film):
|
||||
user_id = namen(client, "Anna")
|
||||
client.post(f"/api/users/{user_id}/schutz", json={"movie_id": film(42)})
|
||||
|
||||
# Ben kennt den Film nicht
|
||||
assert (
|
||||
client2.post(
|
||||
"/api/users/waehlen", json={"user_id": user_id, "movie_id": film(7)}
|
||||
).status_code
|
||||
== 403
|
||||
)
|
||||
# Ganz ohne Film auch nicht
|
||||
assert (
|
||||
client2.post("/api/users/waehlen", json={"user_id": user_id}).status_code == 403
|
||||
)
|
||||
assert client2.get("/api/users").json()["mine"]["name"] == "Ben"
|
||||
|
||||
|
||||
def test_richtiger_film_laesst_durch(client, client2, film):
|
||||
user_id = namen(client, "Anna")
|
||||
client.post(f"/api/users/{user_id}/schutz", json={"movie_id": film(42)})
|
||||
|
||||
antwort = client2.post(
|
||||
"/api/users/waehlen", json={"user_id": user_id, "movie_id": 42}
|
||||
)
|
||||
assert antwort.status_code == 200
|
||||
assert antwort.json()["mine"]["name"] == "Anna"
|
||||
assert client2.get("/api/users").json()["mine"]["name"] == "Anna"
|
||||
|
||||
|
||||
def test_der_film_verlaesst_den_server_nicht(client, film):
|
||||
user_id = namen(client, "Anna")
|
||||
client.post(f"/api/users/{user_id}/schutz", json={"movie_id": film(42)})
|
||||
|
||||
runde = client.get("/api/users").json()
|
||||
anna = next(u for u in runde["items"] if u["id"] == user_id)
|
||||
assert anna["geschuetzt"] is True
|
||||
assert "auth_movie_id" not in anna
|
||||
assert "auth_movie_id" not in runde["mine"]
|
||||
|
||||
|
||||
def test_aufheben_macht_den_namen_wieder_frei(client, client2, film):
|
||||
user_id = namen(client, "Anna")
|
||||
client.post(f"/api/users/{user_id}/schutz", json={"movie_id": film(42)})
|
||||
client.post(f"/api/users/{user_id}/schutz", json={"movie_id": None})
|
||||
|
||||
assert client.get("/api/users").json()["items"][0]["geschuetzt"] is False
|
||||
assert (
|
||||
client2.post("/api/users/waehlen", json={"user_id": user_id}).status_code == 200
|
||||
)
|
||||
|
||||
|
||||
def test_host_braucht_den_film_nicht(client, zuschauer, film, hostmodus):
|
||||
user_id = namen(client, "Anna")
|
||||
client.post(f"/api/users/{user_id}/schutz", json={"movie_id": film(42)})
|
||||
|
||||
hostmodus(zuschauer)
|
||||
assert zuschauer.get("/api/users").json()["host"] is True
|
||||
assert (
|
||||
zuschauer.post("/api/users/waehlen", json={"user_id": user_id}).status_code
|
||||
== 200
|
||||
)
|
||||
|
||||
# Hostmodus aus: die Hürde steht wieder
|
||||
zuschauer.post("/api/users/waehlen", json={"user_id": None})
|
||||
zuschauer.post("/api/host", json={"an": False})
|
||||
assert (
|
||||
zuschauer.post("/api/users/waehlen", json={"user_id": user_id}).status_code
|
||||
== 403
|
||||
)
|
||||
|
||||
|
||||
def test_wer_schon_angemeldet_ist_muss_nicht_nochmal(client, film):
|
||||
user_id = namen(client, "Anna")
|
||||
client.post("/api/users/waehlen", json={"user_id": user_id})
|
||||
client.post(f"/api/users/{user_id}/schutz", json={"movie_id": film(42)})
|
||||
# Derselbe Name in derselben Sitzung, etwa nach einem Neuladen
|
||||
assert (
|
||||
client.post("/api/users/waehlen", json={"user_id": user_id}).status_code == 200
|
||||
)
|
||||
|
||||
|
||||
def test_fremden_schutz_darf_man_nicht_setzen(client, client2, film):
|
||||
anna = namen(client, "Anna")
|
||||
assert (
|
||||
client2.post(f"/api/users/{anna}/schutz", json={"movie_id": film(42)}).status_code
|
||||
== 403
|
||||
)
|
||||
assert client2.get("/api/users").json()["items"][0]["geschuetzt"] is False
|
||||
|
||||
|
||||
def test_eigenen_schutz_darf_man_setzen(client, film):
|
||||
"""client ist als Anna angemeldet und ändert ihren eigenen Film."""
|
||||
anna = client.get("/api/users").json()["mine"]["id"]
|
||||
assert (
|
||||
client.post(f"/api/users/{anna}/schutz", json={"movie_id": film(42)}).status_code
|
||||
== 200
|
||||
)
|
||||
|
||||
|
||||
def test_hinterlegten_film_darf_man_nachschlagen(client, film):
|
||||
"""Wer ihn ändern darf, darf ihn auch sehen — sonst rät man beim
|
||||
Überschreiben."""
|
||||
anna = client.get("/api/users").json()["mine"]["id"]
|
||||
client.post(f"/api/users/{anna}/schutz", json={"movie_id": film(42, "Alien")})
|
||||
|
||||
antwort = client.get(f"/api/users/{anna}/schutz")
|
||||
assert antwort.status_code == 200
|
||||
assert antwort.json()["film"]["title"] == "Alien"
|
||||
|
||||
|
||||
def test_ohne_film_kommt_nichts_zurueck(client):
|
||||
anna = client.get("/api/users").json()["mine"]["id"]
|
||||
assert client.get(f"/api/users/{anna}/schutz").json()["film"] is None
|
||||
|
||||
|
||||
def test_fremden_film_darf_man_nicht_nachschlagen(client, client2, film):
|
||||
anna = client.get("/api/users").json()["mine"]["id"]
|
||||
client.post(f"/api/users/{anna}/schutz", json={"movie_id": film(42)})
|
||||
assert client2.get(f"/api/users/{anna}/schutz").status_code == 403
|
||||
|
||||
|
||||
def test_host_darf_jeden_film_nachschlagen(client, zuschauer, film, hostmodus):
|
||||
anna = client.get("/api/users").json()["mine"]["id"]
|
||||
client.post(f"/api/users/{anna}/schutz", json={"movie_id": film(42, "Alien")})
|
||||
|
||||
hostmodus(zuschauer)
|
||||
assert zuschauer.get(f"/api/users/{anna}/schutz").json()["film"]["title"] == "Alien"
|
||||
|
||||
|
||||
def test_ohne_schluessel_kein_hostmodus(zuschauer):
|
||||
"""Die Tastenkombination allein reicht nicht mehr."""
|
||||
assert zuschauer.post("/api/host", json={"an": True}).status_code == 403
|
||||
assert zuschauer.get("/api/host").json()["host"] is False
|
||||
|
||||
|
||||
def test_falscher_schluessel_kein_hostmodus(zuschauer):
|
||||
antwort = zuschauer.post(
|
||||
"/api/host", json={"an": True, "schluessel": "geraten"}
|
||||
)
|
||||
assert antwort.status_code == 403
|
||||
assert zuschauer.get("/api/host").json()["host"] is False
|
||||
|
||||
|
||||
def test_schluessel_schaltet_frei_und_bleibt(zuschauer, hostmodus):
|
||||
assert hostmodus(zuschauer).json()["host"] is True
|
||||
assert zuschauer.get("/api/host").json()["host"] is True
|
||||
# Die Freischaltung hält, ein zweiter Aufruf braucht den Schlüssel nicht
|
||||
assert zuschauer.post("/api/host", json={"an": True}).status_code == 200
|
||||
assert zuschauer.get("/api/host").json()["host"] is True
|
||||
|
||||
|
||||
def test_ausschalten_geht_ohne_schluessel(zuschauer, hostmodus):
|
||||
hostmodus(zuschauer)
|
||||
assert zuschauer.post("/api/host", json={"an": False}).json()["host"] is False
|
||||
# Jetzt ist die Sitzung wieder gewöhnlich
|
||||
assert zuschauer.post("/api/host", json={"an": True}).status_code == 403
|
||||
|
||||
|
||||
def test_der_schluessel_gilt_nur_fuer_die_eigene_sitzung(zuschauer, client2, hostmodus):
|
||||
hostmodus(zuschauer)
|
||||
assert client2.get("/api/host").json()["host"] is False
|
||||
@@ -1,6 +1,5 @@
|
||||
"""Wertungen: Sterne, Text, Schnitt."""
|
||||
|
||||
from app.watched import MAX_TEXT
|
||||
|
||||
|
||||
def gesehen(client, film, movie_id=1):
|
||||
@@ -9,42 +8,47 @@ def gesehen(client, film, movie_id=1):
|
||||
return client.post("/api/watched", json={"movie_id": movie_id}).json()["id"]
|
||||
|
||||
|
||||
def test_wertung_mit_text(client, film):
|
||||
wid = gesehen(client, film)
|
||||
|
||||
antwort = client.post(f"/api/watched/{wid}/rating", json={"stars": 4, "text": "Gruselig"})
|
||||
assert antwort.json() == {"watched_id": wid, "stars": 4, "text": "Gruselig"}
|
||||
|
||||
[eintrag] = client.get("/api/watched").json()
|
||||
def test_wertung_ist_nur_sterne(client, film):
|
||||
wid = client.post("/api/watched", json={"movie_id": film(1)}).json()["id"]
|
||||
antwort = client.post(f"/api/watched/{wid}/rating", json={"stars": 4})
|
||||
assert antwort.json() == {"watched_id": wid, "stars": 4}
|
||||
eintrag = client.get("/api/watched").json()[0]
|
||||
assert eintrag["mine"] == 4
|
||||
assert eintrag["average"] == 4.0
|
||||
assert eintrag["count"] == 1
|
||||
assert eintrag["mine"] == 4
|
||||
assert eintrag["mine_text"] == "Gruselig"
|
||||
assert eintrag["ratings"][0]["mine"] is True
|
||||
assert "text" not in eintrag["ratings"][0]
|
||||
assert "mine_text" not in eintrag
|
||||
|
||||
|
||||
def test_text_none_behaelt_leer_loescht(client, film):
|
||||
wid = gesehen(client, film)
|
||||
client.post(f"/api/watched/{wid}/rating", json={"stars": 4, "text": "Gruselig"})
|
||||
|
||||
# Nur die Sterne nachjustiert: der Satz bleibt, es bleibt eine Stimme
|
||||
client.post(f"/api/watched/{wid}/rating", json={"stars": 3})
|
||||
[eintrag] = client.get("/api/watched").json()
|
||||
assert eintrag["mine"] == 3
|
||||
assert eintrag["mine_text"] == "Gruselig"
|
||||
def test_neue_sterne_ersetzen_die_alten(client, film):
|
||||
wid = client.post("/api/watched", json={"movie_id": film(1)}).json()["id"]
|
||||
client.post(f"/api/watched/{wid}/rating", json={"stars": 4})
|
||||
client.post(f"/api/watched/{wid}/rating", json={"stars": 2})
|
||||
eintrag = client.get("/api/watched").json()[0]
|
||||
assert eintrag["mine"] == 2
|
||||
assert eintrag["count"] == 1
|
||||
|
||||
client.post(f"/api/watched/{wid}/rating", json={"stars": 3, "text": ""})
|
||||
[eintrag] = client.get("/api/watched").json()
|
||||
assert eintrag["mine_text"] is None
|
||||
|
||||
def test_alter_wertungstext_zieht_in_die_diskussion(client, film):
|
||||
"""Vor dem Umbau hing ein Text an den Sternen. Beim Start wandert er
|
||||
als Kommentar in die Diskussion, mit Verfasser und Datum."""
|
||||
from app import db, watched
|
||||
|
||||
def test_text_wird_gekuerzt(client, film):
|
||||
wid = gesehen(client, film)
|
||||
client.post(f"/api/watched/{wid}/rating", json={"stars": 5, "text": "x" * (MAX_TEXT + 100)})
|
||||
wid = client.post("/api/watched", json={"movie_id": film(1)}).json()["id"]
|
||||
client.post(f"/api/watched/{wid}/rating", json={"stars": 5})
|
||||
with db.connect() as conn:
|
||||
conn.execute("UPDATE ratings SET text = 'Alte Rezension'")
|
||||
watched.init()
|
||||
|
||||
[eintrag] = client.get("/api/watched").json()
|
||||
assert len(eintrag["mine_text"]) == MAX_TEXT
|
||||
f = client.get("/api/watched").json()[0]
|
||||
assert [n["text"] for n in f["notes"]] == ["Alte Rezension"]
|
||||
assert f["notes"][0]["name"] == "Anna"
|
||||
assert f["notes"][0]["mine"] is True
|
||||
with db.connect() as conn:
|
||||
assert conn.execute("SELECT COUNT(*) FROM ratings WHERE text IS NOT NULL").fetchone()[0] == 0
|
||||
# Ein zweiter Start zieht nichts doppelt um
|
||||
watched.init()
|
||||
assert len(client.get("/api/watched").json()[0]["notes"]) == 1
|
||||
|
||||
|
||||
def test_fremde_wertung_ist_nicht_meine(client, client2, film):
|
||||
|
||||
110
backend/tests/test_wishlist.py
Normal file
110
backend/tests/test_wishlist.py
Normal file
@@ -0,0 +1,110 @@
|
||||
"""Die Sammlungen.
|
||||
|
||||
Alle sehen alle, ändern darf jeder nur die eigene. Die Karten im
|
||||
Sammlungen-Reiter zeigen dieselben Daten wie im Katalog — die holt die
|
||||
Liste per Join nach.
|
||||
"""
|
||||
|
||||
from app import db
|
||||
|
||||
|
||||
def katalogfilm(movie_id, **felder):
|
||||
"""Film mit allem, was die Detailkarte zeigt."""
|
||||
with db.connect() as conn:
|
||||
db.upsert(
|
||||
conn,
|
||||
{
|
||||
"id": movie_id,
|
||||
"title": "Alien",
|
||||
"year": 1979,
|
||||
"teaser": "Im Weltall hört dich niemand schreien.",
|
||||
"overview": "Die Nostromo fängt ein Signal auf.",
|
||||
"runtime": 117,
|
||||
"vote_average": 8.2,
|
||||
"vote_count": 14000,
|
||||
"genres": ["Horror", "Science Fiction"],
|
||||
"images": ["/a.jpg", "/b.jpg"],
|
||||
**felder,
|
||||
},
|
||||
)
|
||||
return movie_id
|
||||
|
||||
|
||||
def test_karte_bekommt_die_katalogdaten(client):
|
||||
client.post("/api/wishlist", json={"movie_id": katalogfilm(42)})
|
||||
eintrag = client.get("/api/wishlist").json()[0]
|
||||
|
||||
assert eintrag["teaser"].startswith("Im Weltall")
|
||||
assert eintrag["runtime"] == 117
|
||||
assert eintrag["vote_average"] == 8.2
|
||||
assert eintrag["vote_count"] == 14000
|
||||
assert eintrag["genres"] == ["Horror", "Science Fiction"]
|
||||
assert eintrag["images"] == ["/a.jpg", "/b.jpg"]
|
||||
|
||||
|
||||
def test_film_ausserhalb_des_katalogs_bleibt_spaerlich(client, film):
|
||||
"""Titel und Poster stehen in der Sammlung selbst, der Rest fehlt."""
|
||||
client.post("/api/wishlist", json={"movie_id": film(7, "Irgendwas")})
|
||||
eintrag = client.get("/api/wishlist").json()[0]
|
||||
|
||||
assert eintrag["title"] == "Irgendwas"
|
||||
assert eintrag["teaser"] is None
|
||||
assert eintrag["genres"] == []
|
||||
assert eintrag["images"] == []
|
||||
|
||||
|
||||
def test_merken_und_wieder_vergessen(client):
|
||||
movie_id = katalogfilm(42)
|
||||
client.post("/api/wishlist", json={"movie_id": movie_id})
|
||||
assert len(client.get("/api/wishlist").json()) == 1
|
||||
|
||||
assert client.delete(f"/api/wishlist/{movie_id}").json()["removed"] == 1
|
||||
assert client.get("/api/wishlist").json() == []
|
||||
|
||||
|
||||
def test_zweimal_merken_legt_nur_einen_an(client):
|
||||
movie_id = katalogfilm(42)
|
||||
client.post("/api/wishlist", json={"movie_id": movie_id})
|
||||
client.post("/api/wishlist", json={"movie_id": movie_id})
|
||||
assert len(client.get("/api/wishlist").json()) == 1
|
||||
|
||||
|
||||
def test_fremde_sammlung_sieht_man_aber_raeumt_sie_nicht(client, client2):
|
||||
movie_id = katalogfilm(42)
|
||||
client.post("/api/wishlist", json={"movie_id": movie_id})
|
||||
|
||||
# Ben sieht Annas Eintrag, aber er gehört ihm nicht
|
||||
eintrag = client2.get("/api/wishlist").json()[0]
|
||||
assert eintrag["name"] == "Anna"
|
||||
assert eintrag["mine"] is False
|
||||
|
||||
# Sein Löschversuch trifft nur die eigene Sammlung, also nichts
|
||||
assert client2.delete(f"/api/wishlist/{movie_id}").json()["removed"] == 0
|
||||
assert len(client.get("/api/wishlist").json()) == 1
|
||||
|
||||
|
||||
def test_jeder_hat_seine_eigene_sammlung(client, client2):
|
||||
movie_id = katalogfilm(42)
|
||||
client.post("/api/wishlist", json={"movie_id": movie_id})
|
||||
client2.post("/api/wishlist", json={"movie_id": movie_id})
|
||||
|
||||
eintraege = client.get("/api/wishlist").json()
|
||||
assert len(eintraege) == 2
|
||||
assert sorted(e["name"] for e in eintraege) == ["Anna", "Ben"]
|
||||
|
||||
|
||||
def test_sync_verschont_was_jemand_gemerkt_hat(client):
|
||||
"""Der Katalog kennt nur Netflix und den Kanon. Gemerkte Filme müssen
|
||||
trotzdem drinbleiben — sonst verlieren ihre Karten alle Daten."""
|
||||
gemerkt = katalogfilm(42)
|
||||
egal = katalogfilm(99, title="Niemandsfilm")
|
||||
client.post("/api/wishlist", json={"movie_id": gemerkt})
|
||||
|
||||
with db.connect() as conn:
|
||||
# Der Sync behält nur, was er gerade geholt hat — hier gar nichts
|
||||
weg = db.drop_missing(conn, [])
|
||||
uebrig = {r["id"] for r in conn.execute("SELECT id FROM movies")}
|
||||
|
||||
assert weg == 1
|
||||
assert uebrig == {gemerkt}
|
||||
assert egal not in uebrig
|
||||
Reference in New Issue
Block a user