"""Step definitions for exec_env_project_override.feature. Verifies that plan_env and project_env are threaded from ToolCallRouter through to the ExecutionEnvironmentResolver inside ToolRunner.execute(). Issue #1080. """ from __future__ import annotations from typing import Any from behave import given, then, when from behave.runner import Context from cleveragents.tool.registry import ToolRegistry from cleveragents.tool.router import ToolCallRouter from cleveragents.tool.runner import ToolRunner from cleveragents.tool.runtime import ToolSpec __all__: list[str] = [] def _echo_tool(arguments: dict[str, Any]) -> dict[str, Any]: """Simple echo tool for testing.""" return {"echoed": arguments} @given("ee1080- a tool registry with a registered echo tool") def step_ee1080_registry(context: Context) -> None: context.ee1080_registry = ToolRegistry() spec = ToolSpec( name="test/echo", description="Echo tool for testing", input_schema={"type": "object", "properties": {}}, handler=_echo_tool, ) context.ee1080_registry.register(spec) @given("ee1080- a tool runner and resolver spy") def step_ee1080_runner(context: Context) -> None: context.ee1080_runner = ToolRunner(context.ee1080_registry) # Spy on _env_resolver.resolve to capture what it receives context.ee1080_resolve_calls: list[dict[str, Any]] = [] # type: ignore[annotation-unchecked] original_rv = context.ee1080_runner._env_resolver.resolve_and_validate def spy_resolve_and_validate(**kwargs: Any) -> tuple[str, str | None]: context.ee1080_resolve_calls.append( { "plan_env": kwargs.get("plan_env"), "project_env": kwargs.get("project_env"), } ) return original_rv(**kwargs) context.ee1080_runner._env_resolver.resolve_and_validate = spy_resolve_and_validate # type: ignore[method-assign] @given('ee1080- a ToolCallRouter with project_env "{env}"') def step_ee1080_router_project(context: Context, env: str) -> None: context.ee1080_router = ToolCallRouter( registry=context.ee1080_registry, runner=context.ee1080_runner, plan_id="ee1080-plan", project_env=env, ) @given('ee1080- a ToolCallRouter with plan_env "{env}"') def step_ee1080_router_plan(context: Context, env: str) -> None: context.ee1080_router = ToolCallRouter( registry=context.ee1080_registry, runner=context.ee1080_runner, plan_id="ee1080-plan", plan_env=env, ) @given( 'ee1080- a ToolCallRouter with both plan_env "{plan}" and project_env "{project}"' ) def step_ee1080_router_both(context: Context, plan: str, project: str) -> None: context.ee1080_router = ToolCallRouter( registry=context.ee1080_registry, runner=context.ee1080_runner, plan_id="ee1080-plan", plan_env=plan, project_env=project, ) @given("ee1080- a ToolCallRouter without env params") def step_ee1080_router_none(context: Context) -> None: context.ee1080_router = ToolCallRouter( registry=context.ee1080_registry, runner=context.ee1080_runner, plan_id="ee1080-plan", ) @when("ee1080- I route a tool call through the router") def step_ee1080_route(context: Context) -> None: payload = { "name": "test/echo", "arguments": {"msg": "hello"}, } context.ee1080_result = context.ee1080_router.route(payload) @then('ee1080- the resolver should have received project_env "{expected}"') def step_ee1080_check_project_env(context: Context, expected: str) -> None: expected_val = None if expected == "None" else expected calls = context.ee1080_resolve_calls assert len(calls) > 0, "Resolver was never called" actual = calls[-1]["project_env"] assert actual == expected_val, ( f"Expected project_env={expected_val!r}, got {actual!r}" ) @then('ee1080- the resolver should have received plan_env "{expected}"') def step_ee1080_check_plan_env(context: Context, expected: str) -> None: expected_val = None if expected == "None" else expected calls = context.ee1080_resolve_calls assert len(calls) > 0, "Resolver was never called" actual = calls[-1]["plan_env"] assert actual == expected_val, f"Expected plan_env={expected_val!r}, got {actual!r}"