"""Tests for models router — plan-based model availability.""" import pytest from unittest.mock import MagicMock, patch AUTH = {"Authorization": "Bearer test-token"} def _make_sb_with_plan(plan: str): sb = MagicMock() m = MagicMock() m.select.return_value = m m.eq.return_value = m m.execute.return_value = MagicMock(data=[{"plan": plan}]) sb.table.return_value = m sb.auth = MagicMock() return sb class TestModelsAuth: def test_available_requires_auth(self, client): resp = client.get("/api/v1/models/available") assert resp.status_code == 401 class TestModelsAvailable: @pytest.mark.parametrize("plan", ["free", "starter", "pro", "enterprise"]) def test_returns_200_for_all_plans(self, client, plan): with patch("app.routers.models.get_supabase") as mock_sb: mock_sb.return_value = _make_sb_with_plan(plan) resp = client.get("/api/v1/models/available", headers=AUTH) assert resp.status_code == 200 def test_response_shape(self, client): with patch("app.routers.models.get_supabase") as mock_sb: mock_sb.return_value = _make_sb_with_plan("starter") resp = client.get("/api/v1/models/available", headers=AUTH) body = resp.json() assert "models" in body assert "plan" in body assert "has_premium_access" in body assert isinstance(body["models"], list) def test_free_plan_has_no_premium_access(self, client): with patch("app.routers.models.get_supabase") as mock_sb: mock_sb.return_value = _make_sb_with_plan("free") resp = client.get("/api/v1/models/available", headers=AUTH) body = resp.json() assert body["has_premium_access"] is False assert body["upgrade_label"] is not None def test_enterprise_has_premium_access(self, client): with patch("app.routers.models.get_supabase") as mock_sb: mock_sb.return_value = _make_sb_with_plan("enterprise") resp = client.get("/api/v1/models/available", headers=AUTH) body = resp.json() assert body["has_premium_access"] is True assert body["upgrade_label"] is None def test_model_fields_present(self, client): with patch("app.routers.models.get_supabase") as mock_sb: mock_sb.return_value = _make_sb_with_plan("starter") resp = client.get("/api/v1/models/available", headers=AUTH) body = resp.json() if body["models"]: model = body["models"][0] assert "id" in model assert "name" in model assert "provider" in model assert "badge" in model assert "is_default" in model def test_exactly_one_default_model_per_plan(self, client): for plan in ["starter", "pro"]: with patch("app.routers.models.get_supabase") as mock_sb: mock_sb.return_value = _make_sb_with_plan(plan) resp = client.get("/api/v1/models/available", headers=AUTH) body = resp.json() defaults = [m for m in body["models"] if m["is_default"]] assert len(defaults) <= 1, f"Plan {plan} has {len(defaults)} default models" def test_starter_upgrade_label_mentions_business(self, client): with patch("app.routers.models.get_supabase") as mock_sb: mock_sb.return_value = _make_sb_with_plan("starter") resp = client.get("/api/v1/models/available", headers=AUTH) body = resp.json() assert body["upgrade_label"] is not None def test_unknown_plan_falls_back_to_free(self, client): """An unknown plan should fall back to free-tier behavior without crashing.""" with patch("app.routers.models.get_supabase") as mock_sb: mock_sb.return_value = _make_sb_with_plan("banana") resp = client.get("/api/v1/models/available", headers=AUTH) assert resp.status_code == 200 def test_no_active_subscription_defaults_to_free(self, client): with patch("app.routers.models.get_supabase") as mock_sb: sb = MagicMock() m = MagicMock() m.select.return_value = m m.eq.return_value = m m.execute.return_value = MagicMock(data=[]) sb.table.return_value = m sb.auth = MagicMock() mock_sb.return_value = sb resp = client.get("/api/v1/models/available", headers=AUTH) assert resp.status_code == 200 assert resp.json()["plan"] == "free"