Files
contexta_be/tests/test_marketplace.py
belviskhoremk 92d4c2fc5e feat: add appointments, campaigns, admin, storage, tests and various updates
- Add new routers: admin, appointments, campaigns
- Add storage service and logging config
- Add migrations directory and test suite with pytest config
- Add supabase_migration_features.sql
- Update models, dependencies, config, and existing routers
- Remove whatsapp_service (deleted)
- Update pyproject.toml and uv.lock dependencies

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 09:11:58 +00:00

213 lines
8.4 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}
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