mirror of
http://88.130.71.182:3000/BlitTech/contexta_be.git
synced 2026-06-13 08:45:24 +00:00
213 lines
8.5 KiB
Python
213 lines
8.5 KiB
Python
"""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, "rating_count": 1}
|
|
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 "average_rating" in body
|
|
assert body["average_rating"] == 4.5 # (4.0 * 1 + 5) / 2
|
|
|
|
def test_rate_chatbot_first_rating(self, client):
|
|
"""When average_rating is None, should use the submitted rating as the new average."""
|
|
bot = {"id": "bot-1", "average_rating": None, "rating_count": 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
|
|
assert resp.json()["average_rating"] == 5.0
|