Files
temp/features/steps/tdd_tool_runner_env_precedence_steps.py
freemo 8ea00f5185 fix: restore CI quality tests to passing state (#4175)
Co-authored-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
Co-committed-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
2026-04-08 11:02:14 +00:00

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"