1+ #!/usr/bin/env python3
2+ """
3+ Unit tests for Module 1: Basic MCP Server
4+ Run these tests to validate your implementation
5+ """
6+
7+ import json
8+ import pytest
9+ import asyncio
10+ from pathlib import Path
11+ from unittest .mock import patch , MagicMock
12+
13+ # Import your implemented functions
14+ try :
15+ from server import (
16+ mcp ,
17+ analyze_file_changes ,
18+ get_pr_templates ,
19+ suggest_template
20+ )
21+ IMPORTS_SUCCESSFUL = True
22+ except ImportError as e :
23+ IMPORTS_SUCCESSFUL = False
24+ IMPORT_ERROR = str (e )
25+
26+
27+ class TestImplementation :
28+ """Test that the required functions are implemented."""
29+
30+ def test_imports (self ):
31+ """Test that all required functions can be imported."""
32+ assert IMPORTS_SUCCESSFUL , f"Failed to import required functions: { IMPORT_ERROR if not IMPORTS_SUCCESSFUL else '' } "
33+ assert mcp is not None , "FastMCP server instance not found"
34+ assert callable (analyze_file_changes ), "analyze_file_changes should be a callable function"
35+ assert callable (get_pr_templates ), "get_pr_templates should be a callable function"
36+ assert callable (suggest_template ), "suggest_template should be a callable function"
37+
38+
39+ @pytest .mark .skipif (not IMPORTS_SUCCESSFUL , reason = "Imports failed" )
40+ class TestAnalyzeFileChanges :
41+ """Test the analyze_file_changes tool."""
42+
43+ @pytest .mark .asyncio
44+ async def test_returns_json_string (self ):
45+ """Test that analyze_file_changes returns a JSON string."""
46+ with patch ('subprocess.run' ) as mock_run :
47+ mock_run .return_value = MagicMock (stdout = "" , stderr = "" )
48+
49+ result = await analyze_file_changes ()
50+
51+ assert isinstance (result , str ), "Should return a string"
52+ # Should be valid JSON
53+ data = json .loads (result )
54+ assert isinstance (data , dict ), "Should return a JSON object"
55+
56+ @pytest .mark .asyncio
57+ async def test_includes_required_fields (self ):
58+ """Test that the result includes expected fields."""
59+ with patch ('subprocess.run' ) as mock_run :
60+ mock_run .return_value = MagicMock (stdout = "M\t file1.py\n " , stderr = "" )
61+
62+ result = await analyze_file_changes ()
63+ data = json .loads (result )
64+
65+ # For starter code, accept error messages; for full implementation, expect data
66+ is_implemented = not ("error" in data and "Not implemented" in str (data .get ("error" , "" )))
67+ if is_implemented :
68+ # Check for some expected fields (flexible to allow different implementations)
69+ assert any (key in data for key in ["files_changed" , "files" , "changes" , "diff" ]), \
70+ "Result should include file change information"
71+ else :
72+ # Starter code - just verify it returns something structured
73+ assert isinstance (data , dict ), "Should return a JSON object even if not implemented"
74+
75+
76+ @pytest .mark .skipif (not IMPORTS_SUCCESSFUL , reason = "Imports failed" )
77+ class TestGetPRTemplates :
78+ """Test the get_pr_templates tool."""
79+
80+ @pytest .mark .asyncio
81+ async def test_returns_json_string (self ):
82+ """Test that get_pr_templates returns a JSON string."""
83+ result = await get_pr_templates ()
84+
85+ assert isinstance (result , str ), "Should return a string"
86+ # Should be valid JSON
87+ data = json .loads (result )
88+
89+ # For starter code, accept error messages; for full implementation, expect list
90+ is_implemented = not ("error" in data and isinstance (data , dict ))
91+ if is_implemented :
92+ assert isinstance (data , list ), "Should return a JSON array of templates"
93+ else :
94+ # Starter code - just verify it returns something structured
95+ assert isinstance (data , dict ), "Should return a JSON object even if not implemented"
96+
97+ @pytest .mark .asyncio
98+ async def test_returns_templates (self ):
99+ """Test that templates are returned."""
100+ result = await get_pr_templates ()
101+ templates = json .loads (result )
102+
103+ # For starter code, accept error messages; for full implementation, expect templates
104+ is_implemented = not ("error" in templates and isinstance (templates , dict ))
105+ if is_implemented :
106+ assert len (templates ) > 0 , "Should return at least one template"
107+
108+ # Check that templates have expected structure
109+ for template in templates :
110+ assert isinstance (template , dict ), "Each template should be a dictionary"
111+ # Should have some identifying information
112+ assert any (key in template for key in ["filename" , "name" , "type" , "id" ]), \
113+ "Templates should have an identifier"
114+ else :
115+ # Starter code - just verify it's structured correctly
116+ assert isinstance (templates , dict ), "Should return structured error for starter code"
117+
118+
119+ @pytest .mark .skipif (not IMPORTS_SUCCESSFUL , reason = "Imports failed" )
120+ class TestSuggestTemplate :
121+ """Test the suggest_template tool."""
122+
123+ @pytest .mark .asyncio
124+ async def test_returns_json_string (self ):
125+ """Test that suggest_template returns a JSON string."""
126+ result = await suggest_template (
127+ "Fixed a bug in the authentication system" ,
128+ "bug"
129+ )
130+
131+ assert isinstance (result , str ), "Should return a string"
132+ # Should be valid JSON
133+ data = json .loads (result )
134+ assert isinstance (data , dict ), "Should return a JSON object"
135+
136+ @pytest .mark .asyncio
137+ async def test_suggestion_structure (self ):
138+ """Test that the suggestion has expected structure."""
139+ result = await suggest_template (
140+ "Added new feature for user management" ,
141+ "feature"
142+ )
143+ suggestion = json .loads (result )
144+
145+ # For starter code, accept error messages; for full implementation, expect suggestion
146+ is_implemented = not ("error" in suggestion and "Not implemented" in str (suggestion .get ("error" , "" )))
147+ if is_implemented :
148+ # Check for some expected fields (flexible to allow different implementations)
149+ assert any (key in suggestion for key in ["template" , "recommended_template" , "suggestion" ]), \
150+ "Should include a template recommendation"
151+ else :
152+ # Starter code - just verify it's structured correctly
153+ assert isinstance (suggestion , dict ), "Should return structured error for starter code"
154+
155+
156+ @pytest .mark .skipif (not IMPORTS_SUCCESSFUL , reason = "Imports failed" )
157+ class TestToolRegistration :
158+ """Test that tools are properly registered with FastMCP."""
159+
160+ def test_tools_have_decorators (self ):
161+ """Test that tool functions are decorated with @mcp.tool()."""
162+ # In FastMCP, decorated functions should have certain attributes
163+ # This is a basic check that functions exist and are callable
164+ assert hasattr (analyze_file_changes , '__name__' ), \
165+ "analyze_file_changes should be a proper function"
166+ assert hasattr (get_pr_templates , '__name__' ), \
167+ "get_pr_templates should be a proper function"
168+ assert hasattr (suggest_template , '__name__' ), \
169+ "suggest_template should be a proper function"
170+
171+
172+ if __name__ == "__main__" :
173+ if not IMPORTS_SUCCESSFUL :
174+ print (f"❌ Cannot run tests - imports failed: { IMPORT_ERROR } " )
175+ print ("\n Make sure you've:" )
176+ print ("1. Implemented all three tool functions" )
177+ print ("2. Decorated them with @mcp.tool()" )
178+ print ("3. Installed dependencies with: uv sync" )
179+ exit (1 )
180+
181+ # Run tests
182+ pytest .main ([__file__ , "-v" ])
0 commit comments