diff --git a/build.gradle.kts b/build.gradle.kts index 262d10040..71a69dbed 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,6 +14,7 @@ recipeDependencies { parserClasspath("com.google.guava:guava:33.5.0-jre") parserClasspath("com.squareup.okhttp3:mockwebserver:3.14.9") parserClasspath("com.squareup.okhttp3:mockwebserver:4.10.0") + parserClasspath("com.squareup.okhttp3:mockwebserver3:5.1.0") parserClasspath("junit:junit:4.+") parserClasspath("org.apiguardian:apiguardian-api:1.1.2") parserClasspath("org.assertj:assertj-core:3.+") @@ -59,6 +60,10 @@ recipeDependencies { testParserClasspath("org.testcontainers:testcontainers-kafka:2.0.1") testParserClasspath("org.testcontainers:testcontainers-localstack:2.0.1") testParserClasspath("org.testcontainers:testcontainers-mysql:2.0.1") + + + testParserClasspath("com.squareup.okhttp3:okhttp:4.10.0") + testParserClasspath("com.squareup.okhttp3:okhttp-jvm:5.1.0") } val rewriteVersion = rewriteRecipe.rewriteVersion.get() @@ -100,6 +105,8 @@ dependencies { testRuntimeOnly("org.jboss.arquillian.junit:arquillian-junit-core:latest.release") testRuntimeOnly("org.mockito.kotlin:mockito-kotlin:5.4.0") testRuntimeOnly("org.testng:testng:latest.release") +// api("com.squareup.okhttp3:mockwebserver3:5.1.0") +// api("com.squareup.okhttp3:mockwebserver:4.10.0") } tasks.test { diff --git a/src/main/java/org/openrewrite/java/testing/junit5/UpdateMockWebServerMockResponse.java b/src/main/java/org/openrewrite/java/testing/junit5/UpdateMockWebServerMockResponse.java new file mode 100644 index 000000000..86f50190c --- /dev/null +++ b/src/main/java/org/openrewrite/java/testing/junit5/UpdateMockWebServerMockResponse.java @@ -0,0 +1,327 @@ +/* + * Copyright 2025 the original author or authors. + *

+ * Licensed under the Moderne Source Available License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://docs.moderne.io/licensing/moderne-source-available-license + *

+ * 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.openrewrite.java.testing.junit5; + +import org.jspecify.annotations.Nullable; +import org.openrewrite.*; +import org.openrewrite.internal.ListUtils; +import org.openrewrite.java.*; +import org.openrewrite.java.format.AutoFormatVisitor; +import org.openrewrite.java.search.UsesType; +import org.openrewrite.java.style.IntelliJ; +import org.openrewrite.java.style.WrappingAndBracesStyle; +import org.openrewrite.java.tree.*; +import org.openrewrite.marker.Markers; +import org.openrewrite.style.LineWrapSetting; +import org.openrewrite.style.NamedStyles; +import org.openrewrite.style.Style; + +import java.util.*; +import java.util.function.BiFunction; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toList; +import static org.openrewrite.Tree.randomId; +import static org.openrewrite.java.tree.JavaType.ShallowClass.build; + +public class UpdateMockWebServerMockResponse extends Recipe { + private static final String OLD_PACKAGE_NAME = "okhttp3.mockwebserver"; + private static final String NEW_PACKAGE_NAME = "mockwebserver3"; + private static final String OLD_MOCKRESPONSE_FQN = OLD_PACKAGE_NAME + ".MockResponse"; + private static final String OLD_MOCKRESPONSE_CONSTRUCTOR = OLD_MOCKRESPONSE_FQN + " ()"; + private static final String OLD_MOCKRESPONSE_STATUS = OLD_MOCKRESPONSE_FQN + " status(java.lang.String)"; + private static final String OLD_MOCKRESPONSE_SETSTATUS = OLD_MOCKRESPONSE_FQN + " setStatus(java.lang.String)"; + private static final String OLD_MOCKRESPONSE_HEADERS = OLD_MOCKRESPONSE_FQN + " headers(okhttp3.Headers)"; + private static final String OLD_MOCKRESPONSE_SETHEADERS = OLD_MOCKRESPONSE_FQN + " setHeaders(okhttp3.Headers)"; + private static final String NEW_MOCKRESPONSE_FQN = NEW_PACKAGE_NAME + ".MockResponse"; + // TODO: Rename this given this actually includes '$' + private static final String NEW_MOCKRESPONSE_FQN_BUILDER = NEW_MOCKRESPONSE_FQN + "$Builder"; + private static final String NEW_MOCKRESPONSE_BUILDER_FQN = NEW_MOCKRESPONSE_FQN + ".Builder"; + private static final String OLD_MOCKWEBSERVER_FQN = OLD_PACKAGE_NAME + ".MockWebServer"; + private static final String NEW_MOCKWEBSERVER_FQN = NEW_PACKAGE_NAME + ".MockWebServer"; + + @Override + public String getDisplayName() { + return "OkHttp `MockWebServer` `MockResponse` to 5.x `MockWebServer3` `MockResponse`"; + } + + @Override + public String getDescription() { + return "Replace usages of OkHttp MockWebServer `MockResponse` with 5.x MockWebServer3 `MockResponse` and it's `Builder`."; + } + + @Override + public TreeVisitor getVisitor() { + return Preconditions.check(new UsesType<>(OLD_MOCKRESPONSE_FQN, false), new JavaIsoVisitor() { + private final Map> methodInvocationsToAdjust = new HashMap<>(); + private final Set newClassesToAdjust = new HashSet<>(); + private final Set varDeclsToAdjust = new HashSet<>(); + private final Set namedVarsToAdjust = new HashSet<>(); + @Override + public @Nullable J visit(@Nullable Tree tree, ExecutionContext ctx) { + J j = (J) tree; + j = new JavaIsoVisitor() { + private final MethodMatcher constructorMatcher = new MethodMatcher(OLD_MOCKRESPONSE_CONSTRUCTOR); + + @Override + public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext ctx) { + J.NewClass nc = super.visitNewClass(newClass, ctx); + if (constructorMatcher.matches(nc)) { + newClassesToAdjust.add(nc.getId()); + } + return nc; + } + + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInv, ExecutionContext ctx) { + J.MethodInvocation mi = super.visitMethodInvocation(methodInv, ctx); + List indexes = IntStream.range(0, mi.getArguments().size()) + .filter(x -> TypeUtils.isAssignableTo(OLD_MOCKRESPONSE_FQN, mi.getArguments().get(x).getType())) + .boxed().collect(toList()); + if (!indexes.isEmpty()) { + methodInvocationsToAdjust.putIfAbsent(mi.getId(), indexes); + } + return mi; + } + + @Override + public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, ExecutionContext ctx) { + J.VariableDeclarations.NamedVariable nv = super.visitVariable(variable, ctx); + if (TypeUtils.isAssignableTo(OLD_MOCKRESPONSE_FQN, nv.getType())) { + namedVarsToAdjust.add(nv.getId()); + } + return nv; + } + + @Override + public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) { + J.VariableDeclarations mv = super.visitVariableDeclarations(multiVariable, ctx); + if (TypeUtils.isAssignableTo(OLD_MOCKRESPONSE_FQN, mv.getType())) { + varDeclsToAdjust.add(mv.getId()); + } + return mv; + } + }.visit(j, ctx); + j = (J) new ChangeType( + OLD_MOCKRESPONSE_FQN, + NEW_MOCKRESPONSE_FQN_BUILDER, + true + ).getVisitor().visit(j, ctx); + j = (J) new ChangePackage( + OLD_PACKAGE_NAME, + NEW_PACKAGE_NAME, + false + ).getVisitor().visit(j, ctx); + j = new JavaIsoVisitor() { + private final JavaType.FullyQualified newMockResponseBuilderType = (JavaType.FullyQualified) JavaType.buildType(NEW_MOCKRESPONSE_FQN_BUILDER); + private final JavaType.FullyQualified newMockResponseType = (JavaType.FullyQualified) JavaType.buildType(NEW_MOCKRESPONSE_FQN); + private final MethodMatcher TO_ADJUST_MOCKRESPONSE_BUILDER_STATUS_MATCHER = new MethodMatcher(NEW_MOCKRESPONSE_FQN_BUILDER + " status(java.lang.String)"); + private final MethodMatcher TO_ADJUST_MOCKRESPONSE_BUILDER_SETSTATUS_MATCHER = new MethodMatcher(NEW_MOCKRESPONSE_FQN_BUILDER + " setStatus(java.lang.String)"); + private final MethodMatcher TO_ADJUST_MOCKRESPONSE_BUILDER_HEADERS_MATCHER = new MethodMatcher(NEW_MOCKRESPONSE_FQN_BUILDER + " headers(okhttp3.Headers)"); + private final MethodMatcher TO_ADJUST_MOCKRESPONSE_BUILDER_SETHEADERS_MATCHER = new MethodMatcher(NEW_MOCKRESPONSE_FQN_BUILDER + " setHeaders(okhttp3.Headers)"); + + private boolean returnsVoid(J.MethodInvocation method) { + return method.getMethodType() != null && TypeUtils.isAssignableTo(JavaType.Primitive.Void, method.getMethodType().getReturnType()); + } + + private J.MethodInvocation patchReturnTypeAndName(J.MethodInvocation method, JavaType.FullyQualified newDeclaringType, JavaType newReturnType, String newName) { + assert method.getMethodType() != null; + J.MethodInvocation updated = method.withMethodType( + method.getMethodType() + .withDeclaringType(newDeclaringType) + .withReturnType(newReturnType) + .withName(newName) + ); + return updated.withName( + updated.getName() + .withSimpleName(newName) + .withType(updated.getMethodType()) + ); + } + + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + J.MethodInvocation mi = super.visitMethodInvocation(method, ctx); + BiFunction patchFunc = (J.MethodInvocation methodInv, String newName) -> patchReturnTypeAndName(methodInv, newMockResponseBuilderType, newMockResponseBuilderType, newName); + if (returnsVoid(mi)) { + if ( + TO_ADJUST_MOCKRESPONSE_BUILDER_STATUS_MATCHER.matches(mi) || + TO_ADJUST_MOCKRESPONSE_BUILDER_HEADERS_MATCHER.matches(mi) + ) { + return patchFunc.apply(mi, mi.getSimpleName()); + } + } else if (TO_ADJUST_MOCKRESPONSE_BUILDER_SETSTATUS_MATCHER.matches(mi)) { + return patchFunc.apply(mi,"status"); + } else if (TO_ADJUST_MOCKRESPONSE_BUILDER_SETHEADERS_MATCHER.matches(mi)) { + return patchFunc.apply(mi, "headers"); + } + List indexes = methodInvocationsToAdjust.remove(mi.getId()); + if (indexes != null && !indexes.isEmpty()) { + List> oldPaddedArgs = mi.getPadding().getArguments().getPadding().getElements(); + assert mi.getMethodType() != null; + mi = mi.withArguments(ListUtils.map(mi.getArguments(), (index, expr) -> { + if (indexes.contains(index)) { + return patchReturnTypeAndName(new J.MethodInvocation( + randomId(), + Space.EMPTY, + Markers.EMPTY, + JRightPadded.build(expr), + null, + new J.Identifier( + randomId(), + Space.EMPTY, + Markers.EMPTY, + emptyList(), + "build", + null, + null + ), + JContainer.empty(), + new JavaType.Method( + null, + Flag.Public.getBitMask() | Flag.Final.getBitMask(), + newMockResponseBuilderType, + "build", + newMockResponseType, + (List) null, + null, + null, + null, + null, + null + ) + ), newMockResponseBuilderType, newMockResponseType, "build"); + } + return expr; + })); + assert mi.getMethodType() != null; + mi = mi.withMethodType(mi.getMethodType().withParameterTypes(ListUtils.map(mi.getMethodType().getParameterTypes(), (index, type) -> { + if (indexes.contains(index)) { + return newMockResponseType; + } + return type; + }))); + } + return mi; + } + }.visit(j, ctx); + // TODO: harvest padding logic from below and then get rid of rest + new JavaIsoVisitor() { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInv, ExecutionContext ctx) { + J.MethodInvocation mi = super.visitMethodInvocation(methodInv, ctx); + List indexes = methodInvocationsToAdjust.remove(mi.getId()); + if (indexes != null) { + StringBuilder sb = new StringBuilder(); + List> oldArgs = mi.getPadding().getArguments().getPadding().getElements(); + for (int i = 0; i < mi.getArguments().size(); i++) { +// sb.append("#{any()}"); + if (indexes.contains(i)) { + sb.append("#{any(mockwebserver3.MockResponse$Builder)}.build()"); + } else { + sb.append("#{any()}"); + } + if (i < mi.getArguments().size() - 2) { + sb.append(", "); + } + } + Style s1 = IntelliJ.tabsAndIndents().withContinuationIndent(4); + WrappingAndBracesStyle s2 = IntelliJ.wrappingAndBraces(); + s2 = s2.withChainedMethodCalls( + s2.getChainedMethodCalls().withWrap(LineWrapSetting.WrapAlways) + ); + AutoFormatVisitor formatVisitor = new AutoFormatVisitor<>( + null, + false, + new NamedStyles( + randomId(), + "test", + "test", + "test", + emptySet(), + Arrays.asList(s1, s2) + ) + ); + J.MethodInvocation mi3 = (J.MethodInvocation) formatVisitor.visit( + JavaTemplate + .builder(sb.toString()) + .contextSensitive() + .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockwebserver3")) + .imports("mockwebserver3.MockResponse", "mockwebserver3.MockResponse.Builder", "mockwebserver3.MockWebServer") + .build() + .apply(getCursor(), mi.getCoordinates().replaceArguments(), mi.getArguments().toArray()), + ctx, + getCursor().getParent() + ); + // TODO: Cleanup backpatching of parameter types for method invocations + mi3 = mi3.withMethodType(mi.getMethodType().withParameterTypes(ListUtils.map(mi.getMethodType().getParameterTypes(), (index, x) -> { + if (indexes.contains(index)) { + return JavaType.buildType(NEW_MOCKRESPONSE_FQN); + } + return x; + }))); + mi3 = mi3.withName(mi3.getName().withType(mi3.getMethodType())); + mi3 = mi3.getPadding().withArguments(mi3.getPadding().getArguments().getPadding().withElements(ListUtils.map(mi3.getPadding().getArguments().getPadding().getElements(), (index, x) -> { + JRightPadded oldArg = oldArgs.get(index); + if (indexes.contains(index)) { + // TODO: Cleanup backpatching of parameter types for method invocations + x = x.withElement(((J.MethodInvocation) x.getElement()).withMethodType(newMethodType(NEW_MOCKRESPONSE_FQN_BUILDER, NEW_MOCKRESPONSE_FQN, "build"))); + } + // Below is backpatching prefixes and afters to restore formatting prior to chaining `.build()` + return x + .withAfter(oldArg.getAfter()) + .withElement(x.getElement().withPrefix(oldArg.getElement().getPrefix())); + }))); + // TODO: Cleanup backpatching of parameter types for method invocation's name's type + return mi3.withName(mi3.getName().withType(((JavaType.Method) mi3.getName().getType()).withParameterTypes(mi3.getPadding().getArguments().getElements().stream().map(z -> z.getType()).collect(toList())))); + } + return mi; + } + }.visit(j, ctx); + return j; + } + }); + } + + // TODO: figure out a nicer way of doing this potentially. This is taken from MethodMatcherTest in openrewrite/rewrite and altered slightly + private static JavaType.Method newMethodType(String declaringType, @Nullable String returnType, String method, String... parameterTypes) { + List parameterTypeList = Stream.of(parameterTypes) + .map(name -> { + JavaType.Primitive primitive = JavaType.Primitive.fromKeyword(name); + return primitive != null ? primitive : JavaType.ShallowClass.build(name); + }) + .map(JavaType.class::cast) + .collect(toList()); + + return new JavaType.Method( + null, + 1L, + build(declaringType), + method, + returnType == null ? null : build(returnType), + null, + parameterTypeList, + emptyList(), + emptyList(), + emptyList(), + null + ); + } +} diff --git a/src/main/resources/META-INF/rewrite/classpath.tsv.gz b/src/main/resources/META-INF/rewrite/classpath.tsv.gz index 5ba0c908c..0d6ce9bd6 100644 Binary files a/src/main/resources/META-INF/rewrite/classpath.tsv.gz and b/src/main/resources/META-INF/rewrite/classpath.tsv.gz differ diff --git a/src/main/resources/META-INF/rewrite/junit5.yml b/src/main/resources/META-INF/rewrite/junit5.yml index 6ab2f71ec..e09ed213d 100755 --- a/src/main/resources/META-INF/rewrite/junit5.yml +++ b/src/main/resources/META-INF/rewrite/junit5.yml @@ -263,7 +263,7 @@ recipeList: - org.openrewrite.java.dependencies.ChangeDependency: oldGroupId: com.squareup.okhttp3 oldArtifactId: mockwebserver - newArtifactId: mockwebserver3-junit5 + newArtifactId: mockwebserver3 newVersion: 5.x --- type: specs.openrewrite.org/v1beta/recipe diff --git a/src/test/java/org/openrewrite/java/testing/junit5/UpgradeOkHttpMockWebServerTest.java b/src/test/java/org/openrewrite/java/testing/junit5/UpgradeOkHttpMockWebServerTest.java index 0ee888027..826c394d8 100644 --- a/src/test/java/org/openrewrite/java/testing/junit5/UpgradeOkHttpMockWebServerTest.java +++ b/src/test/java/org/openrewrite/java/testing/junit5/UpgradeOkHttpMockWebServerTest.java @@ -23,8 +23,7 @@ import org.openrewrite.test.RewriteTest; import static org.assertj.core.api.Assertions.assertThat; -import static org.openrewrite.java.Assertions.java; -import static org.openrewrite.java.Assertions.mavenProject; +import static org.openrewrite.java.Assertions.*; import static org.openrewrite.maven.Assertions.pomXml; class UpgradeOkHttpMockWebServerTest implements RewriteTest { @@ -33,36 +32,159 @@ class UpgradeOkHttpMockWebServerTest implements RewriteTest { public void defaults(RecipeSpec spec) { spec .parser(JavaParser.fromJavaVersion() - .classpathFromResources(new InMemoryExecutionContext(), "mockwebserver-4.10")) - .recipeFromResource("/META-INF/rewrite/junit5.yml", "org.openrewrite.java.testing.junit5.UpgradeOkHttpMockWebServer"); + .classpathFromResources(new InMemoryExecutionContext(), "mockwebserver-4.10", "okhttp-4.10", "junit-4")) + .recipeFromYaml( + """ + --- + type: specs.openrewrite.org/v1beta/recipe + name: org.openrewrite.test.MigrateMockResponse + displayName: Test + description: Test. + recipeList: + - org.openrewrite.java.testing.junit5.UpdateMockWebServerMockResponse + """, + "org.openrewrite.test.MigrateMockResponse" + ); +// .recipe(new UpdateMockWebServerMockResponse()); + //.recipeFromResource("/META-INF/rewrite/junit5.yml", "org.openrewrite.java.testing.junit5.UpgradeOkHttpMockWebServer"); } - @DocumentExample + // TODO: methods receiving MockResponse - maybe add comment instructing to double check? @Test - void shouldUpgradeMavenDependency() { + void shouldMigrateMockResponseToBuilder() { rewriteRun( - mavenProject("project", - //language=java - java( - """ - import okhttp3.mockwebserver.MockWebServer; + //language=java + java( + """ + import okhttp3.Headers; + import okhttp3.mockwebserver.MockResponse; + import okhttp3.mockwebserver.MockWebServer; - class Test { - void test() { - MockWebServer server = new MockWebServer(); - } - } - """, + class A { + private Headers.Builder headersBuilder = new Headers.Builder(); + private MockWebServer mockWebServer = new MockWebServer(); + private MockResponse mockResponse = new MockResponse() + .setStatus("a") + .setHeaders(headersBuilder.build()) + .setHeader("headerA", "someValue"); + private okhttp3.mockwebserver.MockResponse mockResponse2 = new okhttp3.mockwebserver.MockResponse(); + { + mockResponse.status("b"); + mockResponse.headers(headersBuilder.build()); + mockWebServer.enqueue(mockResponse); + mockResponse.setStatus("c"); + mockResponse.setHeaders(headersBuilder.build()); + mockResponse.removeHeader("headerA"); + mockResponse.clearHeaders(); + mockResponse.addHeaderLenient("headerB", "anotherValue"); + } + + void methodA() { + mockResponse.setStatus("d"); + mockWebServer.enqueue(mockResponse); + mockResponse.status("e"); + String status = mockResponse.getStatus(); + } + + void methodB() { + mockWebServer.enqueue( + new MockResponse() + .setStatus("hi") + .setHeaders(headersBuilder.build()) + ); + } + } + """, + """ + import mockwebserver3.MockResponse; + import mockwebserver3.MockWebServer; + import okhttp3.Headers; + + class A { + private Headers.Builder headersBuilder = new Headers.Builder(); + private MockWebServer mockWebServer = new MockWebServer(); + private MockResponse.Builder mockResponse = new MockResponse.Builder() + .status("a") + .headers(headersBuilder.build()) + .setHeader("headerA", "someValue"); + private mockwebserver3.MockResponse.Builder mockResponse2 = new mockwebserver3.MockResponse.Builder(); + { + mockResponse.status("b"); + mockResponse.headers(headersBuilder.build()); + mockWebServer.enqueue(mockResponse.build()); + mockResponse.status("c"); + mockResponse.headers(headersBuilder.build()); + mockResponse.removeHeader("headerA"); + mockResponse.clearHeaders(); + mockResponse.addHeaderLenient("headerB", "anotherValue"); + } + + void methodA() { + mockResponse.status("d"); + mockWebServer.enqueue(mockResponse.build()); + mockResponse.status("e"); + String status = mockResponse.getStatus(); + } + + void methodB() { + mockWebServer.enqueue( + new MockResponse.Builder() + .status("hi") + .headers(headersBuilder.build()) + .build() + ); + } + } """ - import mockwebserver3.MockWebServer; + ) + ); + } - class Test { - void test() { - MockWebServer server = new MockWebServer(); - } - } - """ - ), +// @Test +// void wip() { +// rewriteRun( +// //language=java +// java( +// """ +// import okhttp3.Headers; +// import okhttp3.mockwebserver.MockResponse; +// +// class A { +// void someMethod() { +// Headers headers = new Headers.Builder().build(); +// MockResponse a = new MockResponse(); +// // .status(String): void +// // .getStatus(): String +// // -- +// // .setStatus(String): MockResponse[this] +// // --- +// // .headers(Headers): void +// // .setHeaders(Headers): MockResponse +// // .getHeaders(): Headers +// // --- +// // .addHeader(String): MockResponse +// // .addHeader(String,Object): MockResponse +// // .addHeaderLenient(String,Object): MockResponse +// // --- +// // .setHeader(String,Object): MockResponse +// // .removeHeader(String): MockResponse +// // .clearHeaders(): MockResponse +// a.header +// a.trailers(headers); +// } +// } +// """ +// ) +// ); +// } + + @DocumentExample + @Test + void shouldUpgradeMavenDependency() { + rewriteRun( + spec -> spec.recipeFromResource("/META-INF/rewrite/junit5.yml", "org.openrewrite.java.testing.junit5.UpgradeOkHttpMockWebServer"), + mavenProject("project", + // TODO: handle solely J.NewClass and update declarative recipe to include new one. //language=xml pomXml( """ @@ -76,11 +198,18 @@ void test() { com.squareup.okhttp3 mockwebserver 4.10.0 + test """, - spec -> spec.after(pom -> assertThat(pom).containsPattern("5\\.(.*)").actual()) + spec -> spec.after(pom -> + assertThat(pom) + .doesNotContain("mockwebserver") + .contains("mockwebserver3") + .containsPattern("5\\.(.*)") + .actual() + ) ) ) ); diff --git a/src/test/resources/META-INF/rewrite/classpath.tsv.gz b/src/test/resources/META-INF/rewrite/classpath.tsv.gz index 7d000976e..321aeaaa6 100644 Binary files a/src/test/resources/META-INF/rewrite/classpath.tsv.gz and b/src/test/resources/META-INF/rewrite/classpath.tsv.gz differ