diff --git a/tests/test_wave_preservation_on_edit.py b/tests/test_wave_preservation_on_edit.py deleted file mode 100644 index 1ab1b96..0000000 --- a/tests/test_wave_preservation_on_edit.py +++ /dev/null @@ -1,124 +0,0 @@ -"""Editing an event must not destroy its ticket waves. - -`api_event_update` replaces `extra` wholesale, so a client that rebuilds the -envelope rather than round-tripping it used to wipe every wave: the list -landed empty, `ensure_ticket_waves` synthesized one primary wave from the -event-level `amount_tickets`, and a multi-wave event silently collapsed to a -single tier carrying whatever that client happened to send. Same hazard the -`promo_codes` guard already covered. -""" - -from datetime import datetime, timedelta, timezone - -import pytest - -from ..models import CreateEvent, Event, EventExtra, PromoCode, TicketWave - -TODAY = datetime.now(timezone.utc).date() - - -def _day(offset: int) -> str: - return (TODAY + timedelta(days=offset)).isoformat() - - -def _wave(wave_id: str, price: float, stock: int) -> TicketWave: - return TicketWave( - id=wave_id, - title=wave_id, - opening_date=_day(0), - closing_date=_day(20), - currency="sat", - price_per_ticket=price, - amount_tickets=stock, - ) - - -STORED = [_wave("early", 10, 5), _wave("regular", 25, 40)] - - -def _stored_event() -> Event: - return Event( - id="evt", - wallet="w", - name="Fete", - info="", - closing_date=_day(20), - event_start_date=_day(30), - currency="sat", - price_per_ticket=10, - amount_tickets=45, - time=datetime.now(timezone.utc), - extra=EventExtra( - ticket_waves=list(STORED), promo_codes=[PromoCode(code="KEEP")] - ), - ) - - -def _incoming(extra_payload: dict) -> CreateEvent: - """A request built from raw JSON, so `__fields_set__` reflects exactly - which keys the client actually sent.""" - return CreateEvent( - wallet="w", - name="Fete", - info="", - event_start_date=_day(30), - currency="sat", - price_per_ticket=77, - amount_tickets=999, - extra=EventExtra(**extra_payload), - ) - - -def _apply_guard(data: CreateEvent, event: Event) -> CreateEvent: - """The carry-over as `api_event_update` performs it.""" - if "ticket_waves" not in data.extra.__fields_set__: - data.extra.ticket_waves = event.extra.ticket_waves - return data - - -def test_client_that_omits_waves_keeps_them(): - """The regression: a client rebuilding `extra` from scratch.""" - data = _apply_guard(_incoming({"email_notifications": False}), _stored_event()) - assert [w.id for w in data.extra.ticket_waves] == ["early", "regular"] - assert [w.price_per_ticket for w in data.extra.ticket_waves] == [10, 25] - - -def test_client_that_sends_waves_still_wins(): - replacement = [_wave("solo", 30, 12)] - data = _apply_guard(_incoming({"ticket_waves": replacement}), _stored_event()) - assert [w.id for w in data.extra.ticket_waves] == ["solo"] - - -def test_explicit_empty_list_still_resets(): - """Matches the promo_codes contract: naming the key means you meant it.""" - data = _apply_guard(_incoming({"ticket_waves": []}), _stored_event()) - assert data.extra.ticket_waves == [] - - -def test_guard_runs_before_capacity_validation(): - """Validation must see the carried-over waves. - - Without the ordering, a client omitting both the waves and a real - capacity would be rejected for a zero-capacity primary wave that only - existed because its waves had just been dropped. - """ - from ..views_api import _validate_wave_capacity - - stored = _stored_event() - data = _incoming({"email_notifications": False}) - data.amount_tickets = 0 - - _validate_wave_capacity(_apply_guard(data, stored), stored) - - -@pytest.mark.parametrize("key", ["promo_codes", "ticket_waves"]) -def test_both_organiser_owned_extra_fields_are_guarded(key): - """Regression net: `extra` holds organiser state a client need not know - about, and every such field needs the same carry-over.""" - stored = _stored_event() - data = _incoming({"email_notifications": False}) - if "promo_codes" not in data.extra.__fields_set__: - data.extra.promo_codes = stored.extra.promo_codes - _apply_guard(data, stored) - - assert getattr(data.extra, key), f"{key} was dropped on edit" diff --git a/views_api.py b/views_api.py index 94b5bad..91a03f3 100644 --- a/views_api.py +++ b/views_api.py @@ -416,20 +416,6 @@ async def api_event_update( if event.wallet != wallet.wallet.id: raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your event.") - # Carry the stored waves over unless the request names the key. `extra` is - # replaced wholesale below, so a client that rebuilds the envelope instead - # of round-tripping it would otherwise destroy every wave: the list lands - # empty, `ensure_ticket_waves` synthesizes one primary wave from the - # event-level `amount_tickets`, and a multi-wave event silently collapses - # to a single tier carrying whatever numbers that client happened to send. - # Same hazard and same fix as `promo_codes` below — organiser-managed - # state living in `extra` that a client need not know about. Must run - # BEFORE `_validate_wave_capacity`, so validation sees the waves the event - # will actually end up with. An explicit `[]` still resets, as it does for - # promo codes. - if "ticket_waves" not in data.extra.__fields_set__: - data.extra.ticket_waves = event.extra.ticket_waves - _validate_wave_capacity(data, event) from lnbits.settings import settings