"""Behave step definitions for PluginState.EXECUTING lifecycle state. Covers the execute_plugin() method on PluginManager, which transitions plugins through ACTIVATED -> EXECUTING -> ACTIVATED (success) or ACTIVATED -> EXECUTING -> ERRORED (failure). Based on issue #5691. """ from __future__ import annotations from unittest.mock import patch from behave import then, when # type: ignore[import-untyped] from behave.runner import Context # type: ignore[import-untyped] from cleveragents.infrastructure.plugins.exceptions import ( PluginError, PluginNotFoundError, ) from cleveragents.infrastructure.plugins.types import PluginState # --------------------------------------------------------------------------- # Execute plugin — happy path # --------------------------------------------------------------------------- @when('I execute method "search" on plugin "{name}" with valid args') def step_execute_plugin_search(context: Context, name: str) -> None: context.caught_exception = None context.execution_result = None try: context.execution_result = context.manager.execute_plugin( name, "search", "test query", scope=frozenset(), max_results=5, ) except PluginError as exc: context.caught_exception = exc @then("the execution result should be an empty list") def step_execution_result_empty_list(context: Context) -> None: assert context.execution_result == [], ( f"Expected empty list, got {context.execution_result!r}" ) # --------------------------------------------------------------------------- # Execute plugin — error path # --------------------------------------------------------------------------- @when('I attempt to execute a failing method on plugin "{name}"') def step_attempt_execute_failing(context: Context, name: str) -> None: context.caught_exception = None instance = context.manager.get_plugin_instance(name) if instance is not None: with patch.object(instance, "search", side_effect=RuntimeError("boom")): try: context.manager.execute_plugin( name, "search", "test query", scope=frozenset(), max_results=5, ) except PluginError as exc: context.caught_exception = exc else: try: context.manager.execute_plugin(name, "search", "test", scope=frozenset()) except PluginError as exc: context.caught_exception = exc # --------------------------------------------------------------------------- # Execute plugin — guard: not activated # --------------------------------------------------------------------------- @when('I attempt to execute method on non-activated plugin "{name}"') def step_attempt_execute_non_activated(context: Context, name: str) -> None: context.caught_exception = None try: context.manager.execute_plugin( name, "search", "test query", scope=frozenset(), max_results=5, ) except PluginError as exc: context.caught_exception = exc @when('I attempt to execute method on unknown plugin "{name}"') def step_attempt_execute_unknown(context: Context, name: str) -> None: context.caught_exception = None try: context.manager.execute_plugin( name, "search", "test query", scope=frozenset(), max_results=5, ) except (PluginError, PluginNotFoundError) as exc: context.caught_exception = exc # --------------------------------------------------------------------------- # Thread safety: EXECUTING state prevents deactivation # --------------------------------------------------------------------------- @when('I manually set plugin "{name}" state to "{state}"') def step_manually_set_state(context: Context, name: str, state: str) -> None: descriptor = context.manager.get_plugin(name) descriptor.state = PluginState(state)