initial commit after one day coding agent session
This commit is contained in:
commit
372af75b90
88 changed files with 22694 additions and 0 deletions
250
tests/server/tools/test_build_environment.py
Normal file
250
tests/server/tools/test_build_environment.py
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
"""Tests for Build Custom Environment Tool."""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, AsyncMock
|
||||
from mcp.types import Tool, TextContent
|
||||
import json
|
||||
|
||||
from mcp_forge.server.tools.build_environment import BuildEnvironmentTool
|
||||
from mcp_forge.builder.environment_builder import BuildResult
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_environment_builder():
|
||||
"""Mock environment builder."""
|
||||
builder = Mock()
|
||||
builder.build_environment = AsyncMock(return_value=BuildResult(
|
||||
success=True,
|
||||
image_name="mcp-forge/custom:test-env",
|
||||
image_id="sha256:abc123",
|
||||
build_time=45.2,
|
||||
size_bytes=524288000, # 500MB
|
||||
installed_packages=["numpy==1.24.0", "pandas==2.0.0"],
|
||||
cache_hit=False
|
||||
))
|
||||
return builder
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_audit_logger():
|
||||
"""Mock audit logger."""
|
||||
logger = Mock()
|
||||
logger.log_environment_build = Mock()
|
||||
return logger
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def build_environment_tool(mock_environment_builder, mock_audit_logger):
|
||||
"""Create BuildEnvironmentTool instance with mocked dependencies."""
|
||||
return BuildEnvironmentTool(
|
||||
environment_builder=mock_environment_builder,
|
||||
audit_logger=mock_audit_logger
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_tool_definition(build_environment_tool):
|
||||
"""Test that tool definition matches MCP spec."""
|
||||
definition = build_environment_tool.get_tool_definition()
|
||||
|
||||
assert isinstance(definition, Tool)
|
||||
assert definition.name == "build_custom_environment"
|
||||
assert definition.description is not None
|
||||
assert "build" in definition.description.lower()
|
||||
|
||||
# Verify required schema properties
|
||||
schema = definition.inputSchema
|
||||
assert schema["type"] == "object"
|
||||
assert "name" in schema["properties"]
|
||||
assert "packages" in schema["properties"]
|
||||
assert "base_image" in schema["properties"]
|
||||
assert "python_version" in schema["properties"]
|
||||
assert "description" in schema["properties"]
|
||||
assert "name" in schema["required"]
|
||||
assert "packages" in schema["required"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_build_with_valid_packages(
|
||||
build_environment_tool,
|
||||
mock_environment_builder,
|
||||
mock_audit_logger
|
||||
):
|
||||
"""Test building environment with valid packages."""
|
||||
arguments = {
|
||||
"name": "test-ml-env",
|
||||
"packages": ["numpy>=1.24.0", "pandas>=2.0.0"]
|
||||
}
|
||||
|
||||
result = await build_environment_tool.execute(arguments)
|
||||
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 1
|
||||
assert isinstance(result[0], TextContent)
|
||||
|
||||
# Parse JSON response
|
||||
response = json.loads(result[0].text)
|
||||
assert response["success"] is True
|
||||
assert response["image_name"] == "mcp-forge/custom:test-env"
|
||||
assert response["build_time"] == 45.2
|
||||
assert len(response["installed_packages"]) == 2
|
||||
|
||||
# Verify builder was called
|
||||
mock_environment_builder.build_environment.assert_called_once()
|
||||
|
||||
# Verify audit log was called
|
||||
mock_audit_logger.log_environment_build.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_build_with_base_image(
|
||||
build_environment_tool,
|
||||
mock_environment_builder
|
||||
):
|
||||
"""Test building with custom base image."""
|
||||
arguments = {
|
||||
"name": "test-env",
|
||||
"packages": ["requests"],
|
||||
"base_image": "python:3.12"
|
||||
}
|
||||
|
||||
result = await build_environment_tool.execute(arguments)
|
||||
|
||||
# Parse JSON response
|
||||
response = json.loads(result[0].text)
|
||||
assert response["success"] is True
|
||||
|
||||
# Verify base_image was passed to builder
|
||||
call_args = mock_environment_builder.build_environment.call_args
|
||||
assert call_args.kwargs["base_image"] == "python:3.12"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_build_with_python_version(
|
||||
build_environment_tool,
|
||||
mock_environment_builder
|
||||
):
|
||||
"""Test building with specific Python version."""
|
||||
arguments = {
|
||||
"name": "test-env",
|
||||
"packages": ["numpy"],
|
||||
"python_version": "3.12"
|
||||
}
|
||||
|
||||
result = await build_environment_tool.execute(arguments)
|
||||
|
||||
# Parse JSON response
|
||||
response = json.loads(result[0].text)
|
||||
assert response["success"] is True
|
||||
|
||||
# Verify python_version was passed to builder
|
||||
call_args = mock_environment_builder.build_environment.call_args
|
||||
assert call_args.kwargs["python_version"] == "3.12"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_build_with_description(
|
||||
build_environment_tool,
|
||||
mock_environment_builder
|
||||
):
|
||||
"""Test building with description."""
|
||||
arguments = {
|
||||
"name": "test-env",
|
||||
"packages": ["pandas"],
|
||||
"description": "Environment for data analysis"
|
||||
}
|
||||
|
||||
result = await build_environment_tool.execute(arguments)
|
||||
|
||||
# Parse JSON response
|
||||
response = json.loads(result[0].text)
|
||||
assert response["success"] is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_arguments_missing_name(build_environment_tool):
|
||||
"""Test that validation fails when name is missing."""
|
||||
arguments = {
|
||||
"packages": ["numpy"]
|
||||
}
|
||||
|
||||
with pytest.raises((ValueError, KeyError)):
|
||||
await build_environment_tool.execute(arguments)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_arguments_missing_packages(build_environment_tool):
|
||||
"""Test that validation fails when packages is missing."""
|
||||
arguments = {
|
||||
"name": "test-env"
|
||||
}
|
||||
|
||||
with pytest.raises((ValueError, KeyError)):
|
||||
await build_environment_tool.execute(arguments)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_arguments_invalid_name(build_environment_tool):
|
||||
"""Test that validation fails for invalid environment name."""
|
||||
arguments = {
|
||||
"name": "invalid name with spaces",
|
||||
"packages": ["numpy"]
|
||||
}
|
||||
|
||||
with pytest.raises(ValueError, match="name"):
|
||||
await build_environment_tool.execute(arguments)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_arguments_invalid_packages_type(build_environment_tool):
|
||||
"""Test that validation fails when packages is not a list."""
|
||||
arguments = {
|
||||
"name": "test-env",
|
||||
"packages": "not-a-list"
|
||||
}
|
||||
|
||||
with pytest.raises(ValueError, match="packages"):
|
||||
await build_environment_tool.execute(arguments)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_arguments_empty_packages(build_environment_tool):
|
||||
"""Test that validation fails when packages list is empty."""
|
||||
arguments = {
|
||||
"name": "test-env",
|
||||
"packages": []
|
||||
}
|
||||
|
||||
with pytest.raises(ValueError, match="packages"):
|
||||
await build_environment_tool.execute(arguments)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_build_error_handling(
|
||||
build_environment_tool,
|
||||
mock_environment_builder
|
||||
):
|
||||
"""Test that build errors are handled gracefully."""
|
||||
mock_environment_builder.build_environment = AsyncMock(return_value=BuildResult(
|
||||
success=False,
|
||||
image_name="",
|
||||
image_id="",
|
||||
build_time=5.0,
|
||||
size_bytes=0,
|
||||
installed_packages=[],
|
||||
cache_hit=False,
|
||||
error="Package 'invalid-pkg' not found"
|
||||
))
|
||||
|
||||
arguments = {
|
||||
"name": "test-env",
|
||||
"packages": ["invalid-pkg"]
|
||||
}
|
||||
|
||||
result = await build_environment_tool.execute(arguments)
|
||||
|
||||
# Parse JSON response
|
||||
response = json.loads(result[0].text)
|
||||
assert response["success"] is False
|
||||
assert "error" in response
|
||||
assert "invalid-pkg" in response["error"]
|
||||
Loading…
Add table
Add a link
Reference in a new issue