mirror of
https://github.com/katanemo/plano.git
synced 2026-06-23 15:38:07 +02:00
feat(cli): add planoai launch group + claude-desktop integration
This commit is contained in:
parent
b71a555f19
commit
151d3a83c5
11 changed files with 2500 additions and 76 deletions
231
cli/test/test_launch_cmd.py
Normal file
231
cli/test/test_launch_cmd.py
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
"""Tests for the `planoai launch claude-desktop` click command.
|
||||
|
||||
Focused on the wiring between the CLI flags and the underlying
|
||||
`claude_desktop` module / `up` invocation. The actual JSON-rewriting and key
|
||||
validation are covered in `test_claude_desktop.py`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from click.testing import CliRunner
|
||||
|
||||
from planoai import claude_desktop as cd
|
||||
from planoai import launch_cmd as lc
|
||||
|
||||
|
||||
def _stub_cd(monkeypatch):
|
||||
"""Replace ``claude_desktop`` side-effects with no-ops + call recorders."""
|
||||
calls: dict[str, list] = {
|
||||
"configure": [],
|
||||
"restore": [],
|
||||
"launch_or_restart": [],
|
||||
}
|
||||
monkeypatch.setattr(cd, "supported", lambda: None)
|
||||
monkeypatch.setattr(
|
||||
cd,
|
||||
"configure",
|
||||
lambda base_url, **_kw: calls["configure"].append(base_url),
|
||||
)
|
||||
monkeypatch.setattr(cd, "restore", lambda: calls["restore"].append(True))
|
||||
monkeypatch.setattr(
|
||||
cd,
|
||||
"launch_or_restart",
|
||||
lambda prompt, yes: calls["launch_or_restart"].append((prompt, yes)),
|
||||
)
|
||||
return calls
|
||||
|
||||
|
||||
def test_config_path_starts_plano_when_not_running(tmp_path, monkeypatch):
|
||||
config = tmp_path / "plano_config.yaml"
|
||||
config.write_text(
|
||||
"version: v0.4.0\n"
|
||||
"listeners:\n"
|
||||
" - name: llm\n"
|
||||
" type: model\n"
|
||||
" port: 12345\n"
|
||||
" address: 0.0.0.0\n"
|
||||
"model_providers: []\n"
|
||||
)
|
||||
|
||||
cd_calls = _stub_cd(monkeypatch)
|
||||
monkeypatch.setattr(lc, "_is_plano_running", lambda: False)
|
||||
|
||||
up_calls = []
|
||||
|
||||
def fake_up(
|
||||
file,
|
||||
path,
|
||||
foreground,
|
||||
with_tracing,
|
||||
tracing_port,
|
||||
docker,
|
||||
verbose,
|
||||
listener_port,
|
||||
):
|
||||
up_calls.append(
|
||||
{
|
||||
"file": file,
|
||||
"foreground": foreground,
|
||||
"docker": docker,
|
||||
"listener_port": listener_port,
|
||||
}
|
||||
)
|
||||
|
||||
from planoai.main import up as up_cmd
|
||||
|
||||
monkeypatch.setattr(up_cmd, "callback", fake_up)
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
lc.launch,
|
||||
["claude-desktop", "--config", str(config), "--yes"],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
assert len(up_calls) == 1
|
||||
assert up_calls[0]["file"] == str(config)
|
||||
assert up_calls[0]["foreground"] is False
|
||||
assert cd_calls["configure"] == ["http://localhost:12345"]
|
||||
# --yes implies we restart Claude Desktop after configuring.
|
||||
assert cd_calls["launch_or_restart"]
|
||||
assert cd_calls["launch_or_restart"][0][1] is True
|
||||
|
||||
|
||||
def test_config_path_skips_up_when_plano_already_running(tmp_path, monkeypatch):
|
||||
config = tmp_path / "plano_config.yaml"
|
||||
config.write_text(
|
||||
"version: v0.4.0\n"
|
||||
"listeners:\n"
|
||||
" - name: llm\n"
|
||||
" type: model\n"
|
||||
" port: 12500\n"
|
||||
"model_providers: []\n"
|
||||
)
|
||||
|
||||
cd_calls = _stub_cd(monkeypatch)
|
||||
monkeypatch.setattr(lc, "_is_plano_running", lambda: True)
|
||||
|
||||
sentinel = []
|
||||
|
||||
def boom(*args, **kwargs):
|
||||
sentinel.append("called")
|
||||
|
||||
from planoai.main import up as up_cmd
|
||||
|
||||
monkeypatch.setattr(up_cmd, "callback", boom)
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
lc.launch,
|
||||
["claude-desktop", "--config", str(config), "--no-launch"],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
assert sentinel == [], "should not invoke up.callback when Plano is already running"
|
||||
assert cd_calls["configure"] == ["http://localhost:12500"]
|
||||
# --no-launch skips the restart step.
|
||||
assert cd_calls["launch_or_restart"] == []
|
||||
|
||||
|
||||
def test_config_path_must_exist(tmp_path, monkeypatch):
|
||||
cd_calls = _stub_cd(monkeypatch)
|
||||
monkeypatch.setattr(lc, "_is_plano_running", lambda: False)
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
lc.launch,
|
||||
["claude-desktop", "--config", str(tmp_path / "nope.yaml")],
|
||||
)
|
||||
|
||||
assert result.exit_code != 0
|
||||
assert "not found" in result.output.lower()
|
||||
assert cd_calls["configure"] == []
|
||||
|
||||
|
||||
def test_no_launch_skips_open(monkeypatch):
|
||||
cd_calls = _stub_cd(monkeypatch)
|
||||
monkeypatch.setattr(lc, "_is_plano_running", lambda: True)
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
lc.launch,
|
||||
["claude-desktop", "--no-launch", "--base-url", "http://localhost:9999"],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
assert cd_calls["configure"] == ["http://localhost:9999"]
|
||||
assert cd_calls["launch_or_restart"] == []
|
||||
|
||||
|
||||
def test_restore_ignores_config_path(tmp_path, monkeypatch):
|
||||
config = tmp_path / "plano_config.yaml"
|
||||
config.write_text("version: v0.4.0\nmodel_providers: []\n")
|
||||
|
||||
cd_calls = _stub_cd(monkeypatch)
|
||||
monkeypatch.setattr(lc, "_is_plano_running", lambda: True)
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
lc.launch,
|
||||
["claude-desktop", "--restore", "--config", str(config), "--yes"],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
assert cd_calls["restore"] == [True]
|
||||
assert cd_calls["configure"] == []
|
||||
assert "ignored" in result.output.lower()
|
||||
|
||||
|
||||
def test_base_url_overrides_config_file(tmp_path, monkeypatch):
|
||||
config = tmp_path / "plano_config.yaml"
|
||||
config.write_text(
|
||||
"version: v0.4.0\n"
|
||||
"listeners:\n"
|
||||
" - name: llm\n"
|
||||
" type: model\n"
|
||||
" port: 12345\n"
|
||||
"model_providers: []\n"
|
||||
)
|
||||
|
||||
cd_calls = _stub_cd(monkeypatch)
|
||||
monkeypatch.setattr(lc, "_is_plano_running", lambda: True)
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
lc.launch,
|
||||
[
|
||||
"claude-desktop",
|
||||
"--config",
|
||||
str(config),
|
||||
"--base-url",
|
||||
"http://10.0.0.5:8080",
|
||||
"--no-launch",
|
||||
],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
assert cd_calls["configure"] == ["http://10.0.0.5:8080"]
|
||||
|
||||
|
||||
def test_unsupported_platform_errors(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
cd,
|
||||
"supported",
|
||||
lambda: "Claude Desktop launch is only supported on macOS and Windows",
|
||||
)
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(lc.launch, ["claude-desktop"])
|
||||
|
||||
assert result.exit_code != 0
|
||||
assert "macOS" in result.output
|
||||
|
||||
|
||||
def test_help_lists_new_flags(monkeypatch):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(lc.launch, ["claude-desktop", "--help"])
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
assert "--config" in result.output
|
||||
assert "--no-launch" in result.output
|
||||
assert "--restore" in result.output
|
||||
Loading…
Add table
Add a link
Reference in a new issue