Skip to content

Commit

Permalink
Add CrateDB testcontainer
Browse files Browse the repository at this point in the history
  • Loading branch information
matriv committed Mar 20, 2023
1 parent 3b0c1cb commit 3c3116f
Show file tree
Hide file tree
Showing 9 changed files with 360 additions and 0 deletions.
13 changes: 13 additions & 0 deletions modules/cratedb/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
description = "Testcontainers :: JDBC :: PostgreSQL"

dependencies {
annotationProcessor 'com.google.auto.service:auto-service:1.0.1'
compileOnly 'com.google.auto.service:auto-service:1.0.1'

api project(':jdbc')

testImplementation project(':jdbc-test')
testImplementation 'io.crate:crate-jdbc:2.6.0'

compileOnly 'org.jetbrains:annotations:24.0.0'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package org.testcontainers.containers;

import org.jetbrains.annotations.NotNull;
import org.testcontainers.containers.wait.strategy.DockerHealthcheckWaitStrategy;import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
import org.testcontainers.containers.wait.strategy.Wait;import org.testcontainers.utility.DockerImageName;

import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.List;import java.util.Set;

public class CrateDBContainer<SELF extends CrateDBContainer<SELF>> extends JdbcDatabaseContainer<SELF> {

public static final String NAME = "crate";

public static final String IMAGE = "crate";

public static final String DEFAULT_TAG = "latest";

private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("crate");

public static final Integer CRATEDB_PG_PORT = 5432;
public static final Integer CRATEDB_HTTP_PORT = 4200;

private String databaseName = "crate";

private String username = "crate";

private String password = "crate";


/**
* @deprecated use {@link #CrateDBContainer(DockerImageName)} or {@link #CrateDBContainer(String)} instead
*/
@Deprecated
public CrateDBContainer() {
this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG));
}

public CrateDBContainer(final String dockerImageName) {
this(DockerImageName.parse(dockerImageName));
}

public CrateDBContainer(final DockerImageName dockerImageName) {
super(dockerImageName);
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);

this.waitStrategy =
Wait
.forHttp("/")
.forPort(4200)
.forStatusCode(200);

addExposedPort(CRATEDB_PG_PORT);
addExposedPort(CRATEDB_HTTP_PORT);
}

/**
* @return the ports on which to check if the container is ready
* @deprecated use {@link #getLivenessCheckPortNumbers()} instead
*/
@NotNull
@Override
@Deprecated
protected Set<Integer> getLivenessCheckPorts() {
return super.getLivenessCheckPorts();
}

@Override
public String getDriverClassName() {
return "io.crate.client.jdbc.CrateDriver";
}

@Override
public String getJdbcUrl() {
String additionalUrlParams = constructUrlParameters("?", "&");
return (
"crate://" +
getHost() +
":" +
getMappedPort(CRATEDB_PG_PORT) +
"/" +
databaseName +
additionalUrlParams
);
}

@Override
public String getDatabaseName() {
return databaseName;
}

@Override
public String getUsername() {
return username;
}

@Override
public String getPassword() {
return password;
}

@Override
public String getTestQueryString() {
return "SELECT 1";
}

@Override
public SELF withDatabaseName(final String databaseName) {
this.databaseName = databaseName;
return self();
}

@Override
public SELF withUsername(final String username) {
this.username = username;
return self();
}

@Override
public SELF withPassword(final String password) {
this.password = password;
return self();
}

@Override
protected void waitUntilContainerStarted() {
getWaitStrategy().waitUntilReady(this);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.testcontainers.containers;

import org.testcontainers.jdbc.ConnectionUrl;
import org.testcontainers.utility.DockerImageName;

/**
* Factory for CrateDB containers.
*/
public class CrateDBContainerProvider extends JdbcDatabaseContainerProvider {

public static final String USER_PARAM = "user";

public static final String PASSWORD_PARAM = "password";

@Override
public boolean supports(String databaseType) {
return databaseType.equals(CrateDBContainer.NAME);
}

@Override
public JdbcDatabaseContainer newInstance() {
return newInstance(CrateDBContainer.DEFAULT_TAG);
}

@Override
public JdbcDatabaseContainer newInstance(String tag) {
return new CrateDBContainer(DockerImageName.parse(CrateDBContainer.IMAGE).withTag(tag));
}

@Override
public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) {
return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.testcontainers.containers.CrateDBContainerProvider
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.testcontainers;

import org.testcontainers.utility.DockerImageName;

public interface CrateDBTestImages {
DockerImageName CRATEDB_TEST_IMAGE = DockerImageName.parse("crate:5.2.3");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package org.testcontainers.containers;

import org.junit.Test;
import org.testcontainers.CrateDBTestImages;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;

public class CrateDBConnectionURLTest {

@Test
public void shouldCorrectlyAppendQueryString() {
CrateDBContainer<?> cratedb = new FixedJdbcUrlCrateDBContainer();
String connectionUrl = cratedb.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified");
String queryString = connectionUrl.substring(connectionUrl.indexOf('?'));

assertThat(queryString)
.as("Query String contains expected params")
.contains("?stringtype=unspecified&stringtype=unspecified");
assertThat(queryString.indexOf('?')).as("Query String starts with '?'").isZero();
assertThat(queryString.substring(1)).as("Query String does not contain extra '?'").doesNotContain("?");
}

@Test
public void shouldCorrectlyAppendQueryStringWhenNoBaseParams() {
CrateDBContainer<?> cratedb = new NoParamsUrlCrateDBContainer();
String connectionUrl = cratedb.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified");
String queryString = connectionUrl.substring(connectionUrl.indexOf('?'));

assertThat(queryString)
.as("Query String contains expected params")
.contains("?stringtype=unspecified&stringtype=unspecified");
assertThat(queryString.indexOf('?')).as("Query String starts with '?'").isZero();
assertThat(queryString.substring(1)).as("Query String does not contain extra '?'").doesNotContain("?");
}

@Test
public void shouldReturnOriginalURLWhenEmptyQueryString() {
CrateDBContainer<?> cratedb = new FixedJdbcUrlCrateDBContainer();
String connectionUrl = cratedb.constructUrlForConnection("");

assertThat(cratedb.getJdbcUrl()).as("Query String remains unchanged").isEqualTo(connectionUrl);
}

@Test
public void shouldRejectInvalidQueryString() {
assertThat(
catchThrowable(() -> {
new NoParamsUrlCrateDBContainer().constructUrlForConnection("stringtype=unspecified");
})
)
.as("Fails when invalid query string provided")
.isInstanceOf(IllegalArgumentException.class);
}

static class FixedJdbcUrlCrateDBContainer extends CrateDBContainer<FixedJdbcUrlCrateDBContainer> {

public FixedJdbcUrlCrateDBContainer() {
super(CrateDBTestImages.CRATEDB_TEST_IMAGE);
}

@Override
public String getHost() {
return "localhost";
}

@Override
public Integer getMappedPort(int originalPort) {
return 5432;
}
}

static class NoParamsUrlCrateDBContainer extends CrateDBContainer<FixedJdbcUrlCrateDBContainer> {

public NoParamsUrlCrateDBContainer() {
super(CrateDBTestImages.CRATEDB_TEST_IMAGE);
}

@Override
public String getJdbcUrl() {
return "crate://host:port/database";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package org.testcontainers.junit.cratedb;

import org.junit.Test;
import org.testcontainers.CrateDBTestImages;
import org.testcontainers.containers.CrateDBContainer;
import org.testcontainers.db.AbstractContainerDatabaseTest;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.LogManager;

import static org.assertj.core.api.Assertions.assertThat;

public class SimpleCrateDBTest extends AbstractContainerDatabaseTest {
static {
// Postgres JDBC driver uses JUL; disable it to avoid annoying, irrelevant, stderr logs during connection testing
LogManager.getLogManager().getLogger("").setLevel(Level.OFF);
}

@Test
public void testSimple() throws SQLException {
try (CrateDBContainer<?> cratedb = new CrateDBContainer<>(CrateDBTestImages.CRATEDB_TEST_IMAGE)) {
cratedb.start();

ResultSet resultSet = performQuery(cratedb, "SELECT 1");
int resultSetInt = resultSet.getInt(1);
assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1);
assertHasCorrectExposedAndLivenessCheckPorts(cratedb);
}
}

@Test
public void testCommandOverride() throws SQLException {
try (
CrateDBContainer<?> cratedb = new CrateDBContainer<>(CrateDBTestImages.CRATEDB_TEST_IMAGE)
.withCommand("crate -C cluster.name=testcontainers")
) {
cratedb.start();

ResultSet resultSet = performQuery(cratedb, "select name from sys.cluster");
String result = resultSet.getString(1);
assertThat(result).as("cluster name should be overriden").isEqualTo("testcontainers");
}
}

@Test
public void testExplicitInitScript() throws SQLException {
try (
CrateDBContainer<?> cratedb = new CrateDBContainer<>(CrateDBTestImages.CRATEDB_TEST_IMAGE)
.withInitScript("somepath/init_cratedb.sql")
) {
cratedb.start();

ResultSet resultSet = performQuery(cratedb, "SELECT foo FROM bar");

String firstColumnValue = resultSet.getString(1);
assertThat(firstColumnValue).as("Value from init script should equal real value").isEqualTo("hello world");
}
}

private void assertHasCorrectExposedAndLivenessCheckPorts(CrateDBContainer<?> cratedb) {
assertThat(cratedb.getExposedPorts()).containsExactly(CrateDBContainer.CRATEDB_PG_PORT, CrateDBContainer.CRATEDB_HTTP_PORT);
assertThat(cratedb.getLivenessCheckPortNumbers())
.containsExactlyInAnyOrder(
cratedb.getMappedPort(CrateDBContainer.CRATEDB_PG_PORT),
cratedb.getMappedPort(CrateDBContainer.CRATEDB_HTTP_PORT));
}
}
16 changes: 16 additions & 0 deletions modules/cratedb/src/test/resources/logback-test.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>

<logger name="org.testcontainers" level="INFO"/>
</configuration>
6 changes: 6 additions & 0 deletions modules/cratedb/src/test/resources/somepath/init_cratedb.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CREATE TABLE bar (
foo STRING
);

INSERT INTO bar (foo) VALUES ('hello world');
REFRESH TABLE bar;

0 comments on commit 3c3116f

Please sign in to comment.