DEVX-61: optimise slow unit tests and handle missing tea binary in TeaCLI
Post-merge / detect-type (push) Successful in 19s
Post-merge / validate-commit-msg (push) Failing after 14s
Post-merge / configure-repo (push) Successful in 17s
Post-merge / release (push) Successful in 1m6s
Post-merge / vikunja (push) Successful in 20s
Post-merge / sync-wiki (push) Successful in 48s
Post-merge / badges (push) Successful in 1m12s

This commit was merged in pull request #101.
This commit is contained in:
2026-06-26 16:09:54 +00:00
parent f1adf22c3e
commit 06e80516d4
8 changed files with 62 additions and 19 deletions
+3 -3
View File
@@ -317,9 +317,9 @@ class TestCollectKeys:
assert "completed" in keys
assert "pending" in keys
def test_default_dir_includes_dynamic_keys(self) -> None:
"""The default source dir should include DYNAMIC_KEYS."""
keys = check_translations.collect_keys(check_translations.DEFAULT_SRC_DIR)
def test_default_dir_includes_dynamic_keys(self, tmp_path: Path) -> None:
"""collect_keys includes DYNAMIC_KEYS even with an empty source dir."""
keys = check_translations.collect_keys(tmp_path)
assert "completed" in keys
assert "pending" in keys
assert "in_progress" in keys
+22
View File
@@ -113,6 +113,28 @@ class TestPyprojectReading:
assert cfg.VIKUNJA_PROJECT_ID == 6
importlib.reload(cfg)
def test_pyproject_int_value_used(self, monkeypatch: object, tmp_path: Path) -> None:
"""When pyproject.toml has an int value, it is used (covers _get_int return)."""
(tmp_path / "pyproject.toml").write_text('[project]\nname = "test"\n[tool.devx]\nvikunja_project_id = 42\n')
monkeypatch.chdir(tmp_path)
monkeypatch.delenv("DEVX_VIKUNJA_PROJECT_ID", raising=False)
import devx.config as cfg
importlib.reload(cfg)
assert cfg.VIKUNJA_PROJECT_ID == 42
importlib.reload(cfg)
def test_env_int_override(self, monkeypatch: object, tmp_path: Path) -> None:
"""Env var override for int config takes priority over pyproject.toml."""
(tmp_path / "pyproject.toml").write_text('[project]\nname = "test"\n[tool.devx]\nvikunja_project_id = 42\n')
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("DEVX_VIKUNJA_PROJECT_ID", "99")
import devx.config as cfg
importlib.reload(cfg)
assert cfg.VIKUNJA_PROJECT_ID == 99
importlib.reload(cfg)
def test_tool_not_dict_falls_back_to_defaults(self, monkeypatch: object, tmp_path: Path) -> None:
"""When [tool] is not a dict, defaults are used."""
(tmp_path / "pyproject.toml").write_text('tool = "not a dict"\n')
+6
View File
@@ -77,6 +77,12 @@ class TestTeaCLIRun:
with pytest.raises(TeaCLIError, match="auth error"):
cli._run(["labels", "list"])
def test_run_tea_not_found_raises_tea_error(self) -> None:
cli = TeaCLI(tea_bin="tea")
with patch("subprocess.run", side_effect=FileNotFoundError("tea not found")):
with pytest.raises(TeaCLIError, match="tea binary not found"):
cli._run(["labels", "list"])
def test_run_includes_json_flag(self) -> None:
cli = TeaCLI(tea_bin="/fake/tea")
mock_result = MagicMock(returncode=0, stdout="[]", stderr="")
+3 -3
View File
@@ -116,7 +116,7 @@ class TestCli:
patch("devx.molecule.molecule_ci_guard.get_running_jobs", side_effect=get_jobs_side_effect),
patch("os.killpg") as mock_killpg,
patch("os.getpgid") as mock_getpgid,
patch("time.sleep", side_effect=lambda x: real_sleep(0.1)),
patch("time.sleep", side_effect=lambda x: real_sleep(0)),
):
mock_getpgid.return_value = 123
proc = MagicMock()
@@ -163,7 +163,7 @@ class TestCli:
patch("devx.molecule.molecule_ci_guard.get_running_jobs", side_effect=get_jobs_side_effect),
patch("os.killpg", side_effect=ProcessLookupError("no such process")),
patch("os.getpgid") as mock_getpgid,
patch("time.sleep", side_effect=lambda x: real_sleep(0.1)),
patch("time.sleep", side_effect=lambda x: real_sleep(0)),
):
mock_getpgid.return_value = 123
proc = MagicMock()
@@ -208,7 +208,7 @@ class TestCli:
patch("devx.molecule.molecule_ci_guard.get_running_jobs", side_effect=get_jobs_side_effect),
patch("os.killpg") as mock_killpg,
patch("os.getpgid") as mock_getpgid,
patch("time.sleep", side_effect=lambda x: real_sleep(0.1)),
patch("time.sleep", side_effect=lambda x: real_sleep(0)),
):
mock_getpgid.return_value = 123
proc = MagicMock()
+4 -4
View File
@@ -302,7 +302,7 @@ class TestCli:
patch("devx.molecule.molecule_ci_guard.get_running_jobs", side_effect=get_jobs_side_effect),
patch("os.killpg") as mock_killpg,
patch("os.getpgid") as mock_getpgid,
patch("time.sleep", side_effect=lambda x: real_sleep(0.1)),
patch("time.sleep", side_effect=lambda x: real_sleep(0)),
):
mock_getpgid.return_value = 123
proc = MagicMock()
@@ -338,7 +338,7 @@ class TestCli:
patch("devx.molecule.molecule_ci_guard.subprocess.Popen") as mock_popen,
patch("devx.molecule.molecule_ci_guard.subprocess.run") as mock_run,
patch("devx.molecule.molecule_ci_guard.get_running_jobs") as mock_get_jobs,
patch("time.sleep", side_effect=lambda x: real_sleep(0.05)),
patch("time.sleep", side_effect=lambda x: real_sleep(0)),
):
mock_get_jobs.return_value = [{"name": "molecule-tests (1)", "conclusion": "success"}]
proc = MagicMock()
@@ -385,7 +385,7 @@ class TestCli:
patch("devx.molecule.molecule_ci_guard.get_running_jobs", side_effect=get_jobs_side_effect),
patch("os.killpg") as mock_killpg,
patch("os.getpgid") as mock_getpgid,
patch("time.sleep", side_effect=lambda x: real_sleep(0.1)),
patch("time.sleep", side_effect=lambda x: real_sleep(0)),
):
mock_getpgid.return_value = 123
mock_killpg.side_effect = ProcessLookupError("no such process")
@@ -432,7 +432,7 @@ class TestCli:
patch("devx.molecule.molecule_ci_guard.get_running_jobs", side_effect=get_jobs_side_effect),
patch("os.killpg") as mock_killpg,
patch("os.getpgid") as mock_getpgid,
patch("time.sleep", side_effect=lambda x: real_sleep(0.1)),
patch("time.sleep", side_effect=lambda x: real_sleep(0)),
):
mock_getpgid.return_value = 123
mock_killpg.side_effect = [None, ProcessLookupError("no such process")]
+14 -2
View File
@@ -398,10 +398,16 @@ class TestMain:
@patch.dict("os.environ", {"REPO_TOKEN": "gitea-tok"})
@patch("devx.ci.publish.generate_release_notes", return_value="Release notes")
@patch("devx.ci.publish.TeaCLI")
@patch("devx.ci.publish.publish_to_gitea_registry")
@patch("devx.ci.publish.publish_to_pypi")
@patch("devx.ci.publish.build_package")
def test_create_release_already_exists_is_idempotent(
self, mock_build: MagicMock, mock_publish: MagicMock, mock_tea_cls: MagicMock, mock_notes: MagicMock
self,
mock_build: MagicMock,
mock_publish: MagicMock,
mock_gitea_pub: MagicMock,
mock_tea_cls: MagicMock,
mock_notes: MagicMock,
) -> None:
"""If create_release fails with 'already exists', treat as success."""
mock_tea = MagicMock()
@@ -416,10 +422,16 @@ class TestMain:
@patch.dict("os.environ", {"REPO_TOKEN": "gitea-tok"})
@patch("devx.ci.publish.generate_release_notes", return_value="Release notes")
@patch("devx.ci.publish.TeaCLI")
@patch("devx.ci.publish.publish_to_gitea_registry")
@patch("devx.ci.publish.publish_to_pypi")
@patch("devx.ci.publish.build_package")
def test_create_release_other_error_raises(
self, mock_build: MagicMock, mock_publish: MagicMock, mock_tea_cls: MagicMock, mock_notes: MagicMock
self,
mock_build: MagicMock,
mock_publish: MagicMock,
mock_gitea_pub: MagicMock,
mock_tea_cls: MagicMock,
mock_notes: MagicMock,
) -> None:
"""If create_release fails with a non-'already exists' error, raise."""
mock_tea = MagicMock()