import uuid import pytest from unittest.mock import patch, MagicMock from sqlalchemy import select from app.models.account import Account @pytest.mark.asyncio async def test_billing_portal_returns_url_for_account_with_stripe_customer( client, test_db, test_user, auth_headers, monkeypatch ): """Happy path: account has a stripe_customer_id and Stripe is configured → GET /billing/portal-session returns the portal URL.""" from app.core.config import settings monkeypatch.setattr(settings, "STRIPE_SECRET_KEY", "sk_test_dummy") monkeypatch.setattr(settings, "FRONTEND_URL", "https://app.example.com") account_id = uuid.UUID(test_user["user_data"]["account_id"]) account = (await test_db.execute( select(Account).where(Account.id == account_id) )).scalar_one() account.stripe_customer_id = "cus_test_456" await test_db.commit() fake_session = MagicMock() fake_session.url = "https://billing.stripe.com/p/session/test_abc" with patch( "stripe.billing_portal.Session.create", return_value=fake_session, ) as portal_mock: response = await client.get( "/api/v1/billing/portal-session", headers=auth_headers, ) assert response.status_code == 200, response.json() assert response.json() == {"url": "https://billing.stripe.com/p/session/test_abc"} portal_mock.assert_called_once() call_kwargs = portal_mock.call_args.kwargs assert call_kwargs["customer"] == "cus_test_456" assert call_kwargs["return_url"] == "https://app.example.com/account/billing" @pytest.mark.asyncio async def test_billing_portal_returns_503_when_stripe_not_configured( client, test_db, test_user, auth_headers, monkeypatch ): """STRIPE_SECRET_KEY unset → settings.stripe_enabled is False → 503.""" from app.core.config import settings monkeypatch.setattr(settings, "STRIPE_SECRET_KEY", None) response = await client.get( "/api/v1/billing/portal-session", headers=auth_headers, ) assert response.status_code == 503 assert response.json()["detail"]["error"] == "stripe_not_configured" @pytest.mark.asyncio async def test_billing_portal_returns_400_when_account_has_no_stripe_customer( client, test_db, test_user, auth_headers, monkeypatch ): """Account with no stripe_customer_id (never completed checkout) → 400 with `no_stripe_customer` error.""" from app.core.config import settings monkeypatch.setattr(settings, "STRIPE_SECRET_KEY", "sk_test_dummy") # test_user fixture seeds an account with no stripe_customer_id by default. account_id = uuid.UUID(test_user["user_data"]["account_id"]) account = (await test_db.execute( select(Account).where(Account.id == account_id) )).scalar_one() assert account.stripe_customer_id is None response = await client.get( "/api/v1/billing/portal-session", headers=auth_headers, ) assert response.status_code == 400 assert response.json()["detail"]["error"] == "no_stripe_customer"