Skip to content

Commit

Permalink
Enhancements and Fixes around JDBC URL Based Containers
Browse files Browse the repository at this point in the history
(Rebased and Squashed)
  • Loading branch information
rnorth committed Jun 9, 2018
1 parent 3b974e9 commit 758db80
Show file tree
Hide file tree
Showing 10 changed files with 525 additions and 106 deletions.
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
3 changes: 0 additions & 3 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ jobs:
- checkout
- run:
command: ./gradlew check -x testcontainers:check -x selenium:check -x jdbc-test:check
environment:
# Oracle JDBC drivers require a timezone to be set
TZ: "/usr/share/zoneinfo/ETC/UTC"
- run:
name: Save test results
command: |
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} with specified image tag.
* @param tag
* @return Instance of {@link JdbcDatabaseContainer}
*/
public abstract JdbcDatabaseContainer newInstance(String tag);

/**
* 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");
}

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

0 comments on commit 758db80

Please sign in to comment.