Skip to content

Skip recipe validation for imperative recipes in assertRecipesConfigure()#6841

Merged
MBoegers merged 12 commits intomainfrom
mboegers/recipe-loading-skip-validation-for-imperative
Mar 4, 2026
Merged

Skip recipe validation for imperative recipes in assertRecipesConfigure()#6841
MBoegers merged 12 commits intomainfrom
mboegers/recipe-loading-skip-validation-for-imperative

Conversation

@MBoegers
Copy link
Contributor

@MBoegers MBoegers commented Feb 27, 2026

Summary

Test plan

  • ./gradlew :rewrite-test:test passes
  • Verify RecipeLoadingTest in a recipe repo with optional-param + validate() recipes no longer fails
  • Verify a deliberately misconfigured declarative YAML recipe still fails the loading test

…re()

Recipes with optional parameters and a custom validate() requiring at
least one to be present fail assertRecipesConfigure() because they are
loaded with no arguments, leaving all optional params null.

Declarative recipes (from YAML) should still be validated since their
options are explicitly configured. Imperative recipes loaded with no
user-provided arguments are inherently unconfigured and validation
failures are expected.

Add a recipeValidation flag to RecipeSpec (following the existing
serializationValidation pattern) and gate validateAll() on it. In
assertRecipesConfigure(), disable validation for non-DeclarativeRecipe
instances.

Fixes #6840
Three tests demonstrate the intention:
- rejectUnconfiguredRecipeWithOptionalOrValidation: recipe with two
  optional params and validate() requiring at least one fails when
  constructed with no args (both null) and default validation
- acceptUnconfiguredRecipeWithOptionalOrValidationWhenSkipped: same
  recipe succeeds with validateRecipe(false), as assertRecipesConfigure
  now does for imperative recipes
- rejectConfiguredRecipeWithOptionalOrValidationStillValidated:
  declarative recipes still get validated (broken YAML still caught)
@MBoegers MBoegers marked this pull request as ready for review February 27, 2026 16:29
Co-authored-by: Tim te Beek <tim@moderne.io>
MBoegers added 4 commits March 3, 2026 11:51
The condition for skipping validation was inverted — it ran validation
for recipes WITH optional params instead of skipping them. Fixed to
allMatch(isRequired) so validation is only performed for declarative
recipes or recipes with no optional parameters.

Updated the test to verify that an unconfigured recipe with optional
params and custom validate() now succeeds instead of failing.
// Skip recipe validation for these since custom validate() methods (e.g. requiring at least one of several
// optional parameters) would fail on an unconfigured instance.
if (recipe instanceof DeclarativeRecipe
|| recipe.getDescriptor().getOptions().stream().allMatch(OptionDescriptor::isRequired)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be

Suggested change
|| recipe.getDescriptor().getOptions().stream().allMatch(OptionDescriptor::isRequired)) {
|| recipe.getDescriptor().getOptions().stream().noneMatch(OptionDescriptor::isRequired)) {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking here is we can only fall to validate not provided but required options.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, required options are fine, the validation fills them. Problematic is a recipe that contains at least one non required.
WithnonMatch(isRequired) we would allow recipes with only optional options.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isOptional or Predicat::not would make it a little easier to read

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh and the main problem here are custom validation methods, like or checking for two optional parameter.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the problem is the either/or nature of optional arguments that are both not filled here, do I understand that correctly?

Copy link
Contributor Author

@MBoegers MBoegers Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's the problem. You can find these recipes even in the core, f.ex. ChangeDependency.
Maybe the Validated#or is the real problem, other recipes that use a similar structure work with if, f.e. o.o.docker.ChangeFrom
But these would also fail for RewriteTest#assertRecipesConfigure

@github-project-automation github-project-automation bot moved this from In Progress to Ready to Review in OpenRewrite Mar 3, 2026
@MBoegers
Copy link
Contributor Author

MBoegers commented Mar 4, 2026

The behavior change affects a few tests. Here are the usages of asserts for exceptions in our codebases.

File Pattern Status
rewrite-yaml/.../MergeYamlTest.java assertThrows(AssertionError Fixed
rewrite-maven/.../ChangeDependencyGroupIdAndArtifactIdTest.java assertThatExceptionOfType(AssertionError Fixed
rewrite-maven/.../ChangeManagedDependencyGroupIdAndArtifactIdTest.java assertThatExceptionOfType(AssertionError Fixed
rewrite-test/.../RewriteTestTest.java assertThrows(AssertionError Fine (tests name/scanning validation, not validate())
rewrite-java-test/.../RepeatTest.java assertThrows(AssertionError Fine (tests cursor passing during execution)
rewrite-core/.../AppendToTextFileTest.java assertThatExceptionOfType(AssertionError Fine (tests missing expected generated files)

@MBoegers
Copy link
Contributor Author

MBoegers commented Mar 4, 2026

Thanks @timtebeek ❤️

@MBoegers MBoegers merged commit bf2ca3c into main Mar 4, 2026
1 check passed
@MBoegers MBoegers deleted the mboegers/recipe-loading-skip-validation-for-imperative branch March 4, 2026 15:48
@github-project-automation github-project-automation bot moved this from Ready to Review to Done in OpenRewrite Mar 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

RecipeLoadingTest fails for recipes with optional parameters and custom validate()

2 participants