Skip to content

Commit bdd717f

Browse files
resolve conversation
1 parent 39956f9 commit bdd717f

File tree

8 files changed

+207
-222
lines changed

8 files changed

+207
-222
lines changed

doc/changes/unreleased.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
## ✨ Features
44

5-
* [#378](https://github.com/exasol/python-toolbox/pull/378/files): Add Nox task to trigger a release
5+
* [#378](https://github.com/exasol/python-toolbox/issues/368: Add Nox task to trigger a release
66

77
## ⚒️ Refactorings
88

9-
* [#412](https://github.com/exasol/python-toolbox/pull/412): Refactor pre commit hook package version.py into nox task
9+
* [#412](https://github.com/exasol/python-toolbox/issues/392): Refactor pre commit hook package version.py into nox task

doc/developer_guide/modules/modules.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,3 @@ Modules
77
sphinx/sphinx
88
nox
99
nox_tasks
10-
pre_commit_hooks
11-

doc/developer_guide/modules/pre_commit_hooks.rst

Lines changed: 0 additions & 10 deletions
This file was deleted.

exasol/toolbox/pre_commit_hooks/__init__.py

Whitespace-only changes.

project-template/{{cookiecutter.repo_name}}/exasol/{{cookiecutter.package_name}}/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# ATTENTION:
2-
# This file is generated by exasol/toolbox/pre_commit_hooks/package_version.py when using:
2+
# This file is generated by exasol/toolbox/nox/_package_version.py when using:
33
# * either "poetry run -- nox -s project:fix"
44
# * or "poetry run -- nox -s version:check -- <path/version.py> --fix"
55
# Do not edit this file manually!

test/unit/release_test.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
from datetime import datetime
22
from inspect import cleandoc
3+
from subprocess import CalledProcessError
4+
from unittest.mock import patch, MagicMock
35

46
import pytest
57

8+
from exasol.toolbox.nox._release import _trigger_release, ReleaseError
69
from exasol.toolbox.release import (
710
extract_release_notes,
811
new_changelog,
@@ -84,3 +87,94 @@ def test_extract_release_notes(unreleased_md):
8487
)
8588
actual = extract_release_notes(unreleased_md)
8689
assert expected == actual
90+
91+
92+
@pytest.fixture(scope="class")
93+
def mock_from_poetry():
94+
with patch(
95+
"exasol.toolbox.nox._release.Version.from_poetry", return_value="0.3.0"
96+
) as mock_obj:
97+
yield mock_obj
98+
99+
100+
class TestTriggerReleaseWithMocking:
101+
@staticmethod
102+
def _get_mock_string(args) -> str:
103+
if args == ("git", "remote", "show", "origin"):
104+
return "test\nHEAD branch: main\ntest"
105+
if args in [("git", "tag", "--list"), ("gh", "release", "list")]:
106+
return "0.1.0\n0.2.0"
107+
return ""
108+
109+
def _get_subprocess_run_mock(self, args) -> str:
110+
return MagicMock(returncode=0, stdout=self._get_mock_string(args))
111+
112+
def test_works_as_expected(self, mock_from_poetry):
113+
def simulate_pass(args, **kwargs):
114+
return self._get_subprocess_run_mock(args)
115+
116+
with patch("subprocess.run", side_effect=simulate_pass):
117+
result = _trigger_release()
118+
assert result == mock_from_poetry.return_value
119+
120+
@pytest.mark.parametrize(
121+
"error_cmd",
122+
[
123+
("git", "remote", "show", "origin"),
124+
("git", "checkout", "main"),
125+
("git", "pull"),
126+
("git", "tag", "--list"),
127+
("gh", "release", "list"),
128+
("git", "tag", "0.3.0"),
129+
("git", "push", "origin", "0.3.0"),
130+
],
131+
)
132+
def test_caught_called_process_error_raises_release_error(
133+
self, mock_from_poetry, error_cmd
134+
):
135+
def simulate_fail(args, **kwargs):
136+
if args == error_cmd:
137+
raise CalledProcessError(returncode=1, cmd=error_cmd)
138+
return self._get_subprocess_run_mock(args)
139+
140+
with patch("subprocess.run", side_effect=simulate_fail):
141+
with pytest.raises(ReleaseError) as ex:
142+
_trigger_release()
143+
assert str(error_cmd) in str(ex)
144+
145+
def test_default_branch_could_not_be_found(self, mock_from_poetry):
146+
def simulate_fail(args, **kwargs):
147+
if args == ("git", "remote", "show", "origin"):
148+
return MagicMock(returncode=0, stdout="DUMMY TEXT")
149+
return self._get_subprocess_run_mock(args)
150+
151+
with patch("subprocess.run", side_effect=simulate_fail):
152+
with pytest.raises(ReleaseError) as ex:
153+
_trigger_release()
154+
assert "default branch could not be found" in str(ex)
155+
156+
def test_tag_already_exists(self, mock_from_poetry):
157+
version = mock_from_poetry.return_value
158+
159+
def simulate_fail(args, **kwargs):
160+
if args == ("git", "tag", "--list"):
161+
return MagicMock(returncode=0, stdout=f"0.1.0\n0.2.0\n{version}")
162+
return self._get_subprocess_run_mock(args)
163+
164+
with patch("subprocess.run", side_effect=simulate_fail):
165+
with pytest.raises(ReleaseError) as ex:
166+
_trigger_release()
167+
assert f"tag {version} already exists" in str(ex)
168+
169+
def test_release_already_exists(self, mock_from_poetry):
170+
version = mock_from_poetry.return_value
171+
172+
def simulate_fail(args, **kwargs):
173+
if args == ("gh", "release", "list"):
174+
return MagicMock(returncode=0, stdout=f"0.1.0\n0.2.0\n{version}")
175+
return self._get_subprocess_run_mock(args)
176+
177+
with patch("subprocess.run", side_effect=simulate_fail):
178+
with pytest.raises(ReleaseError) as ex:
179+
_trigger_release()
180+
assert f"release {version} already exists" in str(ex)

test/unit/util/version_test.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import subprocess
2+
from unittest.mock import (
3+
patch,
4+
)
5+
6+
import pytest
7+
8+
from exasol.toolbox.error import ToolboxError
9+
from exasol.toolbox.util.version import (
10+
Version,
11+
poetry_command,
12+
)
13+
14+
15+
@pytest.mark.parametrize(
16+
"input,expected",
17+
[
18+
("1.2.3", Version(1, 2, 3)),
19+
("1.2", Version(1, 2, 0)),
20+
("1", Version(1, 0, 0)),
21+
],
22+
)
23+
def test_create_version_from_string(input, expected):
24+
actual = Version.from_string(input)
25+
assert expected == actual
26+
27+
28+
@pytest.mark.parametrize(
29+
"old_version,new_version,expected",
30+
[
31+
(Version(1, 2, 3), Version(1, 2, 4), True),
32+
(Version(1, 2, 3), Version(1, 3, 3), True),
33+
(Version(1, 2, 3), Version(2, 2, 3), True),
34+
(Version(1, 2, 3), Version(1, 1, 3), False),
35+
(Version(1, 2, 3), Version(1, 2, 1), False),
36+
(Version(1, 2, 3), Version(0, 3, 3), False),
37+
],
38+
)
39+
def test_is_later_version(old_version, new_version, expected):
40+
actual = new_version > old_version
41+
assert expected == actual
42+
43+
44+
@pytest.fixture
45+
def poetry_version():
46+
def set_poetry_version(version):
47+
return subprocess.CompletedProcess(
48+
args=["poetry", "version", "--no-ansi", "--short"],
49+
returncode=0,
50+
stdout=version,
51+
stderr="",
52+
)
53+
54+
yield set_poetry_version
55+
56+
57+
@pytest.mark.parametrize(
58+
"version,expected",
59+
[
60+
("1.2.3", Version(1, 2, 3)),
61+
("1.2", Version(1, 2, 0)),
62+
("1", Version(1, 0, 0)),
63+
],
64+
)
65+
def test_version_from_poetry(poetry_version, version, expected):
66+
with patch("subprocess.run", return_value=poetry_version(version)):
67+
actual = Version.from_poetry()
68+
69+
assert expected == actual
70+
71+
72+
@patch("exasol.toolbox.util.version.which", return_value=None)
73+
def test_poetry_decorator_no_poetry_executable(mock):
74+
@poetry_command
75+
def test():
76+
pass
77+
78+
with pytest.raises(ToolboxError):
79+
test()
80+
81+
82+
@patch("exasol.toolbox.util.version.which", return_value="test/path")
83+
def test_poetry_decorator_subprocess(mock):
84+
@poetry_command
85+
def test():
86+
raise subprocess.CalledProcessError(returncode=1, cmd=["test"])
87+
pass
88+
89+
with pytest.raises(ToolboxError):
90+
test()
91+
92+
93+
def test_version_from_python_module(tmp_path):
94+
tmp_file = tmp_path / "file"
95+
file = """
96+
MAJOR = 1
97+
MINOR = 2
98+
PATCH = 3
99+
VERSION = f"{MAJOR}.{MINOR}.{PATCH}"
100+
__version__ = VERSION
101+
"""
102+
tmp_file.write_text(file)
103+
assert Version.from_python_module(tmp_file) == Version.from_string("1.2.3")
104+
105+
106+
def test_version_from_python_no_module_error(tmp_path):
107+
file_path = tmp_path / "file"
108+
file_path.write_text("")
109+
with pytest.raises(ToolboxError) as ex:
110+
Version.from_python_module(file_path)

0 commit comments

Comments
 (0)