org.testcontainers.containers.Neo4jContainer Maven / Gradle / Ivy
package org.testcontainers.containers;
import static java.net.HttpURLConnection.*;
import static java.util.stream.Collectors.*;
import java.time.Duration;
import java.util.Set;
import java.util.stream.Stream;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
import org.testcontainers.containers.wait.strategy.WaitAllStrategy;
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import org.testcontainers.utility.LicenseAcceptance;
import org.testcontainers.utility.MountableFile;
/**
* Testcontainer for Neo4j.
*
* @param "SELF" to be used in the withXXX methods.
* @author Michael J. Simons
*/
public class Neo4jContainer> extends GenericContainer {
/**
* The image defaults to the official Neo4j image: Neo4j.
*/
private static final String DEFAULT_IMAGE_NAME = "neo4j";
/**
* The default tag (version) to use.
*/
private static final String DEFAULT_TAG = "3.5.0";
private static final String DOCKER_IMAGE_NAME = DEFAULT_IMAGE_NAME + ":" + DEFAULT_TAG;
/**
* Default port for the binary Bolt protocol.
*/
private static final int DEFAULT_BOLT_PORT = 7687;
/**
* The port of the transactional HTTPS endpoint: Neo4j REST API.
*/
private static final int DEFAULT_HTTPS_PORT = 7473;
/**
* The port of the transactional HTTP endpoint: Neo4j REST API.
*/
private static final int DEFAULT_HTTP_PORT = 7474;
/**
* The official image requires a change of password by default from "neo4j" to something else. This defaults to "password".
*/
private static final String DEFAULT_ADMIN_PASSWORD = "password";
private static final String AUTH_FORMAT = "neo4j/%s";
private String adminPassword = DEFAULT_ADMIN_PASSWORD;
private boolean defaultImage = false;
/**
* Creates a Testcontainer using the official Neo4j docker image.
*/
public Neo4jContainer() {
this(DOCKER_IMAGE_NAME);
this.defaultImage = true;
}
/**
* Creates a Testcontainer using a specific docker image.
*
* @param dockerImageName The docker image to use.
*/
public Neo4jContainer(String dockerImageName) {
super(dockerImageName);
WaitStrategy waitForBolt = new LogMessageWaitStrategy()
.withRegEx(String.format(".*Bolt enabled on 0\\.0\\.0\\.0:%d\\.\n", DEFAULT_BOLT_PORT));
WaitStrategy waitForHttp = new HttpWaitStrategy()
.forPort(DEFAULT_HTTP_PORT)
.forStatusCodeMatching(response -> response == HTTP_OK);
this.waitStrategy = new WaitAllStrategy()
.withStrategy(waitForBolt)
.withStrategy(waitForHttp)
.withStartupTimeout(Duration.ofMinutes(2));
}
@Override
public Set getLivenessCheckPortNumbers() {
return Stream.of(DEFAULT_BOLT_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT)
.map(this::getMappedPort)
.collect(toSet());
}
@Override
protected void configure() {
addExposedPorts(DEFAULT_BOLT_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT);
boolean emptyAdminPassword = this.adminPassword == null || this.adminPassword.isEmpty();
String neo4jAuth = emptyAdminPassword ? "none" : String.format(AUTH_FORMAT, this.adminPassword);
addEnv("NEO4J_AUTH", neo4jAuth);
}
/**
* @return Bolt URL for use with Neo4j's Java-Driver.
*/
public String getBoltUrl() {
return String.format("bolt://" + getContainerIpAddress() + ":" + getMappedPort(DEFAULT_BOLT_PORT));
}
/**
* @return URL of the transactional HTTP endpoint.
*/
public String getHttpUrl() {
return String.format("http://" + getContainerIpAddress() + ":" + getMappedPort(DEFAULT_HTTP_PORT));
}
/**
* @return URL of the transactional HTTPS endpoint.
*/
public String getHttpsUrl() {
return String.format("https://" + getContainerIpAddress() + ":" + getMappedPort(DEFAULT_HTTPS_PORT));
}
/**
* Configures the container to use the enterprise edition of the default docker image.
*
* Please have a look at the Neo4j Licensing page. While the Neo4j
* Community Edition can be used for free in your projects under the GPL v3 license, Neo4j Enterprise edition
* needs either a commercial, education or evaluation license.
*
* @return This container.
*/
public S withEnterpriseEdition() {
if (!defaultImage) {
throw new IllegalStateException(
String.format("Cannot use enterprise version with alternative image %s.", getDockerImageName()));
}
setDockerImageName(DOCKER_IMAGE_NAME + "-enterprise");
LicenseAcceptance.assertLicenseAccepted(getDockerImageName());
addEnv("NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes");
return self();
}
/**
* Sets the admin password for the default account (which is neo4j
). A null value or an empty string
* disables authentication.
*
* @param adminPassword The admin password for the default database account.
* @return This container.
*/
public S withAdminPassword(final String adminPassword) {
this.adminPassword = adminPassword;
return self();
}
/**
* Disables authentication.
*
* @return This container.
*/
public S withoutAuthentication() {
return withAdminPassword(null);
}
/**
* Copies an existing {@code graph.db} folder into the container. This can either be a classpath resource or a
* host resource. Please have a look at the factory methods in {@link MountableFile}.
*
* If you want to map your database into the container instead of copying them, please use {@code #withClasspathResourceMapping},
* but this will only work when your test does not run in a container itself.
*
* Mapping would work like this:
*
* @Container
* private static final Neo4jContainer databaseServer = new Neo4jContainer<>()
* .withClasspathResourceMapping("/test-graph.db", "/data/databases/graph.db", BindMode.READ_WRITE);
*
*
* @param graphDb The graph.db folder to copy into the container
* @return This container.
*/
public S withDatabase(MountableFile graphDb) {
return withCopyFileToContainer(graphDb, "/data/databases/graph.db");
}
/**
* Adds plugins to the given directory to the container. If {@code plugins} denotes a directory, than all of that
* directory is mapped to Neo4j's plugins. Otherwise, single resources are copied over.
*
* If you want to map your plugins into the container instead of copying them, please use {@code #withClasspathResourceMapping},
* but this will only work when your test does not run in a container itself.
*
* @param plugins
* @return This container.
*/
public S withPlugins(MountableFile plugins) {
return withCopyFileToContainer(plugins, "/var/lib/neo4j/plugins/");
}
/**
* Adds Neo4j configuration properties to the container. The properties can be added as in the official Neo4j
* configuration, the method automatically translate them into the format required by the Neo4j container.
*
* @param key The key to configure, i.e. {@code dbms.security.procedures.unrestricted}
* @param value The value to set
* @return This container.
*/
public S withNeo4jConfig(String key, String value) {
addEnv(formatConfigurationKey(key), value);
return self();
}
/**
* @return The admin password for the neo4j account or literal null if auth is disabled.
*/
public String getAdminPassword() {
return adminPassword;
}
private static String formatConfigurationKey(String plainConfigKey) {
final String prefix = "NEO4J_";
return String.format("%s%s", prefix, plainConfigKey
.replaceAll("_", "__")
.replaceAll("\\.", "_"));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy