initial commit after one day coding agent session

This commit is contained in:
Hans Aschauer 2026-02-07 07:45:57 +01:00
commit 372af75b90
88 changed files with 22694 additions and 0 deletions

View 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"]