Public Access
Post-merge / detect-type (push) Successful in 6s
Post-merge / configure-repo (push) Successful in 7s
Post-merge / validate-commit-msg (push) Successful in 15s
Post-merge / release (push) Successful in 52s
Post-merge / vikunja (push) Successful in 14s
Post-merge / sync-wiki (push) Successful in 49s
Post-merge / badges (push) Successful in 1m8s
172 lines
5.9 KiB
Python
172 lines
5.9 KiB
Python
"""Unit tests for devx.ci.distribute_files."""
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from click.testing import CliRunner
|
|
|
|
from devx.ci.distribute_files import (
|
|
DEFAULT_MAX_RUNNERS,
|
|
discover_files,
|
|
distribute,
|
|
files_for_runner,
|
|
main,
|
|
)
|
|
|
|
|
|
class TestDiscoverFiles:
|
|
def test_discovers_sorted(self, tmp_path: Path) -> None:
|
|
(tmp_path / "test_b.py").write_text("")
|
|
(tmp_path / "test_a.py").write_text("")
|
|
result = discover_files(str(tmp_path / "test_*.py"))
|
|
assert len(result) == 2
|
|
assert result[0].endswith("test_a.py")
|
|
assert result[1].endswith("test_b.py")
|
|
|
|
def test_no_matches(self, tmp_path: Path) -> None:
|
|
assert discover_files(str(tmp_path / "nonexistent-*.py")) == []
|
|
|
|
|
|
class TestDistribute:
|
|
def test_even_split(self) -> None:
|
|
files = [f"test_{i}.py" for i in range(6)]
|
|
groups = distribute(files, 3)
|
|
assert len(groups) == 3
|
|
assert all(len(g) == 2 for g in groups)
|
|
|
|
def test_uneven_split(self) -> None:
|
|
files = [f"test_{i}.py" for i in range(5)]
|
|
groups = distribute(files, 3)
|
|
assert len(groups[0]) == 2
|
|
assert len(groups[1]) == 2
|
|
assert len(groups[2]) == 1
|
|
|
|
def test_more_runners_than_files(self) -> None:
|
|
files = ["test_a.py"]
|
|
groups = distribute(files, 5)
|
|
assert len(groups) == 5
|
|
assert len(groups[0]) == 1
|
|
assert all(len(g) == 0 for g in groups[1:])
|
|
|
|
def test_empty(self) -> None:
|
|
assert distribute([], 3) == [[], [], []]
|
|
|
|
|
|
class TestFilesForRunner:
|
|
def test_returns_correct_subset(self) -> None:
|
|
files = [f"test_{i}.py" for i in range(6)]
|
|
assert len(files_for_runner(files, 0, 3)) == 2
|
|
assert len(files_for_runner(files, 1, 3)) == 2
|
|
assert len(files_for_runner(files, 2, 3)) == 2
|
|
|
|
def test_out_of_range_raises(self) -> None:
|
|
with pytest.raises(Exception, match="out of range"):
|
|
files_for_runner(["a.py"], 5, 3)
|
|
|
|
|
|
class TestCli:
|
|
def test_no_runner_index_prints_groups(self, tmp_path: Path) -> None:
|
|
for i in range(3):
|
|
(tmp_path / f"test_{i}.py").write_text("")
|
|
runner = CliRunner()
|
|
result = runner.invoke(main, ["--pattern", str(tmp_path / "test_*.py"), "--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:
|
|
for i in range(3):
|
|
(tmp_path / f"test_{i}.py").write_text("")
|
|
runner = CliRunner()
|
|
result = runner.invoke(
|
|
main,
|
|
["--pattern", str(tmp_path / "test_*.py"), "--runner-index", "1", "--max-runners", "3"],
|
|
)
|
|
assert result.exit_code == 0
|
|
assert "test_0.py" in result.output
|
|
|
|
def test_github_env_writes_files(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
gh_file = tmp_path / "env.txt"
|
|
monkeypatch.setenv("GITHUB_ENV", str(gh_file))
|
|
for i in range(2):
|
|
(tmp_path / f"test_{i}.py").write_text("")
|
|
runner = CliRunner()
|
|
result = runner.invoke(
|
|
main,
|
|
["--pattern", str(tmp_path / "test_*.py"), "--runner-index", "1", "--max-runners", "2", "--github-env"],
|
|
)
|
|
assert result.exit_code == 0
|
|
content = gh_file.read_text()
|
|
assert "ASSIGNED_FILES=" in content
|
|
assert "SKIP=false" in content
|
|
|
|
def test_github_env_multiline_uses_heredoc(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
gh_file = tmp_path / "env.txt"
|
|
monkeypatch.setenv("GITHUB_ENV", str(gh_file))
|
|
for i in range(6):
|
|
(tmp_path / f"test_{i}.py").write_text("")
|
|
runner = CliRunner()
|
|
result = runner.invoke(
|
|
main,
|
|
["--pattern", str(tmp_path / "test_*.py"), "--runner-index", "1", "--max-runners", "2", "--github-env"],
|
|
)
|
|
assert result.exit_code == 0
|
|
content = gh_file.read_text()
|
|
# Multi-line values must use heredoc syntax to avoid corrupting $GITHUB_ENV
|
|
assert "ASSIGNED_FILES<<EOF" in content
|
|
assert content.count("EOF") >= 2
|
|
assert "SKIP=false" in content
|
|
|
|
def test_skip_if_excess(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
gh_file = tmp_path / "env.txt"
|
|
monkeypatch.setenv("GITHUB_ENV", str(gh_file))
|
|
(tmp_path / "test.py").write_text("")
|
|
runner = CliRunner()
|
|
result = runner.invoke(
|
|
main,
|
|
[
|
|
"--pattern",
|
|
str(tmp_path / "test_*.py"),
|
|
"--runner-index",
|
|
"5",
|
|
"--max-runners",
|
|
"2",
|
|
"--github-env",
|
|
"--skip-if-excess",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
content = gh_file.read_text()
|
|
assert "ASSIGNED_FILES=\n" in content
|
|
assert "SKIP=true" in content
|
|
|
|
def test_runner_index_zero_raises(self, tmp_path: Path) -> None:
|
|
(tmp_path / "test.py").write_text("")
|
|
runner = CliRunner()
|
|
result = runner.invoke(
|
|
main,
|
|
["--pattern", str(tmp_path / "test_*.py"), "--runner-index", "0", "--max-runners", "3"],
|
|
)
|
|
assert result.exit_code != 0
|
|
|
|
def test_no_env_var_raises(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.delenv("GITHUB_ENV", raising=False)
|
|
(tmp_path / "test.py").write_text("")
|
|
runner = CliRunner()
|
|
result = runner.invoke(
|
|
main,
|
|
["--pattern", str(tmp_path / "test_*.py"), "--runner-index", "1", "--max-runners", "3", "--github-env"],
|
|
)
|
|
assert result.exit_code != 0
|
|
|
|
|
|
def test_default_max_runners() -> None:
|
|
assert DEFAULT_MAX_RUNNERS == 3
|
|
|
|
|
|
def test_main_module_block() -> None:
|
|
import devx.ci.distribute_files as mod
|
|
|
|
assert hasattr(mod, "main")
|