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

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""" 

7 

8import inspect 

9 

10import pytest 

11 

12from src.edwh.release_backend import ( 

13 detect_backend, 

14 pin_backend, 

15 pinned_backend, 

16) 

17 

18PSR = """[project] 

19name = "demo" 

20 

21[tool.semantic_release] 

22branch = "main" 

23version_variable = "src/demo/__about__.py:__version__" 

24upload_to_repository = false 

25""" 

26 

27VOMMIT = """[project] 

28name = "demo" 

29version = "1.0.0" 

30 

31[tool.vommit] 

32prerelease_token = "beta" 

33""" 

34 

35 

36def write(tmp_path, content: str): 

37 pyproject = tmp_path / "pyproject.toml" 

38 pyproject.write_text(content) 

39 return pyproject 

40 

41 

42def test_detects_psr_config(tmp_path): 

43 assert detect_backend(write(tmp_path, PSR)) == "psr" 

44 

45 

46def test_detects_vommit_config(tmp_path): 

47 assert detect_backend(write(tmp_path, VOMMIT)) == "vommit" 

48 

49 

50def test_project_without_release_config_needs_setup(tmp_path): 

51 assert detect_backend(write(tmp_path, '[project]\nname = "demo"\n')) == "none" 

52 

53 

54def test_missing_pyproject_needs_setup(tmp_path): 

55 assert detect_backend(tmp_path / "pyproject.toml") == "none" 

56 

57 

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" 

61 

62 

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) 

67 

68 assert pinned_backend(pyproject) == "psr" 

69 assert detect_backend(pyproject) == "psr" 

70 

71 

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') 

75 

76 assert detect_backend(pyproject) == "vommit" 

77 

78 

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) 

83 

84 assert detect_backend(pyproject) == "none" 

85 

86 

87def test_pin_preserves_comments_and_is_idempotent(tmp_path): 

88 pyproject = write(tmp_path, '# keep me\n[project]\nname = "demo" # and me\n') 

89 

90 pin_backend("psr", pyproject) 

91 once = pyproject.read_text() 

92 pin_backend("psr", pyproject) 

93 

94 assert "# keep me" in once 

95 assert "# and me" in once 

96 assert pyproject.read_text() == once 

97 

98 

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) 

103 

104 text = pyproject.read_text() 

105 assert "ty = false" in text 

106 assert pinned_backend(pyproject) == "psr" 

107 

108 

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") 

115 

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 } 

122 

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}"