GRM-111: feat: unified --become-password-file, --verbose, --no-status, labels fix
Post-merge / detect-type (push) Successful in 51s
Post-merge / release (push) Successful in 1m14s
Post-merge / validate-commit-msg (push) Successful in 1m14s
Post-merge / vikunja (push) Successful in 1m14s
Post-merge / badges (push) Successful in 1m25s
Post-merge / sync-wiki (push) Successful in 1m53s
Post-merge / configure-repo (push) Successful in 1m20s
Post-merge / publish (push) Successful in 1m21s
Post-merge / detect-type (push) Successful in 51s
Post-merge / release (push) Successful in 1m14s
Post-merge / validate-commit-msg (push) Successful in 1m14s
Post-merge / vikunja (push) Successful in 1m14s
Post-merge / badges (push) Successful in 1m25s
Post-merge / sync-wiki (push) Successful in 1m53s
Post-merge / configure-repo (push) Successful in 1m20s
Post-merge / publish (push) Successful in 1m21s
This commit was merged in pull request #178.
This commit is contained in:
+315
-3
@@ -1,16 +1,26 @@
|
||||
"""Unit tests for cli module."""
|
||||
|
||||
import os
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
from gitea_runner_manager import __version__
|
||||
from gitea_runner_manager.cli import cli
|
||||
|
||||
_TEST_ENV = {"GITEA_URL": "https://git.example.com", "CI_GITEA_TOKEN": "", "GITEA_RUNNER_LABELS": ""}
|
||||
_TEST_ENV = {"GITEA_URL": "https://git.example.com", "CI_GITEA_TOKEN": ""}
|
||||
|
||||
|
||||
class TestCLI:
|
||||
@pytest.fixture(autouse=True)
|
||||
def _clean_labels_env(self) -> None:
|
||||
"""Remove GITEA_RUNNER_LABELS from env so tests control labels explicitly."""
|
||||
old = os.environ.pop("GITEA_RUNNER_LABELS", None)
|
||||
yield
|
||||
if old is not None:
|
||||
os.environ["GITEA_RUNNER_LABELS"] = old
|
||||
|
||||
def test_cli_version(self) -> None:
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["--version"])
|
||||
@@ -36,6 +46,8 @@ class TestCLI:
|
||||
integration_retries=3,
|
||||
labels=None,
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -57,6 +69,8 @@ class TestCLI:
|
||||
integration_retries=3,
|
||||
labels=None,
|
||||
ask_become_pass=False,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -96,6 +110,8 @@ class TestCLI:
|
||||
integration_retries=3,
|
||||
labels=None,
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -145,6 +161,8 @@ class TestCLI:
|
||||
integration_retries=3,
|
||||
labels=None,
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -166,6 +184,8 @@ class TestCLI:
|
||||
integration_retries=3,
|
||||
labels=None,
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -181,6 +201,139 @@ class TestCLI:
|
||||
assert result.exit_code != 0
|
||||
assert "fail" in result.output
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
def test_install_with_labels(self, mock_manager_class: MagicMock) -> None:
|
||||
mock_manager = MagicMock()
|
||||
mock_manager_class.return_value = mock_manager
|
||||
|
||||
runner = CliRunner(env=_TEST_ENV)
|
||||
result = runner.invoke(
|
||||
cli, ["install", "host1", "--user", "ubuntu", "--token", "tok", "--labels", "docker:docker://alpine:latest"]
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
mock_manager.install.assert_called_once_with(
|
||||
host="host1",
|
||||
user="ubuntu",
|
||||
key=None,
|
||||
name=None,
|
||||
token="tok",
|
||||
gitea_url="https://git.example.com",
|
||||
admin_token="",
|
||||
integration_retries=3,
|
||||
labels="docker:docker://alpine:latest",
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
def test_install_with_empty_labels(self, mock_manager_class: MagicMock) -> None:
|
||||
"""Explicit empty string labels means 'no labels' (not 'use default')."""
|
||||
mock_manager = MagicMock()
|
||||
mock_manager_class.return_value = mock_manager
|
||||
|
||||
runner = CliRunner(env=_TEST_ENV)
|
||||
result = runner.invoke(cli, ["install", "host1", "--user", "ubuntu", "--token", "tok", "--labels", ""])
|
||||
assert result.exit_code == 0
|
||||
mock_manager.install.assert_called_once_with(
|
||||
host="host1",
|
||||
user="ubuntu",
|
||||
key=None,
|
||||
name=None,
|
||||
token="tok",
|
||||
gitea_url="https://git.example.com",
|
||||
admin_token="",
|
||||
integration_retries=3,
|
||||
labels="",
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
def test_install_labels_from_env(self, mock_manager_class: MagicMock) -> None:
|
||||
"""Labels read from GITEA_RUNNER_LABELS env var when --labels not passed."""
|
||||
mock_manager = MagicMock()
|
||||
mock_manager_class.return_value = mock_manager
|
||||
|
||||
runner = CliRunner(env={**_TEST_ENV, "GITEA_RUNNER_LABELS": "docker:docker://alpine:latest"})
|
||||
result = runner.invoke(cli, ["install", "host1", "--user", "ubuntu", "--token", "tok"])
|
||||
assert result.exit_code == 0
|
||||
mock_manager.install.assert_called_once_with(
|
||||
host="host1",
|
||||
user="ubuntu",
|
||||
key=None,
|
||||
name=None,
|
||||
token="tok",
|
||||
gitea_url="https://git.example.com",
|
||||
admin_token="",
|
||||
integration_retries=3,
|
||||
labels="docker:docker://alpine:latest",
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
def test_install_with_become_password_file(self, mock_manager_class: MagicMock) -> None:
|
||||
"""--become-password-file passes file path to manager."""
|
||||
import tempfile
|
||||
|
||||
mock_manager = MagicMock()
|
||||
mock_manager_class.return_value = mock_manager
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
|
||||
f.write("secret\n")
|
||||
pw_file = f.name
|
||||
|
||||
try:
|
||||
runner = CliRunner(env=_TEST_ENV)
|
||||
result = runner.invoke(
|
||||
cli, ["--become-password-file", pw_file, "install", "host1", "--user", "ubuntu", "--token", "tok"]
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
mock_manager.install.assert_called_once_with(
|
||||
host="host1",
|
||||
user="ubuntu",
|
||||
key=None,
|
||||
name=None,
|
||||
token="tok",
|
||||
gitea_url="https://git.example.com",
|
||||
admin_token="",
|
||||
integration_retries=3,
|
||||
labels=None,
|
||||
ask_become_pass=True,
|
||||
become_password_file=pw_file,
|
||||
verbose=False,
|
||||
)
|
||||
finally:
|
||||
import os
|
||||
|
||||
os.unlink(pw_file)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
def test_install_verbose(self, mock_manager_class: MagicMock) -> None:
|
||||
mock_manager = MagicMock()
|
||||
mock_manager_class.return_value = mock_manager
|
||||
|
||||
runner = CliRunner(env=_TEST_ENV)
|
||||
result = runner.invoke(cli, ["-v", "install", "host1", "--user", "ubuntu", "--token", "tok"])
|
||||
assert result.exit_code == 0
|
||||
mock_manager.install.assert_called_once_with(
|
||||
host="host1",
|
||||
user="ubuntu",
|
||||
key=None,
|
||||
name=None,
|
||||
token="tok",
|
||||
gitea_url="https://git.example.com",
|
||||
admin_token="",
|
||||
integration_retries=3,
|
||||
labels=None,
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
def test_update(self, mock_manager_class: MagicMock) -> None:
|
||||
mock_manager = MagicMock()
|
||||
@@ -207,6 +360,8 @@ class TestCLI:
|
||||
key="/key",
|
||||
version="v0.2.0",
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -223,6 +378,8 @@ class TestCLI:
|
||||
key=None,
|
||||
version=None,
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -252,6 +409,8 @@ class TestCLI:
|
||||
user=None,
|
||||
key=None,
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -268,6 +427,8 @@ class TestCLI:
|
||||
user="newuser",
|
||||
key=None,
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -284,6 +445,8 @@ class TestCLI:
|
||||
user=None,
|
||||
key=None,
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -300,6 +463,8 @@ class TestCLI:
|
||||
user=None,
|
||||
key=None,
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -316,6 +481,8 @@ class TestCLI:
|
||||
user=None,
|
||||
key=None,
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -334,6 +501,8 @@ class TestCLI:
|
||||
token="tok",
|
||||
gitea_url="https://git.example.com",
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -367,6 +536,8 @@ class TestCLI:
|
||||
token="tok",
|
||||
gitea_url="https://git.example.com",
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -396,6 +567,8 @@ class TestCLI:
|
||||
user=None,
|
||||
key=None,
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -415,6 +588,8 @@ class TestCLI:
|
||||
gitea_url="https://git.example.com",
|
||||
force=False,
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -434,6 +609,8 @@ class TestCLI:
|
||||
gitea_url="https://git.example.com",
|
||||
force=True,
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -520,6 +697,8 @@ class TestCLI:
|
||||
gitea_url="https://git.example.com",
|
||||
force=False,
|
||||
ask_become_pass=True,
|
||||
become_password_file=None,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
@@ -555,7 +734,29 @@ class TestCLI:
|
||||
assert "r1" in result.output
|
||||
assert "10.0.0.1" in result.output
|
||||
assert "active" in result.output
|
||||
mock_manager.list_runners.assert_called_once_with(become_pass=None)
|
||||
mock_manager.list_runners.assert_called_once_with(become_pass=None, no_status=False)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
def test_list_no_status(self, mock_manager_class: MagicMock) -> None:
|
||||
"""--no-status skips SSH checks and shows registry only."""
|
||||
mock_manager = MagicMock()
|
||||
mock_manager.list_runners.return_value = [
|
||||
{
|
||||
"name": "r1",
|
||||
"host": "10.0.0.1",
|
||||
"user": "ubuntu",
|
||||
"labels": "",
|
||||
"status": "n/a",
|
||||
},
|
||||
]
|
||||
mock_manager_class.return_value = mock_manager
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["list", "--no-status"])
|
||||
assert result.exit_code == 0
|
||||
assert "r1" in result.output
|
||||
assert "n/a" in result.output
|
||||
mock_manager.list_runners.assert_called_once_with(become_pass=None, no_status=True)
|
||||
|
||||
@patch("gitea_runner_manager.cli.click.prompt", return_value="secret")
|
||||
@patch("gitea_runner_manager.cli.sys.stdin")
|
||||
@@ -581,7 +782,51 @@ class TestCLI:
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["list"], input="secret\n")
|
||||
assert result.exit_code == 0
|
||||
mock_manager.list_runners.assert_called_once_with(become_pass="secret")
|
||||
mock_manager.list_runners.assert_called_once_with(become_pass="secret", no_status=False)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
def test_list_with_become_password_file(self, mock_manager_class: MagicMock) -> None:
|
||||
"""--become-password-file reads password from file for grm list."""
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
mock_manager = MagicMock()
|
||||
mock_manager.list_runners.return_value = []
|
||||
mock_manager_class.return_value = mock_manager
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
|
||||
f.write("secret\n")
|
||||
pw_file = f.name
|
||||
|
||||
try:
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["--become-password-file", pw_file, "list"])
|
||||
assert result.exit_code == 0
|
||||
mock_manager.list_runners.assert_called_once_with(become_pass="secret", no_status=False)
|
||||
finally:
|
||||
os.unlink(pw_file)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
def test_list_with_become_password_file_env(self, mock_manager_class: MagicMock) -> None:
|
||||
"""GRM_BECOME_PASSWORD_FILE env var works for grm list."""
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
mock_manager = MagicMock()
|
||||
mock_manager.list_runners.return_value = []
|
||||
mock_manager_class.return_value = mock_manager
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
|
||||
f.write("envpass\n")
|
||||
pw_file = f.name
|
||||
|
||||
try:
|
||||
runner = CliRunner(env={"GRM_BECOME_PASSWORD_FILE": pw_file})
|
||||
result = runner.invoke(cli, ["list"])
|
||||
assert result.exit_code == 0
|
||||
mock_manager.list_runners.assert_called_once_with(become_pass="envpass", no_status=False)
|
||||
finally:
|
||||
os.unlink(pw_file)
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
def test_list_empty(self, mock_manager_class: MagicMock) -> None:
|
||||
@@ -606,3 +851,70 @@ class TestCLI:
|
||||
result = runner.invoke(cli, ["list"])
|
||||
assert result.exit_code != 0
|
||||
assert "fail" in result.output
|
||||
|
||||
@patch("gitea_runner_manager.cli.os.getlogin", side_effect=OSError("no tty"))
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
def test_default_user_fallback_on_getlogin_error(
|
||||
self, mock_manager_class: MagicMock, mock_getlogin: MagicMock
|
||||
) -> None:
|
||||
"""os.getlogin() failure falls back to USER env var."""
|
||||
mock_manager = MagicMock()
|
||||
mock_manager_class.return_value = mock_manager
|
||||
|
||||
with patch.dict("os.environ", {"USER": "testuser", "GITEA_URL": "https://git.example.com"}, clear=True):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["install", "host1", "--token", "tok"])
|
||||
assert result.exit_code == 0
|
||||
call_kwargs = mock_manager.install.call_args.kwargs
|
||||
assert call_kwargs["user"] == "testuser"
|
||||
|
||||
@patch("gitea_runner_manager.cli.os.getlogin", side_effect=OSError("no tty"))
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
def test_default_user_fallback_to_root(self, mock_manager_class: MagicMock, mock_getlogin: MagicMock) -> None:
|
||||
"""os.getlogin() failure with no USER env falls back to 'root'."""
|
||||
mock_manager = MagicMock()
|
||||
mock_manager_class.return_value = mock_manager
|
||||
|
||||
with patch.dict("os.environ", {"GITEA_URL": "https://git.example.com"}, clear=True):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["install", "host1", "--token", "tok"])
|
||||
assert result.exit_code == 0
|
||||
call_kwargs = mock_manager.install.call_args.kwargs
|
||||
assert call_kwargs["user"] == "root"
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
def test_default_user_from_env(self, mock_manager_class: MagicMock) -> None:
|
||||
"""GITEA_RUNNER_USER env var takes priority over os.getlogin()."""
|
||||
mock_manager = MagicMock()
|
||||
mock_manager_class.return_value = mock_manager
|
||||
|
||||
with patch.dict(
|
||||
"os.environ",
|
||||
{"GITEA_RUNNER_USER": "ciuser", "GITEA_URL": "https://git.example.com"},
|
||||
clear=True,
|
||||
):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["install", "host1", "--token", "tok"])
|
||||
assert result.exit_code == 0
|
||||
call_kwargs = mock_manager.install.call_args.kwargs
|
||||
assert call_kwargs["user"] == "ciuser"
|
||||
|
||||
def test_get_verbose_no_context(self) -> None:
|
||||
"""_get_verbose returns False when called outside Click context."""
|
||||
from gitea_runner_manager.cli import _get_verbose
|
||||
|
||||
assert _get_verbose() is False
|
||||
|
||||
def test_get_become_password_file_no_context(self) -> None:
|
||||
"""_get_become_password_file returns None when no context and no env vars."""
|
||||
from gitea_runner_manager.cli import _get_become_password_file
|
||||
|
||||
with patch.dict("os.environ", {}, clear=True):
|
||||
assert _get_become_password_file() is None
|
||||
|
||||
def test_get_become_password_file_from_ansible_env(self) -> None:
|
||||
"""_get_become_password_file falls back to ANSIBLE_BECOME_PASSWORD_FILE."""
|
||||
from gitea_runner_manager.cli import _get_become_password_file
|
||||
|
||||
with patch.dict("os.environ", {"ANSIBLE_BECOME_PASSWORD_FILE": "/tmp/ansible.txt"}, clear=True):
|
||||
assert _get_become_password_file() == "/tmp/ansible.txt"
|
||||
|
||||
Reference in New Issue
Block a user