Rene reichte ein Partner-Profil ein und sah als Admin nirgends einen Hinweis: 1. Action-Items kannten Partner-Profile nicht — partner_profiles_pending (submitted_at gesetzt, approved=0) jetzt im Endpoint + Chip im Admin-Kopf (Klick -> Partner-Tab). Test ergänzt (7 passed). 2. ADMIN_EMAIL fehlte in BEIDEN .env auf der DS (Prod+Staging) — damit wurden auch Upgrade-Anfragen-Mails still verschluckt (bekanntes Silent-Skip-Muster). Auf der DS nachgetragen; greift je beim nächsten Deploy.
155 lines
6.1 KiB
Python
155 lines
6.1 KiB
Python
"""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})
|