Files
cleveragents-core/robot/helper_resource_handlers.py
T

148 lines
4.8 KiB
Python

"""Helper utilities for resource handler Robot smoke tests.
Each command prints a single ``<command>-ok`` token on success so the
calling Robot test can assert on ``stdout``.
"""
from __future__ import annotations
import sys
from datetime import datetime
from unittest.mock import MagicMock, PropertyMock
from cleveragents.domain.models.core.resource import (
PhysVirt,
Resource,
ResourceCapabilities,
)
from cleveragents.infrastructure.sandbox.factory import SandboxFactory
from cleveragents.infrastructure.sandbox.manager import SandboxManager
from cleveragents.infrastructure.sandbox.protocol import (
SandboxContext,
SandboxStatus,
)
from cleveragents.resource.handlers.fs_directory import FsDirectoryHandler
from cleveragents.resource.handlers.git_checkout import GitCheckoutHandler
from cleveragents.resource.handlers.protocol import ResourceHandler
from cleveragents.resource.handlers.resolver import (
HandlerResolutionError,
clear_handler_cache,
resolve_handler,
)
from cleveragents.tool.context import BoundResource
def _make_resource(rtype: str, location: str) -> Resource:
"""Create a test resource with a valid ULID."""
return Resource(
resource_id="01KJ5C5TPMP8GGX3QC83E2MAQS",
resource_type_name=rtype,
classification=PhysVirt.PHYSICAL,
location=location,
capabilities=ResourceCapabilities(
readable=True, writable=True, sandboxable=True
),
)
def _make_mock_manager() -> SandboxManager:
"""Create a SandboxManager with a mock factory."""
mock_factory = MagicMock(spec=SandboxFactory)
mock_sandbox = MagicMock()
mock_sandbox.sandbox_id = "sb-robot-001"
type(mock_sandbox).status = PropertyMock(return_value=SandboxStatus.CREATED)
mock_sandbox.context = SandboxContext(
sandbox_id="sb-robot-001",
sandbox_path="/tmp/sandbox/sb-robot-001",
original_path="/tmp/original",
resource_id="res-robot",
plan_id="plan-robot",
created_at=datetime.now(),
)
mock_sandbox.create.return_value = mock_sandbox.context
mock_factory.create_sandbox.return_value = mock_sandbox
return SandboxManager(factory=mock_factory, cleanup_on_exit=False)
def _protocol_check() -> None:
"""Verify both handlers satisfy the ResourceHandler protocol."""
git = GitCheckoutHandler()
fs = FsDirectoryHandler()
assert isinstance(git, ResourceHandler), "GitCheckoutHandler not a ResourceHandler"
assert isinstance(fs, ResourceHandler), "FsDirectoryHandler not a ResourceHandler"
print("protocol-check-ok")
def _git_resolve() -> None:
"""Verify GitCheckoutHandler resolves a resource."""
resource = _make_resource("git-checkout", "/tmp/test-repo")
manager = _make_mock_manager()
handler = GitCheckoutHandler()
bound = handler.resolve(
resource=resource,
plan_id="PLAN_ROBOT",
slot_name="repo",
sandbox_manager=manager,
)
assert isinstance(bound, BoundResource)
assert bound.sandbox_path, "sandbox_path empty"
assert bound.resource_type == "git-checkout"
print("git-resolve-ok")
def _fs_resolve() -> None:
"""Verify FsDirectoryHandler resolves a resource."""
resource = _make_resource("fs-directory", "/tmp/test-dir")
manager = _make_mock_manager()
handler = FsDirectoryHandler()
bound = handler.resolve(
resource=resource,
plan_id="PLAN_ROBOT",
slot_name="workdir",
sandbox_manager=manager,
)
assert isinstance(bound, BoundResource)
assert bound.sandbox_path, "sandbox_path empty"
assert bound.resource_type == "fs-directory"
print("fs-resolve-ok")
def _resolver_import() -> None:
"""Verify handler resolver loads handlers from module:class strings."""
clear_handler_cache()
git = resolve_handler(
"cleveragents.resource.handlers.git_checkout:GitCheckoutHandler"
)
assert isinstance(git, GitCheckoutHandler)
fs = resolve_handler(
"cleveragents.resource.handlers.fs_directory:FsDirectoryHandler"
)
assert isinstance(fs, FsDirectoryHandler)
print("resolver-import-ok")
def _resolver_error() -> None:
"""Verify handler resolver raises on bad references."""
clear_handler_cache()
try:
resolve_handler("nonexistent.module:FakeClass")
print("resolver-error-FAIL")
except HandlerResolutionError:
print("resolver-error-ok")
_COMMANDS = {
"protocol-check": _protocol_check,
"git-resolve": _git_resolve,
"fs-resolve": _fs_resolve,
"resolver-import": _resolver_import,
"resolver-error": _resolver_error,
}
if __name__ == "__main__":
if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS:
print(f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>", file=sys.stderr)
sys.exit(1)
_COMMANDS[sys.argv[1]]()