Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancements and Fixes around JDBC URL Based Containers #617

Merged
merged 4 commits into from
Jun 13, 2018
Merged
Show file tree
Hide file tree
Changes from 3 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
11 changes: 6 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ node_modules/
.gradle/
build/

# Eclipse IDE
.settings/
.classpath
.project
bin/
# Eclipse IDE files
**/.project
**/.classpath
**/.settings
**/bin/
**/out/
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ All notable changes to this project will be documented in this file.
## UNRELEASED

### Fixed
- Fixed JDBC URL Regex Pattern to ensure all supported Database URL's are accepted ([\#596](https://github.com/testcontainers/testcontainers-java/issues/596))
- Filtered out TestContainer parameters (TC_*) from query string before passing to database ([\#345](https://github.com/testcontainers/testcontainers-java/issues/345))

### Changed
- Allow `HttpWaitStrategy` to wait for a specific port ([\#703](https://github.com/testcontainers/testcontainers-java/pull/703))
- New module: Apache Pulsar ([\#713](https://github.com/testcontainers/testcontainers-java/pull/713))
- Add support for defining container labels ([\#725](https://github.com/testcontainers/testcontainers-java/pull/725))
- Use `quay.io/testcontainers/ryuk` instead of `bsideup/ryuk` ([\#721](https://github.com/testcontainers/testcontainers-java/pull/721))
- Added Couchbase module ([\#688](https://github.com/testcontainers/testcontainers-java/pull/688))
- Enhancements and Fixes for JDBC URL usage to create Containers ([\#594](https://github.com/testcontainers/testcontainers-java/pull/594))
- Extracted JDBC URL manipulations to a separate class - `ConnectionUrl`.
- Added an overloaded method `JdbcDatabaseContainerProvider.newInstance(ConnectionUrl)`, with default implementation delegating to the existing `newInstance(tag)` method. (Relates to [\#566](https://github.com/testcontainers/testcontainers-java/issues/566))
- Added an implementation of `MySQLContainerProvider.newInstance(ConnectionUrl)` that uses Database Name, User, and Password from JDBC URL while creating new MySQL Container. ([\#566](https://github.com/testcontainers/testcontainers-java/issues/566) for MySQL Container)

## [1.7.3] - 2018-05-16

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,29 @@ public class JDBCDriverTest {
public boolean performTestForCharacterSet;
@Parameter(3)
public boolean performTestForCustomIniFile;
@Parameter(4)
public boolean performTestForJDBCParams;

@Parameterized.Parameters(name = "{index} - {0}")
public static Iterable<Object[]> data() {
return asList(
new Object[][]{
{"jdbc:tc:mysql://hostname/databasename", false, false, false},
{"jdbc:tc:mysql:5.5.43://hostname/databasename?TC_INITSCRIPT=somepath/init_mysql.sql", true, false, false},
{"jdbc:tc:mysql:5.5.43://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false},
{"jdbc:tc:mysql:5.5.43://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false},
{"jdbc:tc:mysql:5.5.43://hostname/databasename", false, false, false},
{"jdbc:tc:mysql:5.5.43://hostname/databasename?useSSL=false", false, false, false},
{"jdbc:tc:postgresql:9.6.8://hostname/databasename", false, false, false},
{"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", false, false, true},
{"jdbc:tc:mariadb://hostname/databasename", false, false, false},
{"jdbc:tc:mariadb:10.2.14://hostname/databasename", false, false, false},
{"jdbc:tc:mariadb:10.2.14://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false},
{"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_mariadb.sql", true, false, false},
{"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false},
{"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", false, false, true}
});
{"jdbc:tc:mysql://hostname/databasename", false, false, false, false},
{"jdbc:tc:mysql://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mysql.sql", true, false, false, true},
{"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false, true},
{"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mysql.sql", true, false, false, true},
{"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false, true},
{"jdbc:tc:mysql:5.5.43://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false, false},
{"jdbc:tc:mysql:5.5.43://hostname/databasename", false, false, false, false},
{"jdbc:tc:mysql:5.5.43://hostname/databasename?useSSL=false", false, false, false, false},
{"jdbc:tc:postgresql:9.6.8://hostname/databasename", false, false, false, false},
{"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", false, false, true, false},
{"jdbc:tc:mariadb://hostname/databasename", false, false, false, false},
{"jdbc:tc:mariadb:10.2.14://hostname/databasename", false, false, false, false},
{"jdbc:tc:mariadb:10.2.14://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false, false},
{"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_mariadb.sql", true, false, false, false},
{"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false, false},
{"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", false, false, true, false}});
}

public static void sampleInitFunction(Connection connection) throws SQLException {
Expand All @@ -77,7 +80,14 @@ public void test() throws SQLException {
performTestForScriptedSchema(jdbcUrl);
}

if (performTestForJDBCParams) {
performTestForJDBCParamUsage(jdbcUrl);
}

if (performTestForCharacterSet) {
//Called twice to ensure that the query string parameters are used when
//connections are created from cached containers.
performSimpleTestWithCharacterSet(jdbcUrl);
performSimpleTestWithCharacterSet(jdbcUrl);
}

Expand Down Expand Up @@ -107,25 +117,57 @@ private void performTestForScriptedSchema(String jdbcUrl) throws SQLException {
assertEquals("A basic SELECT query succeeds where the schema has been applied from a script", "hello world", resultSetString);
return true;
});

assertTrue("The database returned a record as expected", result);

}
}

private void performSimpleTestWithCharacterSet(String jdbcUrl) throws SQLException {
private void performTestForJDBCParamUsage(String jdbcUrl) throws SQLException {
try (HikariDataSource dataSource = getDataSource(jdbcUrl, 1)) {
boolean result = new QueryRunner(dataSource).query("SHOW VARIABLES LIKE 'character\\_set\\_connection'", rs -> {
boolean result = new QueryRunner(dataSource).query("select CURRENT_USER()", rs -> {
rs.next();
String resultUser = rs.getString(1);
assertEquals("User from query param is created.", "someuser@%", resultUser);
return true;
});

result = new QueryRunner(dataSource).query("SELECT DATABASE()", rs -> {
rs.next();
String resultSetInt = rs.getString(2);
assertEquals("Passing query parameters to set DB connection encoding is successful", "utf8", resultSetInt);
String resultDB = rs.getString(1);
assertEquals("Database name from URL String is used.", "databasename", resultDB);
return true;
});

assertTrue("The database returned a record as expected", result);

}
}

/**
* This method intentionally verifies encoding twice to ensure that the query string parameters are used when
* Connections are created from cached containers.
*
* @param jdbcUrl
* @throws SQLException
*/
private void performSimpleTestWithCharacterSet(String jdbcUrl) throws SQLException {
HikariDataSource datasource1 = verifyCharacterSet(jdbcUrl);
HikariDataSource datasource2 = verifyCharacterSet(jdbcUrl);
datasource1.close();
datasource2.close();
}

private HikariDataSource verifyCharacterSet(String jdbcUrl) throws SQLException {
HikariDataSource dataSource = getDataSource(jdbcUrl, 1);
boolean result = new QueryRunner(dataSource).query("SHOW VARIABLES LIKE 'character\\_set\\_connection'", rs -> {
rs.next();
String resultSetInt = rs.getString(2);
assertEquals("Passing query parameters to set DB connection encoding is successful", "utf8", resultSetInt);
return true;
});

assertTrue("The database returned a record as expected", result);
return dataSource;
}

private void performTestForCustomIniFile(final String jdbcUrl) throws SQLException {
assumeFalse(SystemUtils.IS_OS_WINDOWS);
try (HikariDataSource ds = getDataSource(jdbcUrl, 1)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.testcontainers.containers;

import org.testcontainers.jdbc.ConnectionUrl;

import lombok.extern.slf4j.Slf4j;

/**
Expand All @@ -8,14 +10,43 @@
@Slf4j
public abstract class JdbcDatabaseContainerProvider {

/**
* Tests if the specified database type is supported by this Container Provider. It should match to the base image name.
* @param databaseType {@link String}
* @return <code>true</code> when provider can handle this database type, else <code>false</code>.
*/
public abstract boolean supports(String databaseType);

/**
* Instantiate a new {@link JdbcDatabaseContainer} without any specified image tag. Subclasses <i>should</i>
* override this method if possible, to provide a default tag that is more stable than <code>latest</code>`.
*
* @return Instance of {@link JdbcDatabaseContainer}
*/
public JdbcDatabaseContainer newInstance() {
log.warn("No explicit version tag was provided in JDBC URL and this class ({}) does not " +
"override newInstance() to set a default tag. `latest` will be used but results may " +
"be unreliable!", this.getClass().getCanonicalName());
return this.newInstance("latest");
}

/**
* Instantiate a new {@link JdbcDatabaseContainer} with specified image tag.
* @param tag
* @return Instance of {@link JdbcDatabaseContainer}
*/
public abstract JdbcDatabaseContainer newInstance(String tag);

/**
* Instantiate a new {@link JdbcDatabaseContainer} using information provided with {@link ConnectionUrl}.
* @param url {@link ConnectionUrl}
* @return Instance of {@link JdbcDatabaseContainer}
*/
public JdbcDatabaseContainer newInstance(ConnectionUrl url) {
if (url.getImageTag().isPresent()) {
return newInstance(url.getImageTag().get());
} else {
return newInstance();
}
}
}
Loading