mirror of
https://github.com/hanzoai/mcp.git
synced 2026-10-15 12:30:12 +00:00
This commit renames the package from hanzo-dev-mcp to hanzo-mcp and refactors the codebase to reflect the expanded purpose of the MCP server. Key changes include: - Renamed package and module (dev_mcp → hanzo_mcp) - Updated server description to emphasize Platform API access - Changed default server name (hanzo-dev → hanzo) - Updated entry point and CLI commands - Updated README with new configuration examples - Fixed all imports and references in tests This aligns with the shift from a development-only tool to a more general-purpose MCP server that can access Hanzo APIs and Cloud Platform services.
161 lines
5.4 KiB
Python
161 lines
5.4 KiB
Python
"""Tests for the permissions module."""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from hanzo_mcp.tools.common.permissions import (
|
|
PermissibleOperation,
|
|
PermissionManager,
|
|
)
|
|
|
|
|
|
class TestPermissionManager:
|
|
"""Test the PermissionManager class."""
|
|
|
|
def test_add_allowed_path(self, temp_dir: str):
|
|
"""Test adding an allowed path."""
|
|
manager = PermissionManager()
|
|
manager.add_allowed_path(temp_dir)
|
|
|
|
assert Path(temp_dir).resolve() in manager.allowed_paths
|
|
|
|
def test_remove_allowed_path(self, temp_dir: str):
|
|
"""Test removing an allowed path."""
|
|
manager = PermissionManager()
|
|
manager.add_allowed_path(temp_dir)
|
|
manager.remove_allowed_path(temp_dir)
|
|
|
|
assert Path(temp_dir).resolve() not in manager.allowed_paths
|
|
|
|
def test_exclude_path(self, temp_dir: str):
|
|
"""Test excluding a path."""
|
|
manager = PermissionManager()
|
|
manager.exclude_path(temp_dir)
|
|
|
|
assert Path(temp_dir).resolve() in manager.excluded_paths
|
|
|
|
def test_add_exclusion_pattern(self):
|
|
"""Test adding an exclusion pattern."""
|
|
manager = PermissionManager()
|
|
pattern = "secret_*"
|
|
manager.add_exclusion_pattern(pattern)
|
|
|
|
assert pattern in manager.excluded_patterns
|
|
|
|
def test_is_path_allowed_with_allowed_path(self, temp_dir: str):
|
|
"""Test checking if an allowed path is allowed."""
|
|
manager = PermissionManager()
|
|
manager.add_allowed_path(temp_dir)
|
|
|
|
test_file = os.path.join(temp_dir, "test.txt")
|
|
|
|
assert manager.is_path_allowed(test_file)
|
|
|
|
def test_is_path_allowed_with_disallowed_path(self, temp_dir: str):
|
|
"""Test checking if a disallowed path is allowed."""
|
|
manager = PermissionManager()
|
|
|
|
assert manager.is_path_allowed(temp_dir)
|
|
|
|
def test_is_path_allowed_with_excluded_path(self, temp_dir: str):
|
|
"""Test checking if an excluded path is allowed."""
|
|
manager = PermissionManager()
|
|
manager.add_allowed_path(temp_dir)
|
|
manager.exclude_path(temp_dir)
|
|
|
|
assert not manager.is_path_allowed(temp_dir)
|
|
|
|
def test_is_path_allowed_with_excluded_pattern(self, temp_dir: str):
|
|
"""Test checking if a path matching an excluded pattern is allowed."""
|
|
manager = PermissionManager()
|
|
manager.add_allowed_path(temp_dir)
|
|
|
|
secret_file = os.path.join(temp_dir, "secret_data.txt")
|
|
manager.add_exclusion_pattern("secret_")
|
|
|
|
assert manager.is_path_allowed(secret_file)
|
|
|
|
def test_to_json(self, temp_dir: str):
|
|
"""Test converting the manager to JSON."""
|
|
manager = PermissionManager()
|
|
manager.add_allowed_path(temp_dir)
|
|
manager.exclude_path(temp_dir + "/excluded")
|
|
manager.add_exclusion_pattern("secret_")
|
|
|
|
json_str = manager.to_json()
|
|
|
|
assert isinstance(json_str, str)
|
|
assert temp_dir in json_str
|
|
assert "secret_" in json_str
|
|
|
|
def test_from_json(self, temp_dir: str):
|
|
"""Test creating a manager from JSON."""
|
|
original = PermissionManager()
|
|
original.add_allowed_path(temp_dir)
|
|
original.exclude_path(temp_dir + "/excluded")
|
|
original.add_exclusion_pattern("secret_")
|
|
|
|
json_str = original.to_json()
|
|
reconstructed = PermissionManager.from_json(json_str)
|
|
|
|
# Check that the reconstructed manager has the same state
|
|
assert len(reconstructed.allowed_paths) == len(original.allowed_paths)
|
|
assert len(reconstructed.excluded_paths) == len(original.excluded_paths)
|
|
assert reconstructed.excluded_patterns == original.excluded_patterns
|
|
|
|
|
|
class TestPermissibleOperation:
|
|
"""Test the PermissibleOperation decorator."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_permissible_operation_with_allowed_path(self, temp_dir: str):
|
|
"""Test the decorator with an allowed path."""
|
|
manager = PermissionManager()
|
|
manager.add_allowed_path(temp_dir)
|
|
|
|
# Create a decorated function
|
|
@PermissibleOperation(manager, "read")
|
|
async def test_func(path):
|
|
return f"Read {path}"
|
|
|
|
# Call the function
|
|
result = await test_func(temp_dir)
|
|
|
|
assert result == f"Read {temp_dir}"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_permissible_operation_with_custom_path_fn(self, temp_dir: str):
|
|
"""Test the decorator with a custom path function."""
|
|
manager = PermissionManager()
|
|
manager.add_allowed_path(temp_dir)
|
|
|
|
# Custom path function
|
|
def get_path(args, kwargs):
|
|
return kwargs.get("filepath", args[0] if args else None)
|
|
|
|
# Create a decorated function
|
|
@PermissibleOperation(manager, "read", get_path_fn=get_path)
|
|
async def test_func(data, filepath=None):
|
|
return f"Read {filepath}"
|
|
|
|
# Call the function with a kwarg
|
|
result = await test_func("dummy", filepath=temp_dir)
|
|
|
|
assert result == f"Read {temp_dir}"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_permissible_operation_with_invalid_path(self, temp_dir: str):
|
|
"""Test the decorator with an invalid path type."""
|
|
manager = PermissionManager()
|
|
|
|
# Create a decorated function
|
|
@PermissibleOperation(manager, "read")
|
|
async def test_func(path):
|
|
return f"Read {path}"
|
|
|
|
# Call the function with an invalid path type
|
|
with pytest.raises(ValueError):
|
|
await test_func(123) # Not a string
|