July 3rd - Review and optimize codes
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
Unit tests for the Stripe webhook dispatcher (app/billing/webhooks.py).
|
||||
|
||||
Covers the pure, DB-free surface:
|
||||
- _ts_to_dt timestamp conversion
|
||||
- _STRIPE_STATUS_MAP status normalization (the 'canceled'/'cancelled' trap)
|
||||
- handle_event dispatch: unknown types are a no-op; known types call the right
|
||||
handler; a handler that raises is swallowed (one bad event must not abort the
|
||||
rest of a delivery batch).
|
||||
|
||||
The individual _on_* handlers open a control_session and are covered separately
|
||||
by integration tests against a live control DB (not included here — see
|
||||
tests/README.md).
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import app.billing.webhooks as wh
|
||||
|
||||
|
||||
# ── _ts_to_dt ────────────────────────────────────────────────────────────────
|
||||
|
||||
def test_ts_to_dt_none_returns_none():
|
||||
assert wh._ts_to_dt(None) is None
|
||||
|
||||
|
||||
def test_ts_to_dt_epoch():
|
||||
# 0 → 1970-01-01T00:00:00, naive (tzinfo stripped)
|
||||
dt = wh._ts_to_dt(0)
|
||||
assert dt == datetime(1970, 1, 1, 0, 0, 0)
|
||||
assert dt.tzinfo is None
|
||||
|
||||
|
||||
def test_ts_to_dt_known_value():
|
||||
# 2021-01-01T00:00:00Z == 1609459200
|
||||
assert wh._ts_to_dt(1609459200) == datetime(2021, 1, 1, 0, 0, 0)
|
||||
|
||||
|
||||
# ── _STRIPE_STATUS_MAP ───────────────────────────────────────────────────────
|
||||
|
||||
def test_status_map_both_cancel_spellings():
|
||||
# Stripe emits 'canceled' (one L); we must normalize to our 'cancelled'.
|
||||
assert wh._STRIPE_STATUS_MAP['canceled'] == 'cancelled'
|
||||
assert wh._STRIPE_STATUS_MAP['cancelled'] == 'cancelled'
|
||||
|
||||
|
||||
def test_status_map_trial_and_pastdue():
|
||||
assert wh._STRIPE_STATUS_MAP['trialing'] == 'trial'
|
||||
assert wh._STRIPE_STATUS_MAP['past_due'] == 'past_due'
|
||||
assert wh._STRIPE_STATUS_MAP['unpaid'] == 'past_due'
|
||||
|
||||
|
||||
# ── handle_event dispatch ────────────────────────────────────────────────────
|
||||
|
||||
def test_unknown_event_type_is_noop():
|
||||
# Should simply return without raising and without touching any handler.
|
||||
wh.handle_event({'type': 'ping.unhandled', 'data': {'object': {}}})
|
||||
|
||||
|
||||
def test_known_event_dispatches_to_handler(monkeypatch):
|
||||
called = {}
|
||||
|
||||
def fake_handler(obj):
|
||||
called['obj'] = obj
|
||||
|
||||
monkeypatch.setattr(wh, '_on_payment_failed', fake_handler)
|
||||
|
||||
payload = {'customer': 'cus_123'}
|
||||
wh.handle_event({'type': 'invoice.payment_failed', 'data': {'object': payload}})
|
||||
|
||||
assert called.get('obj') == payload
|
||||
|
||||
|
||||
def test_handler_exception_is_swallowed(monkeypatch):
|
||||
def boom(obj):
|
||||
raise RuntimeError('stripe handler blew up')
|
||||
|
||||
monkeypatch.setattr(wh, '_on_subscription_deleted', boom)
|
||||
|
||||
# Must NOT propagate — a single malformed event cannot abort the batch.
|
||||
wh.handle_event({'type': 'customer.subscription.deleted', 'data': {'object': {}}})
|
||||
Reference in New Issue
Block a user