feat(providers): implement Packet S4 agent-harness-setup CLI wrapper and provider handoff
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
"""Tests for Packet S4: CmdForge CLI integration with agent-harness-setup."""
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from cmdforge.cli import main
|
||||
from cmdforge.cli.provider_commands import _find_setup_helper, _get_provider_defaults_for_harness
|
||||
from cmdforge.providers import Provider, get_provider, load_providers, save_providers
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def temp_providers_file(tmp_path):
|
||||
providers_file = tmp_path / ".cmdforge" / "providers.yaml"
|
||||
with patch("cmdforge.providers.PROVIDERS_FILE", providers_file):
|
||||
save_providers([])
|
||||
yield providers_file
|
||||
|
||||
|
||||
def _mock_helper_run(status: str, harness_id: str = "claude-code", exit_code: int = 0, diagnostics: str | None = None, executable_path: str = "/usr/local/bin/claude"):
|
||||
"""Create a mock subprocess.run that writes a valid completion result to --result-file."""
|
||||
def _run(cmd, *args, **kwargs):
|
||||
# Extract --result-file and --request-id from cmd
|
||||
res_idx = cmd.index("--result-file")
|
||||
result_file_path = Path(cmd[res_idx + 1])
|
||||
req_idx = cmd.index("--request-id")
|
||||
request_id = cmd[req_idx + 1]
|
||||
|
||||
data = {
|
||||
"schema_version": 1,
|
||||
"helper_version": "0.1.0",
|
||||
"request_id": request_id,
|
||||
"timestamp": "2026-09-11T22:00:00Z",
|
||||
"status": status,
|
||||
"harness_id": harness_id,
|
||||
"recipe_id": harness_id,
|
||||
"recipe_version": "2026-09-10",
|
||||
"install_method": "argv",
|
||||
"installer_exit_code": 0 if status == "success" else None,
|
||||
"verification": {
|
||||
"verified": True,
|
||||
"executable_path": executable_path,
|
||||
"version_output": f"{harness_id} 1.0.0",
|
||||
} if status == "success" else None,
|
||||
"path_updates": ["/usr/local/bin"],
|
||||
"auth_status": "setup_required",
|
||||
"manual_steps_remaining": ["Run 'claude' to complete browser sign-in."],
|
||||
"partial_changes": {
|
||||
"observed_created": [executable_path] if status == "success" else [],
|
||||
"observed_modified": [],
|
||||
"unknown_changes": True,
|
||||
},
|
||||
"error_diagnostics": diagnostics,
|
||||
}
|
||||
result_file_path.write_text(json.dumps(data), encoding="utf-8")
|
||||
return SimpleNamespace(returncode=exit_code)
|
||||
|
||||
return _run
|
||||
|
||||
|
||||
def test_helper_discovery_finds_executable():
|
||||
"""Verify _find_setup_helper locates agent-harness-setup."""
|
||||
with patch("shutil.which", return_value="/usr/local/bin/agent-harness-setup"):
|
||||
helper = _find_setup_helper()
|
||||
assert helper == ["/usr/local/bin/agent-harness-setup"]
|
||||
|
||||
|
||||
def test_get_provider_defaults_for_claude():
|
||||
"""Verify provider defaults derived for claude-code."""
|
||||
defaults = _get_provider_defaults_for_harness("claude-code", "/custom/bin/claude")
|
||||
assert defaults["name"] == "claude"
|
||||
assert defaults["command"] == "/custom/bin/claude -p"
|
||||
assert "Claude" in defaults["description"]
|
||||
|
||||
|
||||
def test_providers_install_verified_success_registers_provider(temp_providers_file, capsys):
|
||||
"""Test verified installation prompts and registers provider in providers.yaml."""
|
||||
with (
|
||||
patch("cmdforge.cli.provider_commands._find_setup_helper", return_value=["agent-harness-setup"]),
|
||||
patch("subprocess.run", side_effect=_mock_helper_run("success", "claude-code", 0)) as mock_run,
|
||||
patch("builtins.input", return_value="y"),
|
||||
patch("sys.argv", ["cmdforge", "providers", "install", "--harness", "claude-code"]),
|
||||
):
|
||||
exit_code = main()
|
||||
|
||||
assert exit_code == 0
|
||||
captured = capsys.readouterr()
|
||||
assert "Harness Setup Verified: claude-code" in captured.out
|
||||
assert "Provider 'claude' successfully added" in captured.out
|
||||
|
||||
# Verify provider stored in providers.yaml
|
||||
provider = get_provider("claude")
|
||||
assert provider is not None
|
||||
assert provider.command == "/usr/local/bin/claude -p"
|
||||
assert "code" in provider.tags
|
||||
|
||||
# Verify subprocess invocation arguments
|
||||
call_args = mock_run.call_args[0][0]
|
||||
assert call_args[0] == "agent-harness-setup"
|
||||
assert "--result-file" in call_args
|
||||
assert "--request-id" in call_args
|
||||
assert "--harness" in call_args
|
||||
assert "claude-code" in call_args
|
||||
|
||||
|
||||
def test_providers_install_declined_registration(temp_providers_file, capsys):
|
||||
"""Test declining the provider registration prompt leaves providers.yaml unchanged."""
|
||||
with (
|
||||
patch("cmdforge.cli.provider_commands._find_setup_helper", return_value=["agent-harness-setup"]),
|
||||
patch("subprocess.run", side_effect=_mock_helper_run("success", "claude-code", 0)),
|
||||
patch("builtins.input", return_value="n"),
|
||||
patch("sys.argv", ["cmdforge", "providers", "install"]),
|
||||
):
|
||||
exit_code = main()
|
||||
|
||||
assert exit_code == 0
|
||||
captured = capsys.readouterr()
|
||||
assert "Harness Setup Verified: claude-code" in captured.out
|
||||
assert "Skipped provider registration" in captured.out
|
||||
|
||||
assert get_provider("claude") is None
|
||||
|
||||
|
||||
def test_providers_install_already_configured(temp_providers_file, capsys):
|
||||
"""Test that existing provider is recognized and not re-registered."""
|
||||
save_providers([Provider("claude", "existing-claude -p")])
|
||||
|
||||
with (
|
||||
patch("cmdforge.cli.provider_commands._find_setup_helper", return_value=["agent-harness-setup"]),
|
||||
patch("subprocess.run", side_effect=_mock_helper_run("success", "claude-code", 0)),
|
||||
patch("sys.argv", ["cmdforge", "providers", "install"]),
|
||||
):
|
||||
exit_code = main()
|
||||
|
||||
assert exit_code == 0
|
||||
captured = capsys.readouterr()
|
||||
assert "already configured in ~/.cmdforge/providers.yaml" in captured.out
|
||||
assert get_provider("claude").command == "existing-claude -p"
|
||||
|
||||
|
||||
def test_providers_install_cancelled(temp_providers_file, capsys):
|
||||
"""Test cancellation exits cleanly without changing providers."""
|
||||
with (
|
||||
patch("cmdforge.cli.provider_commands._find_setup_helper", return_value=["agent-harness-setup"]),
|
||||
patch("subprocess.run", side_effect=_mock_helper_run("cancelled", "claude-code", 10)),
|
||||
patch("sys.argv", ["cmdforge", "providers", "install"]),
|
||||
):
|
||||
exit_code = main()
|
||||
|
||||
assert exit_code == 0
|
||||
captured = capsys.readouterr()
|
||||
assert "Setup cancelled. No provider configuration was changed." in captured.out
|
||||
assert get_provider("claude") is None
|
||||
|
||||
|
||||
def test_providers_install_execution_failure(temp_providers_file, capsys):
|
||||
"""Test helper failure reports error diagnostics and does not register provider."""
|
||||
with (
|
||||
patch("cmdforge.cli.provider_commands._find_setup_helper", return_value=["agent-harness-setup"]),
|
||||
patch("subprocess.run", side_effect=_mock_helper_run("failed", "claude-code", 1, "Missing node.js")),
|
||||
patch("sys.argv", ["cmdforge", "providers", "install"]),
|
||||
):
|
||||
exit_code = main()
|
||||
|
||||
assert exit_code == 1
|
||||
captured = capsys.readouterr()
|
||||
assert "Setup failed: Missing node.js" in captured.out
|
||||
assert get_provider("claude") is None
|
||||
|
||||
|
||||
def test_providers_install_incoherent_result_rejected(temp_providers_file, capsys):
|
||||
"""Test exit code 1 with status 'success' is rejected as incoherent."""
|
||||
with (
|
||||
patch("cmdforge.cli.provider_commands._find_setup_helper", return_value=["agent-harness-setup"]),
|
||||
patch("subprocess.run", side_effect=_mock_helper_run("success", "claude-code", 1)),
|
||||
patch("sys.argv", ["cmdforge", "providers", "install"]),
|
||||
):
|
||||
exit_code = main()
|
||||
|
||||
assert exit_code == 1
|
||||
captured = capsys.readouterr()
|
||||
assert "Treating as failure" in captured.out
|
||||
assert get_provider("claude") is None
|
||||
|
||||
|
||||
def test_providers_install_helper_not_found(temp_providers_file, capsys):
|
||||
"""Test that missing helper prints actionable instructions without crashing."""
|
||||
with (
|
||||
patch("cmdforge.cli.provider_commands._find_setup_helper", return_value=None),
|
||||
patch("sys.argv", ["cmdforge", "providers", "install"]),
|
||||
):
|
||||
exit_code = main()
|
||||
|
||||
assert exit_code == 0
|
||||
captured = capsys.readouterr()
|
||||
assert "agent-harness-setup" in captured.out
|
||||
assert "pip install" in captured.out
|
||||
assert "PYTHONPATH=" in captured.out
|
||||
|
||||
|
||||
def test_providers_install_dry_run(temp_providers_file, capsys):
|
||||
"""Test --dry-run passes flag to helper and makes no provider changes."""
|
||||
with (
|
||||
patch("cmdforge.cli.provider_commands._find_setup_helper", return_value=["agent-harness-setup"]),
|
||||
patch("subprocess.run", side_effect=_mock_helper_run("dry_run", "claude-code", 0)) as mock_run,
|
||||
patch("sys.argv", ["cmdforge", "providers", "install", "--dry-run"]),
|
||||
):
|
||||
exit_code = main()
|
||||
|
||||
assert exit_code == 0
|
||||
call_args = mock_run.call_args[0][0]
|
||||
assert "--dry-run" in call_args
|
||||
assert get_provider("claude") is None
|
||||
Reference in New Issue
Block a user