FastMCP Todo Server

  • tests
# Set test environment variables before importing import os # Test configuration TEST_MONGODB_URI = "mongodb://localhost:27017" TEST_MONGODB_DB = "test_swarmonomicon" TEST_MONGODB_COLLECTION = "test_todos" # Set environment variables os.environ["MONGODB_URI"] = TEST_MONGODB_URI os.environ["MONGODB_DB"] = TEST_MONGODB_DB os.environ["MONGODB_COLLECTION"] = TEST_MONGODB_COLLECTION # Import everything else after setting environment variables import pytest from unittest.mock import MagicMock, AsyncMock from src.fastmcp_todo_server import TodoServer import json from pymongo import MongoClient from datetime import datetime, UTC @pytest.fixture def todo_server(): # Mock MongoDB client for unit tests server = TodoServer() server.mongo_client = MagicMock() server.db = MagicMock() server.collection = MagicMock() return server @pytest.fixture def integration_todo_server(): # Create MongoDB client and clean up any existing data client = MongoClient(TEST_MONGODB_URI) db = client[TEST_MONGODB_DB] # Drop the collection if it exists if TEST_MONGODB_COLLECTION in db.list_collection_names(): db[TEST_MONGODB_COLLECTION].drop() # Create a new collection without any unique indexes collection = db.create_collection(TEST_MONGODB_COLLECTION) # Close the client client.close() # Create server with real MongoDB connection return TodoServer() @pytest.fixture def mongo_client(): # Create a MongoDB client for verification client = MongoClient(TEST_MONGODB_URI) yield client # Cleanup: Drop test database after tests client.drop_database(TEST_MONGODB_DB) client.close() # Unit Tests @pytest.mark.asyncio async def test_add_todo_success(todo_server): # Test data description = "Test todo" priority = "high" target_agent = "test_agent" # Call handler through FastMCP response = await todo_server.server.call_tool( "add_todo", { "description": description, "priority": priority, "target_agent": target_agent } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "success" assert "todo_id" in result # Verify MongoDB interaction todo_server.collection.insert_one.assert_called_once() @pytest.mark.asyncio async def test_add_todo_with_defaults(todo_server): # Test with minimal data description = "Test todo" # Call handler through FastMCP response = await todo_server.server.call_tool( "add_todo", { "description": description } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "success" assert "todo_id" in result # Verify MongoDB interaction todo_server.collection.insert_one.assert_called_once() @pytest.mark.asyncio async def test_add_todo_with_error(todo_server): # Test data description = "Test todo" error_message = "Database error" # Mock error response todo_server.collection.insert_one.side_effect = Exception(error_message) # Call handler through FastMCP response = await todo_server.server.call_tool( "add_todo", { "description": description } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "error" assert result["message"] == error_message @pytest.mark.asyncio async def test_delete_todo_success(todo_server): # Mock a todo in the database todo_id = "test_id" todo_server.collection.delete_one.return_value = MagicMock(deleted_count=1) # Call handler through FastMCP response = await todo_server.server.call_tool( "delete_todo", { "todo_id": todo_id } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "success" # Verify MongoDB interaction todo_server.collection.delete_one.assert_called_once_with({"id": todo_id}) @pytest.mark.asyncio async def test_delete_todo_not_found(todo_server): # Mock a todo not found in the database todo_id = "test_id" todo_server.collection.delete_one.return_value = MagicMock(deleted_count=0) # Call handler through FastMCP response = await todo_server.server.call_tool( "delete_todo", { "todo_id": todo_id } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "error" assert result["message"] == "Todo not found" # Verify MongoDB interaction todo_server.collection.delete_one.assert_called_once_with({"id": todo_id}) @pytest.mark.asyncio async def test_get_todo_success(todo_server): # Mock a todo in the database todo_id = "test_id" todo = { "id": todo_id, "description": "Test todo", "priority": "high", "source_agent": "fastmcp", "target_agent": "test_agent", "status": "pending", "created_at": 1234567890, "completed_at": None } todo_server.collection.find_one.return_value = todo # Call handler through FastMCP response = await todo_server.server.call_tool( "get_todo", { "todo_id": todo_id } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "success" assert result["todo"] == todo # Verify MongoDB interaction todo_server.collection.find_one.assert_called_once_with({"id": todo_id}) @pytest.mark.asyncio async def test_get_todo_not_found(todo_server): # Mock a todo not found in the database todo_id = "test_id" todo_server.collection.find_one.return_value = None # Call handler through FastMCP response = await todo_server.server.call_tool( "get_todo", { "todo_id": todo_id } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "error" assert result["message"] == "Todo not found" # Verify MongoDB interaction todo_server.collection.find_one.assert_called_once_with({"id": todo_id}) @pytest.mark.asyncio async def test_mark_todo_complete_success(todo_server): # Mock a todo in the database todo_id = "test_id" todo_server.collection.update_one.return_value = MagicMock(modified_count=1) # Call handler through FastMCP response = await todo_server.server.call_tool( "mark_todo_complete", { "todo_id": todo_id } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "success" # Verify MongoDB interaction todo_server.collection.update_one.assert_called_once_with( {"id": todo_id}, {"$set": {"status": "completed", "completed_at": int(datetime.now(UTC).timestamp())}} ) @pytest.mark.asyncio async def test_mark_todo_complete_not_found(todo_server): # Mock a todo not found in the database todo_id = "test_id" todo_server.collection.update_one.return_value = MagicMock(modified_count=0) # Call handler through FastMCP response = await todo_server.server.call_tool( "mark_todo_complete", { "todo_id": todo_id } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "error" assert result["message"] == "Todo not found" # Verify MongoDB interaction todo_server.collection.update_one.assert_called_once_with( {"id": todo_id}, {"$set": {"status": "completed", "completed_at": int(datetime.now(UTC).timestamp())}} ) @pytest.mark.asyncio async def test_list_todos_by_status_success(todo_server): # Mock todos in the database todos = [ { "id": "test_id_1", "description": "Test todo 1", "priority": "high", "source_agent": "fastmcp", "target_agent": "test_agent", "status": "pending", "created_at": 1234567890, "completed_at": None }, { "id": "test_id_2", "description": "Test todo 2", "priority": "medium", "source_agent": "fastmcp", "target_agent": "test_agent", "status": "pending", "created_at": 1234567891, "completed_at": None } ] todo_server.collection.find.return_value = todos # Call handler through FastMCP response = await todo_server.server.call_tool( "list_todos_by_status", { "status": "pending" } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "success" assert result["todos"] == todos # Verify MongoDB interaction todo_server.collection.find.assert_called_once_with({"status": "pending"}, limit=100) @pytest.mark.asyncio async def test_list_todos_by_status_no_results(todo_server): # Mock no todos in the database todo_server.collection.find.return_value = [] # Call handler through FastMCP response = await todo_server.server.call_tool( "list_todos_by_status", { "status": "completed" } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "success" assert result["todos"] == [] # Verify MongoDB interaction todo_server.collection.find.assert_called_once_with({"status": "completed"}, limit=100) # Unit Tests for Lessons Learned @pytest.mark.asyncio async def test_add_lesson_success(todo_server): # Test data language = "Python" topic = "Testing" lesson_learned = "Always write tests for your code." tags = ["testing", "best practices"] # Call handler through FastMCP response = await todo_server.server.call_tool( "add_lesson", { "language": language, "topic": topic, "lesson_learned": lesson_learned, "tags": tags } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "success" assert "lesson_id" in result # Verify MongoDB interaction todo_server.lessons_collection.insert_one.assert_called_once() @pytest.mark.asyncio async def test_get_lesson_success(todo_server): # Mock a lesson in the database lesson_id = "lesson_id_1" lesson = { "id": lesson_id, "language": "Python", "topic": "Testing", "lesson_learned": "Always write tests for your code.", "tags": ["testing", "best practices"], "created_at": 1234567890 } todo_server.lessons_collection.find_one.return_value = lesson # Call handler through FastMCP response = await todo_server.server.call_tool( "get_lesson", { "lesson_id": lesson_id } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "success" assert result["lesson"] == lesson # Verify MongoDB interaction todo_server.lessons_collection.find_one.assert_called_once_with({"id": lesson_id}) @pytest.mark.asyncio async def test_update_lesson_success(todo_server): # Mock a lesson in the database lesson_id = "lesson_id_1" todo_server.lessons_collection.update_one.return_value = MagicMock(modified_count=1) # Call handler through FastMCP response = await todo_server.server.call_tool( "update_lesson", { "lesson_id": lesson_id, "updates": {"lesson_learned": "Updated lesson learned."} } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "success" # Verify MongoDB interaction todo_server.lessons_collection.update_one.assert_called_once_with( {"id": lesson_id}, {"$set": {"lesson_learned": "Updated lesson learned."}} ) @pytest.mark.asyncio async def test_delete_lesson_success(todo_server): # Mock a lesson in the database lesson_id = "lesson_id_1" todo_server.lessons_collection.delete_one.return_value = MagicMock(deleted_count=1) # Call handler through FastMCP response = await todo_server.server.call_tool( "delete_lesson", { "lesson_id": lesson_id } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "success" # Verify MongoDB interaction todo_server.lessons_collection.delete_one.assert_called_once_with({"id": lesson_id}) @pytest.mark.asyncio async def test_list_lessons_success(todo_server): # Mock lessons in the database lessons = [ { "id": "lesson_id_1", "language": "Python", "topic": "Testing", "lesson_learned": "Always write tests for your code.", "tags": ["testing", "best practices"], "created_at": 1234567890 }, { "id": "lesson_id_2", "language": "JavaScript", "topic": "Async Programming", "lesson_learned": "Use promises for better readability.", "tags": ["async", "promises"], "created_at": 1234567891 } ] todo_server.lessons_collection.find.return_value = lessons # Call handler through FastMCP response = await todo_server.server.call_tool( "list_lessons", { "limit": 100 } ) # Parse response result = json.loads(response[0].text) assert result["status"] == "success" assert result["lessons"] == lessons # Verify MongoDB interaction todo_server.lessons_collection.find.assert_called_once_with(limit=100) # Integration Tests @pytest.mark.asyncio async def test_integration_add_todo(integration_todo_server): # Test data description = "Integration test todo" priority = "high" target_agent = "test_agent" # Call handler through FastMCP response = await integration_todo_server.server.call_tool( "add_todo", { "description": description, "priority": priority, "target_agent": target_agent } ) # Parse response result = json.loads(response[0].text) if result["status"] == "error": print(f"Error in test_integration_add_todo: {result['message']}") assert result["status"] == "success" todo_id = result["todo_id"] # Verify the todo was actually inserted in MongoDB client = MongoClient(TEST_MONGODB_URI) db = client[TEST_MONGODB_DB] collection = db[TEST_MONGODB_COLLECTION] todo = collection.find_one({"id": todo_id}) client.close() # Debugging output print(f"Expected: {description}, {priority}, {target_agent}") print(f"Actual: {todo['description']}, {todo['priority']}, {todo['target_agent']}") assert todo is not None assert todo["description"] == description assert todo["priority"] == priority assert todo["target_agent"] == target_agent assert todo["status"] == "pending" # Optionally, you can assert that the _id is present assert "_id" in todo @pytest.mark.asyncio async def test_integration_add_todo_with_defaults(integration_todo_server): # Test with minimal data description = "Integration test todo with defaults" # Call handler through FastMCP response = await integration_todo_server.server.call_tool( "add_todo", { "description": description } ) # Parse response result = json.loads(response[0].text) if result["status"] == "error": print(f"Error in test_integration_add_todo_with_defaults: {result['message']}") assert result["status"] == "success" todo_id = result["todo_id"] # Verify the todo was actually inserted in MongoDB with default values client = MongoClient(TEST_MONGODB_URI) db = client[TEST_MONGODB_DB] collection = db[TEST_MONGODB_COLLECTION] todo = collection.find_one({"id": todo_id}) client.close() assert todo is not None assert todo["description"] == description assert todo["priority"] == "inital" # Default value assert todo["target_agent"] == "user" # Default value assert todo["status"] == "pending" @pytest.mark.asyncio async def test_integration_delete_todo(integration_todo_server, mongo_client): # Add a todo to the database todo_id = "integration_test_id" todo = { "id": todo_id, "description": "Integration test todo", "priority": "high", "source_agent": "fastmcp", "target_agent": "test_agent", "status": "pending", "created_at": int(datetime.now(UTC).timestamp()), "completed_at": None } db = mongo_client[TEST_MONGODB_DB] collection = db[TEST_MONGODB_COLLECTION] collection.insert_one(todo) # Call handler through FastMCP response = await integration_todo_server.server.call_tool( "delete_todo", { "todo_id": todo_id } ) # Parse response result = json.loads(response[0].text) if result["status"] == "error": print(f"Error in test_integration_delete_todo: {result['message']}") assert result["status"] == "success" # Verify the todo was actually deleted from MongoDB todo_after_delete = collection.find_one({"id": todo_id}) assert todo_after_delete is None @pytest.mark.asyncio async def test_integration_get_todo(integration_todo_server, mongo_client): # Add a todo to the database todo_id = "integration_test_id" todo = { "id": todo_id, "description": "Integration test todo", "priority": "high", "source_agent": "fastmcp", "target_agent": "test_agent", "status": "pending", "created_at": int(datetime.now(UTC).timestamp()), "completed_at": None } db = mongo_client[TEST_MONGODB_DB] collection = db[TEST_MONGODB_COLLECTION] collection.insert_one(todo) # Call handler through FastMCP response = await integration_todo_server.server.call_tool( "get_todo", { "todo_id": todo_id } ) # Parse response result = json.loads(response[0].text) if result["status"] == "error": print(f"Error in test_integration_get_todo: {result['message']}") assert result["status"] == "success" assert result["todo"]["id"] == todo["id"] assert result["todo"]["description"] == todo["description"] assert result["todo"]["priority"] == todo["priority"] assert result["todo"]["source_agent"] == todo["source_agent"] assert result["todo"]["target_agent"] == todo["target_agent"] assert result["todo"]["status"] == todo["status"] assert result["todo"]["created_at"] == todo["created_at"] assert result["todo"]["completed_at"] == todo["completed_at"] @pytest.mark.asyncio async def test_integration_mark_todo_complete(integration_todo_server, mongo_client): # Add a todo to the database todo_id = "integration_test_id" todo = { "id": todo_id, "description": "Integration test todo", "priority": "high", "source_agent": "fastmcp", "target_agent": "test_agent", "status": "pending", "created_at": int(datetime.now(UTC).timestamp()), "completed_at": None } db = mongo_client[TEST_MONGODB_DB] collection = db[TEST_MONGODB_COLLECTION] collection.insert_one(todo) # Call handler through FastMCP response = await integration_todo_server.server.call_tool( "mark_todo_complete", { "todo_id": todo_id } ) # Parse response result = json.loads(response[0].text) if result["status"] == "error": print(f"Error in test_integration_mark_todo_complete: {result['message']}") assert result["status"] == "success" # Verify the todo was actually marked as completed in MongoDB updated_todo = collection.find_one({"id": todo_id}) assert updated_todo["status"] == "completed" assert updated_todo["completed_at"] is not None @pytest.mark.asyncio async def test_integration_list_todos_by_status(integration_todo_server, mongo_client): # Add todos to the database todos = [ { "id": "integration_test_id_1", "description": "Integration test todo 1", "priority": "high", "source_agent": "fastmcp", "target_agent": "test_agent", "status": "pending", "created_at": int(datetime.now(UTC).timestamp()), "completed_at": None }, { "id": "integration_test_id_2", "description": "Integration test todo 2", "priority": "medium", "source_agent": "fastmcp", "target_agent": "test_agent", "status": "pending", "created_at": int(datetime.now(UTC).timestamp()), "completed_at": None } ] db = mongo_client[TEST_MONGODB_DB] collection = db[TEST_MONGODB_COLLECTION] collection.insert_many(todos) # Call handler through FastMCP response = await integration_todo_server.server.call_tool( "list_todos_by_status", { "status": "pending" } ) # Parse response result = json.loads(response[0].text) if result["status"] == "error": print(f"Error in test_integration_list_todos_by_status: {result['message']}") assert result["status"] == "success" assert len(result["todos"]) == 2 assert result["todos"][0]["id"] == "integration_test_id_1" assert result["todos"][1]["id"] == "integration_test_id_2"