mirror of
http://88.130.71.182:3000/BlitTech/contexta_be.git
synced 2026-06-13 08:45:24 +00:00
- 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>
91 lines
3.3 KiB
Python
91 lines
3.3 KiB
Python
"""Tests for chatbot endpoints."""
|
|
import pytest
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
|
|
class TestChatbotProtection:
|
|
def test_list_chatbots_requires_auth(self, client):
|
|
resp = client.get("/api/v1/chatbots")
|
|
assert resp.status_code == 401
|
|
|
|
def test_create_chatbot_requires_auth(self, client):
|
|
resp = client.post("/api/v1/chatbots", json={"name": "Test"})
|
|
assert resp.status_code == 401
|
|
|
|
def test_delete_chatbot_requires_auth(self, client):
|
|
resp = client.delete("/api/v1/chatbots/some-id")
|
|
assert resp.status_code == 401
|
|
|
|
|
|
class TestInputValidation:
|
|
def test_create_chatbot_rejects_long_name(self, client):
|
|
"""ChatbotCreate should reject names > 100 chars."""
|
|
from app.models import ChatbotCreate
|
|
import pytest
|
|
|
|
with pytest.raises(Exception):
|
|
ChatbotCreate(name="x" * 101)
|
|
|
|
def test_create_chatbot_strips_script_tags(self):
|
|
"""System prompt with script tags should be sanitized."""
|
|
from app.models import ChatbotCreate
|
|
data = ChatbotCreate(
|
|
name="Test",
|
|
system_prompt="Hello <script>alert('xss')</script> world",
|
|
)
|
|
assert "<script>" not in (data.system_prompt or "")
|
|
assert "world" in (data.system_prompt or "")
|
|
|
|
def test_create_chatbot_rejects_long_system_prompt(self):
|
|
"""System prompt > 10000 chars should raise validation error."""
|
|
from app.models import ChatbotCreate
|
|
import pytest
|
|
|
|
with pytest.raises(Exception):
|
|
ChatbotCreate(name="Test", system_prompt="x" * 10001)
|
|
|
|
def test_create_chatbot_strips_name_whitespace(self):
|
|
from app.models import ChatbotCreate
|
|
data = ChatbotCreate(name=" My Bot ")
|
|
assert data.name == "My Bot"
|
|
|
|
|
|
class TestQdrantOrphanCleanup:
|
|
def test_qdrant_collection_deleted_on_db_failure(self):
|
|
"""If DB insert fails after Qdrant creation, collection should be cleaned up."""
|
|
with patch("app.routers.chatbots.vector_store") as mock_vs, \
|
|
patch("app.routers.chatbots.get_supabase") as mock_sb, \
|
|
patch("app.routers.chatbots.get_current_user") as mock_auth:
|
|
|
|
# Auth passes
|
|
user = MagicMock()
|
|
user.id = "user-id"
|
|
mock_auth.return_value = user
|
|
|
|
sb = MagicMock()
|
|
# company lookup succeeds
|
|
sb.table.return_value.select.return_value.eq.return_value.execute.return_value = MagicMock(
|
|
data=[{"id": "company-id", "owner_id": "user-id"}]
|
|
)
|
|
# DB insert fails
|
|
sb.table.return_value.insert.return_value.execute.side_effect = Exception("DB error")
|
|
mock_sb.return_value = sb
|
|
|
|
mock_vs.create_collection = MagicMock()
|
|
mock_vs.delete_collection = MagicMock()
|
|
|
|
from fastapi.testclient import TestClient
|
|
from app.main import app
|
|
test_client = TestClient(app)
|
|
|
|
resp = test_client.post(
|
|
"/api/v1/chatbots",
|
|
json={"name": "Test Bot"},
|
|
headers={"Authorization": "Bearer test"},
|
|
)
|
|
|
|
# Should have attempted cleanup
|
|
# (The response will be 500 due to DB failure)
|
|
# The key assertion is that delete_collection was attempted
|
|
# (This is a partial integration test — full assertion needs auth mock)
|