Skip to main content
Glama

OpenEdu MCP Server

test_full_server.pyโ€ข23.9 kB
""" Comprehensive Integration Tests for Education MCP Server This module provides end-to-end testing for the complete Education MCP Server, testing all API integrations working together and cross-API educational workflows. """ import asyncio import pytest import sys from pathlib import Path from typing import Dict, Any, List from unittest.mock import AsyncMock, patch, MagicMock # Add src to path for imports sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src")) from main import ( initialize_services, cleanup_services, mcp, search_educational_books, get_book_details_by_isbn, search_books_by_subject, get_book_recommendations, search_educational_articles, get_article_summary, get_article_content, get_featured_article, get_articles_by_subject, get_word_definition, get_vocabulary_analysis, get_word_examples, get_pronunciation_guide, get_related_vocabulary, search_academic_papers, get_paper_summary, get_recent_research, get_research_by_level, analyze_research_trends, get_server_status ) from config import load_config from exceptions import OpenEdMCPError class MockContext: """Mock context for testing MCP tools.""" def __init__(self, session_id: str = "test_session"): self.session_id = session_id @pytest.fixture async def server_setup(): """Set up the server for testing.""" # Mock external dependencies with patch('aiohttp.ClientSession') as mock_session: mock_response = AsyncMock() mock_response.status = 200 mock_response.json = AsyncMock() mock_response.text = AsyncMock() mock_session.return_value.__aenter__.return_value.get.return_value.__aenter__.return_value = mock_response mock_session.return_value.__aenter__.return_value.post.return_value.__aenter__.return_value = mock_response # Initialize services await initialize_services() yield # Cleanup await cleanup_services() class TestFullServerIntegration: """Test complete server integration and cross-API workflows.""" @pytest.mark.asyncio async def test_server_initialization(self, server_setup): """Test that all server services initialize correctly.""" ctx = MockContext() # Test server status status = await get_server_status(ctx) assert status["status"] == "healthy" assert "server" in status assert "cache" in status assert "rate_limits" in status assert "usage" in status assert status["server"]["name"] == "openedu-mcp-server" @pytest.mark.asyncio async def test_all_tools_registered(self, server_setup): """Test that all 20 MCP tools are properly registered.""" # Get all registered tools tools = await mcp.list_tools() tool_names = [tool.name for tool in tools] # Expected tools from all APIs expected_tools = [ # Open Library tools (4) "search_educational_books", "get_book_details_by_isbn", "search_books_by_subject", "get_book_recommendations", # Wikipedia tools (5) "search_educational_articles", "get_article_summary", "get_article_content", "get_featured_article", "get_articles_by_subject", # Dictionary tools (5) "get_word_definition", "get_vocabulary_analysis", "get_word_examples", "get_pronunciation_guide", "get_related_vocabulary", # arXiv tools (5) "search_academic_papers", "get_paper_summary", "get_recent_research", "get_research_by_level", "analyze_research_trends", # Server tool (1) "get_server_status" ] # Verify all tools are registered for tool_name in expected_tools: assert tool_name in tool_names, f"Tool {tool_name} not registered" assert len(tool_names) >= 20, f"Expected at least 20 tools, got {len(tool_names)}" @pytest.mark.asyncio async def test_elementary_education_workflow(self, server_setup): """Test complete elementary education workflow (K-2).""" ctx = MockContext() # Mock responses for elementary workflow with patch('src.api.openlibrary.OpenLibraryAPI.search_books') as mock_books, \ patch('src.api.dictionary.DictionaryAPI.get_definition') as mock_definition, \ patch('src.api.wikipedia.WikipediaAPI.search_articles') as mock_articles: # Mock book search for K-2 mock_books.return_value = [{ "title": "The Very Hungry Caterpillar", "author": "Eric Carle", "grade_level": "K-2", "subject": "Science", "educational_value": 0.9, "reading_level": "Beginning Reader" }] # Mock simple definition for young learners mock_definition.return_value = { "word": "caterpillar", "definition": "A small creature that turns into a butterfly", "grade_level": "K-2", "complexity_score": 0.3, "educational_context": "Science - Life Cycles" } # Mock age-appropriate articles mock_articles.return_value = [{ "title": "Butterfly Life Cycle", "summary": "Learn how caterpillars become butterflies", "grade_level": "K-2", "educational_value": 0.8, "subject": "Science" }] # 1. Search for K-2 appropriate books books = await search_educational_books( ctx, query="caterpillar", grade_level="K-2", subject="Science" ) assert len(books) > 0 assert books[0]["grade_level"] == "K-2" # 2. Get simple definitions for vocabulary definition = await get_word_definition( ctx, word="caterpillar", grade_level="K-2" ) assert definition["grade_level"] == "K-2" assert definition["complexity_score"] <= 0.5 # 3. Find age-appropriate Wikipedia articles articles = await search_educational_articles( ctx, query="butterfly life cycle", grade_level="K-2", subject="Science" ) assert len(articles) > 0 assert articles[0]["grade_level"] == "K-2" @pytest.mark.asyncio async def test_high_school_stem_workflow(self, server_setup): """Test high school STEM education workflow (9-12).""" ctx = MockContext() with patch('src.api.openlibrary.OpenLibraryAPI.search_books') as mock_books, \ patch('src.api.dictionary.DictionaryAPI.get_definition') as mock_definition, \ patch('src.api.wikipedia.WikipediaAPI.search_articles') as mock_articles, \ patch('src.api.arxiv.ArxivAPI.search_papers') as mock_papers: # Mock high school science books mock_books.return_value = [{ "title": "Physics: Principles and Problems", "author": "McGraw-Hill", "grade_level": "9-12", "subject": "Physics", "educational_value": 0.95, "curriculum_alignment": ["NGSS"] }] # Mock technical definitions mock_definition.return_value = { "word": "quantum", "definition": "The smallest possible discrete unit of any physical property", "grade_level": "9-12", "complexity_score": 0.8, "examples": ["quantum mechanics", "quantum physics"] } # Mock educational articles mock_articles.return_value = [{ "title": "Quantum Mechanics", "summary": "Introduction to quantum mechanics principles", "grade_level": "9-12", "subject": "Physics", "educational_value": 0.9 }] # Mock accessible research papers mock_papers.return_value = [{ "title": "Introduction to Quantum Computing", "abstract": "A beginner-friendly overview of quantum computing", "academic_level": "High School", "educational_relevance": 0.85, "subject": "Physics" }] # 1. Search for 9-12 science books books = await search_educational_books( ctx, query="physics", grade_level="9-12", subject="Science" ) assert len(books) > 0 assert books[0]["grade_level"] == "9-12" # 2. Get technical definitions with examples definition = await get_word_definition( ctx, word="quantum", grade_level="9-12" ) assert definition["grade_level"] == "9-12" assert definition["complexity_score"] >= 0.7 # 3. Find educational Wikipedia articles on STEM topics articles = await search_educational_articles( ctx, query="quantum mechanics", grade_level="9-12", subject="Physics" ) assert len(articles) > 0 # 4. Search for accessible research papers papers = await search_academic_papers( ctx, query="quantum computing", academic_level="High School", subject="Physics" ) assert len(papers) > 0 assert papers[0]["academic_level"] == "High School" @pytest.mark.asyncio async def test_college_research_workflow(self, server_setup): """Test college-level research workflow.""" ctx = MockContext() with patch('src.api.openlibrary.OpenLibraryAPI.search_books') as mock_books, \ patch('src.api.dictionary.DictionaryAPI.get_definition') as mock_definition, \ patch('src.api.wikipedia.WikipediaAPI.get_article') as mock_article, \ patch('src.api.arxiv.ArxivAPI.search_papers') as mock_papers: # Mock academic books mock_books.return_value = [{ "title": "Advanced Calculus", "author": "Academic Press", "grade_level": "College", "subject": "Mathematics", "educational_value": 0.98, "type": "textbook" }] # Mock comprehensive definitions mock_definition.return_value = { "word": "derivative", "definition": "The rate of change of a function with respect to its variable", "grade_level": "College", "complexity_score": 0.9, "etymology": "From Latin derivatus", "related_terms": ["integral", "limit", "calculus"] } # Mock detailed articles mock_article.return_value = { "title": "Calculus", "content": "Detailed mathematical content...", "grade_level": "College", "subject": "Mathematics", "educational_value": 0.95 } # Mock recent research papers mock_papers.return_value = [{ "title": "Recent Advances in Differential Equations", "abstract": "This paper presents new methods...", "academic_level": "Graduate", "publication_date": "2024-01-15", "subject": "Mathematics" }] # 1. Search for academic books and textbooks books = await search_educational_books( ctx, query="calculus textbook", grade_level="College", subject="Mathematics" ) assert len(books) > 0 assert books[0]["grade_level"] == "College" # 2. Get comprehensive definitions and etymology definition = await get_word_definition( ctx, word="derivative", grade_level="College" ) assert definition["grade_level"] == "College" assert "etymology" in definition # 3. Find detailed Wikipedia articles article = await get_article_content( ctx, title="Calculus", include_images=True ) assert "content" in article # 4. Search for recent research papers by subject papers = await search_academic_papers( ctx, query="differential equations", academic_level="Graduate", subject="Mathematics" ) assert len(papers) > 0 @pytest.mark.asyncio async def test_educator_resource_workflow(self, server_setup): """Test educator resource discovery workflow.""" ctx = MockContext() with patch('src.api.openlibrary.OpenLibraryAPI.search_books') as mock_books, \ patch('src.api.dictionary.DictionaryAPI.get_examples') as mock_examples, \ patch('src.api.wikipedia.WikipediaAPI.search_articles') as mock_articles, \ patch('src.api.arxiv.ArxivAPI.get_recent_papers') as mock_recent: # Mock curriculum-aligned books mock_books.return_value = [{ "title": "Teaching Mathematics Effectively", "grade_level": "6-8", "subject": "Mathematics", "curriculum_alignment": ["Common Core"], "educational_value": 0.92, "teacher_resource": True }] # Mock vocabulary for lesson planning mock_examples.return_value = { "word": "fraction", "examples": [ "1/2 of a pizza", "3/4 of the students", "2/3 cup of flour" ], "grade_level": "6-8", "subject_contexts": ["Mathematics", "Cooking", "Science"] } # Mock educational articles for teaching materials mock_articles.return_value = [{ "title": "Fraction Concepts for Middle School", "summary": "Teaching strategies for fractions", "grade_level": "6-8", "subject": "Mathematics", "teacher_resource": True }] # Mock research for professional development mock_recent.return_value = [{ "title": "Effective Mathematics Pedagogy", "abstract": "Research on teaching mathematics", "academic_level": "Research", "subject": "Education", "relevance_to_teaching": 0.95 }] # 1. Search for curriculum-aligned books books = await search_books_by_subject( ctx, subject="Mathematics", grade_level="6-8" ) assert len(books) > 0 assert "Common Core" in books[0].get("curriculum_alignment", []) # 2. Get vocabulary for lesson planning examples = await get_word_examples( ctx, word="fraction", grade_level="6-8", subject="Mathematics" ) assert len(examples["examples"]) > 0 # 3. Find educational articles for teaching materials articles = await get_articles_by_subject( ctx, subject="Mathematics", grade_level="6-8" ) assert len(articles) > 0 # 4. Get research papers for professional development research = await get_recent_research( ctx, subject="Education", academic_level="Research" ) assert len(research) > 0 @pytest.mark.asyncio async def test_cross_api_educational_filtering(self, server_setup): """Test that educational filtering works consistently across all APIs.""" ctx = MockContext() with patch('src.api.openlibrary.OpenLibraryAPI.search_books') as mock_books, \ patch('src.api.wikipedia.WikipediaAPI.search_articles') as mock_articles, \ patch('src.api.dictionary.DictionaryAPI.get_definition') as mock_definition, \ patch('src.api.arxiv.ArxivAPI.search_papers') as mock_papers: # Mock responses with consistent educational metadata mock_books.return_value = [{ "title": "Elementary Science", "grade_level": "3-5", "educational_value": 0.85, "subject": "Science" }] mock_articles.return_value = [{ "title": "Plants for Kids", "grade_level": "3-5", "educational_value": 0.82, "subject": "Science" }] mock_definition.return_value = { "word": "photosynthesis", "grade_level": "3-5", "complexity_score": 0.6, "subject": "Science" } mock_papers.return_value = [{ "title": "Plant Biology Education", "academic_level": "Undergraduate", "educational_relevance": 0.88, "subject": "Science" }] # Test grade level filtering across APIs books = await search_educational_books(ctx, "plants", grade_level="3-5") articles = await search_educational_articles(ctx, "plants", grade_level="3-5") definition = await get_word_definition(ctx, "photosynthesis", grade_level="3-5") papers = await search_academic_papers(ctx, "plant biology", academic_level="Undergraduate") # Verify consistent grade level filtering assert books[0]["grade_level"] == "3-5" assert articles[0]["grade_level"] == "3-5" assert definition["grade_level"] == "3-5" assert papers[0]["academic_level"] == "Undergraduate" # Verify educational value thresholds assert books[0]["educational_value"] >= 0.7 assert articles[0]["educational_value"] >= 0.7 assert papers[0]["educational_relevance"] >= 0.7 @pytest.mark.asyncio async def test_caching_and_rate_limiting(self, server_setup): """Test caching effectiveness and rate limiting across all services.""" ctx = MockContext() with patch('src.api.openlibrary.OpenLibraryAPI.search_books') as mock_books: mock_books.return_value = [{"title": "Test Book"}] # First call - should hit API result1 = await search_educational_books(ctx, "test query") # Second call - should hit cache result2 = await search_educational_books(ctx, "test query") # Verify results are consistent assert result1 == result2 # Verify API was called only once (second call used cache) assert mock_books.call_count == 1 @pytest.mark.asyncio async def test_error_handling_across_services(self, server_setup): """Test error handling consistency across all services.""" ctx = MockContext() # Test with invalid parameters with pytest.raises(OpenEdMCPError): await search_educational_books(ctx, "") # Empty query with pytest.raises(OpenEdMCPError): await get_book_details_by_isbn(ctx, "invalid-isbn") with pytest.raises(OpenEdMCPError): await get_word_definition(ctx, "") # Empty word with pytest.raises(OpenEdMCPError): await search_academic_papers(ctx, "", max_results=0) # Invalid limit @pytest.mark.asyncio async def test_educational_metadata_enrichment(self, server_setup): """Test that educational metadata is properly enriched across all APIs.""" ctx = MockContext() with patch('src.api.openlibrary.OpenLibraryAPI.search_books') as mock_books, \ patch('src.api.wikipedia.WikipediaAPI.get_article') as mock_article, \ patch('src.api.dictionary.DictionaryAPI.get_definition') as mock_definition, \ patch('src.api.arxiv.ArxivAPI.get_paper') as mock_paper: # Mock responses with educational enrichment mock_books.return_value = [{ "title": "Math Concepts", "educational_metadata": { "grade_level": "6-8", "subject": "Mathematics", "curriculum_alignment": ["Common Core"], "reading_level": "Grade 7", "educational_value": 0.9 } }] mock_article.return_value = { "title": "Algebra", "educational_analysis": { "complexity_score": 0.7, "grade_level": "6-8", "key_concepts": ["variables", "equations"], "prerequisite_knowledge": ["arithmetic"] } } mock_definition.return_value = { "word": "variable", "educational_context": { "grade_level": "6-8", "subject_applications": ["Mathematics", "Science"], "complexity_progression": ["simple", "intermediate"] } } mock_paper.return_value = { "title": "Algebra Education Research", "educational_relevance": { "academic_level": "Graduate", "teaching_applications": 0.85, "classroom_relevance": "High" } } # Test educational metadata enrichment books = await search_educational_books(ctx, "math") article = await get_article_content(ctx, "Algebra") definition = await get_word_definition(ctx, "variable") paper = await get_paper_summary(ctx, "2301.00001") # Verify educational metadata is present assert "educational_metadata" in books[0] assert "educational_analysis" in article assert "educational_context" in definition assert "educational_relevance" in paper if __name__ == "__main__": pytest.main([__file__, "-v"])

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Cicatriiz/openedu-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server