Skip to content

Commit f212fc4

Browse files
authored
Rewrite testcontainers to use explicit image and version (#433)
* Rewrite testcontainers to use explicit image and version Fixes openrewrite/rewrite-testcontainers#1 * Upgrade dependencies and replace ContainerState getContainerIpAddress() * Exclude kafka, localstack, mockserver and pulsar for now * Add option to parse docker images for Kafka et al.
1 parent 598d7cd commit f212fc4

File tree

6 files changed

+444
-0
lines changed

6 files changed

+444
-0
lines changed

build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,16 @@ dependencies {
3939
compileOnly("org.projectlombok:lombok:latest.release")
4040
annotationProcessor("org.projectlombok:lombok:latest.release")
4141

42+
implementation("org.testcontainers:testcontainers:latest.release")
43+
4244
testImplementation("org.openrewrite:rewrite-java-17")
4345
testImplementation("org.openrewrite:rewrite-groovy")
4446

4547
testRuntimeOnly("org.gradle:gradle-tooling-api:latest.release")
4648

4749
testRuntimeOnly("com.tngtech.archunit:archunit:0.23.1")
50+
testRuntimeOnly("org.testcontainers:testcontainers:latest.release")
51+
testRuntimeOnly("org.testcontainers:nginx:latest.release")
4852

4953
// testImplementation("org.hamcrest:hamcrest:latest.release")
5054
// testImplementation("org.assertj:assertj-core:latest.release")
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.testing.testcontainers;
17+
18+
import lombok.RequiredArgsConstructor;
19+
import org.jetbrains.annotations.NotNull;
20+
import org.openrewrite.*;
21+
import org.openrewrite.java.JavaIsoVisitor;
22+
import org.openrewrite.java.JavaParser;
23+
import org.openrewrite.java.JavaTemplate;
24+
import org.openrewrite.java.MethodMatcher;
25+
import org.openrewrite.java.search.UsesMethod;
26+
import org.openrewrite.java.tree.Expression;
27+
import org.openrewrite.java.tree.J;
28+
import org.openrewrite.java.tree.JavaType;
29+
import org.openrewrite.java.tree.Space;
30+
import org.openrewrite.marker.Markers;
31+
32+
import java.util.Arrays;
33+
import java.util.UUID;
34+
35+
@RequiredArgsConstructor
36+
public class ExplicitContainerImage extends Recipe {
37+
@Option(displayName = "Container class",
38+
description = "The fully qualified name of the container class to use.",
39+
example = "org.testcontainers.containers.NginxContainer")
40+
private final String containerClass;
41+
42+
@Option(displayName = "Image to use",
43+
description = "The image to use for the container.",
44+
example = "nginx:1.9.4")
45+
private final String image;
46+
47+
@Option(displayName = "Parse image",
48+
description = "Whether to call `DockerImageName.parse(image)`.",
49+
required = false)
50+
private final Boolean parseImage;
51+
52+
@Override
53+
public String getDisplayName() {
54+
return "Add image argument to container constructor";
55+
}
56+
57+
@Override
58+
public String getDescription() {
59+
return "Set the image to use for a container explicitly if unset, rather than relying on the default image for the container class.";
60+
}
61+
62+
@Override
63+
public TreeVisitor<?, ExecutionContext> getVisitor() {
64+
final MethodMatcher methodMatcher = new MethodMatcher(containerClass + " <constructor>()");
65+
return Preconditions.check(new UsesMethod<>(methodMatcher), new JavaIsoVisitor<ExecutionContext>() {
66+
@Override
67+
public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext executionContext) {
68+
J.NewClass nc = super.visitNewClass(newClass, executionContext);
69+
if (methodMatcher.matches(newClass)) {
70+
return nc.withArguments(Arrays.asList(getConstructorArgument(newClass)));
71+
}
72+
return nc;
73+
}
74+
75+
@NotNull
76+
private Expression getConstructorArgument(J.NewClass newClass) {
77+
if (parseImage != null && parseImage) {
78+
maybeAddImport("org.testcontainers.utility.DockerImageName");
79+
return JavaTemplate.builder("DockerImageName.parse(\"" + image + "\")")
80+
.imports("org.testcontainers.utility.DockerImageName")
81+
.javaParser(JavaParser.fromJavaVersion().classpath("testcontainers"))
82+
.build()
83+
.apply(getCursor(), newClass.getArguments().get(0).getCoordinates().replace())
84+
.withPrefix(Space.EMPTY);
85+
}
86+
return new J.Literal(UUID.randomUUID(), Space.EMPTY, Markers.EMPTY, image, "\"" + image + "\"", null, JavaType.Primitive.String);
87+
}
88+
});
89+
}
90+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
@NonNullApi
17+
package org.openrewrite.java.testing.testcontainers;
18+
19+
import org.openrewrite.internal.lang.NonNullApi;
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
#
2+
# Copyright 2023 the original author or authors.
3+
# <p>
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
# <p>
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
# <p>
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
---
17+
type: specs.openrewrite.org/v1beta/recipe
18+
name: org.openrewrite.java.testing.testcontainers.TestContainersBestPractices
19+
displayName: Testcontainers best practices
20+
description: Apply best practices to Testcontainers usage.
21+
recipeList:
22+
# Upgrade to latest version of Testcontainers
23+
- org.openrewrite.java.dependencies.UpgradeDependencyVersion:
24+
groupId: org.testcontainers
25+
artifactId: "*"
26+
newVersion: 1.19.x
27+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImages
28+
- org.openrewrite.java.testing.testcontainers.GetHostMigration
29+
---
30+
type: specs.openrewrite.org/v1beta/recipe
31+
name: org.openrewrite.java.testing.testcontainers.GetHostMigration
32+
displayName: Replace `ContainerState.getContainerIpAddress()` with `getHost()`
33+
description: Replace `org.testcontainers.containers.ContainerState.getContainerIpAddress()` with `getHost()`.
34+
recipeList:
35+
- org.openrewrite.java.ChangeMethodName:
36+
methodPattern: org.testcontainers.containers.ContainerState getContainerIpAddress()
37+
newMethodName: getHost
38+
---
39+
type: specs.openrewrite.org/v1beta/recipe
40+
name: org.openrewrite.java.testing.testcontainers.ExplicitContainerImages
41+
displayName: Explicit container images and versions
42+
description: Replace implicit default container images and versions with explicit versions.
43+
recipeList:
44+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
45+
containerClass: org.testcontainers.containers.CassandraContainer
46+
image: "cassandra:3.11.2"
47+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
48+
containerClass: org.testcontainers.containers.ClickHouseContainer
49+
image: "yandex/clickhouse-server:18.10.3"
50+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
51+
containerClass: org.testcontainers.containers.CockroachContainer
52+
image: "cockroachdb/cockroach:v19.2.11"
53+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
54+
containerClass: org.testcontainers.couchbase.CouchbaseContainer
55+
image: "couchbase/server:6.5.1"
56+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
57+
containerClass: org.testcontainers.containers.Db2Container
58+
image: "ibmcom/db2:11.5.0.0a"
59+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
60+
containerClass: org.testcontainers.dynamodb.DynaliteContainer
61+
image: "quay.io/testcontainers/dynalite:v1.2.1-1"
62+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
63+
containerClass: org.testcontainers.elasticsearch.ElasticsearchContainer
64+
image: "docker.elastic.co/elasticsearch/elasticsearch:7.9.2"
65+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
66+
containerClass: org.testcontainers.containers.InfluxDBContainer
67+
image: "influxdb:1.4.3"
68+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
69+
containerClass: org.testcontainers.containers.MariaDBContainer
70+
image: "mariadb:10.3.6"
71+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
72+
containerClass: org.testcontainers.containers.MongoDBContainer
73+
image: "mongo:4.0.10"
74+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
75+
containerClass: org.testcontainers.containers.MSSQLServerContainer
76+
image: "mcr.microsoft.com/mssql/server:2017-CU12"
77+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
78+
containerClass: org.testcontainers.containers.MySQLContainer
79+
image: "mysql:5.7.34"
80+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
81+
containerClass: org.testcontainers.containers.Neo4jContainer
82+
image: "neo4j:4.4"
83+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
84+
containerClass: org.testcontainers.containers.NginxContainer
85+
image: "nginx:1.9.4"
86+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
87+
containerClass: org.testcontainers.containers.OracleContainer
88+
image: "gvenzl/oracle-xe:18.4.0-slim"
89+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
90+
containerClass: org.testcontainers.containers.OrientDBContainer
91+
image: "orientdb:3.0.24-tp3"
92+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
93+
containerClass: org.testcontainers.containers.PostgreSQLContainer
94+
image: "postgres:9.6.12"
95+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
96+
containerClass: org.testcontainers.containers.RabbitMQContainer
97+
image: "rabbitmq:3.7.25-management-alpine"
98+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
99+
containerClass: org.testcontainers.containers.SolrContainer
100+
image: "solr:8.3.0"
101+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
102+
containerClass: org.testcontainers.containers.ToxiproxyContainer
103+
image: "shopify/toxiproxy:2.1.0"
104+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
105+
containerClass: org.testcontainers.vault.VaultContainer
106+
image: "vault:1.1.3"
107+
# The following require a call to `DockerImageName.parse(image)`
108+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
109+
containerClass: org.testcontainers.containers.KafkaContainer
110+
image: "confluentinc/cp-kafka:5.4.3"
111+
parseImage: true
112+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
113+
containerClass: org.testcontainers.containers.localstack.LocalStackContainer
114+
image: "localstack/localstack:0.11.2"
115+
parseImage: true
116+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
117+
containerClass: org.testcontainers.containers.MockServerContainer
118+
image: "jamesdbloom/mockserver:mockserver-5.5.4"
119+
parseImage: true
120+
- org.openrewrite.java.testing.testcontainers.ExplicitContainerImage:
121+
containerClass: org.testcontainers.containers.PulsarContainer
122+
image: "apachepulsar/pulsar:2.10.0"
123+
parseImage: true
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.testing.testcontainers;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.openrewrite.DocumentExample;
20+
import org.openrewrite.java.JavaParser;
21+
import org.openrewrite.test.RewriteTest;
22+
23+
import static org.openrewrite.java.Assertions.java;
24+
25+
class ExplicitContainerImageTest implements RewriteTest {
26+
@Test
27+
@DocumentExample
28+
void explicitContainerImage() {
29+
rewriteRun(
30+
spec -> spec
31+
.recipe(new ExplicitContainerImage("org.testcontainers.containers.NginxContainer", "nginx:1.9.4", null))
32+
.parser(JavaParser.fromJavaVersion().classpath("nginx")),
33+
//language=java
34+
java(
35+
"""
36+
import org.testcontainers.containers.NginxContainer;
37+
class Foo {
38+
NginxContainer container = new NginxContainer();
39+
}
40+
""",
41+
"""
42+
import org.testcontainers.containers.NginxContainer;
43+
class Foo {
44+
NginxContainer container = new NginxContainer("nginx:1.9.4");
45+
}
46+
"""
47+
)
48+
);
49+
}
50+
51+
@Test
52+
void explicitContainerImageParsed() {
53+
rewriteRun(
54+
spec -> spec
55+
.recipe(new ExplicitContainerImage("org.testcontainers.containers.NginxContainer", "nginx:1.9.4", true))
56+
.parser(JavaParser.fromJavaVersion().classpath("nginx")),
57+
//language=java
58+
java(
59+
"""
60+
import org.testcontainers.containers.NginxContainer;
61+
62+
class Foo {
63+
NginxContainer container = new NginxContainer();
64+
}
65+
""",
66+
"""
67+
import org.testcontainers.containers.NginxContainer;
68+
import org.testcontainers.utility.DockerImageName;
69+
70+
class Foo {
71+
NginxContainer container = new NginxContainer(DockerImageName.parse("nginx:1.9.4"));
72+
}
73+
"""
74+
)
75+
);
76+
}
77+
78+
@Test
79+
void explicitContainerImages() {
80+
rewriteRun(
81+
spec -> spec
82+
.recipeFromResource("/META-INF/rewrite/testcontainers.yml", "org.openrewrite.java.testing.testcontainers.ExplicitContainerImages")
83+
.parser(JavaParser.fromJavaVersion().classpath("nginx")),
84+
//language=java
85+
java(
86+
"""
87+
import org.testcontainers.containers.NginxContainer;
88+
class Foo {
89+
NginxContainer container = new NginxContainer();
90+
}
91+
""",
92+
"""
93+
import org.testcontainers.containers.NginxContainer;
94+
class Foo {
95+
NginxContainer container = new NginxContainer("nginx:1.9.4");
96+
}
97+
"""
98+
)
99+
);
100+
}
101+
}

0 commit comments

Comments
 (0)