Spaces:
Build error
Build error
| import pytest | |
| from unittest.mock import patch, MagicMock | |
| from fastapi.testclient import TestClient | |
| from app.main import app | |
| # Every Stripe call in this module is mocked -- nothing here reaches the | |
| # network, and no real credentials are involved. This module used to skip | |
| # entirely whenever STRIPE_SECRET_KEY was unset (always, in CI), so none of | |
| # it ran; stripe.api_key is patched below instead of relying on env vars, | |
| # since routes_payments.py reads it at import time. | |
| client = TestClient(app) | |
| def stripe_configured(monkeypatch): | |
| monkeypatch.setattr("stripe.api_key", "sk_test_fake") | |
| def mock_stripe(): | |
| with patch("stripe.checkout.Session.create") as mock: | |
| yield mock | |
| def test_create_checkout_session_missing_stripe_key(monkeypatch): | |
| # routes_payments.py sets stripe.api_key from the environment at import | |
| # time and checks `stripe.api_key`, so setenv here would be a no-op. | |
| monkeypatch.setattr("stripe.api_key", None) | |
| response = client.post( | |
| "/api/v1/payments/create-checkout-session", | |
| headers={"Authorization": "Bearer test_key"}, | |
| json={ | |
| "success_url": "https://example.com/success", | |
| "cancel_url": "https://example.com/cancel"}) | |
| assert response.status_code == 500 | |
| assert "Stripe not configured" in response.json()["detail"] | |
| def test_create_checkout_session_free_key(mock_stripe): | |
| # Mock tracker.get_tier to return Tier.FREE | |
| with patch("app.core.usage_tracker.tracker") as mock_tracker: | |
| mock_tracker.get_tier.return_value = "free" | |
| mock_tracker.get_tenant_id.return_value = "tenant_test_123" | |
| mock_stripe.return_value = MagicMock( | |
| id="cs_test_123", url="https://checkout.stripe.com/pay") | |
| response = client.post( | |
| "/api/v1/payments/create-checkout-session", | |
| headers={"Authorization": "Bearer test_key"}, | |
| json={ | |
| "success_url": "https://example.com/success", | |
| "cancel_url": "https://example.com/cancel"}) | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert "sessionId" in data | |
| assert "url" in data | |
| def test_create_checkout_session_pro_key(): | |
| with patch("app.core.usage_tracker.tracker") as mock_tracker: | |
| mock_tracker.get_tier.return_value = "pro" | |
| response = client.post( | |
| "/api/v1/payments/create-checkout-session", | |
| headers={"Authorization": "Bearer test_key"}, | |
| json={ | |
| "success_url": "https://example.com/success", | |
| "cancel_url": "https://example.com/cancel"}) | |
| assert response.status_code == 400 | |
| assert "Only free tier keys can be upgraded" in response.json()[ | |
| "detail"] | |
| def test_create_checkout_session_requires_authentication(mock_stripe): | |
| # Regression test: this endpoint used to take `api_key` as a plain JSON | |
| # body field with no Depends() gate at all, so any caller could request | |
| # a session for any tenant_id-bearing key string without proving they | |
| # held it. No Authorization header (and no api_key in the body -- the | |
| # field no longer exists on CheckoutRequest) must be rejected before | |
| # ever reaching Stripe. | |
| response = client.post( | |
| "/api/v1/payments/create-checkout-session", | |
| json={ | |
| "success_url": "https://example.com/success", | |
| "cancel_url": "https://example.com/cancel"}) | |
| assert response.status_code == 401 | |
| mock_stripe.assert_not_called() | |
| def test_create_checkout_session_rejects_invalid_key(mock_stripe): | |
| with patch("app.core.usage_tracker.tracker") as mock_tracker: | |
| mock_tracker.get_tier.return_value = None | |
| response = client.post( | |
| "/api/v1/payments/create-checkout-session", | |
| headers={"Authorization": "Bearer not-a-real-key"}, | |
| json={ | |
| "success_url": "https://example.com/success", | |
| "cancel_url": "https://example.com/cancel"}) | |
| assert response.status_code == 403 | |
| mock_stripe.assert_not_called() | |