From 4693013779e5baaadba41710e291f94e9da535e7 Mon Sep 17 00:00:00 2001 From: Stefan Yonkov <156345010+s-yonkov-yonkov@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:30:47 +0200 Subject: [PATCH 1/4] update dependencies and refactor code (#323) LMCROSSITXSADEPLOY-3341 LMCROSSITXSADEPLOY-2817 --- multiapps-common/pom.xml | 2 +- multiapps-mta/pom.xml | 2 +- multiapps-mta/src/main/java/module-info.java | 2 +- .../multiapps/mta/model/Version.java | 30 +-- .../mta/parsers/PartialVersionConverter.java | 48 ---- .../multiapps/mta/model/VersionTest.java | 205 ++++++++++++++++++ .../parsers/PartialVersionConverterTest.java | 72 ------ pom.xml | 31 ++- 8 files changed, 231 insertions(+), 161 deletions(-) delete mode 100644 multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/parsers/PartialVersionConverter.java create mode 100644 multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/model/VersionTest.java delete mode 100644 multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/parsers/PartialVersionConverterTest.java diff --git a/multiapps-common/pom.xml b/multiapps-common/pom.xml index fb01a8e5..a5260ab7 100644 --- a/multiapps-common/pom.xml +++ b/multiapps-common/pom.xml @@ -29,7 +29,7 @@ commons-lang3 - com.fasterxml.jackson.core + tools.jackson.core jackson-databind diff --git a/multiapps-mta/pom.xml b/multiapps-mta/pom.xml index 47512e9b..ae725f9f 100644 --- a/multiapps-mta/pom.xml +++ b/multiapps-mta/pom.xml @@ -21,7 +21,7 @@ commons-collections4 - com.vdurmont + org.semver4j semver4j diff --git a/multiapps-mta/src/main/java/module-info.java b/multiapps-mta/src/main/java/module-info.java index 1a092d01..e337fdb6 100644 --- a/multiapps-mta/src/main/java/module-info.java +++ b/multiapps-mta/src/main/java/module-info.java @@ -29,7 +29,7 @@ requires org.apache.commons.collections4; requires org.apache.commons.io; requires org.apache.commons.lang3; - requires semver4j; + requires org.semver4j; requires org.apache.commons.compress; requires static java.compiler; diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/Version.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/Version.java index f8f2efc3..67eaf16b 100644 --- a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/Version.java +++ b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/Version.java @@ -4,16 +4,12 @@ import org.cloudfoundry.multiapps.common.ParsingException; import org.cloudfoundry.multiapps.mta.Messages; -import org.cloudfoundry.multiapps.mta.parsers.PartialVersionConverter; -import com.vdurmont.semver4j.Semver; -import com.vdurmont.semver4j.Semver.SemverType; -import com.vdurmont.semver4j.SemverException; +import org.semver4j.Semver; +import org.semver4j.SemverException; public class Version implements Comparable { - private static final PartialVersionConverter PARTIAL_VERSION_CONVERTER = new PartialVersionConverter(); - private final Semver version; private Version(Semver version) { @@ -32,21 +28,12 @@ public int getPatch() { return version.getPatch(); } - public String getBuild() { - return version.getBuild(); - } - - public String[] getSuffixTokens() { - return version.getSuffixTokens(); - } - public static Version parseVersion(String versionString) { - try { - String fullVersionString = PARTIAL_VERSION_CONVERTER.convertToFullVersionString(versionString); - return new Version(new Semver(fullVersionString, SemverType.NPM)); - } catch (SemverException e) { - throw new ParsingException(e, Messages.UNABLE_TO_PARSE_VERSION, versionString); + var version = Semver.coerce(versionString); + if (version == null) { + throw new ParsingException(MessageFormat.format(Messages.UNABLE_TO_PARSE_VERSION, versionString)); } + return new Version(version); } @Override @@ -56,7 +43,7 @@ public int hashCode() { @Override public String toString() { - return version.getValue(); + return version.toString(); } @Override @@ -81,7 +68,8 @@ public boolean equals(Object obj) { public boolean satisfies(String requirement) { try { - return version.satisfies(requirement); + // The second parameter is preventing the default behavior of Semver to treat the suffix as a pre-release tag. + return version.satisfies(requirement, true); } catch (SemverException e) { throw new IllegalArgumentException(MessageFormat.format(Messages.UNABLE_TO_PARSE_VERSION_REQUIREMENT, requirement)); } diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/parsers/PartialVersionConverter.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/parsers/PartialVersionConverter.java deleted file mode 100644 index 22059356..00000000 --- a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/parsers/PartialVersionConverter.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.cloudfoundry.multiapps.mta.parsers; - -import com.vdurmont.semver4j.Semver; -import com.vdurmont.semver4j.Semver.SemverType; - -public class PartialVersionConverter { - - private static final String VERSION_STRING_TEMPLATE = "%s.%s.%s%s%s"; - private static final String VERSION_SUFFIX_STRING_TEMPLATE = "-%s"; - private static final String VERSION_BUILD_STRING_TEMPLATE = "+%s"; - private static final String DEFAULT_VERSION_SUFFIX = ""; - - public String convertToFullVersionString(String partialVersionString) { - Semver partialVersion = new Semver(partialVersionString, SemverType.LOOSE); - Integer majorVersion = partialVersion.getMajor(); - Integer minorVersion = partialVersion.getMinor(); - Integer patchVersion = partialVersion.getPatch(); - - if (minorVersion == null) { - minorVersion = 0; - } - if (patchVersion == null) { - patchVersion = 0; - } - return buildVersionString(majorVersion, minorVersion, patchVersion, partialVersion.getSuffixTokens(), partialVersion.getBuild()); - } - - private String buildVersionString(int major, int minor, int patch, String[] suffixTokens, String buildVersion) { - String formattedSuffixTokens = formatSuffixTokens(suffixTokens); - String formattedBuildVersion = formatBuildVersion(buildVersion); - return String.format(VERSION_STRING_TEMPLATE, major, minor, patch, formattedSuffixTokens, formattedBuildVersion); - } - - private String formatSuffixTokens(String[] suffixTokens) { - if (suffixTokens.length == 0) { - return DEFAULT_VERSION_SUFFIX; - } - return String.format(VERSION_SUFFIX_STRING_TEMPLATE, String.join(".", suffixTokens)); - } - - private String formatBuildVersion(String buildVersion) { - if (buildVersion == null) { - return DEFAULT_VERSION_SUFFIX; - } - return String.format(VERSION_BUILD_STRING_TEMPLATE, buildVersion); - } - -} diff --git a/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/model/VersionTest.java b/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/model/VersionTest.java new file mode 100644 index 00000000..c3a0d3d5 --- /dev/null +++ b/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/model/VersionTest.java @@ -0,0 +1,205 @@ +package org.cloudfoundry.multiapps.mta.model; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class VersionTest { + + private static final String UNABLE_TO_PARSE_VERSION = "Unable to parse version"; + + // --------------------------------------------------------------------------------------------- + // PARSING AND NORMALIZATION TESTS + // --------------------------------------------------------------------------------------------- + @Nested + @DisplayName("parseVersion() and normalization behavior") + class ParseVersionTests { + + @Test + @DisplayName("should parse complex versions with build metadata and timestamps") + void testParseComplexVersions() { + Version v1 = Version.parseVersion("0.0.1-20251105063220+e303076b5d51da7f0f3a10cd69de018ac0a3853d"); + Version v2 = Version.parseVersion("1.2.0-20251105043948+cb754700fb069b3367c869938ac6beb396c800e4"); + Version v3 = Version.parseVersion("1.0.0-SNAPSHOT-20240116224612+200df4832787863cc2f94d998c6cbcd711518933"); + + assertNotNull(v1); + assertNotNull(v2); + assertNotNull(v3); + + assertVersionParts(v1, 0, 0, 1); + assertVersionParts(v2, 1, 2, 0); + assertVersionParts(v3, 1, 0, 0); + } + + private void assertVersionParts(Version version, int major, int minor, int patch) { + assertEquals(major, version.getMajor()); + assertEquals(minor, version.getMinor()); + assertEquals(patch, version.getPatch()); + } + + @ParameterizedTest(name = "Partial version \"{0}\" should normalize to \"{1}\"") + @MethodSource + void testNormalizePartialVersions(String input, String expected) { + Version version = Version.parseVersion(input); + assertEquals(expected, version.toString()); + } + + static Stream testNormalizePartialVersions() { + return Stream.of( + Arguments.of("1.0.0", "1.0.0"), + Arguments.of("2.1", "2.1.0"), + Arguments.of("2", "2.0.0"), + Arguments.of("1.9.0-SNAPSHOT", "1.9.0-SNAPSHOT"), + Arguments.of("1.9-SNAPSHOT", "1.9.0"), + Arguments.of("1-SNAPSHOT", "1.0.0"), + Arguments.of("1.2.0-beta+exp.sha.5114f85", "1.2.0-beta+exp.sha.5114f85"), + Arguments.of("1.2-beta+exp.sha.5114f85", "1.2.0"), + Arguments.of("1-beta+exp.sha.5114f85", "1.0.0"), + Arguments.of("v1.2.3", "1.2.3") + ); + } + + @ParameterizedTest(name = "Invalid version \"{0}\" should throw SemverException or ParsingException") + @MethodSource + void testInvalidVersions(String invalid, String expectedMessage) { + Exception ex = assertThrows(Exception.class, () -> Version.parseVersion(invalid)); + assertTrue(ex.getMessage() + .contains(expectedMessage)); + } + + static Stream testInvalidVersions() { + return Stream.of( + Arguments.of("a.b.c", UNABLE_TO_PARSE_VERSION), + Arguments.of("", UNABLE_TO_PARSE_VERSION), + Arguments.of("null", UNABLE_TO_PARSE_VERSION), + Arguments.of("not-a-version", UNABLE_TO_PARSE_VERSION) + ); + } + } + + // --------------------------------------------------------------------------------------------- + // COMPARISON AND EQUALITY TESTS + // --------------------------------------------------------------------------------------------- + @Nested + @DisplayName("compareTo() and equality behavior") + class CompareTests { + + @Test + void testVersionComparison() { + Version v1 = Version.parseVersion("1.2.3"); + Version v2 = Version.parseVersion("1.3.0"); + Version v3 = Version.parseVersion("1.2.3"); + + assertTrue(v1.compareTo(v2) < 0); + assertTrue(v2.compareTo(v1) > 0); + assertEquals(0, v1.compareTo(v3)); + } + + @Test + void testEqualsAndHashCode() { + Version v1 = Version.parseVersion("2.0.0"); + Version v2 = Version.parseVersion("2.0.0"); + Version v3 = Version.parseVersion("2.0.1"); + + assertEquals(v1, v2); + assertNotEquals(v1, v3); + assertEquals(v1.hashCode(), v2.hashCode()); + assertNotEquals(v1.hashCode(), v3.hashCode()); + } + } + + // --------------------------------------------------------------------------------------------- + // TO STRING & EDGE CASE TESTS + // --------------------------------------------------------------------------------------------- + @Nested + class ToStringAndEdgeCases { + + @Test + void testToStringRepresentation() { + String raw = "3.4.5-alpha+build"; + Version version = Version.parseVersion(raw); + assertEquals(raw, version.toString()); + } + + @Test + void testLargeVersionNumbers() { + Version version = Version.parseVersion("9999.9999.9999"); + assertEquals(9999, version.getMajor()); + } + + @Test + void testPreReleaseVersion() { + Version version = Version.parseVersion("1.0.0-alpha"); + assertTrue(version.toString() + .contains("alpha")); + } + } + + // --------------------------------------------------------------------------------------------- + // SATISFIES() RANGE CHECK TESTS + // --------------------------------------------------------------------------------------------- + @Nested + class SatisfiesBehavior { + + @Test + void testSimpleComparisons() { + Version v = Version.parseVersion("3.1.4"); + + assertTrue(v.satisfies(">3.0.0")); + assertTrue(v.satisfies(">=3.1.4")); + assertFalse(v.satisfies("<3.0.0")); + assertFalse(v.satisfies("<=3.1.3")); + } + + @Test + void testCompositeRanges() { + Version v = Version.parseVersion("3.1.4"); + assertTrue(v.satisfies(">=3.1.4 <4.0.0")); + assertTrue(v.satisfies(">=3.0.0 <=3.1.4")); + assertFalse(v.satisfies(">=3.2.0 <4.0.0")); + } + + @Test + void testCaretAndTilde() { + Version v = Version.parseVersion("1.3.5"); + assertTrue(v.satisfies("^1.3.0")); + assertTrue(v.satisfies("~1.3.0")); + assertFalse(v.satisfies("^2.0.0")); + } + + @Test + void testMalformedRequirementsReturnFalse() { + Version v = Version.parseVersion("3.1.4"); + assertFalse(v.satisfies("not-a-requirement")); + assertFalse(v.satisfies("pesho3.1.4")); + assertFalse(v.satisfies("3.1.4.2.3.4.5")); + } + + @Test + void testNullRequirement() { + Version v = Version.parseVersion("3.1.4"); + assertThrows(NullPointerException.class, () -> v.satisfies(null)); + } + + @Test + void testSatisfiesWithComplexVersions() { + Version v = Version.parseVersion("1.2.3-20251105063220+e303076b5d51da7f0f3a10cd69de018ac0a3853d"); + assertTrue(v.satisfies(">=1.2.0")); + assertFalse(v.satisfies(">1.2.3")); + assertTrue(v.satisfies(">=1.2.0 <2.0.0")); + assertFalse(v.satisfies(">1.2.3 <2.0.0")); + } + } +} \ No newline at end of file diff --git a/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/parsers/PartialVersionConverterTest.java b/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/parsers/PartialVersionConverterTest.java deleted file mode 100644 index 5a539d72..00000000 --- a/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/parsers/PartialVersionConverterTest.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.cloudfoundry.multiapps.mta.parsers; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.util.stream.Stream; - -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import com.vdurmont.semver4j.SemverException; - -class PartialVersionConverterTest { - - private final PartialVersionConverter partialVersionConverter = new PartialVersionConverter(); - - @ParameterizedTest - @MethodSource - void testConvertWithInvalidVersions(String versionString, String expectedExceptionMessage) { - SemverException exception = assertThrows(SemverException.class, - () -> partialVersionConverter.convertToFullVersionString(versionString)); - - assertEquals(expectedExceptionMessage, exception.getMessage()); - } - - static Stream testConvertWithInvalidVersions() { - return Stream.of( -// @formatter:off - Arguments.of("1.0.0-beta+", "The build cannot be empty."), - Arguments.of("3.a", "Invalid version (no minor version): 3.a"), - Arguments.of("a.b.c", "Invalid version (no major version): a.b.c"), - Arguments.of( "", "Invalid version (no major version): "), - Arguments.of("[ 2.0, 2.1 ]", "Invalid version (no major version): [ 2.0, 2.1 ]") -// @formatter:on - ); - } - - @ParameterizedTest - @MethodSource - void testConvertWithValidVersions(String versionString, String expectedResult) { - String fullVersionString = partialVersionConverter.convertToFullVersionString(versionString); - - assertEquals(expectedResult, fullVersionString); - } - - static Stream testConvertWithValidVersions() { - return Stream.of( -// @formatter:off - // Full version: - Arguments.of("1.0.0", "1.0.0"), - // Partial version with minor version: - Arguments.of("2.1", "2.1.0"), - // Partial version with patch version: - Arguments.of("2", "2.0.0"), - // Full version with suffix tokens: - Arguments.of("1.9.0-SHAPSHOT", "1.9.0-SHAPSHOT"), - // Partial version with suffix tokens: - Arguments.of("1.9-SHAPSHOT", "1.9.0-SHAPSHOT"), - // Partial version with suffix tokens: - Arguments.of("1-SHAPSHOT", "1.0.0-SHAPSHOT"), - // Full version with suffix tokens and build information: - Arguments.of("1.2.0-beta+exp.sha.5114f85", "1.2.0-beta+exp.sha.5114f85"), - // Partial version with suffix tokens and build information: - Arguments.of("1.2-beta+exp.sha.5114f85", "1.2.0-beta+exp.sha.5114f85"), - // Partial version with suffix tokens and build information: - Arguments.of("1-beta+exp.sha.5114f85", "1.0.0-beta+exp.sha.5114f85") -// @formatter:on - ); - } - -} diff --git a/pom.xml b/pom.xml index d333ff11..92e4c30b 100644 --- a/pom.xml +++ b/pom.xml @@ -18,20 +18,18 @@ cloudfoundry ../multiapps-coverage/target/site/jacoco-aggregate/jacoco.xml - 5.13.4 - 1.13.2 - 5.18.0 - 3.18.0 + 6.0.1 + 5.20.0 + 3.19.0 4.5.0 2.20.0 - 2.4 - - - 3.1.0 - 2.10.1 - 2.19.2 - 4.0.5 - 4.0.2 + 2.5 + 6.0.0 + 2.11.6 + 3.0.1 + 2.20.1 + 4.0.6 + 4.0.4 1.28.0 @@ -217,7 +215,7 @@ org.junit.platform junit-platform-launcher - ${junit-platform.version} + ${junit.version} test @@ -248,14 +246,14 @@ - com.fasterxml.jackson.core + tools.jackson.core jackson-databind ${jackson.version} com.fasterxml.jackson.datatype jackson-datatype-jsr310 - ${jackson.version} + ${jackson.datatype.jsr310.version} @@ -281,9 +279,8 @@ snakeyaml ${snakeyaml.version} - - com.vdurmont + org.semver4j semver4j ${semver4j.version} From fff8a4a3e8fbca4a503be38c9d5a5f812d38cded Mon Sep 17 00:00:00 2001 From: Stefan Yonkov <156345010+s-yonkov-yonkov@users.noreply.github.com> Date: Fri, 14 Nov 2025 13:30:58 +0200 Subject: [PATCH 2/4] small fixes on dependency sources (#324) --- pom.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 92e4c30b..ac29f872 100644 --- a/pom.xml +++ b/pom.xml @@ -244,12 +244,13 @@ jaxb-runtime ${jaxb-runtime.version} - + tools.jackson.core jackson-databind ${jackson.version} + com.fasterxml.jackson.datatype jackson-datatype-jsr310 @@ -279,6 +280,7 @@ snakeyaml ${snakeyaml.version} + org.semver4j semver4j From 30b1a65a46a9dff2e3f92dd4ec1a32b1560d7901 Mon Sep 17 00:00:00 2001 From: Stefan Yonkov <156345010+s-yonkov-yonkov@users.noreply.github.com> Date: Thu, 27 Nov 2025 11:47:03 +0200 Subject: [PATCH 3/4] update dependencies (#327) --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ac29f872..4f0ec48a 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 2.20.0 2.5 6.0.0 - 2.11.6 + 2.11.7 3.0.1 2.20.1 4.0.6 @@ -119,7 +119,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.11.2 + 3.12.0 From cc32f9021bb41a270e5c690e8c78f74d43301031 Mon Sep 17 00:00:00 2001 From: Stefan Yonkov Date: Mon, 22 Dec 2025 16:47:50 +0200 Subject: [PATCH 4/4] add small version updates --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 4f0ec48a..8c54f570 100644 --- a/pom.xml +++ b/pom.xml @@ -19,14 +19,14 @@ ../multiapps-coverage/target/site/jacoco-aggregate/jacoco.xml 6.0.1 - 5.20.0 - 3.19.0 + 5.21.0 + 3.20.0 4.5.0 - 2.20.0 + 2.21.0 2.5 6.0.0 - 2.11.7 - 3.0.1 + 2.12.0 + 3.0.3 2.20.1 4.0.6 4.0.4