From 5f026644dfcd31140a4ab1d82fc32b119e9ae130 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 25 Nov 2023 15:46:45 +0100 Subject: [PATCH 1/4] Rewrite testcontainers to use explicit image and version Fixes https://github.com/openrewrite/rewrite-testcontainers/issues/1 --- build.gradle.kts | 1 + .../ExplicitContainerImage.java | 68 +++++++++++++ .../testing/testcontainers/package-info.java | 19 ++++ .../META-INF/rewrite/testcontainers.yml | 96 +++++++++++++++++++ .../ExplicitContainerImageTest.java | 72 ++++++++++++++ 5 files changed, 256 insertions(+) create mode 100644 src/main/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImage.java create mode 100644 src/main/java/org/openrewrite/java/testing/testcontainers/package-info.java create mode 100644 src/main/resources/META-INF/rewrite/testcontainers.yml create mode 100644 src/test/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImageTest.java diff --git a/build.gradle.kts b/build.gradle.kts index 946c1a97f..a58ed393d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -45,6 +45,7 @@ dependencies { testRuntimeOnly("org.gradle:gradle-tooling-api:latest.release") testRuntimeOnly("com.tngtech.archunit:archunit:0.23.1") + testRuntimeOnly("org.testcontainers:nginx:latest.release") // testImplementation("org.hamcrest:hamcrest:latest.release") // testImplementation("org.assertj:assertj-core:latest.release") diff --git a/src/main/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImage.java b/src/main/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImage.java new file mode 100644 index 000000000..9325104c3 --- /dev/null +++ b/src/main/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImage.java @@ -0,0 +1,68 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed 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 + *

+ * https://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.openrewrite.java.testing.testcontainers; + +import lombok.RequiredArgsConstructor; +import org.openrewrite.*; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.search.UsesMethod; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.Space; +import org.openrewrite.marker.Markers; + +import java.util.Arrays; +import java.util.UUID; + +@RequiredArgsConstructor +public class ExplicitContainerImage extends Recipe { + @Option(displayName = "Container class", + description = "The fully qualified name of the container class to use.", + example = "org.testcontainers.containers.NginxContainer") + private final String containerClass; + + @Option(displayName = "Image to use", + description = "The image to use for the container.", + example = "nginx:1.9.4") + private final String image; + + @Override + public String getDisplayName() { + return "Add image argument to container constructor"; + } + + @Override + public String getDescription() { + return "Set the image to use for a container explicitly if unset, rather than relying on the default image for the container class."; + } + + @Override + public TreeVisitor getVisitor() { + final MethodMatcher methodMatcher = new MethodMatcher(containerClass + " ()"); + return Preconditions.check(new UsesMethod<>(methodMatcher), new JavaIsoVisitor() { + @Override + public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext executionContext) { + J.NewClass nc = super.visitNewClass(newClass, executionContext); + if (methodMatcher.matches(newClass)) { + return nc.withArguments(Arrays.asList( + new J.Literal(UUID.randomUUID(), Space.EMPTY, Markers.EMPTY, image, "\"" + image + "\"", null, JavaType.Primitive.String))); + } + return nc; + } + }); + } +} diff --git a/src/main/java/org/openrewrite/java/testing/testcontainers/package-info.java b/src/main/java/org/openrewrite/java/testing/testcontainers/package-info.java new file mode 100644 index 000000000..956a3fb3f --- /dev/null +++ b/src/main/java/org/openrewrite/java/testing/testcontainers/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed 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 + *

+ * https://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. + */ +@NonNullApi +package org.openrewrite.java.testing.testcontainers; + +import org.openrewrite.internal.lang.NonNullApi; diff --git a/src/main/resources/META-INF/rewrite/testcontainers.yml b/src/main/resources/META-INF/rewrite/testcontainers.yml new file mode 100644 index 000000000..49dcea813 --- /dev/null +++ b/src/main/resources/META-INF/rewrite/testcontainers.yml @@ -0,0 +1,96 @@ +# +# Copyright 2023 the original author or authors. +#

+# Licensed 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 +#

+# https://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. +# + +type: specs.openrewrite.org/v1beta/recipe +name: org.testcontainers.openrewrite.ExplicitContainerImages +displayName: Explicit container images and versions +description: Replace implicit default container images and versions with explicit versions. +recipeList: + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.CassandraContainer + image: "cassandra:3.11.2" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.ClickHouseContainer + image: "yandex/clickhouse-server:18.10.3" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.CockroachContainer + image: "cockroachdb/cockroach:v19.2.11" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.couchbase.CouchbaseContainer + image: "couchbase/server:6.5.1" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.Db2Container + image: "ibmcom/db2:11.5.0.0a" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.dynamodb.DynaliteContainer + image: "quay.io/testcontainers/dynalite:v1.2.1-1" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.elasticsearch.ElasticsearchContainer + image: "docker.elastic.co/elasticsearch/elasticsearch:7.9.2" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.InfluxDBContainer + image: "influxdb:1.4.3" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.KafkaContainer + image: "confluentinc/cp-kafka:5.4.3" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.localstack.LocalStackContainer + image: "localstack/localstack:0.11.2" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.MariaDBContainer + image: "mariadb:10.3.6" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.MockServerContainer + image: "jamesdbloom/mockserver:mockserver-5.5.4" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.MongoDBContainer + image: "mongo:4.0.10" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.MSSQLServerContainer + image: "mcr.microsoft.com/mssql/server:2017-CU12" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.MySQLContainer + image: "mysql:5.7.34" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.Neo4jContainer + image: "neo4j:4.4" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.NginxContainer + image: "nginx:1.9.4" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.OracleContainer + image: "gvenzl/oracle-xe:18.4.0-slim" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.OrientDBContainer + image: "orientdb:3.0.24-tp3" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.PostgreSQLContainer + image: "postgres:9.6.12" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.PulsarContainer + image: "apachepulsar/pulsar:2.10.0" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.RabbitMQContainer + image: "rabbitmq:3.7.25-management-alpine" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.SolrContainer + image: "solr:8.3.0" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.ToxiproxyContainer + image: "shopify/toxiproxy:2.1.0" + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.vault.VaultContainer + image: "vault:1.1.3" diff --git a/src/test/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImageTest.java b/src/test/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImageTest.java new file mode 100644 index 000000000..f4ee61cce --- /dev/null +++ b/src/test/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImageTest.java @@ -0,0 +1,72 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed 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 + *

+ * https://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.openrewrite.java.testing.testcontainers; + +import org.junit.jupiter.api.Test; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class ExplicitContainerImageTest implements RewriteTest { + @Test + void explicitContainerImage() { + rewriteRun( + spec -> spec + .recipe(new ExplicitContainerImage("org.testcontainers.containers.NginxContainer", "nginx:1.9.4")) + .parser(JavaParser.fromJavaVersion().classpath("nginx")), + //language=java + java( + """ + import org.testcontainers.containers.NginxContainer; + class Foo { + NginxContainer container = new NginxContainer(); + } + """, + """ + import org.testcontainers.containers.NginxContainer; + class Foo { + NginxContainer container = new NginxContainer("nginx:1.9.4"); + } + """ + ) + ); + } + + @Test + void explicitContainerImages() { + rewriteRun( + spec -> spec + .recipeFromResource("/META-INF/rewrite/testcontainers.yml", "org.testcontainers.openrewrite.ExplicitContainerImages") + .parser(JavaParser.fromJavaVersion().classpath("nginx")), + //language=java + java( + """ + import org.testcontainers.containers.NginxContainer; + class Foo { + NginxContainer container = new NginxContainer(); + } + """, + """ + import org.testcontainers.containers.NginxContainer; + class Foo { + NginxContainer container = new NginxContainer("nginx:1.9.4"); + } + """ + ) + ); + } +} \ No newline at end of file From 5a309e06c4cd8594ec971b0890f17958c8f45632 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 25 Nov 2023 16:58:39 +0100 Subject: [PATCH 2/4] Upgrade dependencies and replace ContainerState getContainerIpAddress() --- build.gradle.kts | 1 + .../META-INF/rewrite/testcontainers.yml | 26 ++++- .../ExplicitContainerImageTest.java | 2 +- .../TestcontainersBestPracticesTest.java | 107 ++++++++++++++++++ 4 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/openrewrite/java/testing/testcontainers/TestcontainersBestPracticesTest.java diff --git a/build.gradle.kts b/build.gradle.kts index a58ed393d..20ac56484 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -45,6 +45,7 @@ dependencies { testRuntimeOnly("org.gradle:gradle-tooling-api:latest.release") testRuntimeOnly("com.tngtech.archunit:archunit:0.23.1") + testRuntimeOnly("org.testcontainers:testcontainers:latest.release") testRuntimeOnly("org.testcontainers:nginx:latest.release") // testImplementation("org.hamcrest:hamcrest:latest.release") diff --git a/src/main/resources/META-INF/rewrite/testcontainers.yml b/src/main/resources/META-INF/rewrite/testcontainers.yml index 49dcea813..5f974228a 100644 --- a/src/main/resources/META-INF/rewrite/testcontainers.yml +++ b/src/main/resources/META-INF/rewrite/testcontainers.yml @@ -13,9 +13,31 @@ # See the License for the specific language governing permissions and # limitations under the License. # - +--- type: specs.openrewrite.org/v1beta/recipe -name: org.testcontainers.openrewrite.ExplicitContainerImages +name: org.openrewrite.java.testing.testcontainers.TestContainersBestPractices +displayName: Testcontainers best practices +description: Apply best practices to Testcontainers usage. +recipeList: + # Upgrade to latest version of Testcontainers + - org.openrewrite.java.dependencies.UpgradeDependencyVersion: + groupId: org.testcontainers + artifactId: "*" + newVersion: 1.19.x + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImages + - org.openrewrite.java.testing.testcontainers.GetHostMigration +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.testing.testcontainers.GetHostMigration +displayName: Replace `ContainerState.getContainerIpAddress()` with `getHost()` +description: Replace `org.testcontainers.containers.ContainerState.getContainerIpAddress()` with `getHost()`. +recipeList: + - org.openrewrite.java.ChangeMethodName: + methodPattern: org.testcontainers.containers.ContainerState getContainerIpAddress() + newMethodName: getHost +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.testing.testcontainers.ExplicitContainerImages displayName: Explicit container images and versions description: Replace implicit default container images and versions with explicit versions. recipeList: diff --git a/src/test/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImageTest.java b/src/test/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImageTest.java index f4ee61cce..4afb9f9ad 100644 --- a/src/test/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImageTest.java +++ b/src/test/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImageTest.java @@ -50,7 +50,7 @@ class Foo { void explicitContainerImages() { rewriteRun( spec -> spec - .recipeFromResource("/META-INF/rewrite/testcontainers.yml", "org.testcontainers.openrewrite.ExplicitContainerImages") + .recipeFromResource("/META-INF/rewrite/testcontainers.yml", "org.openrewrite.java.testing.testcontainers.ExplicitContainerImages") .parser(JavaParser.fromJavaVersion().classpath("nginx")), //language=java java( diff --git a/src/test/java/org/openrewrite/java/testing/testcontainers/TestcontainersBestPracticesTest.java b/src/test/java/org/openrewrite/java/testing/testcontainers/TestcontainersBestPracticesTest.java new file mode 100644 index 000000000..d523ace62 --- /dev/null +++ b/src/test/java/org/openrewrite/java/testing/testcontainers/TestcontainersBestPracticesTest.java @@ -0,0 +1,107 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed 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 + *

+ * https://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.openrewrite.java.testing.testcontainers; + +import org.junit.jupiter.api.Test; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.maven.Assertions.pomXml; + +class TestcontainersBestPracticesTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec + .recipeFromResource("/META-INF/rewrite/testcontainers.yml", + "org.openrewrite.java.testing.testcontainers.TestContainersBestPractices") + .parser(JavaParser.fromJavaVersion().classpath("testcontainers")); + } + + @Test + void dependencyUpdate() { + rewriteRun( + pomXml( + //language=xml + """ + + 4.0.0 + org.openrewrite.example + testcontainers + 1.0-SNAPSHOT + + + org.testcontainers + testcontainers + 1.15.3 + + + + """, + spec -> spec.after(after -> { + Matcher matcher = Pattern.compile("(1\\.19\\.\\d+)").matcher(after); + assertTrue(matcher.find()); + //language=xml + return """ + + 4.0.0 + org.openrewrite.example + testcontainers + 1.0-SNAPSHOT + + + org.testcontainers + testcontainers + %s + + + + """.formatted(matcher.group(1)); + }) + ) + ); + } + + @Test + void getHost() { + rewriteRun( + //language=java + java( + """ + import org.testcontainers.containers.ContainerState; + class Foo { + String method(ContainerState container) { + return container.getContainerIpAddress(); + } + } + """, + """ + import org.testcontainers.containers.ContainerState; + class Foo { + String method(ContainerState container) { + return container.getHost(); + } + } + """ + ) + ); + } +} From ba3181d6dd2971a63cedd8b4ab88eb4d9d1cb47e Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 27 Nov 2023 12:12:00 +0100 Subject: [PATCH 3/4] Exclude kafka, localstack, mockserver and pulsar for now --- .../META-INF/rewrite/testcontainers.yml | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/resources/META-INF/rewrite/testcontainers.yml b/src/main/resources/META-INF/rewrite/testcontainers.yml index 5f974228a..390f58121 100644 --- a/src/main/resources/META-INF/rewrite/testcontainers.yml +++ b/src/main/resources/META-INF/rewrite/testcontainers.yml @@ -65,18 +65,9 @@ recipeList: - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: containerClass: org.testcontainers.containers.InfluxDBContainer image: "influxdb:1.4.3" - - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: - containerClass: org.testcontainers.containers.KafkaContainer - image: "confluentinc/cp-kafka:5.4.3" - - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: - containerClass: org.testcontainers.containers.localstack.LocalStackContainer - image: "localstack/localstack:0.11.2" - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: containerClass: org.testcontainers.containers.MariaDBContainer image: "mariadb:10.3.6" - - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: - containerClass: org.testcontainers.containers.MockServerContainer - image: "jamesdbloom/mockserver:mockserver-5.5.4" - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: containerClass: org.testcontainers.containers.MongoDBContainer image: "mongo:4.0.10" @@ -101,9 +92,6 @@ recipeList: - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: containerClass: org.testcontainers.containers.PostgreSQLContainer image: "postgres:9.6.12" - - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: - containerClass: org.testcontainers.containers.PulsarContainer - image: "apachepulsar/pulsar:2.10.0" - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: containerClass: org.testcontainers.containers.RabbitMQContainer image: "rabbitmq:3.7.25-management-alpine" @@ -116,3 +104,16 @@ recipeList: - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: containerClass: org.testcontainers.vault.VaultContainer image: "vault:1.1.3" +# TODO the following containers should adopt `DockerImageName.parse(:)` +# - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: +# containerClass: org.testcontainers.containers.KafkaContainer +# image: "confluentinc/cp-kafka:5.4.3" +# - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: +# containerClass: org.testcontainers.containers.localstack.LocalStackContainer +# image: "localstack/localstack:0.11.2" +# - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: +# containerClass: org.testcontainers.containers.MockServerContainer +# image: "jamesdbloom/mockserver:mockserver-5.5.4" +# - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: +# containerClass: org.testcontainers.containers.PulsarContainer +# image: "apachepulsar/pulsar:2.10.0" From 0ac879ac093c545c8677e74e7499fb033bd1312b Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 27 Nov 2023 12:43:30 +0100 Subject: [PATCH 4/4] Add option to parse docker images for Kafka et al. --- build.gradle.kts | 2 ++ .../ExplicitContainerImage.java | 26 ++++++++++++++-- .../META-INF/rewrite/testcontainers.yml | 30 ++++++++++-------- .../ExplicitContainerImageTest.java | 31 ++++++++++++++++++- 4 files changed, 73 insertions(+), 16 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 20ac56484..aa19eca88 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -39,6 +39,8 @@ dependencies { compileOnly("org.projectlombok:lombok:latest.release") annotationProcessor("org.projectlombok:lombok:latest.release") + implementation("org.testcontainers:testcontainers:latest.release") + testImplementation("org.openrewrite:rewrite-java-17") testImplementation("org.openrewrite:rewrite-groovy") diff --git a/src/main/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImage.java b/src/main/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImage.java index 9325104c3..6cdea5e55 100644 --- a/src/main/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImage.java +++ b/src/main/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImage.java @@ -16,10 +16,14 @@ package org.openrewrite.java.testing.testcontainers; import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; import org.openrewrite.*; import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaParser; +import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.search.UsesMethod; +import org.openrewrite.java.tree.Expression; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.Space; @@ -40,6 +44,11 @@ public class ExplicitContainerImage extends Recipe { example = "nginx:1.9.4") private final String image; + @Option(displayName = "Parse image", + description = "Whether to call `DockerImageName.parse(image)`.", + required = false) + private final Boolean parseImage; + @Override public String getDisplayName() { return "Add image argument to container constructor"; @@ -58,11 +67,24 @@ public TreeVisitor getVisitor() { public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext executionContext) { J.NewClass nc = super.visitNewClass(newClass, executionContext); if (methodMatcher.matches(newClass)) { - return nc.withArguments(Arrays.asList( - new J.Literal(UUID.randomUUID(), Space.EMPTY, Markers.EMPTY, image, "\"" + image + "\"", null, JavaType.Primitive.String))); + return nc.withArguments(Arrays.asList(getConstructorArgument(newClass))); } return nc; } + + @NotNull + private Expression getConstructorArgument(J.NewClass newClass) { + if (parseImage != null && parseImage) { + maybeAddImport("org.testcontainers.utility.DockerImageName"); + return JavaTemplate.builder("DockerImageName.parse(\"" + image + "\")") + .imports("org.testcontainers.utility.DockerImageName") + .javaParser(JavaParser.fromJavaVersion().classpath("testcontainers")) + .build() + .apply(getCursor(), newClass.getArguments().get(0).getCoordinates().replace()) + .withPrefix(Space.EMPTY); + } + return new J.Literal(UUID.randomUUID(), Space.EMPTY, Markers.EMPTY, image, "\"" + image + "\"", null, JavaType.Primitive.String); + } }); } } diff --git a/src/main/resources/META-INF/rewrite/testcontainers.yml b/src/main/resources/META-INF/rewrite/testcontainers.yml index 390f58121..4bef3dd90 100644 --- a/src/main/resources/META-INF/rewrite/testcontainers.yml +++ b/src/main/resources/META-INF/rewrite/testcontainers.yml @@ -104,16 +104,20 @@ recipeList: - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: containerClass: org.testcontainers.vault.VaultContainer image: "vault:1.1.3" -# TODO the following containers should adopt `DockerImageName.parse(:)` -# - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: -# containerClass: org.testcontainers.containers.KafkaContainer -# image: "confluentinc/cp-kafka:5.4.3" -# - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: -# containerClass: org.testcontainers.containers.localstack.LocalStackContainer -# image: "localstack/localstack:0.11.2" -# - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: -# containerClass: org.testcontainers.containers.MockServerContainer -# image: "jamesdbloom/mockserver:mockserver-5.5.4" -# - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: -# containerClass: org.testcontainers.containers.PulsarContainer -# image: "apachepulsar/pulsar:2.10.0" + # The following require a call to `DockerImageName.parse(image)` + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.KafkaContainer + image: "confluentinc/cp-kafka:5.4.3" + parseImage: true + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.localstack.LocalStackContainer + image: "localstack/localstack:0.11.2" + parseImage: true + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.MockServerContainer + image: "jamesdbloom/mockserver:mockserver-5.5.4" + parseImage: true + - org.openrewrite.java.testing.testcontainers.ExplicitContainerImage: + containerClass: org.testcontainers.containers.PulsarContainer + image: "apachepulsar/pulsar:2.10.0" + parseImage: true diff --git a/src/test/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImageTest.java b/src/test/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImageTest.java index 4afb9f9ad..e17ca2239 100644 --- a/src/test/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImageTest.java +++ b/src/test/java/org/openrewrite/java/testing/testcontainers/ExplicitContainerImageTest.java @@ -16,6 +16,7 @@ package org.openrewrite.java.testing.testcontainers; import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; import org.openrewrite.java.JavaParser; import org.openrewrite.test.RewriteTest; @@ -23,10 +24,11 @@ class ExplicitContainerImageTest implements RewriteTest { @Test + @DocumentExample void explicitContainerImage() { rewriteRun( spec -> spec - .recipe(new ExplicitContainerImage("org.testcontainers.containers.NginxContainer", "nginx:1.9.4")) + .recipe(new ExplicitContainerImage("org.testcontainers.containers.NginxContainer", "nginx:1.9.4", null)) .parser(JavaParser.fromJavaVersion().classpath("nginx")), //language=java java( @@ -46,6 +48,33 @@ class Foo { ); } + @Test + void explicitContainerImageParsed() { + rewriteRun( + spec -> spec + .recipe(new ExplicitContainerImage("org.testcontainers.containers.NginxContainer", "nginx:1.9.4", true)) + .parser(JavaParser.fromJavaVersion().classpath("nginx")), + //language=java + java( + """ + import org.testcontainers.containers.NginxContainer; + + class Foo { + NginxContainer container = new NginxContainer(); + } + """, + """ + import org.testcontainers.containers.NginxContainer; + import org.testcontainers.utility.DockerImageName; + + class Foo { + NginxContainer container = new NginxContainer(DockerImageName.parse("nginx:1.9.4")); + } + """ + ) + ); + } + @Test void explicitContainerImages() { rewriteRun(