| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- """Tests for generation destination resolution."""
- from __future__ import annotations
- from pathlib import Path
- from typing import Any
- import pytest
- from cli.core.input import InputManager
- from cli.core.module.generation_destination import (
- build_remote_shell_path,
- build_scp_remote_target,
- prompt_generation_destination,
- resolve_cli_destination,
- resolve_remote_upload_target,
- )
- def test_resolve_cli_destination_uses_remote_defaults() -> None:
- """Remote generation should default the remote path to the template slug."""
- destination = resolve_cli_destination(
- output=None,
- remote="deploy",
- remote_path=None,
- slug="whoami",
- )
- assert destination is not None
- assert destination.is_remote is True
- assert destination.remote_host == "deploy"
- assert destination.remote_path == "~/whoami"
- def test_resolve_cli_destination_rejects_mixed_local_and_remote_flags() -> None:
- """Local and remote destination flags are mutually exclusive."""
- with pytest.raises(ValueError, match="either --output"):
- resolve_cli_destination(
- output="./out",
- remote="deploy",
- remote_path=None,
- slug="whoami",
- )
- def test_build_remote_shell_path_quotes_home_paths_with_spaces() -> None:
- """SSH mkdir paths should stay safe when the target contains spaces."""
- assert build_remote_shell_path("~/My Dir/app", trailing_slash=True) == "\"$HOME\"/'My Dir/app/'"
- def test_build_scp_remote_target_preserves_home_expansion() -> None:
- """SCP targets should quote already-resolved absolute paths safely."""
- assert (
- build_scp_remote_target("deploy", "/home/test/My Dir/app", trailing_slash=True)
- == "deploy:'/home/test/My Dir/app/'"
- )
- def test_resolve_remote_upload_target_expands_home_via_ssh(monkeypatch: pytest.MonkeyPatch) -> None:
- """SCP uploads should resolve ~ to the remote home directory before copying."""
- class _Result:
- def __init__(self, returncode: int, stdout: str = "", stderr: str = "") -> None:
- self.returncode = returncode
- self.stdout = stdout
- self.stderr = stderr
- def fake_run(args: list[str], check: bool, capture_output: bool, text: bool) -> _Result:
- del check, capture_output, text
- assert args == ["ssh", "deploy", "printf '%s' \"$HOME\""]
- return _Result(returncode=0, stdout="/home/deploy")
- monkeypatch.setattr("cli.core.module.generation_destination.subprocess.run", fake_run)
- assert resolve_remote_upload_target("deploy", "~/dockhand", trailing_slash=True) == "deploy:/home/deploy/dockhand/"
- def test_numbered_choice_accepts_numeric_selection(monkeypatch: pytest.MonkeyPatch) -> None:
- """Numbered choices should accept the displayed index."""
- responses = iter(["2"])
- def fake_ask(*args: Any, **kwargs: Any) -> str:
- del args, kwargs
- return next(responses)
- monkeypatch.setattr("cli.core.input.input_manager.Prompt.ask", fake_ask)
- input_mgr = InputManager()
- assert input_mgr.numbered_choice("Store generated template in", ["local", "remote"], default="local") == "remote"
- def test_numbered_choice_uses_numeric_default(monkeypatch: pytest.MonkeyPatch) -> None:
- """Numbered choices should display a clean numeric default."""
- captured: dict[str, Any] = {}
- def fake_ask(prompt: str, default: str = "", show_default: bool = False, **kwargs: Any) -> str:
- del prompt, kwargs
- captured["default"] = default
- captured["show_default"] = show_default
- return ""
- monkeypatch.setattr("cli.core.input.input_manager.Prompt.ask", fake_ask)
- input_mgr = InputManager()
- assert input_mgr.numbered_choice("Store generated template in", ["local", "remote"], default="local") == "local"
- assert captured == {"default": "1", "show_default": True}
- def test_prompt_generation_destination_uses_numbered_choice_for_local(
- monkeypatch: pytest.MonkeyPatch,
- tmp_path: Path,
- ) -> None:
- """Interactive destination selection should resolve local output via numbered choices."""
- calls: list[tuple[str, tuple[Any, ...], dict[str, Any]]] = []
- def fake_numbered_choice(self, prompt: str, choices: list[str], default: str | None = None) -> str:
- calls.append(("numbered_choice", (prompt, tuple(choices)), {"default": default}))
- del self
- return "local"
- def fake_text(self, prompt: str, default: str | None = None, **kwargs: Any) -> str:
- calls.append(("text", (prompt,), {"default": default, **kwargs}))
- del self
- return str(tmp_path / "whoami")
- monkeypatch.setattr(InputManager, "numbered_choice", fake_numbered_choice)
- monkeypatch.setattr(InputManager, "text", fake_text)
- destination = prompt_generation_destination("whoami")
- assert destination.mode == "local"
- assert destination.local_output_dir == tmp_path / "whoami"
- assert calls[0] == (
- "numbered_choice",
- ("Store generated template in", ("local", "remote")),
- {"default": "local"},
- )
- assert calls[1] == (
- "text",
- ("Local output directory",),
- {"default": str(Path.cwd() / "whoami")},
- )
- def test_prompt_generation_destination_asks_for_remote_host_and_path(monkeypatch: pytest.MonkeyPatch) -> None:
- """Interactive remote destination should use direct text prompts for host and path."""
- calls: list[tuple[str, tuple[Any, ...], dict[str, Any]]] = []
- responses = iter(["srv-test-1.home.clcreative.de", "~/dockhand"])
- def fake_numbered_choice(self, prompt: str, choices: list[str], default: str | None = None) -> str:
- calls.append(("numbered_choice", (prompt, tuple(choices)), {"default": default}))
- del self
- return "remote"
- def fake_text(self, prompt: str, default: str | None = None, **kwargs: Any) -> str:
- calls.append(("text", (prompt,), {"default": default, **kwargs}))
- del self
- return next(responses)
- monkeypatch.setattr(InputManager, "numbered_choice", fake_numbered_choice)
- monkeypatch.setattr(InputManager, "text", fake_text)
- destination = prompt_generation_destination("dockhand")
- assert destination.mode == "remote"
- assert destination.remote_host == "srv-test-1.home.clcreative.de"
- assert destination.remote_path == "~/dockhand"
- assert calls == [
- ("numbered_choice", ("Store generated template in", ("local", "remote")), {"default": "local"}),
- ("text", ("Remote server host or IP address",), {"default": None}),
- ("text", ("Remote target directory",), {"default": "~/dockhand"}),
- ]
|