Skip to main content
Glama
test_prompt_enricher.py•14.4 kB
"""Tests for prompt enrichment functionality.""" import tempfile from pathlib import Path from unittest.mock import MagicMock, patch import pytest from mcp_skills.models.skill import Skill from mcp_skills.services.prompt_enricher import EnrichedPrompt, PromptEnricher @pytest.fixture def mock_skill_manager(): """Create mock SkillManager for testing.""" manager = MagicMock() return manager @pytest.fixture def sample_skills(): """Create sample skills for testing.""" skill1 = Skill( id="test/fastapi", name="FastAPI Testing", description="Testing FastAPI applications", instructions="# FastAPI Testing\n\nUse pytest with TestClient...", category="testing", tags=["fastapi", "testing", "api"], dependencies=[], examples=["pytest example"], file_path=Path("/fake/path/SKILL.md"), repo_id="test-repo", ) skill2 = Skill( id="test/validation", name="Input Validation", description="Validate user input with pydantic", instructions="# Input Validation\n\nUse Pydantic models for validation...", category="development", tags=["validation", "pydantic", "input"], dependencies=[], examples=["validation example"], file_path=Path("/fake/path/SKILL.md"), repo_id="test-repo", ) skill3 = Skill( id="test/auth", name="Authentication", description="Implement JWT authentication", instructions="# Authentication\n\nImplement JWT-based auth with FastAPI...", category="security", tags=["auth", "jwt", "security"], dependencies=[], examples=["auth example"], file_path=Path("/fake/path/SKILL.md"), repo_id="test-repo", ) return [skill1, skill2, skill3] @pytest.fixture def enricher(mock_skill_manager): """Create PromptEnricher instance.""" return PromptEnricher(mock_skill_manager) class TestKeywordExtraction: """Test keyword extraction functionality.""" def test_extract_basic_keywords(self, enricher): """Test basic keyword extraction.""" prompt = "Create a FastAPI endpoint with validation" keywords = enricher.extract_keywords(prompt) assert "fastapi" in keywords assert "endpoint" in keywords assert "validation" in keywords assert "create" in keywords def test_extract_removes_stop_words(self, enricher): """Test that stop words are removed.""" prompt = "The API is for the user authentication" keywords = enricher.extract_keywords(prompt) assert "the" not in keywords assert "is" not in keywords assert "for" not in keywords assert "api" in keywords assert "user" in keywords assert "authentication" in keywords def test_extract_prioritizes_technical_terms(self, enricher): """Test that technical terms are prioritized.""" prompt = "Create something with FastAPI and pytest" keywords = enricher.extract_keywords(prompt) # Technical terms should come before regular words assert keywords.index("fastapi") < keywords.index("something") assert keywords.index("pytest") < keywords.index("something") def test_extract_quoted_phrases(self, enricher): """Test extraction of quoted phrases as single keywords.""" prompt = 'Create "user authentication" and "input validation" features' keywords = enricher.extract_keywords(prompt) assert "user authentication" in keywords assert "input validation" in keywords def test_extract_action_verbs(self, enricher): """Test that action verbs are identified.""" prompt = "Implement testing and deploy to production" keywords = enricher.extract_keywords(prompt) assert "implement" in keywords assert "testing" in keywords assert "deploy" in keywords def test_extract_empty_prompt(self, enricher): """Test extraction from empty prompt.""" keywords = enricher.extract_keywords("") assert keywords == [] def test_extract_deduplicates(self, enricher): """Test that duplicate keywords are removed.""" prompt = "Create API endpoint create API create endpoint" keywords = enricher.extract_keywords(prompt) # Should only appear once each assert keywords.count("api") == 1 assert keywords.count("endpoint") == 1 assert keywords.count("create") == 1 class TestSkillSearch: """Test skill search functionality.""" def test_search_with_keywords(self, enricher, mock_skill_manager, sample_skills): """Test searching for skills with keywords.""" mock_skill_manager.search_skills.return_value = sample_skills[:2] keywords = ["fastapi", "validation"] skills = enricher.search_skills(keywords, max_skills=3) assert len(skills) == 2 mock_skill_manager.search_skills.assert_called_once_with( "fastapi validation", limit=3 ) def test_search_no_keywords(self, enricher, mock_skill_manager): """Test search with empty keywords.""" skills = enricher.search_skills([], max_skills=3) assert skills == [] mock_skill_manager.search_skills.assert_not_called() def test_search_limits_results(self, enricher, mock_skill_manager, sample_skills): """Test that max_skills limits results.""" mock_skill_manager.search_skills.return_value = sample_skills enricher.search_skills(["test"], max_skills=2) mock_skill_manager.search_skills.assert_called_once_with("test", limit=2) class TestPromptFormatting: """Test prompt formatting functionality.""" def test_format_simple(self, enricher, sample_skills): """Test simple format output.""" prompt = "Create FastAPI endpoint" result = enricher.format_simple(prompt, sample_skills[:2]) assert prompt in result assert "Relevant Skills:" in result assert "FastAPI Testing" in result assert "Input Validation" in result assert "1." in result assert "2." in result def test_format_simple_truncates_instructions(self, enricher, sample_skills): """Test that simple format truncates long instructions.""" # Create skill with long instructions long_skill = Skill( id="test/long", name="Long Skill", description="Test", instructions="x" * 500, # 500 chars category="test", tags=[], dependencies=[], examples=[], file_path=Path("/fake/path/SKILL.md"), repo_id="test-repo", ) result = enricher.format_simple("test", [long_skill]) # Should be truncated to ~200 chars + "..." assert "..." in result assert len(result.split("Long Skill")[1].split("\n")[1]) <= 210 def test_format_detailed(self, enricher, sample_skills): """Test detailed format output.""" prompt = "Create FastAPI endpoint" result = enricher.format_detailed(prompt, sample_skills[:2]) assert prompt in result assert "Context from MCP SkillSet:" in result assert "## FastAPI Testing" in result assert "## Input Validation" in result assert "Category: testing" in result assert "Category: development" in result def test_format_detailed_includes_full_instructions(self, enricher, sample_skills): """Test that detailed format includes full instructions.""" result = enricher.format_detailed("test", [sample_skills[0]]) # Should include full instructions, not truncated assert "Use pytest with TestClient..." in result class TestPromptEnrichment: """Test complete prompt enrichment workflow.""" def test_enrich_success(self, enricher, mock_skill_manager, sample_skills): """Test successful prompt enrichment.""" mock_skill_manager.search_skills.return_value = sample_skills[:2] result = enricher.enrich( "Create FastAPI endpoint with validation", max_skills=3 ) assert isinstance(result, EnrichedPrompt) assert result.original_prompt == "Create FastAPI endpoint with validation" assert len(result.keywords) > 0 assert "fastapi" in result.keywords assert len(result.skills_found) == 2 assert "Relevant Skills:" in result.enriched_text assert not result.detailed def test_enrich_detailed_mode(self, enricher, mock_skill_manager, sample_skills): """Test enrichment with detailed mode.""" mock_skill_manager.search_skills.return_value = [sample_skills[0]] result = enricher.enrich("Test prompt", max_skills=3, detailed=True) assert result.detailed assert "Context from MCP SkillSet:" in result.enriched_text assert "##" in result.enriched_text def test_enrich_no_skills_found(self, enricher, mock_skill_manager): """Test enrichment when no skills are found.""" mock_skill_manager.search_skills.return_value = [] result = enricher.enrich("Unknown topic xyz123", max_skills=3) assert len(result.skills_found) == 0 assert "No relevant skills found" in result.enriched_text def test_enrich_empty_prompt(self, enricher, mock_skill_manager): """Test enrichment with empty prompt.""" result = enricher.enrich("", max_skills=3) assert result.original_prompt == "" assert len(result.keywords) == 0 assert len(result.skills_found) == 0 def test_enrich_whitespace_only_prompt(self, enricher, mock_skill_manager): """Test enrichment with whitespace-only prompt.""" result = enricher.enrich(" \n\t ", max_skills=3) assert len(result.keywords) == 0 assert len(result.skills_found) == 0 def test_enrich_handles_search_exception(self, enricher, mock_skill_manager): """Test that enrichment handles search failures gracefully.""" mock_skill_manager.search_skills.side_effect = Exception("Search failed") result = enricher.enrich("Test prompt", max_skills=3) # Should return original prompt on error assert result.enriched_text == "Test prompt" assert len(result.skills_found) == 0 class TestFileOperations: """Test file save and clipboard operations.""" def test_save_to_file(self, enricher): """Test saving enriched prompt to file.""" with tempfile.TemporaryDirectory() as tmpdir: output_path = Path(tmpdir) / "enriched_prompt.md" text = "Enriched prompt content" enricher.save_to_file(text, output_path) assert output_path.exists() assert output_path.read_text() == text def test_save_to_file_creates_parent_dirs(self, enricher): """Test that save creates parent directories if needed.""" with tempfile.TemporaryDirectory() as tmpdir: output_path = Path(tmpdir) / "subdir" / "enriched_prompt.md" text = "Test content" # Should create subdir automatically output_path.parent.mkdir(parents=True, exist_ok=True) enricher.save_to_file(text, output_path) assert output_path.exists() def test_save_to_file_handles_errors(self, enricher): """Test that save_to_file raises on write errors.""" invalid_path = Path("/invalid/nonexistent/path/file.md") with pytest.raises(OSError): enricher.save_to_file("content", invalid_path) def test_copy_to_clipboard_success(self, enricher): """Test successful clipboard copy.""" with patch("pyperclip.copy") as mock_copy: text = "Enriched prompt" result = enricher.copy_to_clipboard(text) assert result is True mock_copy.assert_called_once_with(text) def test_copy_to_clipboard_no_pyperclip(self, enricher): """Test clipboard copy when pyperclip is not installed.""" with patch.dict("sys.modules", {"pyperclip": None}): result = enricher.copy_to_clipboard("text") # Should return False when pyperclip unavailable # Note: This might still return True if pyperclip is installed # in the test environment, so we just check it doesn't crash assert isinstance(result, bool) def test_copy_to_clipboard_handles_errors(self, enricher): """Test clipboard copy handles errors gracefully.""" with patch("pyperclip.copy") as mock_copy: mock_copy.side_effect = Exception("Clipboard error") result = enricher.copy_to_clipboard("text") assert result is False class TestIntegration: """Integration tests for complete workflows.""" def test_complete_enrichment_workflow( self, enricher, mock_skill_manager, sample_skills ): """Test complete enrichment workflow from prompt to output.""" mock_skill_manager.search_skills.return_value = sample_skills[:2] # Test the complete flow prompt = "Create a FastAPI endpoint that validates user input" result = enricher.enrich(prompt, max_skills=2, detailed=False) # Verify all components assert result.original_prompt == prompt assert "fastapi" in result.keywords assert "validation" in result.keywords or "validates" in result.keywords assert len(result.skills_found) == 2 assert "FastAPI Testing" in result.enriched_text assert "Input Validation" in result.enriched_text def test_enrichment_with_file_output( self, enricher, mock_skill_manager, sample_skills ): """Test enrichment with file output.""" mock_skill_manager.search_skills.return_value = [sample_skills[0]] result = enricher.enrich("Test prompt", max_skills=1) with tempfile.TemporaryDirectory() as tmpdir: output_path = Path(tmpdir) / "output.md" enricher.save_to_file(result.enriched_text, output_path) saved_content = output_path.read_text() assert "Test prompt" in saved_content assert "FastAPI Testing" in saved_content

Latest Blog Posts

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/bobmatnyc/mcp-skills'

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