Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ buildscript {

plugins {
id 'me.champeau.gradle.japicmp' version '0.2.8'
id "java-test-fixtures"
}

apply plugin: 'com.github.johnrengelman.shadow'
Expand Down Expand Up @@ -167,6 +168,10 @@ dependencies {
testCompile 'org.assertj:assertj-core:3.15.0'
testCompile project(':test-support')

testFixturesCompile ('org.mockito:mockito-core:3.3.3') {
exclude(module: 'hamcrest-core')
}

jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}"
jarFileTestAnnotationProcessor "org.projectlombok:lombok:${lombok.version}"
jarFileTestCompile 'junit:junit:4.12'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import lombok.experimental.UtilityClass;

import java.net.URL;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* Utility class to ensure that licenses have been accepted by the developer.
Expand All @@ -15,7 +17,12 @@ public class LicenseAcceptance {

private static final String ACCEPTANCE_FILE_NAME = "container-license-acceptance.txt";

static final Set<String> ACCEPTED_IMAGE_NAMES = new HashSet<>();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
static final Set<String> ACCEPTED_IMAGE_NAMES = new HashSet<>();
final Set<String> ACCEPTED_IMAGE_NAMES = new HashSet<>();

Since we use @UtilityClass, all static modifiers in the class can be omitted.


public static void assertLicenseAccepted(final String imageName) {
if (ACCEPTED_IMAGE_NAMES.contains(imageName)) {
return;
}
try {
final URL url = Resources.getResource(ACCEPTANCE_FILE_NAME);
final List<String> acceptedLicences = Resources.readLines(url, Charsets.UTF_8);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.testcontainers.utility;

import org.jetbrains.annotations.NotNull;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class MockLicenseAcceptanceRule implements TestRule {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this is not a mock, but more a way to programmatically accept licences, maybe LicenseAcceptanceRule would fit better (just dropping the mock)?

Also renaming MockTestcontainersConfigurationRule to something like TestcontainersConfigurationRolebackRule accordingly would make sense? (stumbled upon this during review and took a bit to understand the idea behind the class)


@NotNull
@Override
public Statement apply(@NotNull Statement base, @NotNull Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
try {
base.evaluate();
} finally {
LicenseAcceptance.ACCEPTED_IMAGE_NAMES.clear();
}
}
};
}

public void acceptLicense(String imageName) {
LicenseAcceptance.ACCEPTED_IMAGE_NAMES.add(imageName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,30 @@
import io.r2dbc.spi.ConnectionFactoryMetadata;
import io.r2dbc.spi.ConnectionFactoryOptions;
import org.mariadb.r2dbc.MariadbConnectionFactoryProvider;
import org.testcontainers.r2dbc.AbstractR2DBCDatabaseContainerProvider;
import org.testcontainers.r2dbc.R2DBCDatabaseContainer;
import org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider;

import javax.annotation.Nullable;

@AutoService(R2DBCDatabaseContainerProvider.class)
public class MariaDBR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider {
public class MariaDBR2DBCDatabaseContainerProvider extends AbstractR2DBCDatabaseContainerProvider {

static final String DRIVER = MariadbConnectionFactoryProvider.MARIADB_DRIVER;

@Override
public boolean supports(ConnectionFactoryOptions options) {
return DRIVER.equals(options.getRequiredValue(ConnectionFactoryOptions.DRIVER));
public MariaDBR2DBCDatabaseContainerProvider() {
super(DRIVER);
}

@Override
public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) {
String image = MariaDBContainer.IMAGE + ":" + options.getRequiredValue(IMAGE_TAG_OPTION);
public R2DBCDatabaseContainer doCreateContainer(ConnectionFactoryOptions options) {
String image = String.format(
"%s:%s",
options.hasOption(IMAGE_OPTION)
? options.getValue(IMAGE_OPTION)
: MariaDBContainer.IMAGE,
options.getRequiredValue(IMAGE_TAG_OPTION)
);
MariaDBContainer<?> container = new MariaDBContainer<>(image)
.withDatabaseName(options.getRequiredValue(ConnectionFactoryOptions.DATABASE));

Expand All @@ -41,6 +47,6 @@ public ConnectionFactoryMetadata getMetadata(ConnectionFactoryOptions options) {
if (!options.hasOption(ConnectionFactoryOptions.PASSWORD)) {
builder.option(ConnectionFactoryOptions.PASSWORD, MariaDBContainer.DEFAULT_PASSWORD);
}
return R2DBCDatabaseContainerProvider.super.getMetadata(builder.build());
return super.getMetadata(builder.build());
}
}
2 changes: 1 addition & 1 deletion modules/mssqlserver/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ dependencies {
testCompile project(':r2dbc')
testCompile 'io.r2dbc:r2dbc-mssql:0.8.1.RELEASE'

// MSSQL's wait strategy requires the JDBC driver
testCompile testFixtures(project(':r2dbc'))
// MSSQL's wait strategy requires the JDBC driver
testCompile 'com.microsoft.sqlserver:mssql-jdbc:6.1.0.jre8'
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,31 @@
import io.r2dbc.mssql.MssqlConnectionFactoryProvider;
import io.r2dbc.spi.ConnectionFactoryMetadata;
import io.r2dbc.spi.ConnectionFactoryOptions;
import lombok.NonNull;
import org.testcontainers.r2dbc.AbstractR2DBCDatabaseContainerProvider;
import org.testcontainers.r2dbc.R2DBCDatabaseContainer;
import org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider;

import javax.annotation.Nullable;

@AutoService(R2DBCDatabaseContainerProvider.class)
public class MSSQLR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider {
public class MSSQLR2DBCDatabaseContainerProvider extends AbstractR2DBCDatabaseContainerProvider {

static final String DRIVER = MssqlConnectionFactoryProvider.MSSQL_DRIVER;

@Override
public boolean supports(ConnectionFactoryOptions options) {
return DRIVER.equals(options.getRequiredValue(ConnectionFactoryOptions.DRIVER));
public MSSQLR2DBCDatabaseContainerProvider() {
super(DRIVER);
}

@Override
public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) {
String image = MSSQLServerContainer.IMAGE + ":" + options.getRequiredValue(IMAGE_TAG_OPTION);
public R2DBCDatabaseContainer doCreateContainer(ConnectionFactoryOptions options) {
String image = String.format(
"%s:%s",
options.hasOption(IMAGE_OPTION)
? options.getValue(IMAGE_OPTION)
: MSSQLServerContainer.IMAGE,
options.getRequiredValue(IMAGE_TAG_OPTION)
);
MSSQLServerContainer<?> container = new MSSQLServerContainer<>(image);

if (Boolean.TRUE.equals(options.getValue(REUSABLE_OPTION))) {
Expand All @@ -40,6 +47,6 @@ public ConnectionFactoryMetadata getMetadata(ConnectionFactoryOptions options) {
if (!options.hasOption(ConnectionFactoryOptions.PASSWORD)) {
builder.option(ConnectionFactoryOptions.PASSWORD, MSSQLServerContainer.DEFAULT_PASSWORD);
}
return R2DBCDatabaseContainerProvider.super.getMetadata(builder.build());
return super.getMetadata(builder.build());
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package org.testcontainers.containers;

import io.r2dbc.spi.ConnectionFactoryOptions;
import org.junit.Rule;
import org.testcontainers.r2dbc.AbstractR2DBCDatabaseContainerTest;
import org.testcontainers.utility.MockLicenseAcceptanceRule;

public class MSSQLR2DBCDatabaseContainerTest extends AbstractR2DBCDatabaseContainerTest<MSSQLServerContainer<?>> {

@Rule
public final MockLicenseAcceptanceRule licenseAcceptanceRule = new MockLicenseAcceptanceRule();

@Override
protected ConnectionFactoryOptions getOptions(MSSQLServerContainer<?> container) {
return MSSQLR2DBCDatabaseContainer.getOptions(container);
Expand All @@ -19,4 +24,9 @@ protected String createR2DBCUrl() {
protected MSSQLServerContainer<?> createContainer() {
return new MSSQLServerContainer<>();
}

@Override
protected void imageTagged(String image) {
licenseAcceptanceRule.acceptLicense(image);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,30 @@
import dev.miku.r2dbc.mysql.MySqlConnectionFactoryProvider;
import io.r2dbc.spi.ConnectionFactoryMetadata;
import io.r2dbc.spi.ConnectionFactoryOptions;
import org.testcontainers.r2dbc.AbstractR2DBCDatabaseContainerProvider;
import org.testcontainers.r2dbc.R2DBCDatabaseContainer;
import org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider;

import javax.annotation.Nullable;

@AutoService(R2DBCDatabaseContainerProvider.class)
public class MySQLR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider {
public class MySQLR2DBCDatabaseContainerProvider extends AbstractR2DBCDatabaseContainerProvider {

static final String DRIVER = MySqlConnectionFactoryProvider.MYSQL_DRIVER;

@Override
public boolean supports(ConnectionFactoryOptions options) {
return DRIVER.equals(options.getRequiredValue(ConnectionFactoryOptions.DRIVER));
public MySQLR2DBCDatabaseContainerProvider() {
super(DRIVER);
}

@Override
public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) {
String image = MySQLContainer.IMAGE + ":" + options.getRequiredValue(IMAGE_TAG_OPTION);
public R2DBCDatabaseContainer doCreateContainer(ConnectionFactoryOptions options) {
String image = String.format(
"%s:%s",
options.hasOption(IMAGE_OPTION)
? options.getValue(IMAGE_OPTION)
: MySQLContainer.IMAGE,
options.getRequiredValue(IMAGE_TAG_OPTION)
);
MySQLContainer<?> container = new MySQLContainer<>(image)
.withDatabaseName(options.getRequiredValue(ConnectionFactoryOptions.DATABASE));

Expand All @@ -41,6 +47,6 @@ public ConnectionFactoryMetadata getMetadata(ConnectionFactoryOptions options) {
if (!options.hasOption(ConnectionFactoryOptions.PASSWORD)) {
builder.option(ConnectionFactoryOptions.PASSWORD, MySQLContainer.DEFAULT_PASSWORD);
}
return R2DBCDatabaseContainerProvider.super.getMetadata(builder.build());
return super.getMetadata(builder.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,31 @@
import io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider;
import io.r2dbc.spi.ConnectionFactoryMetadata;
import io.r2dbc.spi.ConnectionFactoryOptions;
import lombok.NonNull;
import org.testcontainers.r2dbc.AbstractR2DBCDatabaseContainerProvider;
import org.testcontainers.r2dbc.R2DBCDatabaseContainer;
import org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider;

import javax.annotation.Nullable;

@AutoService(R2DBCDatabaseContainerProvider.class)
public final class PostgreSQLR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider {
public final class PostgreSQLR2DBCDatabaseContainerProvider extends AbstractR2DBCDatabaseContainerProvider {

static final String DRIVER = PostgresqlConnectionFactoryProvider.POSTGRESQL_DRIVER;

@Override
public boolean supports(ConnectionFactoryOptions options) {
return DRIVER.equals(options.getRequiredValue(ConnectionFactoryOptions.DRIVER));
public PostgreSQLR2DBCDatabaseContainerProvider() {
super(DRIVER);
}

@Override
public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) {
String image = PostgreSQLContainer.IMAGE + ":" + options.getRequiredValue(IMAGE_TAG_OPTION);
public R2DBCDatabaseContainer doCreateContainer(ConnectionFactoryOptions options) {
String image = String.format(
"%s:%s",
options.hasOption(IMAGE_OPTION)
? options.getValue(IMAGE_OPTION)
: PostgreSQLContainer.IMAGE,
options.getRequiredValue(IMAGE_TAG_OPTION)
);
PostgreSQLContainer<?> container = new PostgreSQLContainer<>(image)
.withDatabaseName(options.getRequiredValue(ConnectionFactoryOptions.DATABASE));

Expand All @@ -41,6 +48,6 @@ public ConnectionFactoryMetadata getMetadata(ConnectionFactoryOptions options) {
if (!options.hasOption(ConnectionFactoryOptions.PASSWORD)) {
builder.option(ConnectionFactoryOptions.PASSWORD, PostgreSQLContainer.DEFAULT_PASSWORD);
}
return R2DBCDatabaseContainerProvider.super.getMetadata(builder.build());
return super.getMetadata(builder.build());
}
}
1 change: 1 addition & 0 deletions modules/r2dbc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies {
testCompile 'io.r2dbc:r2dbc-postgresql:0.8.1.RELEASE'
testCompile project(':postgresql')

testFixturesCompile testFixtures(project(':testcontainers'))
testFixturesCompile 'io.projectreactor:reactor-core:3.3.4.RELEASE'
testFixturesCompile 'org.assertj:assertj-core:3.14.0'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.testcontainers.r2dbc;

import io.r2dbc.spi.ConnectionFactoryOptions;
import lombok.NonNull;
import org.testcontainers.utility.TestcontainersConfiguration;

import java.util.Properties;

public abstract class AbstractR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider {

final String originalDriver;

protected AbstractR2DBCDatabaseContainerProvider(@NonNull String originalDriver) {
this.originalDriver = originalDriver;
}

@Override
public boolean supports(ConnectionFactoryOptions options) {
return originalDriver.equals(getDriver(options));
}

private String getDriver(ConnectionFactoryOptions options) {
Properties properties = TestcontainersConfiguration.getInstance().getProperties();
return properties.getProperty(
driverPropertyName(options.getRequiredValue(ConnectionFactoryOptions.DRIVER)),
originalDriver

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
originalDriver
options.getRequiredValue(ConnectionFactoryOptions.DRIVER)

This will fix failing TestcontainersR2DBCConnectionFactoryTest.failsOnUnknownProvider() .

Fix is already included in #3019

);
}

private String driverPropertyName(String alias) {
return String.format("db.alias.%s.r2dbcDriver", alias);
}

@Override
public final R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) {
String driver = options.getRequiredValue(ConnectionFactoryOptions.DRIVER);

Properties properties = TestcontainersConfiguration.getInstance().getProperties();
if (properties.containsKey(driverPropertyName(driver))) {
String imagePropertyName = String.format("db.alias.%s.image", driver);
String image = properties.getProperty(imagePropertyName);
if (image == null) {
throw new IllegalArgumentException(String.format("Property '%s' is not set", imagePropertyName));
}

options = options.mutate()
.option(ConnectionFactoryOptions.DRIVER, originalDriver)
.option(IMAGE_OPTION, image)
.build();
}

R2DBCDatabaseContainer container = doCreateContainer(options);
return new R2DBCDatabaseContainer() {
@Override
public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) {
options = options.mutate()
.option(ConnectionFactoryOptions.DRIVER, originalDriver)
.build();
return container.configure(options);
}

@Override
public void start() {
container.start();
}

@Override
public void stop() {
container.stop();
}
};
}

protected abstract R2DBCDatabaseContainer doCreateContainer(ConnectionFactoryOptions options);
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ private ConnectionFactoryOptions sanitize(ConnectionFactoryOptions options) {
return builder.build();
}

private ConnectionFactoryOptions removeProxying(ConnectionFactoryOptions options) {
static ConnectionFactoryOptions removeProxying(ConnectionFactoryOptions options) {
// To delegate to the next factory provider, inspect the PROTOCOL and convert it to the next DRIVER and PROTOCOL values.
//
// example:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public interface R2DBCDatabaseContainerProvider {

Option<String> IMAGE_TAG_OPTION = Option.valueOf("TC_IMAGE_TAG");

Option<String> IMAGE_OPTION = Option.valueOf("TC_IMAGE");

boolean supports(ConnectionFactoryOptions options);

R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public Publisher<? extends Connection> create() {
}
}
return future.thenApply(it -> {
return ConnectionFactories.find(
return ConnectionFactories.get(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be replaced by a lambda expression.

it.configure(options)
);
});
Expand Down
Loading