-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add tests and support for database usage during test instance constru…
…ction (#50) * Add tests and support for database usage during test instance construction * Extract DatabaseEngine
- Loading branch information
Showing
14 changed files
with
322 additions
and
102 deletions.
There are no files selected for viewing
48 changes: 48 additions & 0 deletions
48
base/src/main/java/io/github/rieske/dbtest/extension/DatabaseEngine.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package io.github.rieske.dbtest.extension; | ||
|
||
import javax.sql.DataSource; | ||
import java.sql.Connection; | ||
import java.sql.SQLException; | ||
import java.util.function.Consumer; | ||
|
||
abstract class DatabaseEngine { | ||
private volatile boolean templateDatabaseMigrated = false; | ||
|
||
void ensureTemplateDatabaseMigrated(Consumer<DataSource> migrator) { | ||
if (!templateDatabaseMigrated) { | ||
synchronized (this) { | ||
if (!templateDatabaseMigrated) { | ||
migrateTemplateDatabase(migrator, dataSourceForDatabase(getTemplateDatabaseName())); | ||
templateDatabaseMigrated = true; | ||
} | ||
} | ||
} | ||
} | ||
|
||
void createDatabase(String databaseName) { | ||
executePrivileged("CREATE DATABASE " + databaseName); | ||
} | ||
|
||
void dropDatabase(String databaseName) { | ||
executePrivileged("DROP DATABASE " + databaseName); | ||
} | ||
|
||
abstract void cloneTemplateDatabaseTo(String targetDatabaseName); | ||
|
||
void executePrivileged(String sql) { | ||
DataSource dataSource = getPrivilegedDataSource(); | ||
try (Connection conn = dataSource.getConnection()) { | ||
conn.createStatement().execute(sql); | ||
} catch (SQLException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
abstract DataSource dataSourceForDatabase(String databaseName); | ||
|
||
abstract String getTemplateDatabaseName(); | ||
|
||
abstract DataSource getPrivilegedDataSource(); | ||
|
||
abstract void migrateTemplateDatabase(Consumer<DataSource> migrator, DataSource templateDataSource); | ||
} |
79 changes: 65 additions & 14 deletions
79
base/src/main/java/io/github/rieske/dbtest/extension/DatabaseState.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1,112 @@ | ||
package io.github.rieske.dbtest.extension; | ||
|
||
import javax.sql.DataSource; | ||
import java.util.Map; | ||
import java.util.UUID; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.function.Consumer; | ||
import java.util.function.BiConsumer; | ||
|
||
interface DatabaseState { | ||
String ensureDatabaseCreated(Class<?> testClass, Consumer<String> databaseCreator); | ||
abstract class DatabaseState { | ||
protected final DatabaseEngine database; | ||
|
||
static String newDatabaseName() { | ||
DatabaseState(DatabaseEngine database) { | ||
this.database = database; | ||
} | ||
|
||
abstract String ensureDatabaseCreated(String databaseName, Class<?> testClass, BiConsumer<DatabaseEngine, String> databaseCreator); | ||
abstract void afterTestMethod(String databaseName); | ||
|
||
private static String newDatabaseName() { | ||
return "testdb_" + UUID.randomUUID().toString().replace('-', '_'); | ||
} | ||
|
||
class PerMethod implements DatabaseState { | ||
DataSource dataSourceForDatabase(String databaseName) { | ||
return database.dataSourceForDatabase(databaseName); | ||
} | ||
|
||
static class PerMethod extends DatabaseState { | ||
PerMethod(DatabaseEngine database) { | ||
super(database); | ||
} | ||
|
||
@Override | ||
public String ensureDatabaseCreated(Class<?> testClass, Consumer<String> databaseCreator) { | ||
String databaseName = newDatabaseName(); | ||
databaseCreator.accept(databaseName); | ||
String ensureDatabaseCreated(String databaseName, Class<?> testClass, BiConsumer<DatabaseEngine, String> databaseCreator) { | ||
if (databaseName != null) { | ||
return databaseName; | ||
} | ||
databaseName = newDatabaseName(); | ||
databaseCreator.accept(database, databaseName); | ||
return databaseName; | ||
} | ||
|
||
@Override | ||
void afterTestMethod(String databaseName) { | ||
if (databaseName != null) { | ||
database.dropDatabase(databaseName); | ||
} | ||
} | ||
} | ||
|
||
class PerClass implements DatabaseState { | ||
static class PerClass extends DatabaseState { | ||
private final Map<Class<?>, String> perClassDatabases = new ConcurrentHashMap<>(); | ||
|
||
PerClass(DatabaseEngine database) { | ||
super(database); | ||
} | ||
|
||
@Override | ||
public String ensureDatabaseCreated(Class<?> testClass, Consumer<String> databaseCreator) { | ||
String ensureDatabaseCreated(String databaseName, Class<?> testClass, BiConsumer<DatabaseEngine, String> databaseCreator) { | ||
if (testClass == null) { | ||
throw new IllegalStateException("Per-class database extension must be registered as a static test class field in order to use the datasource during test instance construction."); | ||
} | ||
if (databaseName != null && databaseName.equals(perClassDatabases.get(testClass))) { | ||
return databaseName; | ||
} | ||
String perClassDatabaseName = perClassDatabases.get(testClass); | ||
if (perClassDatabaseName == null) { | ||
synchronized (perClassDatabases) { | ||
perClassDatabaseName = perClassDatabases.get(testClass); | ||
if (perClassDatabaseName == null) { | ||
perClassDatabaseName = newDatabaseName(); | ||
databaseCreator.accept(perClassDatabaseName); | ||
databaseCreator.accept(database, perClassDatabaseName); | ||
perClassDatabases.put(testClass, perClassDatabaseName); | ||
} | ||
} | ||
} | ||
return perClassDatabaseName; | ||
} | ||
|
||
@Override | ||
void afterTestMethod(String databaseName) { | ||
} | ||
} | ||
|
||
class PerExecution implements DatabaseState { | ||
static class PerExecution extends DatabaseState { | ||
private final String perExecutionDatabaseName = newDatabaseName(); | ||
private volatile boolean perExecutionDatabaseCreated = false; | ||
|
||
PerExecution(DatabaseEngine database) { | ||
super(database); | ||
} | ||
|
||
@Override | ||
public String ensureDatabaseCreated(Class<?> testClass, Consumer<String> databaseCreator) { | ||
String ensureDatabaseCreated(String databaseName, Class<?> testClass, BiConsumer<DatabaseEngine, String> databaseCreator) { | ||
if (perExecutionDatabaseName.equals(databaseName)) { | ||
return databaseName; | ||
} | ||
if (!perExecutionDatabaseCreated) { | ||
synchronized (perExecutionDatabaseName) { | ||
if (!perExecutionDatabaseCreated) { | ||
databaseCreator.accept(perExecutionDatabaseName); | ||
databaseCreator.accept(database, perExecutionDatabaseName); | ||
perExecutionDatabaseCreated = true; | ||
} | ||
} | ||
} | ||
return perExecutionDatabaseName; | ||
} | ||
|
||
@Override | ||
void afterTestMethod(String databaseName) { | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 19 additions & 45 deletions
64
base/src/main/java/io/github/rieske/dbtest/extension/TestDatabase.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,26 @@ | ||
package io.github.rieske.dbtest.extension; | ||
|
||
import javax.sql.DataSource; | ||
import java.sql.Connection; | ||
import java.sql.SQLException; | ||
import java.util.function.Consumer; | ||
|
||
abstract class TestDatabase { | ||
final DatabaseState perMethod = new DatabaseState.PerMethod(); | ||
final DatabaseState perClass = new DatabaseState.PerClass(); | ||
final DatabaseState perExecution = new DatabaseState.PerExecution(); | ||
|
||
private volatile boolean templateDatabaseMigrated = false; | ||
|
||
void ensureTemplateDatabaseMigrated(Consumer<DataSource> migrator) { | ||
if (!templateDatabaseMigrated) { | ||
synchronized (this) { | ||
if (!templateDatabaseMigrated) { | ||
migrateTemplateDatabase(migrator, dataSourceForDatabase(getTemplateDatabaseName())); | ||
templateDatabaseMigrated = true; | ||
} | ||
} | ||
} | ||
class TestDatabase { | ||
private final DatabaseState perMethod; | ||
private final DatabaseState perClass; | ||
private final DatabaseState perExecution; | ||
|
||
TestDatabase(DatabaseEngine databaseEngine) { | ||
this.perMethod = new DatabaseState.PerMethod(databaseEngine); | ||
this.perClass = new DatabaseState.PerClass(databaseEngine); | ||
this.perExecution = new DatabaseState.PerExecution(databaseEngine); | ||
} | ||
|
||
void createDatabase(String databaseName) { | ||
executePrivileged("CREATE DATABASE " + databaseName); | ||
} | ||
|
||
void dropDatabase(String databaseName) { | ||
executePrivileged("DROP DATABASE " + databaseName); | ||
} | ||
|
||
abstract void cloneTemplateDatabaseTo(String targetDatabaseName); | ||
|
||
void executePrivileged(String sql) { | ||
DataSource dataSource = getPrivilegedDataSource(); | ||
try (Connection conn = dataSource.getConnection()) { | ||
conn.createStatement().execute(sql); | ||
} catch (SQLException e) { | ||
throw new RuntimeException(e); | ||
DatabaseState getState(DatabaseTestExtension.Mode mode) { | ||
switch (mode) { | ||
case DATABASE_PER_TEST_METHOD: | ||
return perMethod; | ||
case DATABASE_PER_TEST_CLASS: | ||
return perClass; | ||
case DATABASE_PER_EXECUTION: | ||
return perExecution; | ||
default: | ||
throw new IllegalStateException("No database state strategy exists for " + this + " mode"); | ||
} | ||
} | ||
|
||
abstract DataSource dataSourceForDatabase(String databaseName); | ||
|
||
abstract String getTemplateDatabaseName(); | ||
|
||
abstract DataSource getPrivilegedDataSource(); | ||
|
||
abstract void migrateTemplateDatabase(Consumer<DataSource> migrator, DataSource templateDataSource); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.