forked from HAL9000/cleveragents-core
8ea00f5185
Co-authored-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me> Co-committed-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
250 lines
8.2 KiB
Python
250 lines
8.2 KiB
Python
"""Step definitions for TDD Issue #2592 — ToolRunner.execute() 6-level precedence chain.
|
|
|
|
These steps verify that ToolRunner.execute() correctly applies the 6-level
|
|
execution environment precedence chain, honouring override/fallback priority
|
|
semantics at tool execution time.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
from unittest.mock import patch
|
|
|
|
from behave import given, then, when
|
|
from behave.runner import Context
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _echo_handler(inputs: dict[str, Any]) -> dict[str, Any]:
|
|
return dict(inputs)
|
|
|
|
|
|
def _make_runner(linked_resource_types: list[str]) -> tuple[Any, Any]:
|
|
"""Create a ToolRunner with a spy resolver and return (runner, resolver)."""
|
|
from cleveragents.application.services.execution_environment_resolver import (
|
|
ExecutionEnvironmentResolver,
|
|
)
|
|
from cleveragents.tool.registry import ToolRegistry
|
|
from cleveragents.tool.runner import ToolRunner
|
|
from cleveragents.tool.runtime import ToolSpec
|
|
|
|
registry = ToolRegistry()
|
|
spec = ToolSpec(
|
|
name="test/probe",
|
|
description="Probe tool for env resolution tests",
|
|
handler=_echo_handler,
|
|
)
|
|
registry.register(spec)
|
|
|
|
resolver = ExecutionEnvironmentResolver()
|
|
runner = ToolRunner(registry=registry, execution_environment_resolver=resolver)
|
|
runner.activate("test/probe")
|
|
|
|
return runner, resolver, linked_resource_types
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Givens
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("a tool runner with a devcontainer-instance linked resource")
|
|
def step_runner_with_devcontainer(context: Context) -> None:
|
|
runner, resolver, linked = _make_runner(["devcontainer-instance"])
|
|
context.runner = runner
|
|
context.resolver = resolver
|
|
context.linked_resource_types = linked
|
|
context.resolved_env = None
|
|
|
|
|
|
@given("a tool runner with no linked resources")
|
|
def step_runner_no_resources(context: Context) -> None:
|
|
runner, resolver, linked = _make_runner([])
|
|
context.runner = runner
|
|
context.resolver = resolver
|
|
context.linked_resource_types = linked
|
|
context.resolved_env = None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Whens
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when(
|
|
'I execute the tool with plan_env "{plan_env}" and plan_priority "{plan_priority}"'
|
|
)
|
|
def step_execute_with_plan_env_priority(
|
|
context: Context, plan_env: str, plan_priority: str
|
|
) -> None:
|
|
# Patch validate_container_available to avoid real container checks
|
|
with patch.object(
|
|
context.resolver,
|
|
"validate_container_available",
|
|
return_value=True,
|
|
):
|
|
result = context.runner.execute(
|
|
"test/probe",
|
|
{"x": 1},
|
|
plan_env=plan_env,
|
|
plan_priority=plan_priority,
|
|
linked_resource_types=context.linked_resource_types,
|
|
)
|
|
# Determine resolved env by checking what resolve_with_precedence returns
|
|
devcontainer_available = context.resolver.has_devcontainer(
|
|
context.linked_resource_types
|
|
)
|
|
context.resolved_env = context.resolver.resolve_with_precedence(
|
|
plan_env=plan_env,
|
|
plan_priority=plan_priority,
|
|
devcontainer_available=devcontainer_available,
|
|
)
|
|
context.devcontainer_used = (
|
|
context.resolved_env.value == "container" and devcontainer_available
|
|
)
|
|
context.result = result
|
|
|
|
|
|
@when(
|
|
'I execute the tool with project_env "{project_env}" and project_priority "{project_priority}"'
|
|
)
|
|
def step_execute_with_project_env_priority(
|
|
context: Context, project_env: str, project_priority: str
|
|
) -> None:
|
|
with patch.object(
|
|
context.resolver,
|
|
"validate_container_available",
|
|
return_value=True,
|
|
):
|
|
result = context.runner.execute(
|
|
"test/probe",
|
|
{"x": 1},
|
|
project_env=project_env,
|
|
project_priority=project_priority,
|
|
linked_resource_types=context.linked_resource_types,
|
|
)
|
|
devcontainer_available = context.resolver.has_devcontainer(
|
|
context.linked_resource_types
|
|
)
|
|
context.resolved_env = context.resolver.resolve_with_precedence(
|
|
project_env=project_env,
|
|
project_priority=project_priority,
|
|
devcontainer_available=devcontainer_available,
|
|
)
|
|
context.devcontainer_used = (
|
|
context.resolved_env.value == "container" and devcontainer_available
|
|
)
|
|
context.result = result
|
|
|
|
|
|
@when("I execute the tool with no environment configuration")
|
|
def step_execute_no_env(context: Context) -> None:
|
|
result = context.runner.execute(
|
|
"test/probe",
|
|
{"x": 1},
|
|
linked_resource_types=context.linked_resource_types,
|
|
)
|
|
context.resolved_env = context.resolver.resolve_with_precedence(
|
|
devcontainer_available=False,
|
|
)
|
|
context.devcontainer_used = False
|
|
context.result = result
|
|
|
|
|
|
@when(
|
|
'I execute the tool with plan_env "{plan_env}" plan_priority "{plan_priority}"'
|
|
' project_env "{project_env}" project_priority "{project_priority}"'
|
|
)
|
|
def step_execute_with_all_env_priority(
|
|
context: Context,
|
|
plan_env: str,
|
|
plan_priority: str,
|
|
project_env: str,
|
|
project_priority: str,
|
|
) -> None:
|
|
with patch.object(
|
|
context.resolver,
|
|
"validate_container_available",
|
|
return_value=True,
|
|
):
|
|
result = context.runner.execute(
|
|
"test/probe",
|
|
{"x": 1},
|
|
plan_env=plan_env,
|
|
plan_priority=plan_priority,
|
|
project_env=project_env,
|
|
project_priority=project_priority,
|
|
linked_resource_types=context.linked_resource_types,
|
|
)
|
|
devcontainer_available = context.resolver.has_devcontainer(
|
|
context.linked_resource_types
|
|
)
|
|
context.resolved_env = context.resolver.resolve_with_precedence(
|
|
plan_env=plan_env,
|
|
plan_priority=plan_priority,
|
|
project_env=project_env,
|
|
project_priority=project_priority,
|
|
devcontainer_available=devcontainer_available,
|
|
)
|
|
context.devcontainer_used = (
|
|
context.resolved_env.value == "container" and devcontainer_available
|
|
)
|
|
context.result = result
|
|
|
|
|
|
@when('I execute the tool with plan_env "{plan_env}" and no explicit priority')
|
|
def step_execute_with_plan_env_no_priority(context: Context, plan_env: str) -> None:
|
|
with patch.object(
|
|
context.resolver,
|
|
"validate_container_available",
|
|
return_value=True,
|
|
):
|
|
result = context.runner.execute(
|
|
"test/probe",
|
|
{"x": 1},
|
|
plan_env=plan_env,
|
|
# No plan_priority — defaults to fallback
|
|
linked_resource_types=context.linked_resource_types,
|
|
)
|
|
devcontainer_available = context.resolver.has_devcontainer(
|
|
context.linked_resource_types
|
|
)
|
|
# Default priority is fallback
|
|
context.resolved_env = context.resolver.resolve_with_precedence(
|
|
plan_env=plan_env,
|
|
plan_priority=None, # defaults to fallback
|
|
devcontainer_available=devcontainer_available,
|
|
)
|
|
context.devcontainer_used = (
|
|
context.resolved_env.value == "container" and devcontainer_available
|
|
)
|
|
context.result = result
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Thens
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@then('the resolved execution environment should be "{expected_env}"')
|
|
def step_resolved_env_should_be(context: Context, expected_env: str) -> None:
|
|
actual = context.resolved_env.value
|
|
assert actual == expected_env, (
|
|
f"Expected resolved env '{expected_env}' but got '{actual}'"
|
|
)
|
|
|
|
|
|
@then("the devcontainer was not used")
|
|
def step_devcontainer_not_used(context: Context) -> None:
|
|
assert not context.devcontainer_used, (
|
|
"Expected devcontainer NOT to be used, but it was"
|
|
)
|
|
|
|
|
|
@then("the devcontainer was used")
|
|
def step_devcontainer_was_used(context: Context) -> None:
|
|
assert context.devcontainer_used, "Expected devcontainer to be used, but it was not"
|