Coverage for tests/test_release_backend.py: 100%
52 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-13 17:03 +0200
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-13 17:03 +0200
1# SPDX-FileCopyrightText: 2023-present Remco Boerma <remco.b@educationwarehouse.nl>
2#
3# SPDX-License-Identifier: MIT
4"""
5Which release tool owns a project, and whether we can pin that choice.
6"""
8import inspect
10import pytest
12from src.edwh.release_backend import (
13 detect_backend,
14 pin_backend,
15 pinned_backend,
16)
18PSR = """[project]
19name = "demo"
21[tool.semantic_release]
22branch = "main"
23version_variable = "src/demo/__about__.py:__version__"
24upload_to_repository = false
25"""
27VOMMIT = """[project]
28name = "demo"
29version = "1.0.0"
31[tool.vommit]
32prerelease_token = "beta"
33"""
36def write(tmp_path, content: str):
37 pyproject = tmp_path / "pyproject.toml"
38 pyproject.write_text(content)
39 return pyproject
42def test_detects_psr_config(tmp_path):
43 assert detect_backend(write(tmp_path, PSR)) == "psr"
46def test_detects_vommit_config(tmp_path):
47 assert detect_backend(write(tmp_path, VOMMIT)) == "vommit"
50def test_project_without_release_config_needs_setup(tmp_path):
51 assert detect_backend(write(tmp_path, '[project]\nname = "demo"\n')) == "none"
54def test_missing_pyproject_needs_setup(tmp_path):
55 assert detect_backend(tmp_path / "pyproject.toml") == "none"
58def test_malformed_pyproject_does_not_raise(tmp_path):
59 """A broken pyproject is the build backend's problem to report, not ours."""
60 assert detect_backend(write(tmp_path, "[project\nname = ")) == "none"
63def test_pin_stops_the_migration_offer(tmp_path):
64 """Answering 'never' has to survive into the next release."""
65 pyproject = write(tmp_path, PSR)
66 pin_backend("psr", pyproject)
68 assert pinned_backend(pyproject) == "psr"
69 assert detect_backend(pyproject) == "psr"
72def test_vommit_config_beats_a_stale_psr_pin(tmp_path):
73 """Migrating by hand should not also require removing the opt-out."""
74 pyproject = write(tmp_path, VOMMIT + '\n[tool.edwh.release]\nbackend = "psr"\n')
76 assert detect_backend(pyproject) == "vommit"
79def test_pin_to_vommit_without_config_asks_for_setup(tmp_path):
80 """Pinned to vommit but never configured: offer setup, don't fall back to psr."""
81 pyproject = write(tmp_path, PSR)
82 pin_backend("vommit", pyproject)
84 assert detect_backend(pyproject) == "none"
87def test_pin_preserves_comments_and_is_idempotent(tmp_path):
88 pyproject = write(tmp_path, '# keep me\n[project]\nname = "demo" # and me\n')
90 pin_backend("psr", pyproject)
91 once = pyproject.read_text()
92 pin_backend("psr", pyproject)
94 assert "# keep me" in once
95 assert "# and me" in once
96 assert pyproject.read_text() == once
99def test_pin_alongside_existing_tool_edwh_table(tmp_path):
100 """[tool.edwh.lint] is the established neighbour; don't clobber it."""
101 pyproject = write(tmp_path, '[project]\nname = "demo"\n\n[tool.edwh.lint]\nty = false\n')
102 pin_backend("psr", pyproject)
104 text = pyproject.read_text()
105 assert "ty = false" in text
106 assert pinned_backend(pyproject) == "psr"
109def test_vommit_tasks_accept_the_arguments_we_pass():
110 """
111 `vommit.tasks` is excluded from vommit's coverage, so it is the surface most
112 likely to be reshaped. Fail here rather than at someone's release.
113 """
114 vommit_tasks = pytest.importorskip("vommit.tasks")
116 expected = {
117 "setup": {"project_dir"},
118 "migrate": {"project_dir"},
119 "bump": {"major", "minor", "patch", "prerelease", "noop"},
120 "release": {"major", "minor", "patch", "prerelease", "noop", "yes"},
121 }
123 for name, arguments in expected.items():
124 task = getattr(vommit_tasks, name)
125 parameters = set(inspect.signature(task.body).parameters)
126 assert arguments <= parameters, f"vommit.tasks.{name} lost {arguments - parameters}"