`CreateTicket` no longer rejects `user_id` together with `name`/`email`
(the exclusion was a fork-only dispatch convenience from dfabcb8; nothing
needed it). `crud.create_ticket` stops blanking name/email when a user_id
is present, so logged-in webapp buyers can have their ticket emailed —
until now `_send_ticket_notification` short-circuited on the empty
address for every app purchase.
New optional `frontend_url` (absolute http(s) root, no query/fragment/..,
trailing slash stripped) lets a buyer-side client name the app the buyer
should be returned to and linked into from the ticket email; the origin
allow-list lives in views_api.
Also folds in the pending black reflow of migrations_fork.py.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYwoAkZZmXMMmaBp4WGUBo
110 lines
2.9 KiB
Python
110 lines
2.9 KiB
Python
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from ..models import (
|
|
CreateEvent,
|
|
CreateTicket,
|
|
EventExtra,
|
|
effective_payment_methods,
|
|
)
|
|
|
|
|
|
def _ticket(**kwargs) -> CreateTicket:
|
|
return CreateTicket(**kwargs)
|
|
|
|
|
|
def test_user_id_only_is_a_valid_identity():
|
|
assert _ticket(user_id="u1").user_id == "u1"
|
|
|
|
|
|
def test_name_and_email_is_a_valid_guest_identity():
|
|
ticket = _ticket(name="Guest", email="guest@example.com")
|
|
assert ticket.user_id is None
|
|
assert ticket.email == "guest@example.com"
|
|
|
|
|
|
def test_user_id_may_carry_an_email_for_delivery():
|
|
ticket = _ticket(user_id="u1", email="me@example.com")
|
|
assert ticket.user_id == "u1"
|
|
assert ticket.email == "me@example.com"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"kwargs",
|
|
[
|
|
{},
|
|
{"name": "Guest"},
|
|
{"email": "guest@example.com"},
|
|
],
|
|
)
|
|
def test_missing_identity_is_rejected(kwargs):
|
|
with pytest.raises(ValidationError):
|
|
_ticket(**kwargs)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"url,expected",
|
|
[
|
|
("https://app.example/events", "https://app.example/events"),
|
|
("https://app.example/events/", "https://app.example/events"),
|
|
("http://localhost:5173/", "http://localhost:5173"),
|
|
(" ", None),
|
|
],
|
|
)
|
|
def test_frontend_url_is_normalised(url, expected):
|
|
assert _ticket(user_id="u1", frontend_url=url).frontend_url == expected
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"url",
|
|
[
|
|
"/events", # relative
|
|
"ftp://app.example/events",
|
|
"https://app.example/events?x=1",
|
|
"https://app.example/events#top",
|
|
"https://app.example/../events",
|
|
"https://" + "a" * 520,
|
|
],
|
|
)
|
|
def test_frontend_url_rejects_unsafe_values(url):
|
|
with pytest.raises(ValidationError):
|
|
_ticket(user_id="u1", frontend_url=url)
|
|
|
|
|
|
def _event(**overrides) -> CreateEvent:
|
|
data = {
|
|
"wallet": "w",
|
|
"name": "Test",
|
|
"info": "",
|
|
"closing_date": "2030-01-01",
|
|
"event_start_date": "2030-01-01",
|
|
"event_end_date": "2030-01-02",
|
|
"amount_tickets": 10,
|
|
"price_per_ticket": 5,
|
|
}
|
|
data.update(overrides)
|
|
return CreateEvent(**data)
|
|
|
|
|
|
def test_effective_payment_methods_legacy_rule():
|
|
assert effective_payment_methods(_event()) == ["lightning"]
|
|
assert effective_payment_methods(_event(allow_fiat=True)) == ["lightning", "fiat"]
|
|
|
|
|
|
def test_effective_payment_methods_explicit_list_wins():
|
|
event = _event(allow_fiat=True, extra=EventExtra(payment_methods=["fiat"]))
|
|
assert effective_payment_methods(event) == ["fiat"]
|
|
|
|
|
|
def test_payment_methods_are_normalised_and_deduplicated():
|
|
extra = EventExtra(payment_methods=["Fiat", " lightning ", "fiat"])
|
|
assert extra.payment_methods == ["fiat", "lightning"]
|
|
assert EventExtra(payment_methods="lightning,fiat").payment_methods == [
|
|
"lightning",
|
|
"fiat",
|
|
]
|
|
|
|
|
|
def test_unknown_payment_method_is_rejected():
|
|
with pytest.raises(ValidationError):
|
|
EventExtra(payment_methods=["cash"])
|