todo-app / tests /test_chatbot_integration.py
Claude
feat: update AI agent with MCP server integration
453b80e
Raw
History Blame Contribute Delete
10.1 kB
"""
Task: T033
Spec: 010-ai-chatbot/spec.md - Chatbot Integration Tests
Integration tests for the AI chatbot functionality to verify the complete user flow.
Tests the API endpoints, agent integration, and database operations.
"""
import pytest
import asyncio
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool
from app.main import app
from app.database import get_session
from app.models import User, Task, Conversation, Message
from app.auth.jwt import create_access_token
from datetime import datetime, timedelta
from unittest.mock import patch, MagicMock
import json
# Create an in-memory SQLite database for testing
SQLALCHEMY_DATABASE_URL = "sqlite:///:memory:"
engine = create_engine(
SQLALCHEMY_DATABASE_URL,
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
def override_get_session():
"""Override the get_session dependency for testing."""
try:
db = TestingSessionLocal()
yield db
finally:
db.close()
# Override the session dependency
app.dependency_overrides[get_session] = override_get_session
@pytest.fixture(scope="module")
def test_app():
"""Create a test client for the FastAPI app."""
with TestClient(app) as client:
yield client
@pytest.fixture(scope="module")
def sample_user():
"""Create a sample user for testing."""
user = User(
id="test_user_123",
username="testuser",
email="test@example.com",
hashed_password="hashed_test_password",
created_at=datetime.utcnow(),
updated_at=datetime.utcnow()
)
return user
@pytest.fixture(scope="module")
def auth_token(sample_user):
"""Create a JWT token for the sample user."""
data = {"sub": sample_user.id}
token = create_access_token(data=data, expires_delta=timedelta(hours=1))
return token
def test_chat_endpoint_success(test_app, sample_user, auth_token):
"""Test successful chat message processing."""
# Add the user to the database
db = TestingSessionLocal()
try:
db.add(sample_user)
db.commit()
# Mock the AI agent response to avoid actual OpenAI calls
with patch('src.agents.agent_runner.AgentRunner.process_conversation') as mock_process:
mock_response = {
"conversation_id": "test_conv_123",
"response": "I have added the task 'Buy groceries' to your list.",
"tool_calls": [{"name": "add_task", "arguments": {"title": "Buy groceries"}}],
"success": True
}
mock_process.return_value = mock_response
# Send a POST request to the chat endpoint
response = test_app.post(
"/api/chat",
headers={"Authorization": f"Bearer auth_token"},
json={
"message": "Add a task to buy groceries"
}
)
# Assertions
assert response.status_code == 200
response_data = response.json()
assert "conversation_id" in response_data
assert "response" in response_data
assert "tool_calls" in response_data
assert response_data["response"] == "I have added the task 'Buy groceries' to your list."
finally:
db.close()
def test_chat_endpoint_with_existing_conversation(test_app, sample_user, auth_token):
"""Test chat message processing with an existing conversation."""
db = TestingSessionLocal()
try:
db.add(sample_user)
db.commit()
# Mock the AI agent response
with patch('src.agents.agent_runner.AgentRunner.process_conversation') as mock_process:
mock_response = {
"conversation_id": "existing_conv_456",
"response": "I found 3 tasks in your list.",
"tool_calls": [{"name": "list_tasks", "arguments": {"status": "all"}}],
"success": True
}
mock_process.return_value = mock_response
# Send a POST request with an existing conversation ID
response = test_app.post(
"/api/chat",
headers={"Authorization": f"Bearer auth_token"},
json={
"conversation_id": "existing_conv_456",
"message": "Show me my tasks"
}
)
# Assertions
assert response.status_code == 200
response_data = response.json()
assert response_data["conversation_id"] == "existing_conv_456"
assert "response" in response_data
finally:
db.close()
def test_list_conversations_endpoint(test_app, sample_user, auth_token):
"""Test listing user's conversations."""
db = TestingSessionLocal()
try:
# Add user and some conversations to the database
db.add(sample_user)
conv1 = Conversation(
id="conv_1",
user_id=sample_user.id,
created_at=datetime.utcnow(),
updated_at=datetime.utcnow()
)
conv2 = Conversation(
id="conv_2",
user_id=sample_user.id,
created_at=datetime.utcnow(),
updated_at=datetime.utcnow()
)
db.add(conv1)
db.add(conv2)
db.commit()
# Mock the agent runner
with patch('src.agents.agent_runner.AgentRunner.list_user_conversations') as mock_list:
mock_list.return_value = [
{"id": "conv_1", "created_at": "2023-01-01T00:00:00", "updated_at": "2023-01-01T00:00:00"},
{"id": "conv_2", "created_at": "2023-01-02T00:00:00", "updated_at": "2023-01-02T00:00:00"}
]
# Send a GET request to list conversations
response = test_app.get(
"/api/chat/conversations",
headers={"Authorization": f"Bearer auth_token"}
)
# Assertions
assert response.status_code == 200
response_data = response.json()
assert "conversations" in response_data
assert len(response_data["conversations"]) >= 2
finally:
db.close()
def test_get_conversation_history(test_app, sample_user, auth_token):
"""Test getting conversation history."""
db = TestingSessionLocal()
try:
db.add(sample_user)
# Create a conversation
conversation = Conversation(
id="test_conv_history",
user_id=sample_user.id,
created_at=datetime.utcnow(),
updated_at=datetime.utcnow()
)
db.add(conversation)
db.commit()
# Mock the agent runner
with patch('src.agents.agent_runner.AgentRunner.get_conversation_history') as mock_history:
mock_history.return_value = [
{
"role": "user",
"content": "Hello",
"timestamp": "2023-01-01T00:00:00",
"tool_calls": []
},
{
"role": "assistant",
"content": "Hi there!",
"timestamp": "2023-01-01T00:01:00",
"tool_calls": []
}
]
# Send a GET request to get conversation history
response = test_app.get(
f"/api/chat/conversations/test_conv_history",
headers={"Authorization": f"Bearer auth_token"}
)
# Assertions
assert response.status_code == 200
response_data = response.json()
assert "history" in response_data
assert len(response_data["history"]) == 2
assert response_data["history"][0]["role"] == "user"
assert response_data["history"][1]["role"] == "assistant"
finally:
db.close()
def test_chat_endpoint_invalid_token(test_app):
"""Test chat endpoint with invalid token."""
response = test_app.post(
"/api/chat",
headers={"Authorization": "Bearer invalid_token"},
json={
"message": "Test message"
}
)
# Should return 401 for invalid token
assert response.status_code == 401
def test_chat_endpoint_missing_message(test_app, auth_token):
"""Test chat endpoint with missing message."""
response = test_app.post(
"/api/chat",
headers={"Authorization": f"Bearer auth_token"},
json={}
)
# Should return 422 for validation error (missing required field)
assert response.status_code == 422
def test_unauthorized_access_to_conversation(test_app, sample_user):
"""Test unauthorized access to another user's conversation."""
db = TestingSessionLocal()
try:
# Add sample user to the database
db.add(sample_user)
# Create a conversation belonging to a different user
other_user_conv = Conversation(
id="other_users_conv",
user_id="different_user_123",
created_at=datetime.utcnow(),
updated_at=datetime.utcnow()
)
db.add(other_user_conv)
db.commit()
# Try to access the conversation with the sample user's token
response = test_app.get(
"/api/chat/conversations/other_users_conv",
headers={"Authorization": f"Bearer auth_token"}
)
# Should return 404 or 403 since the conversation doesn't belong to the user
assert response.status_code in [403, 404]
finally:
db.close()
if __name__ == "__main__":
pytest.main([__file__])