Skip to content

Commit 8b92d14

Browse files
meistermeierkiview
andauthored
Feature/neo4j defaults (#6154)
Co-authored-by: Kevin Wittek <[email protected]>
1 parent 77a3c37 commit 8b92d14

File tree

2 files changed

+146
-13
lines changed

2 files changed

+146
-13
lines changed

modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ public class Neo4jContainer<S extends Neo4jContainer<S>> extends GenericContaine
6666

6767
private final Set<String> labsPlugins = new HashSet<>();
6868

69+
/**
70+
* Default wait strategies
71+
*/
72+
public static final WaitStrategy WAIT_FOR_BOLT = new LogMessageWaitStrategy()
73+
.withRegEx(String.format(".*Bolt enabled on .*:%d\\.\n", DEFAULT_BOLT_PORT));
74+
75+
private static final WaitStrategy WAIT_FOR_HTTP = new HttpWaitStrategy()
76+
.forPort(DEFAULT_HTTP_PORT)
77+
.forStatusCodeMatching(response -> response == HttpURLConnection.HTTP_OK);
78+
6979
/**
7080
* Creates a Neo4jContainer using the official Neo4j docker image.
7181
* @deprecated use {@link Neo4jContainer(DockerImageName)} instead
@@ -95,16 +105,10 @@ public Neo4jContainer(final DockerImageName dockerImageName) {
95105

96106
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
97107

98-
WaitStrategy waitForBolt = new LogMessageWaitStrategy()
99-
.withRegEx(String.format(".*Bolt enabled on .*:%d\\.\n", DEFAULT_BOLT_PORT));
100-
WaitStrategy waitForHttp = new HttpWaitStrategy()
101-
.forPort(DEFAULT_HTTP_PORT)
102-
.forStatusCodeMatching(response -> response == HttpURLConnection.HTTP_OK);
103-
104108
this.waitStrategy =
105109
new WaitAllStrategy()
106-
.withStrategy(waitForBolt)
107-
.withStrategy(waitForHttp)
110+
.withStrategy(WAIT_FOR_BOLT)
111+
.withStrategy(WAIT_FOR_HTTP)
108112
.withStartupTimeout(Duration.ofMinutes(2));
109113

110114
addExposedPorts(DEFAULT_BOLT_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT);
@@ -120,15 +124,56 @@ public Set<Integer> getLivenessCheckPortNumbers() {
120124

121125
@Override
122126
protected void configure() {
123-
boolean emptyAdminPassword = this.adminPassword == null || this.adminPassword.isEmpty();
124-
String neo4jAuth = emptyAdminPassword ? "none" : String.format(AUTH_FORMAT, this.adminPassword);
125-
addEnv("NEO4J_AUTH", neo4jAuth);
127+
configureAuth();
128+
configureLabsPlugins();
129+
configureWaitStrategy();
130+
}
131+
132+
/**
133+
* Configured via {@link Neo4jContainer#withAdminPassword(String)} or {@link Neo4jContainer#withoutAuthentication()}
134+
* It is only possible to set the correct auth in the configuration call.
135+
* Also, the custom methods overrule the set env parameter.
136+
*/
137+
private void configureAuth() {
138+
String neo4jAuthEnvKey = "NEO4J_AUTH";
139+
if (!getEnvMap().containsKey(neo4jAuthEnvKey) || !DEFAULT_ADMIN_PASSWORD.equals(this.adminPassword)) {
140+
boolean emptyAdminPassword = this.adminPassword == null || this.adminPassword.isEmpty();
141+
String neo4jAuth = emptyAdminPassword ? "none" : String.format(AUTH_FORMAT, this.adminPassword);
142+
addEnv(neo4jAuthEnvKey, neo4jAuth);
143+
}
144+
}
126145

127-
if (!this.labsPlugins.isEmpty()) {
146+
/**
147+
* Configured via {@link Neo4jContainer#withLabsPlugins}.
148+
* Configuration can only happen in the configuration call because there is no default.
149+
*/
150+
private void configureLabsPlugins() {
151+
String neo4jLabsPluginsEnvKey = "NEO4JLABS_PLUGINS";
152+
if (!getEnv().contains(neo4jLabsPluginsEnvKey) && !this.labsPlugins.isEmpty()) {
128153
String enabledPlugins =
129154
this.labsPlugins.stream().map(pluginName -> "\"" + pluginName + "\"").collect(Collectors.joining(","));
130155

131-
addEnv("NEO4JLABS_PLUGINS", "[" + enabledPlugins + "]");
156+
addEnv(neo4jLabsPluginsEnvKey, "[" + enabledPlugins + "]");
157+
}
158+
}
159+
160+
/**
161+
* Update the default Neo4jContainer wait strategy based on the exposed ports.
162+
* Still possible to override the startup timeout before starting the container via {@link WaitStrategy#withStartupTimeout(Duration)}.
163+
*/
164+
private void configureWaitStrategy() {
165+
List<Integer> exposedPorts = getExposedPorts();
166+
boolean boltExposed = exposedPorts.contains(DEFAULT_BOLT_PORT);
167+
boolean httpExposed = exposedPorts.contains(DEFAULT_HTTP_PORT);
168+
boolean onlyBoltExposed = boltExposed && !httpExposed;
169+
boolean onlyHttpExposed = !boltExposed && httpExposed;
170+
171+
if (onlyBoltExposed) {
172+
this.waitStrategy =
173+
new WaitAllStrategy().withStrategy(WAIT_FOR_BOLT).withStartupTimeout(Duration.ofMinutes(2));
174+
} else if (onlyHttpExposed) {
175+
this.waitStrategy =
176+
new WaitAllStrategy().withStrategy(WAIT_FOR_HTTP).withStartupTimeout(Duration.ofMinutes(2));
132177
}
133178
}
134179

modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import org.neo4j.driver.Record;
99
import org.neo4j.driver.Result;
1010
import org.neo4j.driver.Session;
11+
import org.testcontainers.DockerClientFactory;
12+
import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy;
1113
import org.testcontainers.utility.MountableFile;
1214

1315
import java.util.Collections;
@@ -45,6 +47,8 @@ public void shouldDisableAuthentication() {
4547

4648
@Test
4749
public void shouldCopyDatabase() {
50+
// no aarch64 image available for Neo4j 3.5
51+
assumeThat(DockerClientFactory.instance().getInfo().getArchitecture()).isNotEqualTo("aarch64");
4852
try (
4953
// copyDatabase {
5054
Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:3.5.30")
@@ -169,6 +173,82 @@ public void shouldAddConfigToEnvironment() {
169173
assertThat(neo4jContainer.getEnvMap()).containsEntry("NEO4J_dbms_tx__log_rotation_size", "42M");
170174
}
171175

176+
@Test
177+
public void shouldRespectEnvironmentAuth() {
178+
Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:4.4").withEnv("NEO4J_AUTH", "neo4j/secret");
179+
180+
neo4jContainer.configure();
181+
182+
assertThat(neo4jContainer.getEnvMap()).containsEntry("NEO4J_AUTH", "neo4j/secret");
183+
}
184+
185+
@Test
186+
public void containerAdminPasswordOverrulesEnvironmentAuth() {
187+
Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:4.4")
188+
.withEnv("NEO4J_AUTH", "neo4j/secret")
189+
.withAdminPassword("anotherSecret");
190+
191+
neo4jContainer.configure();
192+
193+
assertThat(neo4jContainer.getEnvMap()).containsEntry("NEO4J_AUTH", "neo4j/anotherSecret");
194+
}
195+
196+
@Test
197+
public void containerWithoutAuthenticationOverrulesEnvironmentAuth() {
198+
Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:4.4")
199+
.withEnv("NEO4J_AUTH", "neo4j/secret")
200+
.withoutAuthentication();
201+
202+
neo4jContainer.configure();
203+
204+
assertThat(neo4jContainer.getEnvMap()).containsEntry("NEO4J_AUTH", "none");
205+
}
206+
207+
@Test
208+
public void shouldRespectAlreadyDefinedPortMappingsBolt() {
209+
Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:4.4").withExposedPorts(7687);
210+
211+
neo4jContainer.configure();
212+
213+
assertThat(neo4jContainer.getExposedPorts()).containsExactly(7687);
214+
}
215+
216+
@Test
217+
public void shouldRespectAlreadyDefinedPortMappingsHttp() {
218+
Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:4.4").withExposedPorts(7474);
219+
220+
neo4jContainer.configure();
221+
222+
assertThat(neo4jContainer.getExposedPorts()).containsExactly(7474);
223+
}
224+
225+
@Test
226+
public void shouldRespectAlreadyDefinedPortMappingsWithoutHttps() {
227+
Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:4.4").withExposedPorts(7687, 7474);
228+
229+
neo4jContainer.configure();
230+
231+
assertThat(neo4jContainer.getExposedPorts()).containsExactlyInAnyOrder(7474, 7687);
232+
}
233+
234+
@Test
235+
public void shouldDefaultExportBoltHttpAndHttps() {
236+
Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:4.4");
237+
238+
neo4jContainer.configure();
239+
240+
assertThat(neo4jContainer.getExposedPorts()).containsExactlyInAnyOrder(7473, 7474, 7687);
241+
}
242+
243+
@Test
244+
public void shouldRespectCustomWaitStrategy() {
245+
Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:4.4").waitingFor(new CustomDummyWaitStrategy());
246+
247+
neo4jContainer.configure();
248+
249+
assertThat(neo4jContainer.getWaitStrategy()).isInstanceOf(CustomDummyWaitStrategy.class);
250+
}
251+
172252
@Test
173253
public void shouldConfigureSingleLabsPlugin() {
174254
try (
@@ -220,6 +300,14 @@ public void shouldConfigureMultipleLabsPluginsWithString() {
220300
}
221301
}
222302

303+
private static class CustomDummyWaitStrategy extends AbstractWaitStrategy {
304+
305+
@Override
306+
protected void waitUntilReady() {
307+
// ehm...ready
308+
}
309+
}
310+
223311
private static Driver getDriver(Neo4jContainer<?> container) {
224312
AuthToken authToken = AuthTokens.none();
225313
if (container.getAdminPassword() != null) {

0 commit comments

Comments
 (0)