This is an automated email from the ASF dual-hosted git repository.
JiriOndrusek pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-upgrade-recipes.git
The following commit(s) were added to refs/heads/main by this push:
new 5812577 Add test to verify camel-latest-version property in pom.xml
is aligned to recipes.
5812577 is described below
commit 58125771f57e59391bd776953347fe72c4a20a10
Author: Jiri Ondrusek <[email protected]>
AuthorDate: Tue May 12 13:08:59 2026 +0200
Add test to verify camel-latest-version property in pom.xml is aligned to
recipes.
---
README.adoc | 2 +
camel-spring-boot-upgrade-recipes/pom.xml | 7 +
.../camel/upgrade/springboot/LatestRecipeTest.java | 103 +--------------
.../camel/upgrade/LatestCamelVersionTest.java | 57 ++++++++
.../org/apache/camel/upgrade/RecipeVersion.java | 145 +++++++++++++++++++++
5 files changed, 216 insertions(+), 98 deletions(-)
diff --git a/README.adoc b/README.adoc
index 6244959..d87b3d7 100644
--- a/README.adoc
+++ b/README.adoc
@@ -70,6 +70,8 @@ This project is released as standard Apache Camel module.
<rewrite-recipe-bom.version>3.24.0</rewrite-recipe-bom.version>
```
+NOTE: The `camel-latest-version` property is automatically verified by the
test suite (`LatestCamelVersionTest`) to match the highest version recipe file.
If you add a new version recipe (e.g., `4.21.yaml`), the test will fail until
you update the property.
+
Check that everything is alright and run:
```bash
diff --git a/camel-spring-boot-upgrade-recipes/pom.xml
b/camel-spring-boot-upgrade-recipes/pom.xml
index 9118474..af3dddf 100644
--- a/camel-spring-boot-upgrade-recipes/pom.xml
+++ b/camel-spring-boot-upgrade-recipes/pom.xml
@@ -47,6 +47,13 @@
<artifactId>camel-upgrade-recipes</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel.upgrade</groupId>
+ <artifactId>camel-upgrade-recipes</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-java-21</artifactId>
diff --git
a/camel-spring-boot-upgrade-recipes/src/test/java/org/apache/camel/upgrade/springboot/LatestRecipeTest.java
b/camel-spring-boot-upgrade-recipes/src/test/java/org/apache/camel/upgrade/springboot/LatestRecipeTest.java
index 59e92c0..319c729 100644
---
a/camel-spring-boot-upgrade-recipes/src/test/java/org/apache/camel/upgrade/springboot/LatestRecipeTest.java
+++
b/camel-spring-boot-upgrade-recipes/src/test/java/org/apache/camel/upgrade/springboot/LatestRecipeTest.java
@@ -16,14 +16,13 @@
*/
package org.apache.camel.upgrade.springboot;
+import org.apache.camel.upgrade.RecipeVersion;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.List;
-import java.util.stream.Stream;
/**
@@ -47,18 +46,10 @@ class LatestRecipeTest {
@Test
void latestRecipeReferencesHighestVersion() throws Exception {
// Get versions from camel-upgrade-recipes dependency - this is the
source of truth
- List<Version> versions = getVersionsFromCoreRecipes();
-
- if (versions.isEmpty()) {
- Path coreCamelRecipes =
Paths.get("../camel-upgrade-recipes/src/main/resources/META-INF/rewrite")
- .toAbsolutePath();
- Assertions.fail(String.format("No core Camel version recipe files
found (pattern: X.Y.yaml). " +
- "Searched in: %s. Ensure multi-module structure is
intact.", coreCamelRecipes));
- }
-
- Version latestVersion = versions.stream()
- .max(Version::compareTo)
- .orElseThrow(() -> new IllegalStateException("No maximum
version found despite non-empty list"));
+ // Navigate to the sibling camel-upgrade-recipes module using relative
path
+ Path coreCamelRecipes =
Paths.get("../camel-upgrade-recipes/src/main/resources/META-INF/rewrite");
+ RecipeVersion latestVersion =
RecipeVersion.getHighestVersionFromRecipeDirectory(coreCamelRecipes,
+ "core Camel recipes (ensure multi-module structure is
intact)");
// Read latest.yaml directly
Path latestYamlPath =
Paths.get("src/main/resources/META-INF/rewrite/latest.yaml").toAbsolutePath();
@@ -96,88 +87,4 @@ class LatestRecipeTest {
"(it's included transitively via Spring Boot
recipe): %s",
latestVersion.toDottedString(),
latestCoreCamelRecipeName));
}
-
- /**
- * Get version objects from YAML files in the camel-upgrade-recipes
dependency.
- * Returns only version files matching the pattern X.Y.yaml (e.g.,
4.20.yaml).
- *
- * @return List of Version objects found in the dependency
- */
- private List<Version> getVersionsFromCoreRecipes() throws Exception {
- // Navigate to the sibling camel-upgrade-recipes module using relative
path
- Path coreCamelRecipes =
Paths.get("../camel-upgrade-recipes/src/main/resources/META-INF/rewrite");
-
- if (!Files.exists(coreCamelRecipes)) {
- return List.of();
- }
-
- try (Stream<Path> paths = Files.walk(coreCamelRecipes)) {
- return paths.filter(Files::isRegularFile)
- .map(Path::getFileName)
- .map(Path::toString)
- .filter(name -> name.endsWith(".yaml") &&
Character.isDigit(name.charAt(0)))
- .map(name -> name.substring(0, name.lastIndexOf('.')))
- .filter(version -> version.matches("^\\d+\\.\\d+$"))
- .map(Version::parse)
- .toList();
- }
- }
-
- /**
- * Semantic version holder for comparing X.Y version strings.
- */
- private static class Version implements Comparable<Version> {
- final int major;
- final int minor;
-
- Version(int major, int minor) {
- this.major = major;
- this.minor = minor;
- }
-
- /**
- * Parse a version string in X.Y format.
- *
- * @param version version string (e.g., "4.20")
- * @return parsed Version object
- * @throws IllegalArgumentException if format is invalid
- */
- static Version parse(String version) {
- String[] parts = version.split("\\.");
- if (parts.length != 2) {
- throw new IllegalArgumentException("Version must be in format
X.Y, got: " + version);
- }
- try {
- return new Version(Integer.parseInt(parts[0]),
Integer.parseInt(parts[1]));
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("Invalid version format: "
+ version, e);
- }
- }
-
- @Override
- public int compareTo(Version other) {
- int majorCompare = Integer.compare(this.major, other.major);
- if (majorCompare != 0) {
- return majorCompare;
- }
- return Integer.compare(this.minor, other.minor);
- }
-
- /**
- * Returns version without dot (e.g., "420" for version 4.20).
- * Used for recipe package names.
- */
- @Override
- public String toString() {
- return String.format("%d%d", major, minor);
- }
-
- /**
- * Returns version with dot (e.g., "4.20").
- * Used for user-facing messages.
- */
- String toDottedString() {
- return String.format("%d.%d", major, minor);
- }
- }
}
diff --git
a/camel-upgrade-recipes/src/test/java/org/apache/camel/upgrade/LatestCamelVersionTest.java
b/camel-upgrade-recipes/src/test/java/org/apache/camel/upgrade/LatestCamelVersionTest.java
new file mode 100644
index 0000000..c1ab188
--- /dev/null
+++
b/camel-upgrade-recipes/src/test/java/org/apache/camel/upgrade/LatestCamelVersionTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.upgrade;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Test to ensure the camel-latest-version property in pom.xml is kept aligned
+ * with the highest version recipe file.
+ *
+ * This test prevents forgetting to update the camel-latest-version property
+ * when a new version recipe is added.
+ */
+class LatestCamelVersionTest {
+
+ @Test
+ void camelLatestVersionPropertyMatchesHighestRecipe() throws Exception {
+ // Find the highest version from recipe YAML files
+ Path recipesPath = Paths.get("src/main/resources/META-INF/rewrite");
+ RecipeVersion highestVersion =
RecipeVersion.getHighestVersionFromRecipeDirectory(recipesPath, "recipe
directory");
+
+ // Read camel-latest-version from versions.properties
+ String camelLatestVersion = CamelTestUtil.getCamelLatestVersion();
+
+ if (camelLatestVersion == null || camelLatestVersion.trim().isEmpty())
{
+ Assertions.fail("camel.latest.version not found in
versions.properties. " +
+ "Ensure the project has been built (mvn
process-resources).");
+ }
+
+ // Parse the version from the property (e.g., "4.20.0")
+ RecipeVersion propertyVersion =
RecipeVersion.parseFromFullVersion(camelLatestVersion);
+
+ // Compare major.minor versions
+ Assertions.assertEquals(highestVersion, propertyVersion,
+ String.format("camel-latest-version property (%s) does not
match the highest recipe version (%s). " +
+ "Please update the <camel-latest-version>
property in pom.xml to %s.0",
+ camelLatestVersion, highestVersion.toDottedString(),
highestVersion.toDottedString()));
+ }
+}
diff --git
a/camel-upgrade-recipes/src/test/java/org/apache/camel/upgrade/RecipeVersion.java
b/camel-upgrade-recipes/src/test/java/org/apache/camel/upgrade/RecipeVersion.java
new file mode 100644
index 0000000..6ea3b1a
--- /dev/null
+++
b/camel-upgrade-recipes/src/test/java/org/apache/camel/upgrade/RecipeVersion.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.upgrade;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.stream.Stream;
+
+/**
+ * Semantic version holder for comparing X.Y version strings used in recipe
files.
+ * This is a test utility class shared across recipe verification tests.
+ */
+public class RecipeVersion implements Comparable<RecipeVersion> {
+ public final int major;
+ public final int minor;
+
+ public RecipeVersion(int major, int minor) {
+ this.major = major;
+ this.minor = minor;
+ }
+
+ /**
+ * Parse a version string in X.Y format (e.g., "4.20").
+ *
+ * @param version version string
+ * @return parsed RecipeVersion object
+ * @throws IllegalArgumentException if format is invalid
+ */
+ public static RecipeVersion parse(String version) {
+ String[] parts = version.split("\\.");
+ if (parts.length != 2) {
+ throw new IllegalArgumentException("Version must be in format X.Y,
got: " + version);
+ }
+ try {
+ return new RecipeVersion(Integer.parseInt(parts[0]),
Integer.parseInt(parts[1]));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid version format: " +
version, e);
+ }
+ }
+
+ /**
+ * Parse a version string in X.Y.Z or X.Y format (e.g., "4.20.0" or
"4.20") and return X.Y.
+ *
+ * @param fullVersion version string in X.Y.Z or X.Y format
+ * @return parsed RecipeVersion object with major.minor
+ * @throws IllegalArgumentException if format is invalid
+ */
+ public static RecipeVersion parseFromFullVersion(String fullVersion) {
+ String[] parts = fullVersion.split("\\.");
+ if (parts.length < 2) {
+ throw new IllegalArgumentException("Version must be in format
X.Y.Z or X.Y, got: " + fullVersion);
+ }
+ try {
+ return new RecipeVersion(Integer.parseInt(parts[0]),
Integer.parseInt(parts[1]));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid version format: " +
fullVersion, e);
+ }
+ }
+
+ @Override
+ public int compareTo(RecipeVersion other) {
+ int majorCompare = Integer.compare(this.major, other.major);
+ if (majorCompare != 0) {
+ return majorCompare;
+ }
+ return Integer.compare(this.minor, other.minor);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+ RecipeVersion version = (RecipeVersion) obj;
+ return major == version.major && minor == version.minor;
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * major + minor;
+ }
+
+ /**
+ * Returns version without dot (e.g., "420" for version 4.20).
+ * Used for recipe package names.
+ */
+ @Override
+ public String toString() {
+ return String.format("%d%d", major, minor);
+ }
+
+ /**
+ * Returns version with dot (e.g., "4.20").
+ * Used for user-facing messages.
+ */
+ public String toDottedString() {
+ return String.format("%d.%d", major, minor);
+ }
+
+ /**
+ * Finds the highest version from recipe YAML files in the specified
directory.
+ * Scans the directory for files matching the pattern X.Y.yaml (e.g.,
4.20.yaml)
+ * and returns the highest version found.
+ *
+ * @param recipesPath the directory to scan for recipe files
+ * @param pathDescription description of the path for error messages
(e.g., "core recipes", "Spring Boot recipes")
+ * @return the highest RecipeVersion found
+ * @throws Exception if an I/O error occurs during directory traversal
+ * @throws IllegalStateException if no version files are found in the
directory
+ */
+ public static RecipeVersion getHighestVersionFromRecipeDirectory(Path
recipesPath, String pathDescription) throws Exception {
+ if (!Files.exists(recipesPath)) {
+ throw new IllegalStateException(
+ String.format("Recipe directory not found for %s: %s",
+ pathDescription, recipesPath.toAbsolutePath()));
+ }
+
+ try (Stream<Path> paths = Files.walk(recipesPath)) {
+ return paths.filter(Files::isRegularFile)
+ .map(Path::getFileName)
+ .map(Path::toString)
+ .filter(name -> name.endsWith(".yaml") &&
Character.isDigit(name.charAt(0)))
+ .map(name -> name.substring(0, name.lastIndexOf('.')))
+ .filter(version -> version.matches("^\\d+\\.\\d+$"))
+ .map(RecipeVersion::parse)
+ .max(RecipeVersion::compareTo)
+ .orElseThrow(() -> new IllegalStateException(
+ String.format("No version recipe files found (pattern:
X.Y.yaml) in %s. Searched in: %s",
+ pathDescription, recipesPath.toAbsolutePath())));
+ }
+ }
+}