payplus-python 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tests/test_models.py ADDED
@@ -0,0 +1,348 @@
1
+ """
2
+ Tests for PayPlus SDK models.
3
+ """
4
+
5
+ import pytest
6
+ from decimal import Decimal
7
+ from datetime import datetime, timedelta
8
+
9
+ from payplus.models.customer import Customer, CustomerStatus, PaymentMethod
10
+ from payplus.models.subscription import Subscription, SubscriptionStatus, BillingCycle
11
+ from payplus.models.payment import Payment, PaymentStatus
12
+ from payplus.models.invoice import Invoice, InvoiceStatus
13
+ from payplus.models.tier import Tier, TierTemplates
14
+
15
+
16
+ class TestCustomer:
17
+ """Tests for Customer model."""
18
+
19
+ def test_create_customer(self):
20
+ customer = Customer(
21
+ id="cust_123",
22
+ email="test@example.com",
23
+ name="Test User",
24
+ )
25
+
26
+ assert customer.id == "cust_123"
27
+ assert customer.email == "test@example.com"
28
+ assert customer.status == CustomerStatus.ACTIVE
29
+
30
+ def test_add_payment_method(self):
31
+ customer = Customer(
32
+ id="cust_123",
33
+ email="test@example.com",
34
+ )
35
+
36
+ pm = customer.add_payment_method(
37
+ token="tok_123",
38
+ card_brand="Visa",
39
+ last_four="4242",
40
+ set_default=True,
41
+ )
42
+
43
+ assert len(customer.payment_methods) == 1
44
+ assert pm.token == "tok_123"
45
+ assert pm.is_default is True
46
+ assert customer.default_payment_method_id == pm.id
47
+
48
+ def test_get_default_payment_method(self):
49
+ customer = Customer(
50
+ id="cust_123",
51
+ email="test@example.com",
52
+ )
53
+
54
+ pm1 = customer.add_payment_method(token="tok_1", set_default=True)
55
+ pm2 = customer.add_payment_method(token="tok_2", set_default=False)
56
+
57
+ default = customer.get_default_payment_method()
58
+ assert default.id == pm1.id
59
+
60
+ def test_remove_payment_method(self):
61
+ customer = Customer(
62
+ id="cust_123",
63
+ email="test@example.com",
64
+ )
65
+
66
+ pm = customer.add_payment_method(token="tok_123")
67
+ assert len(customer.payment_methods) == 1
68
+
69
+ result = customer.remove_payment_method(pm.id)
70
+ assert result is True
71
+ assert len(customer.payment_methods) == 0
72
+
73
+
74
+ class TestSubscription:
75
+ """Tests for Subscription model."""
76
+
77
+ def test_create_subscription(self):
78
+ subscription = Subscription(
79
+ id="sub_123",
80
+ customer_id="cust_123",
81
+ tier_id="pro",
82
+ amount=Decimal("79.00"),
83
+ )
84
+
85
+ assert subscription.id == "sub_123"
86
+ assert subscription.status == SubscriptionStatus.INCOMPLETE
87
+
88
+ def test_subscription_is_active(self):
89
+ subscription = Subscription(
90
+ id="sub_123",
91
+ customer_id="cust_123",
92
+ tier_id="pro",
93
+ amount=Decimal("79.00"),
94
+ status=SubscriptionStatus.ACTIVE,
95
+ )
96
+
97
+ assert subscription.is_active is True
98
+
99
+ def test_subscription_trialing(self):
100
+ subscription = Subscription(
101
+ id="sub_123",
102
+ customer_id="cust_123",
103
+ tier_id="pro",
104
+ amount=Decimal("79.00"),
105
+ status=SubscriptionStatus.TRIALING,
106
+ trial_end=datetime.utcnow() + timedelta(days=14),
107
+ )
108
+
109
+ assert subscription.is_trialing is True
110
+ assert subscription.is_active is True # Trialing is considered active
111
+
112
+ def test_cancel_subscription(self):
113
+ subscription = Subscription(
114
+ id="sub_123",
115
+ customer_id="cust_123",
116
+ tier_id="pro",
117
+ amount=Decimal("79.00"),
118
+ status=SubscriptionStatus.ACTIVE,
119
+ )
120
+
121
+ subscription.cancel(at_period_end=True, reason="Customer request")
122
+
123
+ assert subscription.cancel_at_period_end is True
124
+ assert subscription.cancellation_reason == "Customer request"
125
+ assert subscription.canceled_at is not None
126
+ assert subscription.status == SubscriptionStatus.ACTIVE # Still active until period end
127
+
128
+ def test_cancel_immediately(self):
129
+ subscription = Subscription(
130
+ id="sub_123",
131
+ customer_id="cust_123",
132
+ tier_id="pro",
133
+ amount=Decimal("79.00"),
134
+ status=SubscriptionStatus.ACTIVE,
135
+ )
136
+
137
+ subscription.cancel(at_period_end=False)
138
+
139
+ assert subscription.status == SubscriptionStatus.CANCELED
140
+ assert subscription.ended_at is not None
141
+
142
+ def test_pause_resume(self):
143
+ subscription = Subscription(
144
+ id="sub_123",
145
+ customer_id="cust_123",
146
+ tier_id="pro",
147
+ amount=Decimal("79.00"),
148
+ status=SubscriptionStatus.ACTIVE,
149
+ )
150
+
151
+ subscription.pause()
152
+ assert subscription.status == SubscriptionStatus.PAUSED
153
+ assert subscription.paused_at is not None
154
+
155
+ subscription.resume()
156
+ assert subscription.status == SubscriptionStatus.ACTIVE
157
+ assert subscription.paused_at is None
158
+
159
+
160
+ class TestPayment:
161
+ """Tests for Payment model."""
162
+
163
+ def test_create_payment(self):
164
+ payment = Payment(
165
+ id="pay_123",
166
+ customer_id="cust_123",
167
+ amount=Decimal("100.00"),
168
+ )
169
+
170
+ assert payment.id == "pay_123"
171
+ assert payment.status == PaymentStatus.PENDING
172
+
173
+ def test_mark_succeeded(self):
174
+ payment = Payment(
175
+ id="pay_123",
176
+ customer_id="cust_123",
177
+ amount=Decimal("100.00"),
178
+ )
179
+
180
+ payment.mark_succeeded(transaction_uid="tx_123", approval_number="123456")
181
+
182
+ assert payment.status == PaymentStatus.SUCCEEDED
183
+ assert payment.payplus_transaction_uid == "tx_123"
184
+ assert payment.paid_at is not None
185
+
186
+ def test_mark_failed(self):
187
+ payment = Payment(
188
+ id="pay_123",
189
+ customer_id="cust_123",
190
+ amount=Decimal("100.00"),
191
+ )
192
+
193
+ payment.mark_failed(failure_code="card_declined", failure_message="Card was declined")
194
+
195
+ assert payment.status == PaymentStatus.FAILED
196
+ assert payment.failure_code == "card_declined"
197
+
198
+ def test_refund(self):
199
+ payment = Payment(
200
+ id="pay_123",
201
+ customer_id="cust_123",
202
+ amount=Decimal("100.00"),
203
+ status=PaymentStatus.SUCCEEDED,
204
+ )
205
+
206
+ payment.add_refund(
207
+ refund_id="ref_123",
208
+ amount=Decimal("50.00"),
209
+ )
210
+
211
+ assert payment.amount_refunded == Decimal("50.00")
212
+ assert payment.status == PaymentStatus.PARTIALLY_REFUNDED
213
+ assert payment.net_amount == Decimal("50.00")
214
+
215
+ def test_full_refund(self):
216
+ payment = Payment(
217
+ id="pay_123",
218
+ customer_id="cust_123",
219
+ amount=Decimal("100.00"),
220
+ status=PaymentStatus.SUCCEEDED,
221
+ )
222
+
223
+ payment.add_refund(refund_id="ref_123", amount=Decimal("100.00"))
224
+
225
+ assert payment.status == PaymentStatus.REFUNDED
226
+
227
+
228
+ class TestInvoice:
229
+ """Tests for Invoice model."""
230
+
231
+ def test_create_invoice(self):
232
+ invoice = Invoice(
233
+ id="inv_123",
234
+ customer_id="cust_123",
235
+ )
236
+
237
+ assert invoice.id == "inv_123"
238
+ assert invoice.status == InvoiceStatus.DRAFT
239
+
240
+ def test_add_items(self):
241
+ invoice = Invoice(
242
+ id="inv_123",
243
+ customer_id="cust_123",
244
+ )
245
+
246
+ invoice.add_item(
247
+ item_id="ii_1",
248
+ description="Pro Plan",
249
+ unit_amount=Decimal("79.00"),
250
+ quantity=1,
251
+ )
252
+
253
+ assert len(invoice.items) == 1
254
+ assert invoice.subtotal == Decimal("79.00")
255
+ assert invoice.total == Decimal("79.00")
256
+
257
+ def test_finalize_invoice(self):
258
+ invoice = Invoice(
259
+ id="inv_123",
260
+ customer_id="cust_123",
261
+ )
262
+
263
+ invoice.add_item(
264
+ item_id="ii_1",
265
+ description="Pro Plan",
266
+ unit_amount=Decimal("79.00"),
267
+ )
268
+
269
+ invoice.finalize()
270
+
271
+ assert invoice.status == InvoiceStatus.OPEN
272
+ assert invoice.finalized_at is not None
273
+
274
+ def test_mark_paid(self):
275
+ invoice = Invoice(
276
+ id="inv_123",
277
+ customer_id="cust_123",
278
+ total=Decimal("79.00"),
279
+ amount_due=Decimal("79.00"),
280
+ status=InvoiceStatus.OPEN,
281
+ )
282
+
283
+ invoice.mark_paid(payment_id="pay_123")
284
+
285
+ assert invoice.status == InvoiceStatus.PAID
286
+ assert invoice.payment_id == "pay_123"
287
+ assert invoice.amount_paid == Decimal("79.00")
288
+
289
+
290
+ class TestTier:
291
+ """Tests for Tier model."""
292
+
293
+ def test_create_tier(self):
294
+ tier = Tier(
295
+ id="pro",
296
+ name="Pro",
297
+ price=Decimal("79.00"),
298
+ )
299
+
300
+ assert tier.id == "pro"
301
+ assert tier.price == Decimal("79.00")
302
+ assert tier.billing_cycle == BillingCycle.MONTHLY
303
+
304
+ def test_annual_price(self):
305
+ tier = Tier(
306
+ id="pro",
307
+ name="Pro",
308
+ price=Decimal("79.00"),
309
+ annual_discount_percent=Decimal("20"),
310
+ )
311
+
312
+ annual = tier.get_annual_price()
313
+ expected = Decimal("79.00") * 12 * Decimal("0.80")
314
+ assert annual == expected
315
+
316
+ def test_add_feature(self):
317
+ tier = Tier(
318
+ id="pro",
319
+ name="Pro",
320
+ price=Decimal("79.00"),
321
+ )
322
+
323
+ feature = tier.add_feature(
324
+ feature_id="projects",
325
+ name="Projects",
326
+ included_quantity=100,
327
+ )
328
+
329
+ assert len(tier.features) == 1
330
+ assert tier.has_feature("projects")
331
+ assert tier.get_feature("projects").included_quantity == 100
332
+
333
+ def test_tier_templates(self):
334
+ free = TierTemplates.free()
335
+ assert free.price == Decimal("0")
336
+
337
+ pro = TierTemplates.pro(price=Decimal("99"))
338
+ assert pro.price == Decimal("99")
339
+ assert pro.is_popular is True
340
+
341
+
342
+ class TestBillingCycle:
343
+ """Tests for BillingCycle."""
344
+
345
+ def test_to_interval(self):
346
+ assert BillingCycle.MONTHLY.to_interval() == ("month", 1)
347
+ assert BillingCycle.YEARLY.to_interval() == ("year", 1)
348
+ assert BillingCycle.QUARTERLY.to_interval() == ("month", 3)