Skip to content

Commit bf2ca3c

Browse files
authored
Skip recipe validation for imperative recipes in assertRecipesConfigure() (#6841)
1 parent c4b475f commit bf2ca3c

5 files changed

Lines changed: 173 additions & 80 deletions

File tree

rewrite-maven/src/test/java/org/openrewrite/maven/ChangeDependencyGroupIdAndArtifactIdTest.java

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
import org.junit.jupiter.api.Test;
2020
import org.openrewrite.DocumentExample;
2121
import org.openrewrite.Issue;
22+
import org.openrewrite.Validated;
2223
import org.openrewrite.test.RewriteTest;
2324
import org.openrewrite.test.SourceSpec;
2425

2526
import static org.assertj.core.api.Assertions.assertThat;
26-
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2727
import static org.openrewrite.java.Assertions.mavenProject;
2828
import static org.openrewrite.maven.Assertions.pomXml;
2929

@@ -765,33 +765,17 @@ void changeProfileManagedDependencyGroupIdAndArtifactId() {
765765
@Issue("https://github.com/openrewrite/rewrite-java-dependencies/issues/55")
766766
@Test
767767
void requireNewGroupIdOrNewArtifactId() {
768-
assertThatExceptionOfType(AssertionError.class)
769-
.isThrownBy(() -> rewriteRun(
770-
spec -> spec.recipe(new ChangeDependencyGroupIdAndArtifactId(
771-
"javax.activation",
772-
"javax.activation-api",
773-
null,
774-
null,
775-
null,
776-
null
777-
))
778-
)).withMessageContaining("newGroupId OR newArtifactId must be different from before");
768+
ChangeDependencyGroupIdAndArtifactId recipe = new ChangeDependencyGroupIdAndArtifactId("javax.activation", "javax.activation-api", null, null, null, null);;
769+
assertThat(recipe.validate().failures()).extracting(Validated.Invalid::getMessage)
770+
.contains("newGroupId OR newArtifactId must be different from before");
779771
}
780772

781773
@Issue("https://github.com/openrewrite/rewrite-java-dependencies/issues/55")
782774
@Test
783775
void requireNewGroupIdOrNewArtifactIdToBeDifferentFromBefore() {
784-
assertThatExceptionOfType(AssertionError.class)
785-
.isThrownBy(() -> rewriteRun(
786-
spec -> spec.recipe(new ChangeDependencyGroupIdAndArtifactId(
787-
"javax.activation",
788-
"javax.activation-api",
789-
"javax.activation",
790-
null,
791-
null,
792-
null
793-
))
794-
)).withMessageContaining("newGroupId OR newArtifactId must be different from before");
776+
ChangeDependencyGroupIdAndArtifactId recipe = new ChangeDependencyGroupIdAndArtifactId("javax.activation", "javax.activation-api", "javax.activation", null, null, null);
777+
assertThat(recipe.validate().failures()).extracting(Validated.Invalid::getMessage)
778+
.contains("newGroupId OR newArtifactId must be different from before");
795779
}
796780

797781
@Test

rewrite-maven/src/test/java/org/openrewrite/maven/ChangeManagedDependencyGroupIdAndArtifactIdTest.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
import org.junit.jupiter.api.Test;
1919
import org.openrewrite.DocumentExample;
2020
import org.openrewrite.Issue;
21+
import org.openrewrite.Validated;
2122
import org.openrewrite.test.RewriteTest;
2223

2324
import static org.assertj.core.api.Assertions.assertThat;
24-
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2525
import static org.openrewrite.maven.Assertions.pomXml;
2626

2727
class ChangeManagedDependencyGroupIdAndArtifactIdTest implements RewriteTest {
@@ -79,16 +79,9 @@ void changeManagedDependencyGroupIdAndArtifactId() {
7979
@Issue("https://github.com/openrewrite/rewrite-java-dependencies/issues/55")
8080
@Test
8181
void requireNewGroupIdOrNewArtifactIdToBeDifferentFromBefore() {
82-
assertThatExceptionOfType(AssertionError.class)
83-
.isThrownBy(() -> rewriteRun(
84-
spec -> spec.recipe(new ChangeManagedDependencyGroupIdAndArtifactId(
85-
"javax.activation",
86-
"javax.activation-api",
87-
"javax.activation",
88-
"javax.activation-api",
89-
null
90-
))
91-
)).withMessageContaining("newGroupId OR newArtifactId must be different from before");
82+
ChangeManagedDependencyGroupIdAndArtifactId recipe = new ChangeManagedDependencyGroupIdAndArtifactId("javax.activation", "javax.activation-api", "javax.activation", "javax.activation-api", null);
83+
assertThat(recipe.validate().failures()).extracting(Validated.Invalid::getMessage)
84+
.contains("newGroupId OR newArtifactId must be different from before");
9285
}
9386

9487
@Test

rewrite-test/src/main/java/org/openrewrite/test/RewriteTest.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.jspecify.annotations.Nullable;
2020
import org.openrewrite.*;
2121
import org.openrewrite.config.CompositeRecipe;
22+
import org.openrewrite.config.DeclarativeRecipe;
2223
import org.openrewrite.config.Environment;
2324
import org.openrewrite.config.OptionDescriptor;
2425
import org.openrewrite.internal.*;
@@ -226,11 +227,18 @@ default void rewriteRun(Consumer<RecipeSpec> spec, SourceSpec<?>... sourceSpecs)
226227
for (SourceSpec<?> s : sourceSpecs) {
227228
s.customizeExecutionContext.accept(ctx);
228229
}
229-
List<Validated<Object>> validations = new ArrayList<>();
230-
recipe.validateAll(ctx, validations);
231-
assertThat(validations.stream().filter(Validated::isInvalid))
232-
.as("Recipe validation must have no failures")
233-
.isEmpty();
230+
231+
// Imperative recipes are loaded with no user-provided arguments, so all optional parameters are null.
232+
// Skip recipe validation for these since custom validate() methods (e.g. requiring at least one of several
233+
// optional parameters) would fail on an unconfigured instance.
234+
if (recipe instanceof DeclarativeRecipe
235+
|| recipe.getDescriptor().getOptions().stream().allMatch(OptionDescriptor::isRequired)) {
236+
List<Validated<Object>> validations = new ArrayList<>();
237+
recipe.validateAll(ctx, validations);
238+
assertThat(validations.stream().filter(Validated::isInvalid))
239+
.as("Recipe validation must have no failures")
240+
.isEmpty();
241+
}
234242

235243
Map<Parser.Builder, List<SourceSpec<?>>> sourceSpecsByParser = new HashMap<>();
236244
List<Parser.Builder> methodSpecParsers = testMethodSpec.parsers;

rewrite-test/src/test/java/org/openrewrite/test/internal/RewriteTestTest.java

Lines changed: 120 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
import lombok.Value;
2222
import org.jspecify.annotations.NullMarked;
2323
import org.jspecify.annotations.Nullable;
24+
import org.junit.jupiter.api.Nested;
2425
import org.junit.jupiter.api.Test;
2526
import org.openrewrite.*;
2627
import org.openrewrite.test.RewriteTest;
28+
import org.openrewrite.test.SourceSpecs;
2729
import org.openrewrite.test.TypeValidation;
2830
import org.openrewrite.text.PlainText;
2931
import org.openrewrite.text.PlainTextVisitor;
@@ -35,6 +37,7 @@
3537

3638
import static java.util.Collections.emptyList;
3739
import static org.assertj.core.api.Assertions.assertThat;
40+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
3841
import static org.junit.jupiter.api.Assertions.assertThrows;
3942
import static org.openrewrite.test.SourceSpecs.text;
4043

@@ -121,7 +124,7 @@ void rejectRecipeValidationFailure() {
121124
description: Deliberately has a non-existent recipe in its recipe list to trigger a validation failure.
122125
recipeList:
123126
- org.openrewrite.DoesNotExist
124-
127+
125128
""", "org.openrewrite.RefersToNonExistentRecipe")
126129
));
127130
}
@@ -152,6 +155,59 @@ void allowScannerEdit() {
152155
text("foo")
153156
);
154157
}
158+
159+
@Nested
160+
class RecipeValidation {
161+
162+
@Test
163+
void validateRecipeWithNoOptions() {
164+
rewriteRun(
165+
spec -> spec.recipe(new RecipeWithNoOptions()),
166+
new SourceSpecs[0]
167+
);
168+
}
169+
170+
@Test
171+
void validateRecipeWithOnlyRequiredOptionsPositive() {
172+
rewriteRun(
173+
spec -> spec.recipe(new RecipeWithRequiredOptionValidateNoBlank("Test")),
174+
new SourceSpecs[0]
175+
);
176+
}
177+
178+
@Test
179+
void validateRecipeWithOnlyRequiredOptionsNegative() {
180+
assertThatThrownBy(() ->
181+
rewriteRun(
182+
spec -> spec.recipe(new RecipeWithRequiredOptionValidateNoBlank("")),
183+
new SourceSpecs[0]
184+
)
185+
);
186+
}
187+
188+
@Test
189+
void skipValidationForRecipeWithOptionalOptions() {
190+
rewriteRun(
191+
spec -> spec.recipe(new RecipeWithOptionalOrValidation(null, null)),
192+
new SourceSpecs[0]
193+
);
194+
}
195+
196+
@Test
197+
void validateDeclarativeRecipe() {
198+
assertThrows(AssertionError.class, () ->
199+
rewriteRun(
200+
spec -> spec.recipeFromYaml("""
201+
type: specs.openrewrite.org/v1beta/recipe
202+
name: org.openrewrite.test.internal.StillValidated
203+
displayName: Still validated
204+
description: Declarative recipe with a non-existent sub-recipe should still fail validation.
205+
recipeList:
206+
- org.openrewrite.DoesNotExist
207+
""", "org.openrewrite.test.internal.StillValidated")
208+
));
209+
}
210+
}
155211
}
156212

157213
@EqualsAndHashCode(callSuper = false)
@@ -230,7 +286,7 @@ class CreatesTwoFilesSamePath extends ScanningRecipe<AtomicBoolean> {
230286
String displayName = "Creates two source files with the same path";
231287

232288
String description = "A source file's path must be unique. " +
233-
"This recipe creates two source files with the same path to show that the test framework helps protect against this mistake.";
289+
"This recipe creates two source files with the same path to show that the test framework helps protect against this mistake.";
234290

235291
@Override
236292
public AtomicBoolean getInitialValue(ExecutionContext ctx) {
@@ -296,10 +352,10 @@ class RecipeWithDescriptionListOfLinks extends Recipe {
296352

297353
@Getter
298354
final String description = """
299-
A fancy description.
300-
For more information, see:
301-
- [link 1](https://example.com/link1)
302-
- [link 2](https://example.com/link2)""";
355+
A fancy description.
356+
For more information, see:
357+
- [link 1](https://example.com/link1)
358+
- [link 2](https://example.com/link2)""";
303359
}
304360

305361
@NullMarked
@@ -310,10 +366,10 @@ class RecipeWithDescriptionListOfDescribedLinks extends Recipe {
310366

311367
@Getter
312368
final String description = """
313-
A fancy description.
314-
For more information, see:
315-
- First Resource [link 1](https://example.com/link1).
316-
- Second Resource [link 2](https://example.com/link2).""";
369+
A fancy description.
370+
For more information, see:
371+
- First Resource [link 1](https://example.com/link1).
372+
- Second Resource [link 2](https://example.com/link2).""";
317373
}
318374

319375
@NullMarked
@@ -353,3 +409,57 @@ public Collection<? extends SourceFile> generate(AtomicBoolean acc, ExecutionCon
353409
.build());
354410
}
355411
}
412+
413+
@Value
414+
@EqualsAndHashCode(callSuper = false)
415+
class RecipeWithNoOptions extends Recipe {
416+
String displayName = "Recipe with no options";
417+
String description = "Has no configurable options at all.";
418+
}
419+
420+
@Value
421+
@NullMarked
422+
@EqualsAndHashCode(callSuper = false)
423+
class RecipeWithRequiredOptionValidateNoBlank extends Recipe {
424+
String displayName = "Recipe with required option";
425+
String description = "Has a single required parameter.";
426+
427+
@Option(displayName = "Required param",
428+
description = "A required parameter.")
429+
String requiredParam;
430+
431+
@Override
432+
public Validated<Object> validate() {
433+
return super.validate()
434+
.and(Validated.notBlank("Required param", requiredParam));
435+
}
436+
}
437+
438+
@Value
439+
@NullMarked
440+
@EqualsAndHashCode(callSuper = false)
441+
class RecipeWithOptionalOrValidation extends Recipe {
442+
String displayName = "Recipe with optional OR validation";
443+
String description = "Has two optional parameters where at least one must be set.";
444+
445+
@Option(displayName = "Option A",
446+
description = "First optional parameter.",
447+
example = "valueA",
448+
required = false)
449+
@Nullable
450+
String optionA;
451+
452+
@Option(displayName = "Option B",
453+
description = "Second optional parameter.",
454+
example = "valueB",
455+
required = false)
456+
@Nullable
457+
String optionB;
458+
459+
@Override
460+
public Validated<Object> validate() {
461+
return super.validate().and(
462+
Validated.required("optionA", optionA)
463+
.or(Validated.required("optionB", optionB)));
464+
}
465+
}

rewrite-yaml/src/test/java/org/openrewrite/yaml/MergeYamlTest.java

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818
import org.junit.jupiter.api.Test;
1919
import org.openrewrite.DocumentExample;
2020
import org.openrewrite.Issue;
21+
import org.openrewrite.Validated;
2122
import org.openrewrite.test.RewriteTest;
2223

23-
import static org.junit.jupiter.api.Assertions.assertThrows;
24+
import static org.assertj.core.api.Assertions.assertThat;
2425
import static org.openrewrite.yaml.Assertions.yaml;
2526
import static org.openrewrite.yaml.MergeYaml.InsertMode.After;
2627
import static org.openrewrite.yaml.MergeYaml.InsertMode.Before;
@@ -3059,22 +3060,21 @@ void preventKeysToBeAppendedToPreviousCommentIfManyLineBreaks() {
30593060

30603061
@Test
30613062
void invalidYaml() {
3062-
assertThrows(AssertionError.class, () -> rewriteRun(
3063-
spec -> spec
3064-
.recipe(new MergeYaml(
3065-
"$.some.object",
3066-
//language=text
3067-
"""
3068-
script: |-ParseError
3069-
""",
3070-
false,
3071-
"name",
3072-
null,
3073-
null,
3074-
null,
3075-
null
3076-
))
3077-
));
3063+
MergeYaml recipe = new MergeYaml(
3064+
"$.some.object",
3065+
//language=text
3066+
"""
3067+
script: |-ParseError
3068+
""",
3069+
false,
3070+
"name",
3071+
null,
3072+
null,
3073+
null,
3074+
null
3075+
);
3076+
assertThat(recipe.validate().failures()).extracting(Validated.Invalid::getMessage)
3077+
.contains("Must be valid YAML");
30783078
}
30793079

30803080
@Test
@@ -3144,20 +3144,18 @@ void createNewKeysFalse() {
31443144
@SuppressWarnings("DataFlowIssue")
31453145
@Test
31463146
void sourceNull() {
3147-
assertThrows(AssertionError.class, () ->
3148-
rewriteRun(
3149-
spec -> spec
3150-
.recipe(new MergeYaml(
3151-
"$.some.object",
3152-
null,
3153-
false,
3154-
"name",
3155-
null,
3156-
null,
3157-
null,
3158-
null
3159-
))
3160-
));
3147+
MergeYaml recipe = new MergeYaml(
3148+
"$.some.object",
3149+
null,
3150+
false,
3151+
"name",
3152+
null,
3153+
null,
3154+
null,
3155+
null
3156+
);
3157+
assertThat(recipe.validate().failures()).extracting(Validated.Invalid::getMessage)
3158+
.contains("Must be valid YAML");
31613159
}
31623160

31633161
@Test

0 commit comments

Comments
 (0)