llamacppctl/tests/test_cli.py
dschlueter f83f36fdfb test: close coverage gaps + opt-in integration smoke
Unit-Tests (kein Docker nötig), 130 Tests gesamt:
- config: ${ENV}- und ~-Expansion in hf_home
- prompt_io: _pin_dns (DNS-Rebinding wird abgewiesen, andere Hosts unberührt,
  Resolver wird wiederhergestellt)
- actions: do_check (running/healthy, missing), do_start non-dry-run
  (Happy-Path + Readiness-Fehler mit --logs), _container_lock Force-Bypass
- cli: --max-tokens <= 0 abgelehnt, --expose/--no-expose

scripts/smoke.sh: opt-in End-to-End-Test gegen echten Docker + llama.cpp-Server
(--api-key-Round-Trip inkl. 401/200, start/check/chat/stream/stop, eigener
Container/Port, Cleanup-Trap).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 17:08:10 +02:00

161 lines
4 KiB
Python

import sys
from pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from llamacppctl.cli import build_parser, validate_args # noqa: E402
def parse(argv):
parser = build_parser()
args = parser.parse_args(argv)
validate_args(args, parser)
return args
def parse_error(argv):
parser = build_parser()
with pytest.raises(SystemExit):
args = parser.parse_args(argv)
validate_args(args, parser)
def test_requires_one_action():
parse_error([])
def test_actions_are_mutually_exclusive():
parse_error(["--start", "--stop"])
def test_start_is_valid_minimal():
args = parse(["--start"])
assert args.start is True
def test_chat_requires_prompt_source():
parse_error(["--chat"])
def test_max_tokens_nonpositive_rejected():
parse_error(["--start", "--max-tokens", "0"])
parse_error(["--start", "--max-tokens", "-5"])
def test_max_tokens_positive_ok():
args = parse(["--start", "--max-tokens", "4096"])
assert args.max_tokens == 4096
def test_expose_and_no_expose_flags():
assert parse(["--start", "--expose"]).expose is True
assert parse(["--start", "--no-expose"]).expose is False
assert parse(["--start"]).expose is None # unset => config decides
def test_chat_with_prompt_is_valid():
args = parse(["--chat", "-p", "hello"])
assert args.chat is True
assert args.prompt == "hello"
def test_stop_rejects_prompt_sources():
parse_error(["--stop", "-p", "hello"])
def test_stop_rejects_system_sources():
parse_error(["--stop", "-s", "system text"])
def test_check_with_system_but_no_prompt_rejected():
parse_error(["--check", "-s", "system text"])
def test_check_with_system_and_prompt_ok():
args = parse(["--check", "-s", "system text", "-p", "hello"])
assert args.check is True
def test_mutually_exclusive_system_sources():
parse_error(["--start", "-s", "a", "--system-file", "b.txt"])
def test_mutually_exclusive_prompt_sources():
parse_error(["--start", "-p", "a", "--prompt-url", "https://example.com/p"])
def test_max_input_bytes_must_be_positive():
parse_error(["--start", "--max-input-bytes", "0"])
def test_negative_max_input_bytes_rejected():
parse_error(["--start", "--max-input-bytes", "-5"])
def test_connect_timeout_must_be_positive():
parse_error(["--start", "--connect-timeout", "0"])
def test_read_timeout_must_be_positive():
parse_error(["--start", "--read-timeout", "-1"])
def test_allow_private_url_conflicts_with_allowlist():
parse_error(["--start", "--allow-private-url", "--url-allow-host", "example.com"])
def test_host_port_out_of_range_rejected():
parse_error(["--start", "--host-port", "70000"])
def test_host_port_zero_rejected():
parse_error(["--start", "--host-port", "0"])
def test_container_port_out_of_range_rejected():
parse_error(["--start", "--container-port", "-1"])
def test_container_name_empty_string_rejected():
parse_error(["--start", "--container-name", " "])
def test_container_name_valid():
args = parse(["--start", "--container-name", "my_llm"])
assert args.container_name == "my_llm"
def test_boolean_flag_pairs_default_none():
args = parse(["--start"])
assert args.jinja is None
assert args.fa is None
assert args.kv_unified is None
assert args.cont_batching is None
assert args.no_context_shift is None
def test_boolean_flag_pairs_explicit_true_false():
args = parse(["--start", "--jinja", "--no-fa"])
assert args.jinja is True
assert args.fa is False
def test_dry_run_flag():
args = parse(["--start", "--dry-run"])
assert args.dry_run is True
def test_print_effective_config_flag():
args = parse(["--start", "--print-effective-config"])
assert args.print_effective_config is True
def test_reasoning_choice_validated():
parse_error(["--start", "--reasoning", "maybe"])
def test_reasoning_choice_valid():
args = parse(["--start", "--reasoning", "off"])
assert args.reasoning == "off"