fix(cli): stop --print-effective-config from starting a container
argparse requires exactly one action, so the documented diagnostic form `--print-effective-config --config … --start` never took the early-return branch in main.run(): it printed the resolved config and then executed a real do_start(), silently replacing a running container with the [default] model. README, installation guide and manual all recommended that form. Make it an action in the mutually exclusive group. It can no longer be combined with --start/--check/--stop/--change/--chat (argparse error, exit 2), and it skips the docker_available() check, so it now really is the offline config check the docs promise. --dry-run remains a modifier and still requires a reachable daemon. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
218c3fc791
commit
3e3dd1c150
8 changed files with 104 additions and 30 deletions
|
|
@ -147,9 +147,16 @@ def test_dry_run_flag():
|
|||
assert args.dry_run is True
|
||||
|
||||
|
||||
def test_print_effective_config_flag():
|
||||
args = parse(["--start", "--print-effective-config"])
|
||||
def test_print_effective_config_is_a_standalone_action():
|
||||
args = parse(["--print-effective-config"])
|
||||
assert args.print_effective_config is True
|
||||
assert args.start is False
|
||||
|
||||
|
||||
def test_print_effective_config_cannot_be_combined_with_start():
|
||||
# Regression: as a plain flag this printed the config and then really started
|
||||
# the container, silently replacing a running one.
|
||||
parse_error(["--print-effective-config", "--start"])
|
||||
|
||||
|
||||
def test_reasoning_choice_validated():
|
||||
|
|
|
|||
57
tests/test_main.py
Normal file
57
tests/test_main.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from llamacppctl import actions, main as main_mod # noqa: E402
|
||||
|
||||
CONFIG = """
|
||||
[default]
|
||||
hf_home = /srv/models
|
||||
model_path = qwen3/default.gguf
|
||||
container_name = test_main
|
||||
"""
|
||||
|
||||
|
||||
def _config(tmp_path: Path) -> Path:
|
||||
path = tmp_path / "llama.cpp.config"
|
||||
path.write_text(CONFIG, encoding="utf-8")
|
||||
return path
|
||||
|
||||
|
||||
def test_print_effective_config_has_no_side_effects(tmp_path, monkeypatch, capsys):
|
||||
"""Regression: --print-effective-config used to fall through into do_start()
|
||||
because argparse always required an action, silently replacing a running
|
||||
container."""
|
||||
started = []
|
||||
monkeypatch.setattr(actions, "do_start", lambda *a, **k: started.append(1))
|
||||
monkeypatch.setattr(actions, "do_change", lambda *a, **k: started.append(1))
|
||||
monkeypatch.setattr(actions, "do_stop", lambda *a, **k: started.append(1))
|
||||
|
||||
rc = main_mod.run(["--print-effective-config", "--config", str(_config(tmp_path))])
|
||||
|
||||
assert rc == 0
|
||||
assert started == []
|
||||
assert '"container_name": "test_main"' in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_print_effective_config_works_without_docker(tmp_path, monkeypatch, capsys):
|
||||
"""It is a pure config check, so it must not require a reachable daemon."""
|
||||
def boom() -> bool:
|
||||
raise AssertionError("docker_available() must not be consulted")
|
||||
|
||||
monkeypatch.setattr(main_mod, "docker_available", boom)
|
||||
|
||||
rc = main_mod.run(["--print-effective-config", "--config", str(_config(tmp_path))])
|
||||
|
||||
assert rc == 0
|
||||
assert '"hf_home": "/srv/models"' in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_start_still_requires_docker(tmp_path, monkeypatch, capsys):
|
||||
monkeypatch.setattr(main_mod, "docker_available", lambda: False)
|
||||
|
||||
rc = main_mod.run(["--start", "--config", str(_config(tmp_path))])
|
||||
|
||||
assert rc == 1
|
||||
assert "docker is not available" in capsys.readouterr().err
|
||||
Loading…
Add table
Add a link
Reference in a new issue