d55e610f90
Add mock-based @given steps and 14 new scenarios covering the rich table list, show, enable, disable, remove happy paths, the abort confirmation flow, and the description truncation branch (plugin.py:134). The @when step now patches _get_plugin_manager via context so PluginManager isolation works without a singleton. ISSUES CLOSED: #5756
95 lines
3.1 KiB
Python
95 lines
3.1 KiB
Python
"""Step definitions for plugin CLI commands."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import Any
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from behave import given, then, when
|
|
|
|
from cleveragents.infrastructure.plugins.types import PluginState
|
|
|
|
|
|
def _make_plugin_manager(
|
|
state: PluginState, description: str = "A test plugin"
|
|
) -> MagicMock:
|
|
"""Create a mock PluginManager with one test plugin in the given state."""
|
|
descriptor = MagicMock()
|
|
descriptor.name = "test-plugin"
|
|
descriptor.version = "1.0.0"
|
|
descriptor.description = description
|
|
descriptor.module_path = "test.module"
|
|
descriptor.class_name = "TestPlugin"
|
|
descriptor.state = state
|
|
|
|
manager = MagicMock()
|
|
manager.list_plugins.return_value = [descriptor]
|
|
manager.get_plugin.return_value = descriptor
|
|
return manager
|
|
|
|
|
|
@given("a plugin manager with one active plugin")
|
|
def step_plugin_manager_active(context: Any) -> None:
|
|
"""Set up a mock PluginManager with one ACTIVATED plugin."""
|
|
context.plugin_manager = _make_plugin_manager(PluginState.ACTIVATED)
|
|
|
|
|
|
@given("a plugin manager with one inactive plugin")
|
|
def step_plugin_manager_inactive(context: Any) -> None:
|
|
"""Set up a mock PluginManager with one DEACTIVATED plugin."""
|
|
context.plugin_manager = _make_plugin_manager(PluginState.DEACTIVATED)
|
|
|
|
|
|
@given("a plugin manager with one plugin having a long description")
|
|
def step_plugin_manager_long_desc(context: Any) -> None:
|
|
"""Set up a mock PluginManager with a plugin whose description exceeds 40 chars."""
|
|
context.plugin_manager = _make_plugin_manager(
|
|
PluginState.ACTIVATED,
|
|
description="A" * 50,
|
|
)
|
|
|
|
|
|
@given('the next prompt answer is "{answer}"')
|
|
def step_set_prompt_answer(context: Any, answer: str) -> None:
|
|
"""Store stdin input to be used by the next plugin CLI invocation."""
|
|
context.cli_input = answer + "\n"
|
|
|
|
|
|
@when('I run "agents plugin {args}"')
|
|
def step_run_agents_plugin(context: Any, args: str) -> None:
|
|
"""Invoke a plugin CLI command and store output in context.command_output."""
|
|
from typer.testing import CliRunner
|
|
|
|
from cleveragents.cli.main import app
|
|
|
|
plugin_manager = getattr(context, "plugin_manager", None)
|
|
stdin = getattr(context, "cli_input", None)
|
|
runner = CliRunner()
|
|
split_args = args.split()
|
|
|
|
if plugin_manager is not None:
|
|
with patch(
|
|
"cleveragents.cli.commands.plugin._get_plugin_manager",
|
|
return_value=plugin_manager,
|
|
):
|
|
result = runner.invoke(
|
|
app, ["plugin", *split_args], input=stdin, catch_exceptions=True
|
|
)
|
|
else:
|
|
result = runner.invoke(
|
|
app, ["plugin", *split_args], input=stdin, catch_exceptions=True
|
|
)
|
|
|
|
context.command_output = result.output or ""
|
|
|
|
|
|
@then("the plugin CLI output should be valid JSON")
|
|
def step_plugin_output_is_json(context: Any) -> None:
|
|
"""Check if plugin CLI output is valid JSON."""
|
|
output = getattr(context, "command_output", "")
|
|
try:
|
|
context.json_output = json.loads(output)
|
|
except json.JSONDecodeError as e:
|
|
raise AssertionError(f"Output is not valid JSON: {e}") from e
|