"""Smoke-Tests fuer Partner-Profile (Editor + Freigabe-Workflow + oeffentlicher Showcase).""" import io def _make_partner(user_email: str): """Setzt is_partner=1 direkt in der Test-DB.""" from database import db with db() as conn: conn.execute("UPDATE users SET is_partner=1 WHERE email=?", (user_email,)) def test_my_profile_requires_partner(client, user): """GET /api/partner/my-profile -> 403 fuer normale User.""" r = client.get("/api/partner/my-profile", headers=user["headers"]) assert r.status_code == 403 def test_partner_profile_full_flow(client, user, admin): """Texte speichern -> einreichen -> Admin gibt frei -> oeffentlich sichtbar.""" _make_partner(user["email"]) # Leeres Profil mit Storage-Infos r = client.get("/api/partner/my-profile", headers=user["headers"]) assert r.status_code == 200, r.text assert r.json()["storage_limit_mb"] == 200 # Texte speichern (Website ohne Schema wird normalisiert) r = client.put("/api/partner/my-profile", headers=user["headers"], json={ "display_name": "Hundeblog Test", "tagline": "Testkanal", "bio": "Wir testen Ban Yaro.", "website": "hundeblog-test.de", "instagram": "@hundeblogtest", }) assert r.status_code == 200, r.text p = r.json()["profile"] assert p["display_name"] == "Hundeblog Test" assert p["website"] == "https://hundeblog-test.de" # Vor Freigabe nicht oeffentlich r = client.get("/api/partners/public") assert all(x.get("display_name") != "Hundeblog Test" for x in r.json()["partners"]) # Einreichen r = client.post("/api/partner/my-profile/submit", headers=user["headers"], json={}) assert r.status_code == 200, r.text assert r.json()["profile"]["submitted_at"] # Admin sieht das Profil und gibt frei r = client.get("/api/admin/partner/profiles", headers=admin["headers"]) assert r.status_code == 200 mine = [x for x in r.json() if x.get("display_name") == "Hundeblog Test"] assert mine, "Profil fehlt in der Admin-Liste" uid = mine[0]["user_id"] r = client.post(f"/api/admin/partner/profiles/{uid}/review", headers=admin["headers"], json={"approved": 1}) assert r.status_code == 200 # Jetzt oeffentlich (ohne Login) r = client.get("/api/partners/public") names = [x["display_name"] for x in r.json()["partners"]] assert "Hundeblog Test" in names # Ablehnen entfernt es wieder von der oeffentlichen Seite r = client.post(f"/api/admin/partner/profiles/{uid}/review", headers=admin["headers"], json={"approved": -1}) assert r.status_code == 200 r = client.get("/api/partners/public") assert "Hundeblog Test" not in [x["display_name"] for x in r.json()["partners"]] def test_submit_requires_display_name(client, user): """Einreichen ohne Anzeigename -> 400.""" _make_partner(user["email"]) r = client.post("/api/partner/my-profile/submit", headers=user["headers"], json={}) assert r.status_code == 400 def test_logo_and_photo_upload(client, user): """Logo + Foto hochladen, Foto wieder loeschen.""" from PIL import Image _make_partner(user["email"]) def _png(size=(64, 64), color="red"): buf = io.BytesIO() Image.new("RGB", size, color).save(buf, format="PNG") buf.seek(0) return buf # Logo r = client.post("/api/partner/my-profile/logo", headers=user["headers"], files={"file": ("logo.png", _png(), "image/png")}) assert r.status_code == 200, r.text assert r.json()["logo_url"].startswith("/media/partner/") # Foto r = client.post("/api/partner/my-profile/photos", headers=user["headers"], files={"file": ("foto.png", _png(color="blue"), "image/png")}) assert r.status_code == 200, r.text photos = r.json()["photos"] assert len(photos) == 1 and photos[0].endswith(".webp") # Speicher belegt r = client.get("/api/partner/my-profile", headers=user["headers"]) assert r.json()["storage_mb"] > 0 # Foto loeschen r = client.post("/api/partner/my-profile/photos/0/delete", headers=user["headers"], json={}) assert r.status_code == 200 assert r.json()["photos"] == [] def test_heic_uploads_convert(client, user): """HEIC (iPhone-Format) wird bei Logo UND Foto nach WebP konvertiert.""" import pillow_heif from PIL import Image _make_partner(user["email"]) pillow_heif.register_heif_opener() buf = io.BytesIO() Image.new("RGB", (64, 64), "green").save(buf, format="HEIF") heic_bytes = buf.getvalue() # Logo als HEIC r = client.post("/api/partner/my-profile/logo", headers=user["headers"], files={"file": ("IMG_0001.HEIC", io.BytesIO(heic_bytes), "image/heic")}) assert r.status_code == 200, r.text assert r.json()["logo_url"].endswith(".webp") # Foto als HEIC r = client.post("/api/partner/my-profile/photos", headers=user["headers"], files={"file": ("IMG_0002.heic", io.BytesIO(heic_bytes), "image/heic")}) assert r.status_code == 200, r.text assert r.json()["photos"][0].endswith(".webp") def test_submit_appears_in_admin_action_items(client, user, admin): """Eingereichtes Profil taucht im Admin-'Zu erledigen'-Zaehler auf.""" _make_partner(user["email"]) client.put("/api/partner/my-profile", headers=user["headers"], json={"display_name": "Action-Item-Test"}) before = client.get("/api/admin/action-items", headers=admin["headers"]).json() r = client.post("/api/partner/my-profile/submit", headers=user["headers"], json={}) assert r.status_code == 200 after = client.get("/api/admin/action-items", headers=admin["headers"]).json() assert after["partner_profiles_pending"] == before.get("partner_profiles_pending", 0) + 1 def test_partner_has_pro_access(client, user): """is_partner=1 -> has_pro_access True (Pro gratis fuer Partner).""" from auth import has_pro_access assert has_pro_access({"rolle": "user", "subscription_tier": "standard", "is_partner": 1}) assert not has_pro_access({"rolle": "user", "subscription_tier": "standard", "is_partner": 0})