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,59 @@
"""Shared fixtures for integration tests."""
import pytest
from pathlib import Path
from mcp_forge.config.schema import (
ForgeConfig, ServerConfig, SecurityConfig, ExecutionConfig, SessionConfig,
ImageConfig, VolumeConfig, EnvironmentBuilderConfig, PackageValidationConfig
)
@pytest.fixture
def real_config(tmp_path):
"""Real configuration with all required fields for integration tests."""
# Create required files
(tmp_path / "allowlist.txt").write_text("requests\npandas\nnumpy\n")
(tmp_path / "blocklist.txt").write_text("")
config = ForgeConfig(
server=ServerConfig(
host="localhost",
port=3000,
podman_socket=Path("/run/user/1000/podman/podman.sock")
),
security=SecurityConfig(
audit_log=tmp_path / "audit.log",
enforce_resource_limits=True,
allow_network=False
),
execution=ExecutionConfig(
default_backend="simple",
default_timeout=300,
max_timeout=1800,
default_memory="512m",
max_memory="2g"
),
images=ImageConfig(
python_3_11="mcp-forge/python:3.11",
python_3_12="mcp-forge/python:3.12",
auto_pull=False
),
sessions=SessionConfig(
max_concurrent=10,
idle_timeout=3600
),
volumes=VolumeConfig(
base_path=tmp_path / "volumes"
),
environment_builder=EnvironmentBuilderConfig(
uv_cache_path=tmp_path / "cache",
build_rate_limit={"requests": 5, "period": 60},
package_validation=PackageValidationConfig(
allowlist_path=tmp_path / "allowlist.txt",
blocklist_path=tmp_path / "blocklist.txt"
)
),
mcp_tools={}
)
return config

View file

@ -0,0 +1,56 @@
"""Integration tests for environment building workflow."""
import pytest
from pathlib import Path
@pytest.mark.asyncio
async def test_environment_build_end_to_end(tmp_path):
"""Test: Package list → validation → UV install → image build → container creation."""
# Setup: Create environment builder with all dependencies
# Execute:
# 1. Request environment with packages: ["requests", "pandas"]
# 2. Validate packages (should pass)
# 3. Install with UV
# 4. Build container image
# 5. Create running container
# Verify: Container has packages installed and importable
pytest.skip("TODO: Phase 5.3 - Environment build flow")
@pytest.mark.asyncio
async def test_blocked_package_prevents_build(tmp_path):
"""Test: Security validation prevents building environments with blocked packages."""
# Setup: Config with blocked packages
# Execute: Try to build environment with blocked package
# Verify: Build fails at validation stage, no container created
pytest.skip("TODO: Phase 5.3 - Security validation integration")
@pytest.mark.asyncio
async def test_invalid_package_name_fails_gracefully(tmp_path):
"""Test: Invalid package names are caught early."""
# Setup: Environment builder
# Execute: Request build with non-existent package
# Verify: Validation or install fails with clear error message
pytest.skip("TODO: Phase 5.3 - Error handling in build flow")
@pytest.mark.asyncio
async def test_environment_caching(tmp_path):
"""Test: Building same environment twice uses cache."""
# Setup: Environment builder with caching enabled
# Execute:
# 1. Build environment with ["requests"]
# 2. Build same environment again
# Verify: Second build is faster (uses cached image)
pytest.skip("TODO: Phase 5.3 - Build caching")
@pytest.mark.asyncio
async def test_concurrent_builds_respect_limits(tmp_path):
"""Test: Multiple simultaneous builds respect max_parallel_builds limit."""
# Setup: Environment builder with max_parallel_builds=2
# Execute: Trigger 5 builds simultaneously
# Verify: Only 2 run at once, others wait
pytest.skip("TODO: Phase 5.3 - Build rate limiting")

View file

@ -0,0 +1,48 @@
"""Integration tests for error handling and recovery."""
import pytest
@pytest.mark.asyncio
async def test_container_crash_cleanup(tmp_path):
"""Test: If container crashes, resources are cleaned up."""
# Setup: Create session with container
# Execute: Crash the container (kill -9)
# Verify: Session marked as failed, container removed, resources freed
pytest.skip("TODO: Phase 5.3 - Crash recovery")
@pytest.mark.asyncio
async def test_execution_error_propagation(tmp_path):
"""Test: Python errors in execution are returned with full traceback."""
# Setup: Create backend
# Execute: Code with syntax error or runtime error
# Verify: Error returned to caller with traceback, doesn't crash server
pytest.skip("TODO: Phase 5.3 - Error propagation")
@pytest.mark.asyncio
async def test_podman_connection_loss_handling(tmp_path):
"""Test: If Podman socket disconnects, errors are clear."""
# Setup: Create system connected to Podman
# Execute: Simulate socket disconnect
# Verify: Operations fail with clear "Podman unavailable" error
pytest.skip("TODO: Phase 5.3 - Connection loss handling")
@pytest.mark.asyncio
async def test_concurrent_session_cleanup(tmp_path):
"""Test: Cleaning up many sessions simultaneously doesn't deadlock."""
# Setup: Create 100 sessions
# Execute: Cleanup all simultaneously
# Verify: All cleaned up without deadlock or resource leaks
pytest.skip("TODO: Phase 5.3 - Concurrent cleanup")
@pytest.mark.asyncio
async def test_invalid_mcp_tool_call_error_handling(tmp_path):
"""Test: Calling MCP tool with wrong arguments returns clear error."""
# Setup: Register MCP tool
# Execute: Call with invalid arguments
# Verify: Returns validation error, doesn't crash bridge
pytest.skip("TODO: Phase 5.3 - MCP error handling")

View file

@ -0,0 +1,33 @@
"""Integration tests for end-to-end execution flows."""
import pytest
@pytest.mark.asyncio
async def test_simple_backend_execution_flow(real_config):
"""Test complete flow: code submission → execution → result return."""
pytest.skip("TODO: Phase 5.4 - Requires proper container registration and security setup")
@pytest.mark.asyncio
async def test_jupyter_session_lifecycle(real_config):
"""Test: Create session → execute multiple code blocks → cleanup."""
pytest.skip("TODO: Phase 5.4 - Complex Jupyter session testing requires more setup")
@pytest.mark.asyncio
async def test_jupyter_session_isolation(real_config):
"""Test: Two sessions don't share state."""
pytest.skip("TODO: Phase 5.4 - Complex Jupyter session testing requires more setup")
@pytest.mark.asyncio
async def test_execution_with_timeout(real_config):
"""Test: Long-running code gets killed after timeout."""
pytest.skip("TODO: Phase 5.4 - Requires proper container registration and security setup")
@pytest.mark.asyncio
async def test_execution_with_memory_limit(real_config):
"""Test: Memory-intensive code respects limits."""
pytest.skip("TODO: Phase 5.4 - Requires proper container registration and security setup")

View file

@ -0,0 +1,66 @@
"""Integration tests for MCP tool and bridge integration."""
import pytest
@pytest.mark.asyncio
async def test_mcp_tool_bridge_connection(tmp_path):
"""Test: MCP client → bridge server → tool execution."""
# Setup: Start bridge server, create MCP client, register tools
# Execute: Client calls tool through bridge
# Verify: Tool executes and returns result through bridge
pytest.skip("TODO: Phase 5.3 - MCP bridge integration")
@pytest.mark.asyncio
async def test_tool_injection_into_execution(tmp_path):
"""Test: MCP tools are injected into Python execution environment."""
# Setup: Create session with MCP tools available
# Execute: Python code that calls MCP tool (e.g., mcp_tools.search_web())
# Verify: Tool is callable and returns expected result
pytest.skip("TODO: Phase 5.3 - Tool injection integration")
@pytest.mark.asyncio
async def test_multiple_mcp_clients_no_collision(tmp_path):
"""Test: Multiple MCP clients with same tool names don't conflict."""
# Setup: Register two clients, both with "search" tool
# Execute: Call search tool
# Verify: Collision detected and handled (namespacing or error)
pytest.skip("TODO: Phase 5.3 - Tool collision detection")
@pytest.mark.asyncio
async def test_mcp_tool_handler_execute_python(tmp_path):
"""Test: execute_python MCP tool end-to-end."""
# Setup: Create ForgeServer
# Execute: Call execute_python tool with simple code
# Verify: Code executes and returns stdout/result
pytest.skip("TODO: Phase 5.3 - ExecutePythonTool integration")
@pytest.mark.asyncio
async def test_mcp_tool_handler_document_state(tmp_path):
"""Test: document_state MCP tool shows session variables."""
# Setup: Create session, execute code that sets variables
# Execute: Call document_state tool
# Verify: Returns list of variables and their values
pytest.skip("TODO: Phase 5.3 - DocumentStateTool integration")
@pytest.mark.asyncio
async def test_mcp_tool_handler_build_environment(tmp_path):
"""Test: build_environment MCP tool creates custom environment."""
# Setup: Create ForgeServer
# Execute: Call build_environment with package list
# Verify: Environment built and can be used for execution
pytest.skip("TODO: Phase 5.3 - BuildEnvironmentTool integration")
@pytest.mark.asyncio
async def test_mcp_resource_handlers(tmp_path):
"""Test: MCP resource handlers return correct data."""
# Setup: Create ForgeServer with sessions and environments
# Execute: Read resources (tools/available, sessions/list, environments/list)
# Verify: Resources return expected data
pytest.skip("TODO: Phase 5.3 - Resource handler integration")

View file

@ -0,0 +1,48 @@
"""Integration tests for security enforcement across components."""
import pytest
@pytest.mark.asyncio
async def test_blocked_package_import_prevented(tmp_path):
"""Test: Attempting to import blocked package fails."""
# Setup: Config with blocked packages (e.g., ["subprocess", "os"])
# Execute: Try to execute code that imports blocked package
# Verify: Execution blocked or import fails
pytest.skip("TODO: Phase 5.3 - Package blocking enforcement")
@pytest.mark.asyncio
async def test_resource_limits_enforced_in_container(tmp_path):
"""Test: Container actually respects memory/CPU/timeout limits."""
# Setup: Create container with strict limits
# Execute: Run code that tries to exceed limits
# Verify: Container killed or limited appropriately
pytest.skip("TODO: Phase 5.3 - Resource limit enforcement")
@pytest.mark.asyncio
async def test_audit_logging_across_operations(tmp_path):
"""Test: All operations are logged to audit trail."""
# Setup: Create system with audit logging
# Execute: Multiple operations (create session, execute code, build env)
# Verify: All operations appear in audit log with correct metadata
pytest.skip("TODO: Phase 5.3 - Audit trail integration")
@pytest.mark.asyncio
async def test_network_isolation_in_containers(tmp_path):
"""Test: Containers cannot access external network (if configured)."""
# Setup: Create container with network disabled
# Execute: Try to make HTTP request
# Verify: Request fails (network isolated)
pytest.skip("TODO: Phase 5.3 - Network isolation")
@pytest.mark.asyncio
async def test_filesystem_isolation_in_containers(tmp_path):
"""Test: Containers cannot access host filesystem outside mounts."""
# Setup: Create container
# Execute: Try to read /etc/passwd or other host files
# Verify: Access denied
pytest.skip("TODO: Phase 5.3 - Filesystem isolation")