"""Tests for marketplace endpoints.""" import pytest from unittest.mock import MagicMock, patch AUTH = {"Authorization": "Bearer test-token"} def _make_marketplace_sb(chatbot_data=None, count=0): """Build a supabase mock for marketplace queries.""" sb = MagicMock() def table_side(name): m = MagicMock() m.select.return_value = m m.insert.return_value = m m.update.return_value = m m.delete.return_value = m m.eq.return_value = m m.in_.return_value = m m.ilike.return_value = m m.limit.return_value = m m.order.return_value = m m.range.return_value = m m.gte.return_value = m m.lt.return_value = m if name == "chatbots": m.execute.return_value = MagicMock( data=chatbot_data if chatbot_data is not None else [], count=count, ) elif name == "conversations": m.execute.return_value = MagicMock(data=[], count=0) else: m.execute.return_value = MagicMock(data=[], count=0) return m sb.table.side_effect = table_side sb.auth = MagicMock() return sb class TestMarketplaceList: def test_list_returns_empty_when_no_chatbots(self, client): with patch("app.routers.marketplace.get_supabase") as mock_sb: mock_sb.return_value = _make_marketplace_sb() resp = client.get("/api/v1/marketplace/chatbots") assert resp.status_code == 200 body = resp.json() assert body["chatbots"] == [] assert body["total"] == 0 def test_list_returns_chatbots(self, client): bots = [{ "id": "bot-1", "name": "Support Bot", "description": "A test bot", "category": "Customer Support", "industry": "Technology & SaaS", "languages": ["en"], "primary_color": "#6366f1", "welcome_message": "Hello!", "logo_url": None, "average_rating": 4.5, "total_conversations": 100, "is_published": True, "created_at": "2024-01-01T00:00:00", "published_at": "2024-01-02T00:00:00", "companies": {"name": "Acme Inc", "logo_url": None}, }] with patch("app.routers.marketplace.get_supabase") as mock_sb: mock_sb.return_value = _make_marketplace_sb(chatbot_data=bots, count=1) resp = client.get("/api/v1/marketplace/chatbots") assert resp.status_code == 200 body = resp.json() assert len(body["chatbots"]) == 1 assert body["chatbots"][0]["name"] == "Support Bot" assert body["chatbots"][0]["company_name"] == "Acme Inc" def test_list_pagination_fields(self, client): with patch("app.routers.marketplace.get_supabase") as mock_sb: mock_sb.return_value = _make_marketplace_sb(count=50) resp = client.get("/api/v1/marketplace/chatbots?page=1&limit=20") assert resp.status_code == 200 body = resp.json() assert body["page"] == 1 assert body["limit"] == 20 assert "has_more" in body def test_list_limit_max_100(self, client): with patch("app.routers.marketplace.get_supabase") as mock_sb: mock_sb.return_value = _make_marketplace_sb() resp = client.get("/api/v1/marketplace/chatbots?limit=200") # FastAPI should reject > 100 assert resp.status_code == 422 def test_list_accepts_category_filter(self, client): with patch("app.routers.marketplace.get_supabase") as mock_sb: mock_sb.return_value = _make_marketplace_sb() resp = client.get("/api/v1/marketplace/chatbots?category=Customer+Support") assert resp.status_code == 200 def test_list_accepts_search_filter(self, client): with patch("app.routers.marketplace.get_supabase") as mock_sb: mock_sb.return_value = _make_marketplace_sb() resp = client.get("/api/v1/marketplace/chatbots?search=bot") assert resp.status_code == 200 def test_has_more_true_when_more_results(self, client): with patch("app.routers.marketplace.get_supabase") as mock_sb: mock_sb.return_value = _make_marketplace_sb(count=100) resp = client.get("/api/v1/marketplace/chatbots?page=1&limit=20") assert resp.json()["has_more"] is True def test_has_more_false_on_last_page(self, client): with patch("app.routers.marketplace.get_supabase") as mock_sb: mock_sb.return_value = _make_marketplace_sb(count=10) resp = client.get("/api/v1/marketplace/chatbots?page=1&limit=20") assert resp.json()["has_more"] is False class TestMarketplaceDetail: def test_detail_not_found_returns_404(self, client): with patch("app.routers.marketplace.get_supabase") as mock_sb: mock_sb.return_value = _make_marketplace_sb(chatbot_data=[]) resp = client.get("/api/v1/marketplace/chatbots/nonexistent-id") assert resp.status_code == 404 def test_detail_returns_chatbot(self, client): bot = { "id": "bot-1", "name": "My Bot", "description": "desc", "category": "FAQ & Knowledge Base", "industry": "Education & Training", "languages": ["en", "fr"], "primary_color": "#000000", "welcome_message": "Hi!", "logo_url": None, "average_rating": 3.8, "total_conversations": 50, "is_published": True, "created_at": "2024-01-01T00:00:00", "published_at": "2024-01-02T00:00:00", "companies": {"name": "Test Co", "logo_url": None}, } with patch("app.routers.marketplace.get_supabase") as mock_sb: mock_sb.return_value = _make_marketplace_sb(chatbot_data=[bot]) resp = client.get("/api/v1/marketplace/chatbots/bot-1") assert resp.status_code == 200 body = resp.json() assert body["name"] == "My Bot" assert body["languages"] == ["en", "fr"] class TestMarketplaceCategories: def test_categories_returns_lists(self, client): resp = client.get("/api/v1/marketplace/categories") assert resp.status_code == 200 body = resp.json() assert "categories" in body assert "industries" in body assert isinstance(body["categories"], list) assert isinstance(body["industries"], list) assert len(body["categories"]) > 0 assert len(body["industries"]) > 0 def test_categories_includes_customer_support(self, client): resp = client.get("/api/v1/marketplace/categories") assert "Customer Support" in resp.json()["categories"] class TestMarketplaceRating: def test_rate_requires_auth(self, client): resp = client.post("/api/v1/marketplace/chatbots/bot-1/rate", json={"rating": 4}) assert resp.status_code == 401 def test_rate_chatbot_not_found(self, client): with patch("app.routers.marketplace.get_supabase") as mock_sb: mock_sb.return_value = _make_marketplace_sb(chatbot_data=[]) resp = client.post( "/api/v1/marketplace/chatbots/nonexistent/rate", json={"rating": 4}, headers=AUTH, ) assert resp.status_code == 404 def test_rate_chatbot_success(self, client): bot = {"id": "bot-1", "average_rating": 4.0} with patch("app.routers.marketplace.get_supabase") as mock_sb: mock_sb.return_value = _make_marketplace_sb(chatbot_data=[bot]) resp = client.post( "/api/v1/marketplace/chatbots/bot-1/rate", json={"rating": 5}, headers=AUTH, ) assert resp.status_code == 200 body = resp.json() assert "new_average" in body assert body["new_average"] == 4.5 # (4.0 + 5) / 2 def test_rate_chatbot_first_rating(self, client): """When average_rating is None, should use the submitted rating as both sides.""" bot = {"id": "bot-1", "average_rating": None} with patch("app.routers.marketplace.get_supabase") as mock_sb: mock_sb.return_value = _make_marketplace_sb(chatbot_data=[bot]) resp = client.post( "/api/v1/marketplace/chatbots/bot-1/rate", json={"rating": 5}, headers=AUTH, ) assert resp.status_code == 200 assert resp.json()["new_average"] == 5.0