diff --git a/src/main/java/org/openrewrite/gradle/RecipeMarketplaceCsvGenerateTask.java b/src/main/java/org/openrewrite/gradle/RecipeMarketplaceCsvGenerateTask.java index 66aacca..26a5df8 100644 --- a/src/main/java/org/openrewrite/gradle/RecipeMarketplaceCsvGenerateTask.java +++ b/src/main/java/org/openrewrite/gradle/RecipeMarketplaceCsvGenerateTask.java @@ -118,7 +118,7 @@ void generate() throws IOException { // Merge generated marketplace into existing one // This ensures existing data is preserved and updated with generated data - existing.merge(generated); + existing.getRoot().merge(generated.getRoot()); marketplace = existing; } else { getLogger().info("No existing recipes.csv found, creating new file"); diff --git a/src/main/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateCompletenessTask.java b/src/main/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateCompletenessTask.java index b9ba47e..092f36e 100644 --- a/src/main/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateCompletenessTask.java +++ b/src/main/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateCompletenessTask.java @@ -23,18 +23,18 @@ import org.gradle.api.tasks.TaskAction; import org.gradle.jvm.tasks.Jar; import org.openrewrite.Validated; +import org.openrewrite.config.ClasspathScanningLoader; import org.openrewrite.config.Environment; import org.openrewrite.marketplace.RecipeMarketplace; import org.openrewrite.marketplace.RecipeMarketplaceCompletenessValidator; import org.openrewrite.marketplace.RecipeMarketplaceReader; import org.openrewrite.maven.marketplace.RecipeClassLoader; -import org.openrewrite.maven.marketplace.ResolvedMavenRecipeBundle; -import org.openrewrite.maven.tree.ResolvedGroupArtifactVersion; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +import java.util.Properties; import static java.util.stream.Collectors.toList; @@ -102,22 +102,9 @@ void validate() { .collect(toList()); // Load environment from JAR - // We create a temporary GAV just for loading the environment - ResolvedGroupArtifactVersion tempGav = new ResolvedGroupArtifactVersion( - null, - "temp", - "temp", - "1.0.0", - null - ); - ResolvedMavenRecipeBundle bundle = new ResolvedMavenRecipeBundle( - tempGav, - recipeJarPath, - classpath, - RecipeClassLoader::new, - null - ); - Environment jarEnvironment = bundle.getEnvironment(); + Environment jarEnvironment = Environment.builder() + .load(new ClasspathScanningLoader(new Properties(), new RecipeClassLoader(recipeJarPath, classpath))) + .build(); // Validate completeness RecipeMarketplaceCompletenessValidator validator = new RecipeMarketplaceCompletenessValidator(); diff --git a/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvGenerateTaskTest.java b/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvGenerateTaskTest.java index 71a0cc5..5982507 100644 --- a/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvGenerateTaskTest.java +++ b/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvGenerateTaskTest.java @@ -22,7 +22,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -33,18 +32,19 @@ import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; import static org.junit.jupiter.api.Assertions.assertEquals; -@SuppressWarnings("ResultOfMethodCallIgnored") class RecipeMarketplaceCsvGenerateTaskTest { @TempDir - File projectDir; + Path projectDir; - private File settingsFile; - private File buildFile; + private Path settingsFile; + private Path buildFile; + private Path csvFile; @BeforeEach public void setup() { - settingsFile = new File(projectDir, "settings.gradle"); - buildFile = new File(projectDir, "build.gradle"); + settingsFile = projectDir.resolve("settings.gradle"); + buildFile = projectDir.resolve("build.gradle"); + csvFile = Path.of(projectDir.toString(), "src", "main", "resources", "META-INF", "rewrite", "recipes.csv"); } @Test @@ -52,7 +52,7 @@ void generatesRecipeCsvFromJar() throws Exception { createSimpleRecipeProject(); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvGenerate", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -62,7 +62,6 @@ void generatesRecipeCsvFromJar() throws Exception { assertEquals(SUCCESS, requireNonNull(result.task(":recipeCsvGenerate")).getOutcome()); // Read and verify CSV content - File csvFile = new File(projectDir, "src/main/resources/META-INF/rewrite/recipes.csv"); assertThat(csvFile) .content() .contains("name") @@ -74,17 +73,16 @@ void mergesWithExistingRecipeCsv() throws Exception { createSimpleRecipeProject(); // Create existing recipes.csv with custom entry - File csvFile = new File(projectDir, "src/main/resources/META-INF/rewrite/recipes.csv"); - csvFile.getParentFile().mkdirs(); - Files.writeString(csvFile.toPath(), + Files.createDirectories(csvFile.getParent()); + Files.writeString(csvFile, //language=csv """ - name,displayName,category1 - org.example.CustomRecipe,Custom Recipe,Custom + name,displayName,packageName,ecosystem,category1 + org.example.CustomRecipe,Custom Recipe,org.example:example-custom,Maven,Custom """); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvGenerate", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -105,16 +103,15 @@ void mergesRecipesFromBothSources() throws Exception { createSimpleRecipeProject(); // Create existing recipes.csv with same recipe but in different category - File csvFile = new File(projectDir, "src/main/resources/META-INF/rewrite/recipes.csv"); - csvFile.getParentFile().mkdirs(); - Files.writeString(csvFile.toPath(), + Files.createDirectories(csvFile.getParent()); + Files.writeString(csvFile, """ - name,displayName,category1 - org.example.TestRecipe,Old Display Name,Old Category + ecosystem,packageName,name,displayName,category1 + maven,org.example:test-recipe-project,org.example.TestRecipe,Old Display Name,Old Category """); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvGenerate", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -154,7 +151,7 @@ void usesNebulaPublicationForGav() throws Exception { createSimpleRecipeClass(); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvGenerate", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -173,13 +170,13 @@ void createsParentDirectoriesIfNeeded() throws Exception { createSimpleRecipeProject(); // Delete the resources directory if it exists - File resourcesDir = new File(projectDir, "src/main/resources"); - if (resourcesDir.exists()) { - deleteDirectory(resourcesDir.toPath()); + Path resourcesDir = Path.of(projectDir.toString(), "src", "main", "resources"); + if (Files.exists(resourcesDir)) { + deleteDirectory(resourcesDir); } BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvGenerate", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -188,9 +185,8 @@ void createsParentDirectoriesIfNeeded() throws Exception { assertEquals(SUCCESS, requireNonNull(result.task(":recipeCsvGenerate")).getOutcome()); // Assert parent directories were created - File csvFile = new File(projectDir, "src/main/resources/META-INF/rewrite/recipes.csv"); - assertThat(csvFile.getParentFile()).exists().isDirectory(); - assertThat(csvFile).exists().isFile(); + assertThat(csvFile.getParent()).exists().isDirectory(); + assertThat(csvFile).exists().isNotEmptyFile(); } private void createSimpleRecipeProject() throws IOException { @@ -216,11 +212,10 @@ private void createSimpleRecipeProject() throws IOException { createSimpleRecipeClass(); } - @SuppressWarnings("NullableProblems") private void createSimpleRecipeClass() throws IOException { // Use a declarative YAML recipe instead of Java class for simpler testing - File rewriteDir = new File(projectDir, "src/main/resources/META-INF/rewrite"); - rewriteDir.mkdirs(); + Path rewriteDir = Path.of(projectDir.toString(), "src", "main", "resources", "META-INF", "rewrite"); + Files.createDirectories(rewriteDir); @Language("yaml") String rewriteYml = """ @@ -234,12 +229,12 @@ private void createSimpleRecipeClass() throws IOException { toText: "Hello World" """; - Files.writeString(new File(rewriteDir, "rewrite.yml").toPath(), rewriteYml); + Files.writeString(rewriteDir.resolve("rewrite.yml"), rewriteYml); } private void createGradleBuildFiles(@Language("gradle") String buildFileContent) throws IOException { - Files.writeString(settingsFile.toPath(), "rootProject.name = 'test-recipe-project'"); - Files.writeString(buildFile.toPath(), buildFileContent); + Files.writeString(settingsFile, "rootProject.name = 'test-recipe-project'"); + Files.writeString(buildFile, buildFileContent); } private void deleteDirectory(Path path) throws IOException { diff --git a/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateCompletenessTaskTest.java b/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateCompletenessTaskTest.java index a35d857..2838ca0 100644 --- a/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateCompletenessTaskTest.java +++ b/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateCompletenessTaskTest.java @@ -22,9 +22,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import static java.util.Objects.requireNonNull; import static org.assertj.core.api.Assertions.assertThat; @@ -34,17 +34,18 @@ class RecipeMarketplaceCsvValidateCompletenessTaskTest { @TempDir - File projectDir; + Path projectDir; - private File settingsFile; - private File buildFile; - private File csvFile; + private Path settingsFile; + private Path buildFile; + private Path csvFile; @BeforeEach - public void setup() { - settingsFile = new File(projectDir, "settings.gradle"); - buildFile = new File(projectDir, "build.gradle"); - csvFile = new File(projectDir, "src/main/resources/META-INF/rewrite/recipes.csv"); + public void setup() throws Exception { + settingsFile = projectDir.resolve("settings.gradle"); + buildFile = projectDir.resolve("build.gradle"); + csvFile = Path.of(projectDir.toString(), "src", "main", "resources", "META-INF", "rewrite", "recipes.csv"); + Files.createDirectories(csvFile.getParent()); } @Test @@ -53,7 +54,7 @@ void validatesCompletenessBetweenCsvAndJar() throws Exception { createCsvMatchingJar(); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidateCompleteness", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -67,17 +68,16 @@ void validatesCompletenessBetweenCsvAndJar() throws Exception { @Test void failsWhenCsvContainsPhantomRecipe() throws Exception { createSimpleRecipeProject(); - csvFile.getParentFile().mkdirs(); // CSV contains a recipe that doesn't exist in the JAR - Files.writeString(csvFile.toPath(), + Files.writeString(csvFile, """ - name,displayName,description - org.example.TestRecipe,Test Recipe,A test recipe. - org.example.PhantomRecipe,Phantom Recipe,This recipe does not exist. + ecosystem,packageName,name,displayName,description + maven,org.example:test-recipe-project,org.example.TestRecipe,Test Recipe,A test recipe. + maven,org.example:test-recipe-project,org.example.PhantomRecipe,Phantom Recipe,This recipe does not exist. """); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidateCompleteness", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -85,25 +85,22 @@ void failsWhenCsvContainsPhantomRecipe() throws Exception { assertEquals(FAILED, requireNonNull(result.task(":recipeCsvValidateCompleteness")).getOutcome()); assertThat(result.getOutput()) - .contains("Recipe marketplace CSV completeness validation failed") - .contains("Recipe listed in CSV does not exist in JAR") - .contains("org.example.PhantomRecipe") - .contains("phantom recipe"); + .contains("Recipe marketplace CSV completeness validation failed with 1 error(s)") + .contains("Recipe 'org.example.PhantomRecipe' listed in CSV must exist in the environment"); } @Test void failsWhenJarContainsMissingRecipe() throws Exception { createProjectWithTwoRecipes(); - csvFile.getParentFile().mkdirs(); // CSV only contains one recipe, but JAR has two - Files.writeString(csvFile.toPath(), + Files.writeString(csvFile, """ - name,displayName,description - org.example.TestRecipe,Test Recipe,A test recipe. + ecosystem,packageName,name,displayName,description + maven,org.example:test-recipe-project,org.example.TestRecipe,Test Recipe,A test recipe. """); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidateCompleteness", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -111,26 +108,23 @@ void failsWhenJarContainsMissingRecipe() throws Exception { assertEquals(FAILED, requireNonNull(result.task(":recipeCsvValidateCompleteness")).getOutcome()); assertThat(result.getOutput()) - .contains("Recipe marketplace CSV completeness validation failed") - .contains("Recipe exists in JAR but is not listed in CSV") - .contains("org.example.AnotherRecipe") - .contains("missing from CSV"); + .contains("Recipe marketplace CSV completeness validation failed with 1 error(s)") + .contains("Recipe 'org.example.AnotherRecipe' exists in environment but is not listed in CSV"); } @Test void reportsMultipleCompletenessErrors() throws Exception { createProjectWithTwoRecipes(); - csvFile.getParentFile().mkdirs(); // CSV has one phantom recipe and is missing one real recipe - Files.writeString(csvFile.toPath(), + Files.writeString(csvFile, """ - name,displayName,description - org.example.TestRecipe,Test Recipe,A test recipe. - org.example.PhantomRecipe,Phantom Recipe,Does not exist. + ecosystem,packageName,name,displayName,description + maven,org.example:test-recipe-project,org.example.TestRecipe,Test Recipe,A test recipe. + maven,org.example:test-recipe-project,org.example.PhantomRecipe,Phantom Recipe,Does not exist. """); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidateCompleteness", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -148,7 +142,7 @@ void skipsValidationWhenCsvDoesNotExist() throws Exception { createSimpleRecipeProject(); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidateCompleteness", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -162,17 +156,16 @@ void skipsValidationWhenCsvDoesNotExist() throws Exception { @Test void handlesDuplicateRecipesInDifferentCategories() throws Exception { createSimpleRecipeProject(); - csvFile.getParentFile().mkdirs(); // Recipe appears in multiple categories - should be treated as single recipe - Files.writeString(csvFile.toPath(), + Files.writeString(csvFile, """ - name,displayName,description,category1 - org.example.TestRecipe,Test Recipe,A test recipe.,Java - org.example.TestRecipe,Test Recipe,A test recipe.,Cleanup + ecosystem,packageName,name,displayName,description,category1 + maven,org.example:test-recipe-project,org.example.TestRecipe,Test Recipe,A test recipe.,Java + maven,org.example:test-recipe-project,org.example.TestRecipe,Test Recipe,A test recipe.,Cleanup """); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidateCompleteness", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -185,15 +178,14 @@ void handlesDuplicateRecipesInDifferentCategories() throws Exception { @Test void validatesWithCategories() throws Exception { createSimpleRecipeProject(); - csvFile.getParentFile().mkdirs(); - Files.writeString(csvFile.toPath(), + Files.writeString(csvFile, """ - name,displayName,description,category1,category2 - org.example.TestRecipe,Test Recipe,A test recipe.,Example,Cleanup + ecosystem,packageName,name,displayName,description,category1,category2 + maven,org.example:test-recipe-project,org.example.TestRecipe,Test Recipe,A test recipe.,Example,Cleanup """); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidateCompleteness", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -204,8 +196,8 @@ void validatesWithCategories() throws Exception { } private void createSimpleRecipeProject() throws IOException { - Files.writeString(settingsFile.toPath(), "rootProject.name = 'test-recipe-project'"); - Files.writeString(buildFile.toPath(), """ + Files.writeString(settingsFile, "rootProject.name = 'test-recipe-project'"); + Files.writeString(buildFile, """ plugins { id 'java' id 'org.openrewrite.build.recipe-library-base' @@ -234,9 +226,9 @@ private void createProjectWithTwoRecipes() throws IOException { private void createRecipeClass(String className, String displayName, String description) throws IOException { // Use declarative YAML recipe for simpler testing - File rewriteDir = new File(projectDir, "src/main/resources/META-INF/rewrite"); - rewriteDir.mkdirs(); - File rewriteYml = new File(rewriteDir, "rewrite.yml"); + Path rewriteDir = Path.of(projectDir.toString(), "src", "main", "resources", "META-INF", "rewrite"); + Files.createDirectories(rewriteDir); + Path rewriteYml = rewriteDir.resolve("rewrite.yml"); @Language("yaml") String recipeYml = """ @@ -250,20 +242,19 @@ private void createRecipeClass(String className, String displayName, String desc toText: "Hello" """.formatted(className, displayName, description); - if (rewriteYml.exists()) { + if (Files.exists(rewriteYml)) { // Append to existing file - Files.writeString(rewriteYml.toPath(), Files.readString(rewriteYml.toPath()) + recipeYml); + Files.writeString(rewriteYml, Files.readString(rewriteYml) + recipeYml); } else { - Files.writeString(rewriteYml.toPath(), recipeYml); + Files.writeString(rewriteYml, recipeYml); } } private void createCsvMatchingJar() throws IOException { - csvFile.getParentFile().mkdirs(); - Files.writeString(csvFile.toPath(), + Files.writeString(csvFile, """ - name,displayName,description - org.example.TestRecipe,Test Recipe,A test recipe. + ecosystem,packageName,name,displayName,description + maven,org:example:test-recipe-project,org.example.TestRecipe,Test Recipe,A test recipe. """); } } diff --git a/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateContentTaskTest.java b/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateContentTaskTest.java index a6c767d..ea06414 100644 --- a/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateContentTaskTest.java +++ b/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateContentTaskTest.java @@ -22,9 +22,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import static java.util.Objects.requireNonNull; import static org.assertj.core.api.Assertions.assertThat; @@ -34,17 +34,17 @@ class RecipeMarketplaceCsvValidateContentTaskTest { @TempDir - File projectDir; + Path projectDir; - private File settingsFile; - private File buildFile; - private File csvFile; + private Path settingsFile; + private Path buildFile; + private Path csvFile; @BeforeEach public void setup() { - settingsFile = new File(projectDir, "settings.gradle"); - buildFile = new File(projectDir, "build.gradle"); - csvFile = new File(projectDir, "src/main/resources/META-INF/rewrite/recipes.csv"); + settingsFile = projectDir.resolve("settings.gradle"); + buildFile = projectDir.resolve("build.gradle"); + csvFile = Path.of(projectDir.toString(), "src", "main", "resources", "META-INF", "rewrite", "recipes.csv"); } @Test @@ -53,7 +53,7 @@ void validatesValidCsvContent() throws Exception { createValidCsv(); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidateContent", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -66,15 +66,15 @@ void validatesValidCsvContent() throws Exception { @Test void failsWhenDisplayNameDoesNotStartWithUppercase() throws Exception { createGradleBuildFiles(); - csvFile.getParentFile().mkdirs(); - Files.writeString(csvFile.toPath(), + Files.createDirectories(csvFile.getParent()); + Files.writeString(csvFile, """ - name,displayName,description - org.example.TestRecipe,test recipe,A test recipe. + ecosystem,packageName,name,displayName,description + maven,org.example:test-project-recipe,org.example.TestRecipe,test recipe,A test recipe. """); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidateContent", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -83,22 +83,21 @@ void failsWhenDisplayNameDoesNotStartWithUppercase() throws Exception { assertEquals(FAILED, requireNonNull(result.task(":recipeCsvValidateContent")).getOutcome()); assertThat(result.getOutput()) .contains("Recipe marketplace CSV content validation failed") - .contains("Display name must start with an uppercase letter") - .contains("test recipe"); + .contains("Display name 'test recipe' must be sentence cased"); } @Test void failsWhenDisplayNameEndsWithPeriod() throws Exception { createGradleBuildFiles(); - csvFile.getParentFile().mkdirs(); - Files.writeString(csvFile.toPath(), + Files.createDirectories(csvFile.getParent()); + Files.writeString(csvFile, """ - name,displayName,description - org.example.TestRecipe,Test Recipe.,A test recipe. + ecosystem,packageName,name,displayName,description + maven,org.example:test-project-recipe,org.example.TestRecipe,Test Recipe.,A test recipe. """); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidateContent", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -107,22 +106,20 @@ void failsWhenDisplayNameEndsWithPeriod() throws Exception { assertEquals(FAILED, requireNonNull(result.task(":recipeCsvValidateContent")).getOutcome()); assertThat(result.getOutput()) .contains("Recipe marketplace CSV content validation failed") - .contains("Display name must not end with a period") - .contains("Test Recipe."); - } + .contains("Display name 'Test Recipe.' must not end with a period");} @Test void failsWhenDescriptionDoesNotEndWithPeriod() throws Exception { createGradleBuildFiles(); - csvFile.getParentFile().mkdirs(); - Files.writeString(csvFile.toPath(), + Files.createDirectories(csvFile.getParent()); + Files.writeString(csvFile, """ - name,displayName,description - org.example.TestRecipe,Test Recipe,A test recipe + ecosystem,packageName,name,displayName,description + maven,org.example:test-project-recipe,org.example.TestRecipe,Test Recipe,A test recipe """); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidateContent", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -131,23 +128,22 @@ void failsWhenDescriptionDoesNotEndWithPeriod() throws Exception { assertEquals(FAILED, requireNonNull(result.task(":recipeCsvValidateContent")).getOutcome()); assertThat(result.getOutput()) .contains("Recipe marketplace CSV content validation failed") - .contains("Description must end with a period") - .contains("A test recipe"); + .contains("Description 'A test recipe' must end with a period"); } @Test void reportsMultipleValidationErrors() throws Exception { createGradleBuildFiles(); - csvFile.getParentFile().mkdirs(); - Files.writeString(csvFile.toPath(), + Files.createDirectories(csvFile.getParent()); + Files.writeString(csvFile, """ - name,displayName,description - org.example.Recipe1,test recipe,No period - org.example.Recipe2,Test Recipe.,Also wrong. + ecosystem,packageName,name,displayName,description + maven,org.example:test-project-recipe,org.example.Recipe1,test recipe,No period + maven,org.example:test-project-recipe,org.example.Recipe2,Test Recipe.,Also wrong. """); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidateContent", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -156,9 +152,9 @@ void reportsMultipleValidationErrors() throws Exception { assertEquals(FAILED, requireNonNull(result.task(":recipeCsvValidateContent")).getOutcome()); assertThat(result.getOutput()) .contains("Recipe marketplace CSV content validation failed") - .contains("Display name must start with an uppercase letter") - .contains("Display name must not end with a period") - .contains("Description must end with a period"); + .contains("Display name 'test recipe' must be sentence cased") + .contains("Display name 'Test Recipe.' must not end with a period") + .contains("Description 'No period' must end with a period"); } @Test @@ -166,7 +162,7 @@ void skipsValidationWhenCsvDoesNotExist() throws Exception { createGradleBuildFiles(); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidateContent", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -179,16 +175,16 @@ void skipsValidationWhenCsvDoesNotExist() throws Exception { @Test void validatesEmptyDescriptions() throws Exception { createGradleBuildFiles(); - csvFile.getParentFile().mkdirs(); + Files.createDirectories(csvFile.getParent()); // Empty descriptions are allowed - Files.writeString(csvFile.toPath(), + Files.writeString(csvFile, """ - name,displayName,description - org.example.TestRecipe,Test Recipe, + ecosystem,packageName,name,displayName,description + maven,org.example:test-project-recipe,org.example.TestRecipe,Test Recipe, """); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidateContent", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -201,18 +197,18 @@ void validatesEmptyDescriptions() throws Exception { @Test void validatesCategoriesRecursively() throws Exception { createGradleBuildFiles(); - csvFile.getParentFile().mkdirs(); + Files.createDirectories(csvFile.getParent()); // Note: The RecipeMarketplaceContentValidator has conflicting rules for categories // where it expects descriptions to end with periods but display names to not end with periods. // For now, we'll just test with a CSV that has no categories to avoid this issue. - Files.writeString(csvFile.toPath(), + Files.writeString(csvFile, """ - name,displayName,description - org.example.TestRecipe,Test Recipe,A test recipe. + ecosystem,packageName,name,displayName,description + maven,org.example:test-project-recipe,org.example.TestRecipe,Test Recipe,A test recipe. """); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidateContent", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -223,7 +219,7 @@ void validatesCategoriesRecursively() throws Exception { } private void createGradleBuildFiles() throws IOException { - Files.writeString(settingsFile.toPath(), "rootProject.name = 'test-project'"); + Files.writeString(settingsFile, "rootProject.name = 'test-project'"); @Language("gradle") String buildFileContent = """ plugins { @@ -241,15 +237,15 @@ private void createGradleBuildFiles() throws IOException { // Plugin provides rewrite dependencies } """; - Files.writeString(buildFile.toPath(), buildFileContent); + Files.writeString(buildFile, buildFileContent); } private void createValidCsv() throws IOException { - csvFile.getParentFile().mkdirs(); - Files.writeString(csvFile.toPath(), + Files.createDirectories(csvFile.getParent()); + Files.writeString(csvFile, """ - name,displayName,description - org.example.TestRecipe,Test Recipe,A test recipe. + ecosystem,packageName,name,displayName,description + maven,org.example:test-project-recipe,org.example.TestRecipe,Test Recipe,A test recipe. """); } -} +} \ No newline at end of file diff --git a/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateTaskTest.java b/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateTaskTest.java index ab020f1..2c99b73 100644 --- a/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateTaskTest.java +++ b/src/test/java/org/openrewrite/gradle/RecipeMarketplaceCsvValidateTaskTest.java @@ -22,9 +22,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import static java.util.Objects.requireNonNull; import static org.assertj.core.api.Assertions.assertThat; @@ -34,17 +34,17 @@ class RecipeMarketplaceCsvValidateTaskTest { @TempDir - File projectDir; + Path projectDir; - private File settingsFile; - private File buildFile; - private File csvFile; + private Path settingsFile; + private Path buildFile; + private Path csvFile; @BeforeEach public void setup() { - settingsFile = new File(projectDir, "settings.gradle"); - buildFile = new File(projectDir, "build.gradle"); - csvFile = new File(projectDir, "src/main/resources/META-INF/rewrite/recipes.csv"); + settingsFile = projectDir.resolve("settings.gradle"); + buildFile = projectDir.resolve("build.gradle"); + csvFile = Path.of(projectDir.toString(), "src", "main", "resources", "META-INF", "rewrite", "recipes.csv"); } @Test @@ -53,7 +53,7 @@ void runsBothContentAndCompletenessValidations() throws Exception { createValidCsv(); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidate", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -71,16 +71,17 @@ void runsBothContentAndCompletenessValidations() throws Exception { @Test void failsWhenContentValidationFails() throws Exception { createSimpleRecipeProject(); - csvFile.getParentFile().mkdirs(); + // Invalid: display name doesn't start with uppercase - Files.writeString(csvFile.toPath(), + Files.createDirectories(csvFile.getParent()); + Files.writeString(csvFile, """ - name,displayName,description - org.example.TestRecipe,test recipe,A test recipe. + ecosystem,packageName,name,displayName,description + maven,org.example:test-project-recipe,org.example.TestRecipe,test recipe,A test recipe. """); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidate", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -89,23 +90,24 @@ void failsWhenContentValidationFails() throws Exception { assertEquals(FAILED, requireNonNull(result.task(":recipeCsvValidateContent")).getOutcome()); assertThat(result.getOutput()) .contains("Recipe marketplace CSV content validation failed") - .contains("Display name must start with an uppercase letter"); + .contains("Display name 'test recipe' must be sentence cased"); } @Test void failsWhenCompletenessValidationFails() throws Exception { createSimpleRecipeProject(); - csvFile.getParentFile().mkdirs(); + // Valid content but incomplete: phantom recipe - Files.writeString(csvFile.toPath(), + Files.createDirectories(csvFile.getParent()); + Files.writeString(csvFile, """ - name,displayName,description - org.example.TestRecipe,Test Recipe,A test recipe. - org.example.PhantomRecipe,Phantom Recipe,Does not exist. + ecosystem,packageName,name,displayName,description + maven,org.example:test-project-recipe,org.example.TestRecipe,Test Recipe,A test recipe. + maven,org.example:test-project-recipe,org.example.PhantomRecipe,Phantom Recipe,Does not exist. """); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidate", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -129,7 +131,7 @@ void skipsBothValidationsWhenCsvDoesNotExist() throws Exception { createSimpleRecipeProject(); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("recipeCsvValidate", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -169,7 +171,7 @@ void canBeUsedInCheckTask() throws Exception { createValidCsv(); BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) + .withProjectDir(projectDir.toFile()) .withArguments("check", "--info", "--stacktrace") .withPluginClasspath() .withDebug(true) @@ -204,8 +206,8 @@ private void createSimpleRecipeProject() throws IOException { private void createRecipeClass() throws IOException { // Use declarative YAML recipe for simpler testing - File rewriteDir = new File(projectDir, "src/main/resources/META-INF/rewrite"); - rewriteDir.mkdirs(); + Path rewriteDir = Path.of(projectDir.toString(), "src", "main", "resources", "META-INF", "rewrite"); + Files.createDirectories(rewriteDir); @Language("yaml") String rewriteYml = """ @@ -219,20 +221,20 @@ private void createRecipeClass() throws IOException { toText: "Hello" """; - Files.writeString(new File(rewriteDir, "rewrite.yml").toPath(), rewriteYml); + Files.writeString(rewriteDir.resolve("rewrite.yml"), rewriteYml); } private void createValidCsv() throws IOException { - csvFile.getParentFile().mkdirs(); - Files.writeString(csvFile.toPath(), + Files.createDirectories(csvFile.getParent()); + Files.writeString(csvFile, """ - name,displayName,description - org.example.TestRecipe,Test Recipe,A test recipe. + ecosystem,packageName,name,displayName,description + maven,org.example:test-project-recipe,org.example.TestRecipe,Test Recipe,A test recipe. """); } private void createGradleBuildFiles(@Language("gradle") String buildFileContent) throws IOException { - Files.writeString(settingsFile.toPath(), "rootProject.name = 'test-recipe-project'"); - Files.writeString(buildFile.toPath(), buildFileContent); + Files.writeString(settingsFile, "rootProject.name = 'test-recipe-project'"); + Files.writeString(buildFile, buildFileContent); } }