PyGithub MCP Server

"""Unit tests for the tool registration system. These tests verify that the tool registration system properly registers and loads tools without using mocks. """ import os import json import tempfile from pathlib import Path import pytest from pygithub_mcp_server.tools import ( tool, register_tools, load_tools, _tool_registry, _registered_modules ) class TestToolDecorator: """Tests for the tool decorator.""" def setup_method(self): """Set up test environment before each test.""" # Clear the registry before each test _tool_registry.clear() _registered_modules.clear() def test_tool_registration(self): """Test that the tool decorator registers a function correctly.""" # Define a test function and decorate it @tool() def test_function(param): return param # Verify the function was registered assert "test_function" in _tool_registry assert _tool_registry["test_function"] is test_function # Verify function still works assert test_function("test") == "test" def test_multiple_tool_registration(self): """Test that multiple tools can be registered.""" # Define and decorate test functions @tool() def tool1(): return "tool1" @tool() def tool2(): return "tool2" # Verify both functions were registered assert "tool1" in _tool_registry assert "tool2" in _tool_registry assert len(_tool_registry) == 2 class TestToolRegistration: """Tests for the tool registration functions.""" def setup_method(self): """Set up test environment before each test.""" # Clear the registry before each test _tool_registry.clear() _registered_modules.clear() def test_register_tools(self): """Test that register_tools properly registers tools with MCP server.""" # Define some test functions def test_tool1(): pass def test_tool2(): pass # Create a real server-like object class TestMCP: def __init__(self): self.registered_tools = [] def tool(self): # Return a function that captures the decorated function def decorator(func): self.registered_tools.append(func) return func return decorator # Create server instance server = TestMCP() # Register tools register_tools(server, [test_tool1, test_tool2]) # Check that both tools were registered assert test_tool1 in server.registered_tools assert test_tool2 in server.registered_tools assert len(server.registered_tools) == 2 @pytest.fixture def config_env(): """Fixture to set up and tear down environment variables for config tests.""" # Store original values orig_env = {} env_vars = [ "PYGITHUB_ENABLE_ISSUES", "PYGITHUB_ENABLE_REPOSITORIES", "PYGITHUB_ENABLE_PULL_REQUESTS" ] for var in env_vars: if var in os.environ: orig_env[var] = os.environ[var] yield # Restore original values for var in env_vars: if var in os.environ and var not in orig_env: del os.environ[var] elif var in orig_env: os.environ[var] = orig_env[var] @pytest.fixture def setup_test_module(tmp_path): """Fixture to set up a test module for tool loading tests.""" # Create a test module module_path = tmp_path / "test_module.py" with open(module_path, "w") as f: f.write(""" def test_tool(): return "test_tool" def register(mcp): mcp.registered = True """) # Create an __init__.py file init_path = tmp_path / "__init__.py" with open(init_path, "w") as f: f.write("") # Return module info for tests return { "path": str(module_path), "dir": str(tmp_path) } class TestToolLoading: """Tests for the tool loading system.""" def setup_method(self): """Set up test environment before each test.""" # Clear the registry before each test _tool_registry.clear() _registered_modules.clear() def test_load_tools_enabled_group(self, config_env): """Test loading tools from enabled groups.""" # Create test configuration config = { "tool_groups": { "issues": {"enabled": True}, "repositories": {"enabled": False} } } # Create a server-like object class TestMCP: def __init__(self): self.registered_modules = [] def register_module(self, module_name): self.registered_modules.append(module_name) server = TestMCP() # We'll test the module importing behavior separately since # it's not feasible to create proper Python modules during tests # This test verifies the enabled/disabled logic # Create a custom load_module function def mock_load_module(module_path): class TestModule: def register(self, mcp): mcp.register_module(module_path) return TestModule() # Test with our custom module loader with pytest.MonkeyPatch().context() as monkeypatch: monkeypatch.setattr("importlib.import_module", mock_load_module) # Call load_tools load_tools(server, config) # Verify only enabled modules were processed assert "pygithub_mcp_server.tools.issues" in server.registered_modules assert "pygithub_mcp_server.tools.repositories" not in server.registered_modules def test_load_tools_module_error(self, config_env): """Test handling of module import errors.""" # Create test configuration config = { "tool_groups": { "nonexistent": {"enabled": True} } } # Create a server-like object class TestMCP: def __init__(self): self.registered_modules = [] server = TestMCP() # Call load_tools # This should not raise an exception, but log an error load_tools(server, config) # Verify no modules were registered assert not hasattr(server, "registered_modules") or len(server.registered_modules) == 0