PyGithub MCP Server
by AstroMined
"""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