81 lines
2.7 KiB
Python
81 lines
2.7 KiB
Python
import uuid
|
|
import pytest
|
|
from datetime import datetime, timezone
|
|
from sqlalchemy import select, delete
|
|
from app.models.subscription import Subscription
|
|
from app.services.billing import BillingService
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_start_trial_creates_trialing_pro_subscription(test_db):
|
|
"""Direct service test — bypasses register, creates account inline."""
|
|
from app.models.account import Account
|
|
account = Account(name="DirectTest", display_code="DIRECT01")
|
|
test_db.add(account)
|
|
await test_db.flush()
|
|
|
|
sub = await BillingService.start_trial(test_db, account.id)
|
|
assert sub.plan == "pro"
|
|
assert sub.status == "trialing"
|
|
assert sub.current_period_end is not None
|
|
assert sub.current_period_end > datetime.now(timezone.utc)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_start_trial_is_idempotent(test_db):
|
|
from app.models.account import Account
|
|
account = Account(name="Idempo", display_code="IDEMPO01")
|
|
test_db.add(account)
|
|
await test_db.flush()
|
|
|
|
sub1 = await BillingService.start_trial(test_db, account.id)
|
|
sub2 = await BillingService.start_trial(test_db, account.id)
|
|
assert sub1.id == sub2.id
|
|
|
|
rows = (await test_db.execute(
|
|
select(Subscription).where(Subscription.account_id == account.id)
|
|
)).scalars().all()
|
|
assert len(rows) == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_register_creates_trial_subscription(client, test_db):
|
|
"""Registering a brand-new shop (no invite code) yields a Pro/trialing sub."""
|
|
response = await client.post("/api/v1/auth/register", json={
|
|
"email": "newshop@example.com",
|
|
"password": "Verystrong1Pwd",
|
|
"name": "New Shop",
|
|
})
|
|
assert response.status_code in (200, 201), response.json()
|
|
|
|
body = response.json()
|
|
account_id = uuid.UUID(body["account_id"])
|
|
|
|
sub = (await test_db.execute(
|
|
select(Subscription).where(Subscription.account_id == account_id)
|
|
)).scalar_one()
|
|
assert sub.plan == "pro"
|
|
assert sub.status == "trialing"
|
|
assert sub.current_period_end is not None
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_apply_subscription_event_is_idempotent(test_db):
|
|
payload = {
|
|
"data": {"object": {
|
|
"id": "evt_test_1",
|
|
"customer": "cus_xxx",
|
|
"subscription": "sub_xxx",
|
|
"status": "active",
|
|
}}
|
|
}
|
|
|
|
applied_first = await BillingService.apply_subscription_event(
|
|
test_db, "evt_test_1", "customer.subscription.updated", payload
|
|
)
|
|
applied_second = await BillingService.apply_subscription_event(
|
|
test_db, "evt_test_1", "customer.subscription.updated", payload
|
|
)
|
|
assert applied_first is True
|
|
assert applied_second is False # already-processed → ack without re-applying
|