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