forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_app_paid_price_guard.py
More file actions
103 lines (82 loc) · 4.38 KB
/
Copy pathtest_app_paid_price_guard.py
File metadata and controls
103 lines (82 loc) · 4.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
"""Regression: a paid-app update with a missing or non-numeric price must not 500.
routers.apps.update_app passes the raw request price straight into
utils.apps.upsert_app_payment_link, which does int(price * 100) to build a Stripe recurring
price. A null price (is_paid toggled on without a price) or a non-numeric value used to raise
TypeError/ValueError. The guard treats any non-positive or non-numeric price like the existing
price==0 case: it skips link creation and returns without crashing.
"""
from types import SimpleNamespace
from unittest.mock import MagicMock
import pytest
import utils.apps as apps_mod
from utils.apps import upsert_app_payment_link
def _app_data(**over):
data = {
'id': 'app1',
'name': 'Test App',
'category': 'productivity',
'author': 'Zach',
'description': 'desc',
'image': 'https://storage.googleapis.com/x/y.png',
'capabilities': [],
'uid': 'u1',
'is_paid': True,
'payment_plan': 'monthly_recurring',
'price': 5.0,
}
data.update(over)
return data
@pytest.fixture
def stripe_mock(monkeypatch):
monkeypatch.setattr(apps_mod, 'get_app_by_id_db', lambda app_id: _app_data())
monkeypatch.setattr(apps_mod, 'get_stripe_connect_account_id', lambda uid: 'acct_1')
monkeypatch.setattr(apps_mod, 'update_app_in_db', lambda *a, **k: None)
stripe = MagicMock()
stripe.create_product.return_value = SimpleNamespace(id='prod_1')
stripe.create_app_monthly_recurring_price.return_value = SimpleNamespace(id='price_1')
stripe.create_app_payment_link.return_value = SimpleNamespace(id='link_1', url='https://pay/x')
monkeypatch.setattr(apps_mod, 'stripe', stripe)
return stripe
@pytest.mark.parametrize('bad_price', [None, '5.0', 'abc', -1, 0, True])
def test_invalid_price_skips_link_without_crashing(stripe_mock, bad_price):
# Must not raise, and must not attempt to build a Stripe price from a bad value.
upsert_app_payment_link('app1', True, bad_price, 'monthly_recurring', 'u1')
stripe_mock.create_app_monthly_recurring_price.assert_not_called()
def test_valid_price_creates_the_recurring_price(stripe_mock):
upsert_app_payment_link('app1', True, 5.0, 'monthly_recurring', 'u1')
stripe_mock.create_app_monthly_recurring_price.assert_called_once()
# int(5.0 * 100) == 500 cents, the exact value that used to crash on a bad price.
assert stripe_mock.create_app_monthly_recurring_price.call_args[0][1] == 500
@pytest.mark.parametrize(
'price, expected_cents',
[(5.0, 500), (10.0, 1000), (0.99, 99), (9.99, 999), (29.99, 2999), (1.5, 150), (100.0, 10000)],
)
def test_price_is_billed_in_whole_cents(stripe_mock, price, expected_cents):
"""The amount handed to Stripe is the price in cents."""
upsert_app_payment_link('app1', True, price, 'monthly_recurring', 'u1')
assert stripe_mock.create_app_monthly_recurring_price.call_args[0][1] == expected_cents
@pytest.mark.parametrize(
'price, expected_cents',
[(19.99, 1999), (8.29, 829), (0.29, 29), (1.13, 113), (2.01, 201), (43.15, 4315)],
)
def test_binary_float_prices_are_not_underbilled_by_a_cent(stripe_mock, price, expected_cents):
"""Prices whose cent value is not exactly representable must round, not truncate.
int(19.99 * 100) is 1998, so an app listed at $19.99 had a Stripe price created at
$19.98 while App.price kept saying 19.99 — the database and Stripe disagreed
permanently and every buyer was undercharged a cent.
"""
upsert_app_payment_link('app1', True, price, 'monthly_recurring', 'u1')
assert stripe_mock.create_app_monthly_recurring_price.call_args[0][1] == expected_cents
def test_no_price_in_the_common_range_is_underbilled(stripe_mock):
"""Every non-representable price in $0.01..$100.00 must still bill its full cents.
Only the prices whose cent value is not exactly representable are exercised — the
rest cannot regress — which keeps this inside the fast-unit CPU budget.
"""
truncating = [cents / 100 for cents in range(1, 10001) if int((cents / 100) * 100) != cents]
assert truncating, 'expected some non-representable prices in this range'
wrong = []
for price in truncating:
upsert_app_payment_link('app1', True, price, 'monthly_recurring', 'u1')
if stripe_mock.create_app_monthly_recurring_price.call_args[0][1] != round(price * 100):
wrong.append(price)
assert wrong == []