From 593a4ee376cac57cf6bd26e704f0f6c2a841b99a Mon Sep 17 00:00:00 2001 From: Corey Oordt Date: Sat, 6 Jan 2024 08:16:48 -0600 Subject: [PATCH] Fixed some tests --- bumpversion/versioning/models.py | 4 +- tests/test_version_part.py | 213 ------------------ .../test_models_versioncomponent.py | 34 ++- tests/test_versioning/test_serialization.py | 171 +++++++++----- 4 files changed, 137 insertions(+), 285 deletions(-) diff --git a/bumpversion/versioning/models.py b/bumpversion/versioning/models.py index 967a3627..9953c934 100644 --- a/bumpversion/versioning/models.py +++ b/bumpversion/versioning/models.py @@ -108,7 +108,7 @@ class VersionComponentConfig(BaseModel): # source: Optional[str] = None # Name of environment variable or context variable to use as the source for value depends_on: Optional[str] = None # The name of the component this component depends on - def generate_component(self, value: Union[str, int, None] = None) -> VersionComponent: + def create_component(self, value: Union[str, int, None] = None) -> VersionComponent: """Generate a version component from the configuration.""" return VersionComponent( values=self.values, @@ -147,7 +147,7 @@ def __init__(self, components: Dict[str, VersionComponentConfig], order: Optiona def create_version(self, values: Dict[str, str]) -> "Version": """Generate a version from the given values.""" components = { - key: comp_config.generate_component(value=values.get(key)) + key: comp_config.create_component(value=values.get(key)) for key, comp_config in self.component_configs.items() } return Version(version_spec=self, components=components) diff --git a/tests/test_version_part.py b/tests/test_version_part.py index 21bddde6..8234d6a1 100644 --- a/tests/test_version_part.py +++ b/tests/test_version_part.py @@ -1,9 +1,7 @@ -import logging from pathlib import Path import pytest from click import UsageError -from pytest import LogCaptureFixture, param from bumpversion import exceptions from bumpversion.utils import get_context @@ -76,153 +74,6 @@ def test_serialize_with_environment_var(): del os.environ["BUILD_NUMBER"] -def test_serialize_with_newlines(): - overrides = { - "current_version": "MAJOR=31\nMINOR=0\nPATCH=3\n", - "parse": r"MAJOR=(?P\d+)\nMINOR=(?P\d+)\nPATCH=(?P\d+)\n", - "serialize": ["MAJOR={major}\nMINOR={minor}\nPATCH={patch}\n"], - } - conf, version_config, current_version = get_config_data(overrides) - new_version = current_version.bump("major") - assert version_config.serialize(new_version, get_context(conf)) == "MAJOR=32\nMINOR=0\nPATCH=0\n" - - -@pytest.mark.parametrize( - ["bump_type", "expected"], - [ - param("patch", "0.9.1", id="patch"), - param("minor", "0.10", id="minor"), - param("major", "1", id="major"), - ], -) -def test_serialize_three_part(bump_type: str, expected: str): - overrides = { - "current_version": "0.9", - "parse": r"(?P\d+)(\.(?P\d+)(\.(?P\d+))?)?", - "serialize": ["{major}.{minor}.{patch}", "{major}.{minor}", "{major}"], - } - conf, version_config, current_version = get_config_data(overrides) - - new_version = current_version.bump(bump_type) - assert version_config.serialize(new_version, get_context(conf)) == expected - - -@pytest.mark.parametrize( - ["initial", "bump_type", "expected"], - [ - param("1.5.dev", "release", "1.5", id="release"), - param("1.5", "minor", "1.6.dev", id="minor"), - ], -) -def test_bump_non_numeric_parts(initial: str, bump_type: str, expected: str): - overrides = { - "current_version": initial, - "parse": r"(?P\d+)\.(?P\d+)(\.(?P[a-z]+))?", - "serialize": ["{major}.{minor}.{release}", "{major}.{minor}"], - "parts": { - "release": { - "optional_value": "gamma", - "values": ["dev", "gamma"], - }, - }, - } - conf, version_config, current_version = get_config_data(overrides) - - new_version = current_version.bump(bump_type) - assert version_config.serialize(new_version, get_context(conf)) == expected - - -@pytest.mark.parametrize( - ["initial", "bump_type", "expected"], - [ - param("1.alpha", "release", "1.beta", id="alpha-to-beta-release"), - param("1.beta", "release", "1", id="beta-to-release"), - ], -) -def test_optional_value(initial: str, bump_type: str, expected: str): - overrides = { - "current_version": initial, - "parse": r"(?P\d+)(\.(?P.*))?(\.)?", - "serialize": ["{num}.{release}", "{num}"], - "parts": { - "release": { - "optional_value": "gamma", - "values": ["alpha", "beta", "gamma"], - }, - }, - } - conf, version_config, current_version = get_config_data(overrides) - - new_version = current_version.bump(bump_type) - assert version_config.serialize(new_version, get_context(conf)) == expected - - -@pytest.mark.parametrize( - ["initial", "bump_type", "expected"], - [ - param("1.0a", "prerel", "1.0b", id="a-to-b-release"), - param("1.0b", "prerelversion", "1.0b1", id="prerelease-version"), - param("1.0b1", "prerelversion", "1.0b2", id="prerelease-version2"), - param("1.0b2", "prerel", "1.0c", id="b2-to-c-release"), - param("1.0c", "prerel", "1.0rc", id="c-to-rc-release"), - param("1.0rc", "prerel", "1.0", id="rc-to-d-release"), - param("1.0", "minor", "1.1dev", id="minor-release"), - param("1.1dev", "prerel", "1.1a", id="dev-to-a-release"), - ], -) -def test_python_pre_release_release_post_release(initial: str, bump_type: str, expected: str): - # adapted from http://legacy.python.org/dev/peps/pep-0386/#the-new-versioning-algorithm - overrides = { - "current_version": initial, - "parse": r"""^ - (?P\d+)\.(?P\d+) # minimum 'N.N' - (?: - (?P[abc]|rc|dev) # 'a' = alpha, 'b' = beta - # 'c' or 'rc' = release candidate - (?: - (?P\d+(?:\.\d+)*) - )? - )? - (?P(\.post(?P\d+))?(\.dev(?P\d+))?)?""", - "serialize": [ - "{major}.{minor}{prerel}{prerelversion}", - "{major}.{minor}{prerel}", - "{major}.{minor}", - ], - "parts": { - "prerel": { - "optional_value": "d", - "values": ["dev", "a", "b", "c", "rc", "d"], - }, - }, - } - conf, version_config, current_version = get_config_data(overrides) - - new_version = current_version.bump(bump_type) - assert version_config.serialize(new_version, get_context(conf)) == expected - - -@pytest.mark.parametrize( - ["initial", "bump_type", "expected"], - [ - param("0.9.4", "major", "1.1.0", id="first-value-1"), - ], -) -def test_part_first_value(initial: str, bump_type: str, expected: str): - overrides = { - "current_version": initial, - "parts": { - "minor": { - "first_value": "1", - }, - }, - } - conf, version_config, current_version = get_config_data(overrides) - - new_version = current_version.bump(bump_type) - assert version_config.serialize(new_version, get_context(conf)) == expected - - def test_version_part_invalid_regex_exit(tmp_path: Path) -> None: """A version part with an invalid regex should raise an exception.""" # Arrange @@ -233,67 +84,3 @@ def test_version_part_invalid_regex_exit(tmp_path: Path) -> None: with inside_dir(tmp_path): with pytest.raises(UsageError): get_config_data(overrides) - - -def test_part_does_not_revert_to_zero_if_optional(tmp_path: Path) -> None: - """A non-numeric part with the optional value should not revert to zero.""" - # From https://github.com/c4urself/bump2version/issues/248 - # Arrange - overrides = { - "current_version": "0.3.1", - "parse": r"(?P\d+)\.(?P\d+)\.(?P\d+)((?P\D+)(?P\d*))?", - "serialize": [ - "{major}.{minor}.{patch}{release}{build}", - "{major}.{minor}.{patch}{release}", - "{major}.{minor}.{patch}", - ], - "parts": { - "release": { - "optional_value": "g", - "first_value": "g", - "values": [ - "dev", - "a", - "b", - "g", - ], - }, - }, - } - with inside_dir(tmp_path): - conf, version_config, current_version = get_config_data(overrides) - - new_version = current_version.bump("build") - assert version_config.serialize(new_version, get_context(conf)) == "0.3.1g1" - - -def test_order_of_serialization(tmp_path: Path, caplog: LogCaptureFixture) -> None: - """The order of serialization should be as specified in the config.""" - caplog.set_level(logging.DEBUG) - overrides = { - "current_version": "3.16.dev1", - "parse": r"(?P\d+)\.(?P\d+)\.(?P[a-z]+)?(?P\d+)?", - "serialize": [ - "{major}.{minor}.{release}{patch}", - "{major}.{minor}.{release}", - "{major}.{minor}.{patch}", - ], - "parts": { - "release": { - "optional_value": "prod", - "first_value": "dev", - "values": [ - "dev", - "prod", - ], - }, - }, - } - with inside_dir(tmp_path): - conf, version_config, current_version = get_config_data(overrides) - - new_version = current_version.bump("release") - new_version_str = version_config.serialize(new_version, get_context(conf)) - for msg in caplog.messages: - print(msg) - assert new_version_str == "3.16.prod" diff --git a/tests/test_versioning/test_models_versioncomponent.py b/tests/test_versioning/test_models_versioncomponent.py index 18e9e911..e0f82605 100644 --- a/tests/test_versioning/test_models_versioncomponent.py +++ b/tests/test_versioning/test_models_versioncomponent.py @@ -7,61 +7,59 @@ @pytest.fixture( params=[ - None, - ("0", "1", "2"), - ("0", "3"), + {"optional_value": "0", "first_value": "0"}, + {"first_value": "1"}, + {"values": ["alpha", "beta", "gamma"]}, + {"values": ["alpha", "gamma"]}, ] ) def version_component_config(request): """Return a three-part and a two-part version part configuration.""" - if request.param is None: - return VersionComponentConfig(optional_value="0", first_value="0") - else: - return VersionComponentConfig(values=request.param) + return VersionComponentConfig(**request.param) class TestVersionComponent: class TestCreation: def test_none_value_uses_optional_value(self, version_component_config): - vp = version_component_config.generate_component() + vp = version_component_config.create_component() assert vp.value == vp.func.optional_value assert vp._value is None def test_config_with_values_selects_values_function(self): values = ["0", "1", "2"] - vp = VersionComponentConfig(values=values).generate_component() + vp = VersionComponentConfig(values=values).create_component() assert isinstance(vp.func, ValuesFunction) def test_config_without_values_selects_numeric_function(self): - vp = VersionComponentConfig().generate_component() + vp = VersionComponentConfig().create_component() assert isinstance(vp.func, NumericFunction) def test_copy_returns_new_version_part(self, version_component_config): - vp = version_component_config.generate_component(version_component_config.first_value) + vp = version_component_config.create_component(version_component_config.first_value) vc = vp.copy() assert vp.value == vc.value assert id(vp) != id(vc) def test_bump_increments_value(self, version_component_config): - vp = version_component_config.generate_component(version_component_config.first_value) + vp = version_component_config.create_component(version_component_config.first_value) vc = vp.bump() if version_component_config.values: assert vc.value == str(version_component_config.values[1]) else: - assert vc.value == "1" + assert vc.value == str(int(version_component_config.first_value) + 1) def test_non_first_value_is_not_optional(self, version_component_config): - assert not version_component_config.generate_component(version_component_config.first_value).bump().is_optional + assert not version_component_config.create_component(version_component_config.first_value).bump().is_optional def test_first_value_is_optional(self, version_component_config): - assert version_component_config.generate_component(version_component_config.first_value).is_optional + assert version_component_config.create_component(version_component_config.first_value).is_optional def test_versionparts_with_same_settings_are_equal(self, version_component_config): - version1 = version_component_config.generate_component(version_component_config.first_value) - version2 = version_component_config.generate_component(version_component_config.first_value) + version1 = version_component_config.create_component(version_component_config.first_value) + version2 = version_component_config.create_component(version_component_config.first_value) assert version1 == version2 def test_null_resets_value_to_first_value(self, version_component_config): - version1 = version_component_config.generate_component(version_component_config.first_value) + version1 = version_component_config.create_component(version_component_config.first_value) version2 = version1.bump().null() assert version2 == version1 diff --git a/tests/test_versioning/test_serialization.py b/tests/test_versioning/test_serialization.py index 55475ed9..4d9c6ff5 100644 --- a/tests/test_versioning/test_serialization.py +++ b/tests/test_versioning/test_serialization.py @@ -1,8 +1,8 @@ """Tests for the serialization of versioned objects.""" from bumpversion.versioning.serialization import parse_version, serialize from bumpversion.versioning.conventions import semver_spec, SEMVER_PATTERN -from bumpversion.versioning.models import Version -from bumpversion.exceptions import BumpVersionError, FormattingError, MissingValueError +from bumpversion.versioning.models import Version, VersionSpec, VersionComponentConfig +from bumpversion.exceptions import BumpVersionError, FormattingError import pytest from pytest import param @@ -71,58 +71,125 @@ def test_parse_pattern_with_newlines(self): class TestSerialize: """Test the serialize function.""" - @pytest.mark.parametrize( - ["version", "expected"], - [ - param( - semver_spec().create_version({"major": "1", "minor": "2", "patch": "3"}), - "1.2.3", - id="major-minor-patch", - ), - param( - semver_spec().create_version({"major": "1", "minor": "2", "patch": "0"}), - "1.2", - id="major-minor-patch-zero", - ), - param( - semver_spec().create_version({"major": "1", "minor": "0", "patch": "0"}), - "1", - id="major-minor-zero-patch-zero", - ), - ], - ) - def test_picks_string_with_least_labels(self, version: Version, expected: str): - patterns = ["{major}.{minor}.{patch}", "{major}.{minor}", "{major}"] - assert serialize(version, serialize_patterns=patterns, context=version.values()) == expected - - def test_renders_a_format_with_newlines(self): - """A serialization format with newlines should be rendered correctly.""" - version = semver_spec().create_version({"major": "31", "minor": "0", "patch": "3"}) - assert ( - serialize( - version, serialize_patterns=["MAJOR={major}\nMINOR={minor}\nPATCH={patch}\n"], context=version.values() - ) - == "MAJOR=31\nMINOR=0\nPATCH=3\n" + class TestFormatSelection: + @pytest.mark.parametrize( + ["version", "expected"], + [ + param( + semver_spec().create_version({"major": "1", "minor": "2", "patch": "3"}), + "1.2.3", + id="major-minor-patch", + ), + param( + semver_spec().create_version({"major": "1", "minor": "2", "patch": "0"}), + "1.2", + id="major-minor-patch-zero", + ), + param( + semver_spec().create_version({"major": "1", "minor": "0", "patch": "0"}), + "1", + id="major-minor-zero-patch-zero", + ), + ], ) + def test_is_string_with_fewest_required_labels(self, version: Version, expected: str): + """The smallest pattern with all the required values should be picked.""" + patterns = ["{major}.{minor}.{patch}", "{major}.{minor}", "{major}"] + assert serialize(version, serialize_patterns=patterns, context={}) == expected - def test_renders_a_format_with_additional_context(self): - """A serialization format with additional context should be rendered correctly.""" - version = semver_spec().create_version({"major": "1", "minor": "2", "patch": "3"}) - assert ( - serialize( - version, - serialize_patterns=["{major}.{minor}.{patch}+{$BUILDMETADATA}"], - context={"$BUILDMETADATA": "build.1", "major": "1", "minor": "2", "patch": "3"}, + def test_is_renderable_pattern_with_fewest_labels(self): + """The smallest renderable pattern should be picked if no pattern has all the required values.""" + version = semver_spec().create_version( + {"major": "1", "minor": "2", "patch": "3", "pre_l": "a", "buildmetadata": "build.1"} + ) + # None of the patterns have all the required values, so the smallest renderable pattern should be picked + assert ( + serialize( + version, + serialize_patterns=["{major}.{minor}.{patch}+{buildmetadata}", "{major}.{minor}.{patch}"], + context={}, + ) + == "1.2.3" + ) + + def test_is_first_pattern_with_fewest_labels(self): + """The first pattern with the fewest labels should be picked if multiple have the same number of labels.""" + version = semver_spec().create_version( + {"major": "1", "minor": "2", "patch": "3", "buildmetadata": "build.1"} + ) + assert ( + serialize( + version, + serialize_patterns=["{major}.{minor}.{buildmetadata}", "{major}.{minor}.{patch}"], + context={}, + ) + == "1.2.build.1" + ) + + def test_includes_optional_component_when_dependent_is_not_optional(self): + """A format with an optional component should render when the dependent component is not optional.""" + version_spec = VersionSpec( + components={ + "major": VersionComponentConfig(), + "minor": VersionComponentConfig(), + "patch": VersionComponentConfig(), + "release": VersionComponentConfig( + optional_value="g", + first_value="g", + values=[ + "dev", + "a", + "b", + "g", + ], + ), + "build": VersionComponentConfig(), + }, + ) + serialize_patterns = [ + "{major}.{minor}.{patch}{release}{build}", + "{major}.{minor}.{patch}{release}", + "{major}.{minor}.{patch}", + ] + + version = version_spec.create_version({"major": "0", "minor": "3", "patch": "1", "build": "1"}) + assert ( + serialize( + version, + serialize_patterns=serialize_patterns, + context={}, + ) + == "0.3.1g1" ) - == "1.2.3+build.1" - ) - def test_raises_error_if_context_is_missing_values(self): - """An error is raised if not all parts required in the format have values.""" - version = semver_spec().create_version({"major": "1", "minor": "2"}) - with pytest.raises(FormattingError): - serialize( - version, - serialize_patterns=["{major}.{minor}.{patch}+{$BUILDMETADATA}"], - context={"major": "1", "minor": "2", "patch": "0"}, + class TestRendersFormat: + def test_with_newlines(self): + """A serialization format with newlines should be rendered correctly.""" + version = semver_spec().create_version({"major": "31", "minor": "0", "patch": "3"}) + assert ( + serialize(version, serialize_patterns=["MAJOR={major}\nMINOR={minor}\nPATCH={patch}\n"], context={}) + == "MAJOR=31\nMINOR=0\nPATCH=3\n" ) + + def test_with_additional_context(self): + """A serialization format with additional context should be rendered correctly.""" + version = semver_spec().create_version({"major": "1", "minor": "2", "patch": "3"}) + assert ( + serialize( + version, + serialize_patterns=["{major}.{minor}.{patch}+{$BUILDMETADATA}"], + context={"$BUILDMETADATA": "build.1"}, + ) + == "1.2.3+build.1" + ) + + class TestRaisesError: + def test_if_context_is_missing_values(self): + """An error is raised if not all parts required in the format have values.""" + version = semver_spec().create_version({"major": "1", "minor": "2"}) + with pytest.raises(FormattingError): + serialize( + version, + serialize_patterns=["{major}.{minor}.{patch}+{$BUILDMETADATA}"], + context={}, + )