GRM-26: fix: CI pipeline for rootless Docker runners
This commit was merged in pull request #5.
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
"""Unit tests for scripts/distribute_molecule.py."""
|
||||
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import click
|
||||
import pytest
|
||||
|
||||
from scripts.distribute_molecule import (
|
||||
MOLECULE_ROOT,
|
||||
discover_scenarios,
|
||||
distribute,
|
||||
scenarios_for_runner,
|
||||
)
|
||||
|
||||
|
||||
class TestDiscoverScenarios:
|
||||
def test_discovers_scenarios(self, tmp_path: Path) -> None:
|
||||
root = tmp_path / "molecule"
|
||||
(root / "default").mkdir(parents=True)
|
||||
(root / "binary").mkdir(parents=True)
|
||||
(root / "common").mkdir(parents=True)
|
||||
(root / "_shared").mkdir(parents=True)
|
||||
result = discover_scenarios(root)
|
||||
assert result == ["binary", "default"]
|
||||
|
||||
def test_raises_when_dir_missing(self, tmp_path: Path) -> None:
|
||||
with pytest.raises(click.ClickException) as exc:
|
||||
discover_scenarios(tmp_path / "nonexistent")
|
||||
assert "not found" in str(exc.value)
|
||||
|
||||
def test_default_root_constant(self) -> None:
|
||||
assert Path("ansible/roles/gitea-runner/molecule") == MOLECULE_ROOT
|
||||
|
||||
|
||||
class TestDistribute:
|
||||
def test_even_split(self) -> None:
|
||||
scenarios = ["a", "b", "c", "d", "e", "f"]
|
||||
groups = distribute(scenarios, 3)
|
||||
assert groups == [["a", "d"], ["b", "e"], ["c", "f"]]
|
||||
|
||||
def test_uneven_split(self) -> None:
|
||||
scenarios = ["a", "b", "c", "d", "e"]
|
||||
groups = distribute(scenarios, 3)
|
||||
assert groups == [["a", "d"], ["b", "e"], ["c"]]
|
||||
|
||||
def test_more_runners_than_scenarios(self) -> None:
|
||||
scenarios = ["a", "b"]
|
||||
groups = distribute(scenarios, 5)
|
||||
assert groups == [["a"], ["b"], [], [], []]
|
||||
|
||||
def test_single_runner(self) -> None:
|
||||
scenarios = ["a", "b", "c"]
|
||||
groups = distribute(scenarios, 1)
|
||||
assert groups == [["a", "b", "c"]]
|
||||
|
||||
def test_empty_scenarios(self) -> None:
|
||||
groups = distribute([], 3)
|
||||
assert groups == [[], [], []]
|
||||
|
||||
|
||||
class TestScenariosForRunner:
|
||||
def test_returns_correct_subset(self) -> None:
|
||||
scenarios = ["a", "b", "c", "d", "e", "f"]
|
||||
assert scenarios_for_runner(scenarios, 0, 3) == ["a", "d"]
|
||||
assert scenarios_for_runner(scenarios, 1, 3) == ["b", "e"]
|
||||
assert scenarios_for_runner(scenarios, 2, 3) == ["c", "f"]
|
||||
|
||||
def test_out_of_range_raises(self) -> None:
|
||||
with pytest.raises(click.ClickException) as exc:
|
||||
scenarios_for_runner(["a"], 5, 3)
|
||||
assert "out of range" in str(exc.value)
|
||||
|
||||
def test_negative_index_raises(self) -> None:
|
||||
with pytest.raises(click.ClickException) as exc:
|
||||
scenarios_for_runner(["a"], -1, 3)
|
||||
assert "out of range" in str(exc.value)
|
||||
|
||||
|
||||
class TestCli:
|
||||
def test_list_flag(self, tmp_path: Path) -> None:
|
||||
from click.testing import CliRunner
|
||||
|
||||
from scripts.distribute_molecule import cli
|
||||
|
||||
root = tmp_path / "molecule"
|
||||
(root / "alpha").mkdir(parents=True)
|
||||
(root / "beta").mkdir(parents=True)
|
||||
with patch("scripts.distribute_molecule.MOLECULE_ROOT", root):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["--list"])
|
||||
assert result.exit_code == 0
|
||||
assert "alpha" in result.output
|
||||
assert "beta" in result.output
|
||||
|
||||
def test_no_runner_index_prints_all_groups(self, tmp_path: Path) -> None:
|
||||
from click.testing import CliRunner
|
||||
|
||||
from scripts.distribute_molecule import cli
|
||||
|
||||
root = tmp_path / "molecule"
|
||||
for s in ["a", "b", "c"]:
|
||||
(root / s).mkdir(parents=True)
|
||||
with patch("scripts.distribute_molecule.MOLECULE_ROOT", root):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["--max-runners", "3"])
|
||||
assert result.exit_code == 0
|
||||
assert "Runner 0:" in result.output
|
||||
assert "Runner 1:" in result.output
|
||||
assert "Runner 2:" in result.output
|
||||
|
||||
def test_runner_index_prints_assigned(self, tmp_path: Path) -> None:
|
||||
from click.testing import CliRunner
|
||||
|
||||
from scripts.distribute_molecule import cli
|
||||
|
||||
root = tmp_path / "molecule"
|
||||
for s in ["a", "b", "c"]:
|
||||
(root / s).mkdir(parents=True)
|
||||
with patch("scripts.distribute_molecule.MOLECULE_ROOT", root):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["--runner-index", "1", "--max-runners", "3"])
|
||||
assert result.exit_code == 0
|
||||
assert result.output.strip() == "b"
|
||||
|
||||
|
||||
def test_main_module_block() -> None:
|
||||
import scripts.distribute_molecule as dm
|
||||
|
||||
with open(dm.__file__) as f:
|
||||
source = f.read()
|
||||
source = source.replace('if __name__ == "__main__":\n cli()\n', "")
|
||||
namespace = dict(dm.__dict__)
|
||||
exec(compile(source, dm.__file__, "exec"), namespace)
|
||||
assert callable(namespace["cli"])
|
||||
@@ -111,12 +111,9 @@ class TestAnsibleExecutorExtractStatus:
|
||||
executor = AnsibleExecutor()
|
||||
log_file = tmp_path / "test.log"
|
||||
log_file.write_text("incomplete")
|
||||
log_file.chmod(0o000)
|
||||
try:
|
||||
with patch("gitea_runner_manager.executor.open", side_effect=OSError("read error")):
|
||||
status = executor._extract_status(log_file)
|
||||
assert status is None
|
||||
finally:
|
||||
log_file.chmod(0o644)
|
||||
assert status is None
|
||||
|
||||
|
||||
class TestAnsibleExecutorPrepareLog:
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import platform
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from click import ClickException
|
||||
|
||||
import scripts.install_checkmake as install_checkmake
|
||||
|
||||
|
||||
class TestArch:
|
||||
def test_amd64(self) -> None:
|
||||
with patch.object(platform, "machine", return_value="x86_64"):
|
||||
assert install_checkmake._arch() == "amd64"
|
||||
|
||||
def test_arm64(self) -> None:
|
||||
with patch.object(platform, "machine", return_value="aarch64"):
|
||||
assert install_checkmake._arch() == "arm64"
|
||||
|
||||
def test_unsupported(self) -> None:
|
||||
with patch.object(platform, "machine", return_value="riscv64"):
|
||||
with pytest.raises(ClickException):
|
||||
install_checkmake._arch()
|
||||
|
||||
|
||||
class TestInstallWithGo:
|
||||
def test_no_go(self) -> None:
|
||||
with patch("shutil.which", return_value=None):
|
||||
assert install_checkmake._install_with_go() is False
|
||||
|
||||
def test_with_go(self) -> None:
|
||||
with patch("shutil.which", return_value="/usr/bin/go"):
|
||||
with patch("subprocess.run") as mock_run:
|
||||
assert install_checkmake._install_with_go() is True
|
||||
mock_run.assert_called_once_with(
|
||||
[
|
||||
"/usr/bin/go",
|
||||
"install",
|
||||
"github.com/checkmake/checkmake/cmd/checkmake@latest",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
class TestDownloadBinary:
|
||||
def test_download(self, tmp_path: Path) -> None:
|
||||
target = tmp_path / "checkmake"
|
||||
|
||||
def _write_file(url: str, path: str) -> tuple[str, None]:
|
||||
Path(path).write_bytes(b"binary")
|
||||
return path, None
|
||||
|
||||
with patch.object(install_checkmake, "TARGET_PATH", target):
|
||||
with patch.object(platform, "machine", return_value="x86_64"):
|
||||
with patch("urllib.request.urlretrieve", side_effect=_write_file) as mock_retrieve:
|
||||
install_checkmake._download_binary()
|
||||
mock_retrieve.assert_called_once()
|
||||
assert target.exists()
|
||||
assert target.stat().st_mode & 0o111
|
||||
|
||||
|
||||
class TestMain:
|
||||
def test_already_installed(self) -> None:
|
||||
with patch("shutil.which", return_value="/usr/bin/checkmake"):
|
||||
install_checkmake.main()
|
||||
|
||||
def test_install_with_go(self) -> None:
|
||||
with patch("shutil.which", side_effect=[None, "/usr/bin/go"]):
|
||||
with patch("subprocess.run") as mock_run:
|
||||
install_checkmake.main()
|
||||
mock_run.assert_called_once_with(
|
||||
[
|
||||
"/usr/bin/go",
|
||||
"install",
|
||||
"github.com/checkmake/checkmake/cmd/checkmake@latest",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
|
||||
def test_download_when_no_go(self, tmp_path: Path) -> None:
|
||||
target = tmp_path / "checkmake"
|
||||
|
||||
def _write_file(url: str, path: str) -> tuple[str, None]:
|
||||
Path(path).write_bytes(b"binary")
|
||||
return path, None
|
||||
|
||||
with patch.object(install_checkmake, "TARGET_PATH", target):
|
||||
with patch("shutil.which", side_effect=[None, None]):
|
||||
with patch.object(platform, "machine", return_value="x86_64"):
|
||||
with patch("urllib.request.urlretrieve", side_effect=_write_file) as mock_retrieve:
|
||||
install_checkmake.main()
|
||||
mock_retrieve.assert_called_once()
|
||||
Reference in New Issue
Block a user