Spaces:
Running
Running
| """Unit tests: BillingService — activation, payment events, plan changes.""" | |
| import pytest | |
| from unittest.mock import AsyncMock, MagicMock, patch | |
| from datetime import datetime, timezone | |
| async def test_activate_unpaid_session_raises(db_session): | |
| from app.services.billing_service import BillingService | |
| svc = BillingService(db_session) | |
| mock_session = MagicMock() | |
| mock_session.payment_status = "unpaid" | |
| with patch("app.services.billing_service.get_checkout_session", | |
| new_callable=AsyncMock, return_value=mock_session): | |
| with pytest.raises(ValueError, match="Payment not completed"): | |
| await svc.activate_from_session("cs_fake", "hash123") | |
| async def test_payment_succeeded_resets_tokens(db_session, test_author): | |
| from app.services.billing_service import BillingService | |
| from app.models.client_access import ClientAccess | |
| from sqlalchemy import select | |
| # Set tokens_used to non-zero | |
| result = await db_session.execute( | |
| select(ClientAccess).where(ClientAccess.author_id == test_author.id) | |
| ) | |
| access = result.scalar_one_or_none() | |
| if access: | |
| access.tokens_used = 250_000 | |
| access.stripe_subscription_id = "sub_test_reset" | |
| await db_session.commit() | |
| svc = BillingService(db_session) | |
| await svc.handle_payment_succeeded("sub_test_reset") | |
| await db_session.refresh(access) | |
| assert access.tokens_used == 0 | |
| async def test_payment_failed_sets_grace_period(db_session, test_author): | |
| from app.services.billing_service import BillingService | |
| from app.models.user import User | |
| from sqlalchemy import select | |
| test_author.stripe_subscription_id = "sub_grace_test" | |
| await db_session.commit() | |
| svc = BillingService(db_session) | |
| await svc.handle_payment_failed("sub_grace_test", grace_days=7) | |
| result = await db_session.execute( | |
| select(User).where(User.id == test_author.id) | |
| ) | |
| user = result.scalar_one_or_none() | |
| if user: | |
| assert user.payment_grace_until is not None | |
| assert user.payment_grace_until > datetime.now(timezone.utc) | |
| async def test_subscription_cancelled_revokes_access(db_session, test_author): | |
| from app.services.billing_service import BillingService | |
| from app.models.client_access import ClientAccess | |
| from sqlalchemy import select | |
| result = await db_session.execute( | |
| select(ClientAccess).where(ClientAccess.author_id == test_author.id) | |
| ) | |
| access = result.scalar_one_or_none() | |
| if access: | |
| access.stripe_subscription_id = "sub_cancel_test" | |
| await db_session.commit() | |
| svc = BillingService(db_session) | |
| await svc.handle_subscription_cancelled("sub_cancel_test") | |
| await db_session.refresh(access) | |
| assert access.is_revoked is True | |