banyaro/tests/test_founder_tickets.py
rene 60fb866283 Gründer-Tickets: 50%-Rabatt-Weitergabe pro Gründer gedeckelt + Pro-Wording korrigiert
Rene: 'ungern jemandem auf ewig die Möglichkeit geben 50% Rabatt zu vergeben —
bei 100 Gründern ein großer Faktor. Ich hätte jedem 25–50 Tickets gegeben.'

- users.founder_referral_tickets (Default 25): Kontingent an 50%-Rabatten,
  die ein Gründer an Geworbene weitergeben kann. Technisch = die ersten N
  VERIFIZIERTEN Geworbenen (nach Anmeldedatum) bekommen 50%, danach 0.
  Unbestätigte verbrauchen kein Ticket. In scheduler.py (Rechnung) + admin.py
  (Vorschau) konsistent.
- BUGFIX nebenbei: admin.py zeigte für referred_by_founder fälschlich 100%
  statt 50% (scheduler war korrekt) — jetzt beide 50%.
- Admin: Grant-Formular bekommt Feld 'Gründer-Tickets' (0–200, Vorbelegung
  aus User-Stand); Endpoint /grant akzeptiert founder_tickets.
- Gründer-Seite + Settings + Admin-Hilfe: 'sobald Bezahlfunktionen aktiv sind'
  raus (Pro kostet bereits); Vorteil 'lebenslang Pro gratis' + '25 Freunde
  zum halben Preis' (Ticket-Framing).
- Tests: test_founder_tickets.py (Cap, Unverified-Schutz, 50%-Bugfix, Grant).
  Suite: 64 passed.
2026-06-08 06:20:19 +02:00

87 lines
3.6 KiB
Python

"""Gründer-Tickets: 50%-Rabatt-Weitergabe ist pro Gründer auf sein Kontingent gedeckelt.
Hintergrund: Ein Gründer kann geworbenen Freunden 50% auf Pro schenken. Ohne Cap
könnten 100 Gründer unbegrenzt viele 50%-Rabatte vergeben — unkalkulierbare Liability.
Jeder Gründer hat daher ein Ticket-Kontingent (Standard 25), das die ersten N
verifizierten Geworbenen abdeckt.
"""
import secrets
from datetime import datetime, timedelta
def _make_founder(email, tickets=25):
from database import db
with db() as conn:
uid = conn.execute("SELECT id FROM users WHERE email=?", (email,)).fetchone()["id"]
conn.execute(
"UPDATE users SET is_founder=1, founder_number=99, founder_referral_tickets=? WHERE id=?",
(tickets, uid),
)
return uid
def _add_referred(founder_id, n, verified=True, base_minutes=0):
"""Legt n direkt in der DB an, die vom Gründer geworben wurden (mit gestaffeltem created_at)."""
from database import db
ids = []
with db() as conn:
for i in range(n):
ts = (datetime(2026, 1, 1) + timedelta(minutes=base_minutes + i)).isoformat()
conn.execute(
"""INSERT INTO users (email, name, pw_hash, referred_by, email_verified, created_at)
VALUES (?,?,?,?,?,?)""",
(f"ref-{secrets.token_hex(5)}@example.com", f"r{secrets.token_hex(3)}",
"x", founder_id, 1 if verified else 0, ts),
)
ids.append(conn.execute("SELECT last_insert_rowid()").fetchone()[0])
return ids
def _discount(client, admin, uid):
r = client.get(f"/api/admin/users/{uid}/discount", headers=admin["headers"])
assert r.status_code == 200, r.text
return r.json()
def test_referred_by_founder_is_50_not_100(client, admin, user):
"""Bugfix-Absicherung: Geworbene eines Gründers bekommen 50%, nicht 100%."""
fid = _make_founder(user["email"], tickets=25)
friend = _add_referred(fid, 1)[0]
d = _discount(client, admin, friend)
assert d["discount_pct"] == 50
assert d["reason"] == "referred_by_founder"
def test_tickets_cap_the_50_percent(client, admin, user):
"""Mit 2 Tickets bekommen nur die ersten 2 Geworbenen 50%, der 3. nichts."""
fid = _make_founder(user["email"], tickets=2)
f1, f2, f3 = _add_referred(fid, 3)
assert _discount(client, admin, f1)["discount_pct"] == 50
assert _discount(client, admin, f2)["discount_pct"] == 50
d3 = _discount(client, admin, f3)
assert d3["discount_pct"] == 0
assert d3["reason"] is None
def test_unverified_dont_consume_tickets(client, admin, user):
"""Unbestätigte Geworbene verbrauchen kein Ticket — ein späterer bestätigter bekommt 50%."""
fid = _make_founder(user["email"], tickets=1)
# 2 unbestätigte zuerst, dann 1 bestätigter
_add_referred(fid, 2, verified=False, base_minutes=0)
later = _add_referred(fid, 1, verified=True, base_minutes=10)[0]
assert _discount(client, admin, later)["discount_pct"] == 50
def test_admin_grant_sets_tickets(client, admin, user):
"""Admin kann das Ticket-Kontingent über den Grant-Endpoint setzen."""
from database import db
with db() as conn:
uid = conn.execute("SELECT id FROM users WHERE email=?", (user["email"],)).fetchone()["id"]
r = client.post(f"/api/admin/partner/users/{uid}/grant", headers=admin["headers"],
json={"is_founder": 1, "founder_tickets": 50})
assert r.status_code == 200, r.text
assert r.json()["founder_referral_tickets"] == 50
with db() as conn:
val = conn.execute("SELECT founder_referral_tickets FROM users WHERE id=?", (uid,)).fetchone()[0]
assert val == 50