abf250901f
CI / lint (pull_request) Successful in 21s
CI / quality (pull_request) Successful in 33s
CI / typecheck (pull_request) Successful in 51s
CI / security (pull_request) Successful in 1m1s
CI / build (pull_request) Successful in 17s
CI / helm (pull_request) Successful in 53s
CI / unit_tests (pull_request) Failing after 2m4s
CI / docker (pull_request) Has been skipped
CI / e2e_tests (pull_request) Successful in 17m4s
CI / integration_tests (pull_request) Successful in 23m32s
CI / coverage (pull_request) Failing after 2m7s
CI / status-check (pull_request) Failing after 2s
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Successful in 56m42s
The tool_type parameter in ToolRegistry.list_tools() was explicitly silenced with '_ = tool_type', making it a no-op. Both 'agents tool list --type validation' and 'agents tool list --type tool' returned the full unfiltered tool list. Changes: - src/cleveragents/tool/runtime.py: Add tool_type: Literal['tool', 'validation'] field to ToolSpec (default 'tool') so the registry has a discriminator to filter on. - src/cleveragents/tool/registry.py: Remove '_ = tool_type' no-op and implement actual filter: specs = [s for s in specs if s.tool_type == tool_type]. Update docstring to reflect the parameter is now active. - features/consolidated_tool.feature: Add 7 comprehensive Behave scenarios covering all filter combinations (tool, validation, None, default type, exclusion). - features/steps/tool_runtime_steps.py: Add step definition for registering a ToolSpec with an explicit tool_type. - robot/helper_tool_cli.py: Add tool_list_type_tool() and tool_list_type_validation() helper functions that verify the CLI passes tool_type through to the service layer. - robot/tool_cli.robot: Add two Robot test cases for 'tool list --type tool' and 'tool list --type validation'. The CLI handler (cli/commands/tool.py) and database repository (infrastructure/database/repositories.py) already passed tool_type through correctly — no changes needed there. ISSUES CLOSED: #2974
245 lines
7.5 KiB
Python
245 lines
7.5 KiB
Python
"""Helper script for tool_cli.robot smoke tests.
|
|
|
|
Each subcommand is a self-contained check that prints a sentinel on success.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
from typing import Any
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
# Ensure local source tree is importable
|
|
_SRC = str(Path(__file__).resolve().parents[1] / "src")
|
|
if _SRC not in sys.path:
|
|
sys.path.insert(0, _SRC)
|
|
|
|
from typer.testing import CliRunner # noqa: E402
|
|
|
|
from cleveragents.cli.commands.tool import app as tool_app # noqa: E402
|
|
from cleveragents.cli.commands.validation import app as validation_app # noqa: E402
|
|
|
|
runner = CliRunner()
|
|
|
|
_TOOL_YAML = """\
|
|
name: local/smoke-tool
|
|
description: Smoke test tool
|
|
source: custom
|
|
code: |
|
|
def run(inputs):
|
|
return {"result": True}
|
|
"""
|
|
|
|
_VALIDATION_YAML = """\
|
|
name: local/smoke-val
|
|
description: Smoke test validation
|
|
source: custom
|
|
mode: required
|
|
code: |
|
|
def run(inputs):
|
|
return {"passed": True}
|
|
"""
|
|
|
|
|
|
def _mock_tool(
|
|
name: str = "local/smoke-tool",
|
|
tool_type: str = "tool",
|
|
) -> dict[str, Any]:
|
|
ns, sn = name.split("/", 1)
|
|
return {
|
|
"name": name,
|
|
"description": f"Smoke test {tool_type}",
|
|
"source": "custom",
|
|
"tool_type": tool_type,
|
|
"namespace": ns,
|
|
"short_name": sn,
|
|
"capability": {"read_only": False, "writes": False, "checkpointable": False},
|
|
"timeout": 300,
|
|
}
|
|
|
|
|
|
def _mock_attachment() -> dict[str, Any]:
|
|
return {
|
|
"attachment_id": "att-smoke-001",
|
|
"validation_name": "local/smoke-val",
|
|
"resource_id": "resource/r1",
|
|
"mode": "required",
|
|
"project_name": None,
|
|
"plan_id": None,
|
|
"created_at": "2026-01-01T00:00:00",
|
|
}
|
|
|
|
|
|
def _write_yaml(content: str) -> str:
|
|
fd, path = tempfile.mkstemp(suffix=".yaml")
|
|
with os.fdopen(fd, "w") as fh:
|
|
fh.write(content)
|
|
return path
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Subcommands
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def tool_add_config() -> None:
|
|
"""Verify tool add --config works."""
|
|
path = _write_yaml(_TOOL_YAML)
|
|
try:
|
|
svc = MagicMock()
|
|
svc.register_tool.return_value = _mock_tool()
|
|
svc.get_tool.return_value = None
|
|
with patch(
|
|
"cleveragents.cli.commands.tool._get_tool_registry_service",
|
|
return_value=svc,
|
|
):
|
|
result = runner.invoke(tool_app, ["add", "--config", path])
|
|
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
|
|
print("tool-cli-add-config-ok")
|
|
finally:
|
|
Path(path).unlink(missing_ok=True)
|
|
|
|
|
|
def tool_list_all() -> None:
|
|
"""Verify tool list works."""
|
|
svc = MagicMock()
|
|
svc.list_tools.return_value = [_mock_tool()]
|
|
with patch(
|
|
"cleveragents.cli.commands.tool._get_tool_registry_service",
|
|
return_value=svc,
|
|
):
|
|
result = runner.invoke(tool_app, ["list"])
|
|
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
|
|
print("tool-cli-list-ok")
|
|
|
|
|
|
def tool_show_name() -> None:
|
|
"""Verify tool show accepts a namespaced name."""
|
|
svc = MagicMock()
|
|
svc.get_tool.return_value = _mock_tool()
|
|
with patch(
|
|
"cleveragents.cli.commands.tool._get_tool_registry_service",
|
|
return_value=svc,
|
|
):
|
|
result = runner.invoke(tool_app, ["show", "local/smoke-tool"])
|
|
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
|
|
print("tool-cli-show-ok")
|
|
|
|
|
|
def tool_remove_name() -> None:
|
|
"""Verify tool remove accepts a namespaced name."""
|
|
svc = MagicMock()
|
|
svc.get_tool.return_value = _mock_tool()
|
|
svc.remove_tool.return_value = True
|
|
with patch(
|
|
"cleveragents.cli.commands.tool._get_tool_registry_service",
|
|
return_value=svc,
|
|
):
|
|
result = runner.invoke(tool_app, ["remove", "local/smoke-tool", "--yes"])
|
|
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
|
|
print("tool-cli-remove-ok")
|
|
|
|
|
|
def validation_add_config() -> None:
|
|
"""Verify validation add --config works."""
|
|
path = _write_yaml(_VALIDATION_YAML)
|
|
try:
|
|
svc = MagicMock()
|
|
svc.register_tool.return_value = _mock_tool("local/smoke-val", "validation")
|
|
svc.get_tool.return_value = None
|
|
with patch(
|
|
"cleveragents.cli.commands.validation._get_tool_registry_service",
|
|
return_value=svc,
|
|
):
|
|
result = runner.invoke(validation_app, ["add", "--config", path])
|
|
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
|
|
print("validation-cli-add-ok")
|
|
finally:
|
|
Path(path).unlink(missing_ok=True)
|
|
|
|
|
|
def validation_attach() -> None:
|
|
"""Verify validation attach works."""
|
|
svc = MagicMock()
|
|
svc.attach_validation.return_value = _mock_attachment()
|
|
with patch(
|
|
"cleveragents.cli.commands.validation._get_tool_registry_service",
|
|
return_value=svc,
|
|
):
|
|
result = runner.invoke(
|
|
validation_app,
|
|
["attach", "resource/r1", "local/smoke-val"],
|
|
)
|
|
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
|
|
print("validation-cli-attach-ok")
|
|
|
|
|
|
def validation_detach() -> None:
|
|
"""Verify validation detach works."""
|
|
svc = MagicMock()
|
|
svc.detach_validation.return_value = True
|
|
with patch(
|
|
"cleveragents.cli.commands.validation._get_tool_registry_service",
|
|
return_value=svc,
|
|
):
|
|
result = runner.invoke(
|
|
validation_app,
|
|
["detach", "att-smoke-001", "--yes"],
|
|
)
|
|
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
|
|
print("validation-cli-detach-ok")
|
|
|
|
|
|
def tool_list_type_tool() -> None:
|
|
"""Verify tool list --type tool returns only tools."""
|
|
svc = MagicMock()
|
|
svc.list_tools.return_value = [_mock_tool("local/smoke-tool", "tool")]
|
|
with patch(
|
|
"cleveragents.cli.commands.tool._get_tool_registry_service",
|
|
return_value=svc,
|
|
):
|
|
result = runner.invoke(tool_app, ["list", "--type", "tool"])
|
|
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
|
|
svc.list_tools.assert_called_once_with(
|
|
namespace=None, tool_type="tool", source=None
|
|
)
|
|
print("tool-cli-list-type-tool-ok")
|
|
|
|
|
|
def tool_list_type_validation() -> None:
|
|
"""Verify tool list --type validation returns only validations."""
|
|
svc = MagicMock()
|
|
svc.list_tools.return_value = [_mock_tool("local/smoke-val", "validation")]
|
|
with patch(
|
|
"cleveragents.cli.commands.tool._get_tool_registry_service",
|
|
return_value=svc,
|
|
):
|
|
result = runner.invoke(tool_app, ["list", "--type", "validation"])
|
|
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
|
|
svc.list_tools.assert_called_once_with(
|
|
namespace=None, tool_type="validation", source=None
|
|
)
|
|
print("tool-cli-list-type-validation-ok")
|
|
|
|
|
|
_COMMANDS = {
|
|
"tool-add-config": tool_add_config,
|
|
"tool-list": tool_list_all,
|
|
"tool-list-type-tool": tool_list_type_tool,
|
|
"tool-list-type-validation": tool_list_type_validation,
|
|
"tool-show": tool_show_name,
|
|
"tool-remove": tool_remove_name,
|
|
"validation-add-config": validation_add_config,
|
|
"validation-attach": validation_attach,
|
|
"validation-detach": validation_detach,
|
|
}
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS:
|
|
print(f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>")
|
|
sys.exit(1)
|
|
_COMMANDS[sys.argv[1]]()
|