# tests/integration/api/v1/platform/test_pricing.py """Integration tests for platform pricing API endpoints. Tests the /api/v1/platform/pricing/* endpoints. """ import pytest from models.database.subscription import ( AddOnProduct, SubscriptionTier, TierCode, TIER_LIMITS, ) @pytest.mark.integration @pytest.mark.api @pytest.mark.platform class TestPlatformPricingAPI: """Test platform pricing endpoints at /api/v1/platform/*.""" # ========================================================================= # GET /api/v1/platform/tiers # ========================================================================= def test_get_tiers_returns_all_public_tiers(self, client): """Test getting all subscription tiers.""" response = client.get("/api/v1/platform/tiers") assert response.status_code == 200 data = response.json() assert isinstance(data, list) assert len(data) >= 4 # Essential, Professional, Business, Enterprise def test_get_tiers_has_expected_fields(self, client): """Test that tier response has all expected fields.""" response = client.get("/api/v1/platform/tiers") assert response.status_code == 200 data = response.json() assert len(data) > 0 tier = data[0] assert "code" in tier assert "name" in tier assert "price_monthly" in tier assert "price_monthly_cents" in tier assert "orders_per_month" in tier assert "products_limit" in tier assert "team_members" in tier assert "features" in tier assert "is_popular" in tier assert "is_enterprise" in tier def test_get_tiers_includes_essential(self, client): """Test that Essential tier is included.""" response = client.get("/api/v1/platform/tiers") assert response.status_code == 200 data = response.json() tier_codes = [t["code"] for t in data] assert TierCode.ESSENTIAL.value in tier_codes def test_get_tiers_includes_professional(self, client): """Test that Professional tier is included and marked as popular.""" response = client.get("/api/v1/platform/tiers") assert response.status_code == 200 data = response.json() professional = next( (t for t in data if t["code"] == TierCode.PROFESSIONAL.value), None ) assert professional is not None assert professional["is_popular"] is True def test_get_tiers_includes_enterprise(self, client): """Test that Enterprise tier is included and marked appropriately.""" response = client.get("/api/v1/platform/tiers") assert response.status_code == 200 data = response.json() enterprise = next( (t for t in data if t["code"] == TierCode.ENTERPRISE.value), None ) assert enterprise is not None assert enterprise["is_enterprise"] is True def test_get_tiers_from_database(self, client, db): """Test getting tiers from database when available.""" # Create a tier in the database tier = SubscriptionTier( code="test_tier", name="Test Tier", description="A test tier", price_monthly_cents=9900, price_annual_cents=99000, orders_per_month=1000, products_limit=500, team_members=5, features=["feature1", "feature2"], is_active=True, is_public=True, display_order=99, ) db.add(tier) db.commit() response = client.get("/api/v1/platform/tiers") assert response.status_code == 200 data = response.json() tier_codes = [t["code"] for t in data] assert "test_tier" in tier_codes # ========================================================================= # GET /api/v1/platform/tiers/{tier_code} # ========================================================================= def test_get_tier_by_code_success(self, client): """Test getting a specific tier by code.""" response = client.get(f"/api/v1/platform/tiers/{TierCode.PROFESSIONAL.value}") assert response.status_code == 200 data = response.json() assert data["code"] == TierCode.PROFESSIONAL.value assert data["name"] == TIER_LIMITS[TierCode.PROFESSIONAL]["name"] def test_get_tier_by_code_essential(self, client): """Test getting Essential tier details.""" response = client.get(f"/api/v1/platform/tiers/{TierCode.ESSENTIAL.value}") assert response.status_code == 200 data = response.json() assert data["code"] == TierCode.ESSENTIAL.value assert data["price_monthly"] == TIER_LIMITS[TierCode.ESSENTIAL]["price_monthly_cents"] / 100 def test_get_tier_by_code_not_found(self, client): """Test getting a non-existent tier returns 404.""" response = client.get("/api/v1/platform/tiers/nonexistent_tier") assert response.status_code == 404 data = response.json() assert "not found" in data["message"].lower() # ========================================================================= # GET /api/v1/platform/addons # ========================================================================= def test_get_addons_empty_when_none_configured(self, client): """Test getting add-ons when none are configured.""" response = client.get("/api/v1/platform/addons") assert response.status_code == 200 data = response.json() assert isinstance(data, list) def test_get_addons_returns_configured_addons(self, client, db): """Test getting add-ons when configured in database.""" # Create test add-on addon = AddOnProduct( code="test_domain", name="Custom Domain", description="Use your own domain", category="domain", price_cents=1500, billing_period="annual", is_active=True, display_order=1, ) db.add(addon) db.commit() response = client.get("/api/v1/platform/addons") assert response.status_code == 200 data = response.json() assert len(data) >= 1 addon_codes = [a["code"] for a in data] assert "test_domain" in addon_codes def test_get_addons_has_expected_fields(self, client, db): """Test that addon response has all expected fields.""" addon = AddOnProduct( code="test_ssl", name="Premium SSL", description="EV certificate", category="security", price_cents=4900, billing_period="annual", is_active=True, display_order=1, ) db.add(addon) db.commit() response = client.get("/api/v1/platform/addons") assert response.status_code == 200 data = response.json() assert len(data) > 0 addon_response = data[0] assert "code" in addon_response assert "name" in addon_response assert "description" in addon_response assert "category" in addon_response assert "price" in addon_response assert "price_cents" in addon_response assert "billing_period" in addon_response def test_get_addons_excludes_inactive(self, client, db): """Test that inactive add-ons are excluded.""" # Create active and inactive add-ons active_addon = AddOnProduct( code="active_addon", name="Active Addon", category="test", price_cents=1000, billing_period="monthly", is_active=True, display_order=1, ) inactive_addon = AddOnProduct( code="inactive_addon", name="Inactive Addon", category="test", price_cents=1000, billing_period="monthly", is_active=False, display_order=2, ) db.add_all([active_addon, inactive_addon]) db.commit() response = client.get("/api/v1/platform/addons") assert response.status_code == 200 data = response.json() addon_codes = [a["code"] for a in data] assert "active_addon" in addon_codes assert "inactive_addon" not in addon_codes # ========================================================================= # GET /api/v1/platform/pricing # ========================================================================= def test_get_pricing_returns_complete_info(self, client): """Test getting complete pricing information.""" response = client.get("/api/v1/platform/pricing") assert response.status_code == 200 data = response.json() assert "tiers" in data assert "addons" in data assert "trial_days" in data assert "annual_discount_months" in data def test_get_pricing_includes_trial_days(self, client): """Test that pricing includes correct trial period.""" response = client.get("/api/v1/platform/pricing") assert response.status_code == 200 data = response.json() assert data["trial_days"] == 30 # Updated from 14 to 30 def test_get_pricing_includes_annual_discount(self, client): """Test that pricing includes annual discount info.""" response = client.get("/api/v1/platform/pricing") assert response.status_code == 200 data = response.json() assert data["annual_discount_months"] == 2 # 2 months free def test_get_pricing_tiers_not_empty(self, client): """Test that pricing always includes tiers.""" response = client.get("/api/v1/platform/pricing") assert response.status_code == 200 data = response.json() assert len(data["tiers"]) >= 4