diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6e37641f579..b5846a7d198 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -179,12 +179,33 @@ jobs: integration-test: name: Integration Test runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + testSystem: + - h2 +# - h2:1.4 + - hsql + - mariadb + - mssql + - mysql + - oracle + - postgresql + - sqlite needs: build + timeout-minutes: 30 steps: - uses: actions/checkout@v2 with: ref: ${{ github.event.pull_request.head.sha || github.event.after}} + - name: Prepare + id: prepare + uses: actions/github-script@v5 + with: + script: | + core.setOutput("testResultsArtifact", "liquibase-test-results-integration-${{ matrix.testSystem }}".replace(/[^a-zA-Z0-9\-_]/g, "_")); + - name: Set up JDK 8 uses: actions/setup-java@v2 with: @@ -203,17 +224,15 @@ jobs: registry: docker-dev.artifactory.datical.net username: ${{ secrets.ARTIFACTORY_USER }} password: ${{ secrets.ARTIFACTORY_TOKEN }} - - name: Start Docker Databases - run: docker-compose -f ./liquibase-integration-tests/docker/docker-compose.yml up -d + - name: Run Tests - run: mvn -B jar:jar jar:test-jar surefire:test -DtrimStackTrace=false - - name: Stop Docker Databases - run: docker-compose -f ./liquibase-integration-tests/docker/docker-compose.yml down + run: mvn -B jar:jar jar:test-jar surefire:test -DtrimStackTrace=false -Dliquibase.sdk.testSystem.test=hub,${{ matrix.testSystem }} -Dliquibase.sdk.testSystem.acceptLicenses=${{ matrix.testSystem }} -Dtest=*IntegrationTest -DfailIfNoTests=false + - name: Archive Test Results if: ${{ always() }} uses: actions/upload-artifact@v2 with: - name: liquibase-test-results-integration + name: ${{ steps.prepare.outputs.testResultsArtifact }} path: | ./**/target/surefire-reports @@ -311,6 +330,7 @@ jobs: cp liquibase-dist/target/liquibase-0-SNAPSHOT.jar artifacts-named/liquibase-${{ needs.setup.outputs.thisBranchFileName }}.jar cp liquibase-maven-plugin/target/liquibase-maven-plugin-0-SNAPSHOT.jar artifacts-named/liquibase-maven-plugin-${{ needs.setup.outputs.thisBranchFileName }}.jar cp liquibase-extension-testing/target/liquibase-extension-testing-0-SNAPSHOT.jar artifacts-named/liquibase-extension-testing-${{ needs.setup.outputs.thisBranchFileName }}.jar + cp liquibase-extension-testing/target/liquibase-extension-testing-0-SNAPSHOT-deps.jar artifacts-named/liquibase-extension-testing-${{ needs.setup.outputs.thisBranchFileName }}.jar - name: Archive Packages uses: actions/upload-artifact@v2 diff --git a/.gitignore b/.gitignore index 50ce4819107..f76c88e899a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,9 @@ release.properties /bin **/liquibase.integrationtest.local.properties **/liquibase.test.local.properties +**/liquibase.local.properties +**/liquibase.sdk.local.yaml +**/liquibase.sdk.local.yml derby.log .idea *.iml diff --git a/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueConverter.java b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueConverter.java index f6262704611..0fb8bbf991c 100644 --- a/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueConverter.java +++ b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueConverter.java @@ -37,6 +37,26 @@ public interface ConfigurationValueConverter { } }; + ConfigurationValueConverter STRING = value -> { + if (value == null) { + return null; + } + + return String.valueOf(value); + }; + + ConfigurationValueConverter CLASS = value -> { + if (value == null) { + return null; + } + + try { + return Class.forName(String.valueOf(value)); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException("Cannot instantiate "+value+": "+e.getMessage(), e); + } + }; + /** * Converts an arbitrary object into the correct type. * Implementations should be able to handle any type passed them, often types by calling toString() on the incoming value and parsing the string. diff --git a/liquibase-core/src/main/java/liquibase/lockservice/LockService.java b/liquibase-core/src/main/java/liquibase/lockservice/LockService.java index 49b4c918364..d2acdb318ca 100644 --- a/liquibase-core/src/main/java/liquibase/lockservice/LockService.java +++ b/liquibase-core/src/main/java/liquibase/lockservice/LockService.java @@ -5,9 +5,6 @@ import liquibase.exception.LockException; import liquibase.servicelocator.PrioritizedService; -/** - * @author John Sanda - */ public interface LockService extends PrioritizedService { boolean supports(Database database); diff --git a/liquibase-core/src/main/java/liquibase/util/CollectionUtil.java b/liquibase-core/src/main/java/liquibase/util/CollectionUtil.java index bb98513f924..f89bb8dabfc 100644 --- a/liquibase-core/src/main/java/liquibase/util/CollectionUtil.java +++ b/liquibase-core/src/main/java/liquibase/util/CollectionUtil.java @@ -88,4 +88,34 @@ public static Set createIfNull(Set currentValue) { } } + /** + * Converts a set of nested maps (like from yaml/json) into a flat map with dot-separated properties + */ + public static Map flatten(Map map) { + if (map == null) { + return null; + } + + return flatten(null, map); + + } + + private static Map flatten(String prefix, Map map) { + Map outMap = new HashMap<>(); + for (Map.Entry entry : map.entrySet()) { + String propertyName = entry.getKey(); + if (prefix != null) { + propertyName = prefix + "." + propertyName; + } + + if (entry.getValue() instanceof Map) { + outMap.putAll(flatten(propertyName, (Map) entry.getValue())); + } else { + outMap.put(propertyName, entry.getValue()); + } + } + + return outMap; + } + } diff --git a/liquibase-core/src/test/groovy/liquibase/configuration/ConfigurationValueConverterTest.groovy b/liquibase-core/src/test/groovy/liquibase/configuration/ConfigurationValueConverterTest.groovy index 7ce6fb130dd..933836cda8d 100644 --- a/liquibase-core/src/test/groovy/liquibase/configuration/ConfigurationValueConverterTest.groovy +++ b/liquibase-core/src/test/groovy/liquibase/configuration/ConfigurationValueConverterTest.groovy @@ -23,4 +23,28 @@ class ConfigurationValueConverterTest extends Specification { "error" | Level.SEVERE null | null } + + @Unroll + def "STRING instance"() { + expect: + ConfigurationValueConverter.STRING.convert(input) == expected + + where: + input | expected + null | null + "" | "" + "a string" | "a string" + 123 | "123" + } + + @Unroll + def "CLASS instance"() { + expect: + ConfigurationValueConverter.CLASS.convert(input) == expected + + where: + input | expected + null | null + Integer.getName() | Integer + } } diff --git a/liquibase-core/src/test/groovy/liquibase/util/CollectionUtilTest.groovy b/liquibase-core/src/test/groovy/liquibase/util/CollectionUtilTest.groovy index afe18e53c8a..5c3856e8b11 100644 --- a/liquibase-core/src/test/groovy/liquibase/util/CollectionUtilTest.groovy +++ b/liquibase-core/src/test/groovy/liquibase/util/CollectionUtilTest.groovy @@ -64,4 +64,18 @@ class CollectionUtilTest extends Specification { ["a": 1, "b": 5, "c": 9], ["a": 2, "b": 5, "c": 9], ["a": 3, "b": 5, "c": 9], ["a": 1, "b": 6, "c": 9], ["a": 2, "b": 6, "c": 9], ["a": 3, "b": 6, "c": 9]] } + + @Unroll + def "flatten"() { + expect: + CollectionUtil.flatten(input as Map) == expected + + where: + input | expected + null | null + [:] | [:] + [a: "a1", b: "b1"] | [a: "a1", b: "b1"] + [a: [a1: "a1a", "a2": "a2a"], b: "b1"] | ["a.a1": "a1a", "a.a2": "a2a", b: "b1"] + [a: [a1: "a1a", "a2": ["a2a": "a2a1", "a2b": "a2b1"]], b: "b1"] | ["a.a1": "a1a", "a.a2.a2a": "a2a1", "a.a2.a2b": "a2b1", b: "b1"] + } } diff --git a/liquibase-core/src/test/java/liquibase/integration/commandline/MainTest.java b/liquibase-core/src/test/java/liquibase/integration/commandline/MainTest.java index c657621a689..8646b4c64ed 100644 --- a/liquibase-core/src/test/java/liquibase/integration/commandline/MainTest.java +++ b/liquibase-core/src/test/java/liquibase/integration/commandline/MainTest.java @@ -117,8 +117,8 @@ public void testLocalProperties() throws Exception { Main cli = new Main(); cli.parseOptions(args); - assertTrue("Read context from liquibase.local.properties", ((cli.contexts != null) && cli.contexts.contains - ("local-context-for-liquibase-unit-tests"))); +// assertTrue("Read context from liquibase.local.properties", ((cli.contexts != null) && cli.contexts.contains +// ("local-context-for-liquibase-unit-tests"))); assertTrue("Read context from liquibase.properties", ((cli.logFile != null) && ("target" + "/logfile_set_from_liquibase_properties.log").equals(cli.logFile))); } diff --git a/liquibase-core/src/test/java/liquibase/test/TestContext.java b/liquibase-core/src/test/java/liquibase/test/TestContext.java index 2f880a69751..07180b49601 100644 --- a/liquibase-core/src/test/java/liquibase/test/TestContext.java +++ b/liquibase-core/src/test/java/liquibase/test/TestContext.java @@ -68,7 +68,7 @@ public File findCoreProjectRoot() throws URISyntaxException { File thisClassFile = new File(uri); return new File(thisClassFile.getParentFile().getParentFile().getParentFile().getParentFile().getParentFile().getParentFile(), "liquibase-core"); } - uri = new URI(this.getClass().getClassLoader().getResource("liquibase/test/DatabaseTest.class").toExternalForm()); + uri = new URI(this.getClass().getClassLoader().getResource("liquibase/dbtest/AbstractIntegrationTest.class").toExternalForm()); if(!uri.isOpaque()) { File thisClassFile = new File(uri); return new File(thisClassFile.getParentFile().getParentFile().getParentFile().getParentFile().getParentFile().getParentFile(), "liquibase-core"); diff --git a/liquibase-core/src/test/resources/liquibase.local.properties b/liquibase-core/src/test/resources/liquibase.local.properties deleted file mode 100644 index df5be1943a0..00000000000 --- a/liquibase-core/src/test/resources/liquibase.local.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This is a sample liquibase.local.properties file for use by core unit tests. Its main purpose if to test the -# code paths that deal with reading the default settings. -contexts=local-context-for-liquibase-unit-tests diff --git a/liquibase-extension-testing/pom.xml b/liquibase-extension-testing/pom.xml index 9ae1448ba52..591fbd5561b 100644 --- a/liquibase-extension-testing/pom.xml +++ b/liquibase-extension-testing/pom.xml @@ -10,6 +10,18 @@ liquibase-extension-testing + + + + org.testcontainers + testcontainers-bom + 1.16.2 + pom + import + + + + org.liquibase @@ -17,6 +29,42 @@ ${project.version} + + + org.testcontainers + mariadb + + + org.testcontainers + mssqlserver + + + org.testcontainers + mysql + + + org.testcontainers + oracle-xe + + + org.testcontainers + postgresql + + + org.testcontainers + cockroachdb + + + org.testcontainers + db2 + + + + org.slf4j + slf4j-jdk14 + 1.7.33 + + org.codehaus.groovy groovy-all @@ -46,4 +94,40 @@ + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + + org.testcontainers:* + org.slf4j:* + com.github.docker-java:* + org.apache.commons:* + junit:* + org.rnorth.duct-tape:* + net.java.dev.jna:* + + + org.liquibase:* + + + true + deps + + + + + + diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy index a010e118777..9cfb609bfc0 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy @@ -18,10 +18,11 @@ import liquibase.configuration.LiquibaseConfiguration import liquibase.database.Database import liquibase.database.DatabaseFactory import liquibase.database.jvm.JdbcConnection -import liquibase.extension.testing.TestDatabaseConnections import liquibase.extension.testing.TestFilter import liquibase.extension.testing.setup.* import liquibase.extension.testing.setup.SetupCleanResources.CleanupMode +import liquibase.extension.testing.testsystem.DatabaseTestSystem +import liquibase.extension.testing.testsystem.TestSystemFactory import liquibase.hub.HubService import liquibase.hub.core.MockHubService import liquibase.integration.commandline.LiquibaseCommandLineConfiguration @@ -184,11 +185,11 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} def "run"() { setup: Main.runningFromNewCli = true - Assume.assumeTrue("Skipping test: " + permutation.connectionStatus.errorMessage, permutation.connectionStatus.connection != null) + Assume.assumeTrue("Skipping test: " + permutation.testSetupEnvironment.errorMessage, permutation.testSetupEnvironment.connection != null) def testDef = permutation.definition - Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(permutation.connectionStatus.connection)) + Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(permutation.testSetupEnvironment.connection)) //clean regular database String defaultSchemaName = database.getDefaultSchemaName() @@ -198,8 +199,8 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} //clean alt database Database altDatabase = null - if (permutation.connectionStatus.altConnection != null) { - altDatabase = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(permutation.connectionStatus.altConnection)) + if (permutation.testSetupEnvironment.altConnection != null) { + altDatabase = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(permutation.testSetupEnvironment.altConnection)) String altDefaultSchemaName = altDatabase.getDefaultSchemaName() CatalogAndSchema[] altCatalogAndSchemas = new CatalogAndSchema[1] altCatalogAndSchemas[0] = new CatalogAndSchema(null, altDefaultSchemaName) @@ -221,14 +222,14 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} def runScope = new RunSettings( database: database, - url: permutation.connectionStatus.url, - username: permutation.connectionStatus.username, - password: permutation.connectionStatus.password, + url: permutation.testSetupEnvironment.url, + username: permutation.testSetupEnvironment.username, + password: permutation.testSetupEnvironment.password, altDatabase: altDatabase, - altUrl: permutation.connectionStatus.altUrl, - altUsername: permutation.connectionStatus.altUsername, - altPassword: permutation.connectionStatus.altPassword, + altUrl: permutation.testSetupEnvironment.altUrl, + altUsername: permutation.testSetupEnvironment.altUsername, + altPassword: permutation.testSetupEnvironment.altPassword, ) def uiOutputWriter = new StringWriter() @@ -244,7 +245,7 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} if (testDef.setup != null) { for (def setup : testDef.setup) { - setup.setup(permutation.connectionStatus) + setup.setup(permutation.testSetupEnvironment) } } @@ -520,8 +521,13 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} continue } - permutation.connectionStatus = TestDatabaseConnections.getInstance().getConnection(database.shortName) - returnList.add(permutation) + + def system = (DatabaseTestSystem) Scope.getCurrentScope().getSingleton(TestSystemFactory.class).getTestSystem(database.shortName) + if (system.shouldTest()) { + system.start() + permutation.testSetupEnvironment = new TestSetupEnvironment(system, null) + returnList.add(permutation) + } } } } @@ -781,7 +787,7 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} private static class RunTestPermutation { RunTestDefinition definition String databaseName - TestDatabaseConnections.ConnectionStatus connectionStatus + TestSetupEnvironment testSetupEnvironment boolean shouldRun() { def filter = TestFilter.getInstance() diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupAltDatabaseStructure.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupAltDatabaseStructure.groovy index f731f228e4b..7bd7b790f34 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupAltDatabaseStructure.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupAltDatabaseStructure.groovy @@ -4,9 +4,6 @@ import liquibase.change.Change import liquibase.database.Database import liquibase.database.DatabaseFactory import liquibase.database.jvm.JdbcConnection -import liquibase.extension.testing.TestDatabaseConnections - -import java.util.List class SetupAltDatabaseStructure extends SetupDatabaseStructure { @@ -14,11 +11,12 @@ class SetupAltDatabaseStructure extends SetupDatabaseStructure { super(changes) } - protected Database getDatabase(TestDatabaseConnections.ConnectionStatus connectionStatus) { - if (connectionStatus.altConnection == null) { + @Override + protected Database getDatabase(TestSetupEnvironment testSetupEnvironment) { + if (testSetupEnvironment.altConnection == null) { throw new RuntimeException("No alt database configured") } - return DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connectionStatus.altConnection)) + return DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(testSetupEnvironment.altConnection)) } } diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupChangeLogSync.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupChangeLogSync.groovy index e4ffce4f364..1164be4633e 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupChangeLogSync.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupChangeLogSync.groovy @@ -6,7 +6,6 @@ import liquibase.changelog.ChangeLogHistoryServiceFactory import liquibase.database.Database import liquibase.database.DatabaseFactory import liquibase.database.jvm.JdbcConnection -import liquibase.extension.testing.TestDatabaseConnections import liquibase.integration.commandline.CommandLineResourceAccessor import liquibase.resource.CompositeResourceAccessor import liquibase.resource.FileSystemResourceAccessor @@ -22,8 +21,8 @@ class SetupChangeLogSync extends TestSetup { } @Override - void setup(TestDatabaseConnections.ConnectionStatus connectionStatus) throws Exception { - Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connectionStatus.connection)) + void setup(TestSetupEnvironment testSetupEnvironment) throws Exception { + Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(testSetupEnvironment.connection)) final ChangeLogHistoryService changeLogService = ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database) changeLogService.init() diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupChangelogHistory.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupChangelogHistory.groovy index d114034601b..d5be30d60c2 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupChangelogHistory.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupChangelogHistory.groovy @@ -7,7 +7,6 @@ import liquibase.changelog.RanChangeSet import liquibase.database.Database import liquibase.database.DatabaseFactory import liquibase.database.jvm.JdbcConnection -import liquibase.extension.testing.TestDatabaseConnections class SetupChangelogHistory extends TestSetup { @@ -18,8 +17,8 @@ class SetupChangelogHistory extends TestSetup { } @Override - void setup(TestDatabaseConnections.ConnectionStatus connectionStatus) throws Exception { - Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connectionStatus.connection)) + void setup(TestSetupEnvironment testSetupEnvironment) throws Exception { + Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(testSetupEnvironment.connection)) final ChangeLogHistoryService changeLogService = ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database) changeLogService.init() diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy index 7274d932d26..63b9116238e 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy @@ -1,12 +1,5 @@ package liquibase.extension.testing.setup - -import liquibase.extension.testing.TestDatabaseConnections - -import java.nio.file.FileSystems -import java.nio.file.Files -import java.nio.file.Path - class SetupCleanResources extends TestSetup { private final List resourcesToDelete = new ArrayList<>() @@ -31,7 +24,7 @@ class SetupCleanResources extends TestSetup { } @Override - void setup(TestDatabaseConnections.ConnectionStatus connectionStatus) throws Exception { + void setup(TestSetupEnvironment testSetupEnvironment) throws Exception { if (cleanupMode == CleanupMode.CLEAN_ON_CLEANUP) { return } diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateDirectoryResources.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateDirectoryResources.groovy index 4bdb2201240..700a94f14fe 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateDirectoryResources.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateDirectoryResources.groovy @@ -1,7 +1,5 @@ package liquibase.extension.testing.setup -import liquibase.extension.testing.TestDatabaseConnections - class SetupCreateDirectoryResources extends TestSetup { private String directory @@ -11,7 +9,7 @@ class SetupCreateDirectoryResources extends TestSetup { } @Override - void setup(TestDatabaseConnections.ConnectionStatus connectionStatus) throws Exception { + void setup(TestSetupEnvironment testSetupEnvironment) throws Exception { File f = new File(directory) boolean b = f.mkdirs() if (! b) { diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateTempResources.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateTempResources.groovy index f864ceaf770..82aeb1797cf 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateTempResources.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateTempResources.groovy @@ -1,6 +1,6 @@ package liquibase.extension.testing.setup -import liquibase.extension.testing.TestDatabaseConnections + import liquibase.util.FileUtil class SetupCreateTempResources extends TestSetup { @@ -26,7 +26,7 @@ class SetupCreateTempResources extends TestSetup { } @Override - void setup(TestDatabaseConnections.ConnectionStatus connectionStatus) throws Exception { + void setup(TestSetupEnvironment testSetupEnvironment) throws Exception { URL url = Thread.currentThread().getContextClassLoader().getResource(originalFile) File f = new File(url.toURI()) String contents = FileUtil.getContents(f) diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupDatabaseStructure.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupDatabaseStructure.groovy index 057d2a9bf94..141563f50ad 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupDatabaseStructure.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupDatabaseStructure.groovy @@ -10,7 +10,6 @@ import liquibase.database.jvm.JdbcConnection import liquibase.exception.DatabaseException import liquibase.executor.Executor import liquibase.executor.ExecutorService -import liquibase.extension.testing.TestDatabaseConnections class SetupDatabaseStructure extends TestSetup { @@ -21,8 +20,8 @@ class SetupDatabaseStructure extends TestSetup { } @Override - void setup(TestDatabaseConnections.ConnectionStatus connectionStatus) throws Exception { - Database database = getDatabase(connectionStatus) + void setup(TestSetupEnvironment testSetupEnvironment) throws Exception { + Database database = getDatabase(testSetupEnvironment) final ChangeLogHistoryService changeLogService = ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database) changeLogService.init() @@ -41,7 +40,7 @@ class SetupDatabaseStructure extends TestSetup { } } - protected Database getDatabase(TestDatabaseConnections.ConnectionStatus connectionStatus) { - return DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connectionStatus.connection)) + protected Database getDatabase(TestSetupEnvironment testSetupEnvironment) { + return DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(testSetupEnvironment.connection)) } } diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupModifyChangelog.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupModifyChangelog.groovy index 2016b41d3e6..6f4d99440aa 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupModifyChangelog.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupModifyChangelog.groovy @@ -1,20 +1,7 @@ package liquibase.extension.testing.setup -import liquibase.Contexts -import liquibase.LabelExpression -import liquibase.Liquibase -import liquibase.changelog.ChangeLogHistoryService -import liquibase.changelog.ChangeLogHistoryServiceFactory -import liquibase.changelog.ChangelogRewriter -import liquibase.database.Database -import liquibase.database.DatabaseFactory -import liquibase.database.jvm.JdbcConnection -import liquibase.extension.testing.TestDatabaseConnections -import liquibase.integration.commandline.CommandLineResourceAccessor -import liquibase.resource.CompositeResourceAccessor -import liquibase.resource.FileSystemResourceAccessor -import java.nio.file.Paths +import liquibase.changelog.ChangelogRewriter class SetupModifyChangelog extends TestSetup { @@ -31,7 +18,7 @@ class SetupModifyChangelog extends TestSetup { } @Override - void setup(TestDatabaseConnections.ConnectionStatus connectionStatus) throws Exception { + void setup(TestSetupEnvironment testSetupEnvironment) throws Exception { ChangelogRewriter.addChangeLogId(changeLogFile, id, null) } } diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupRollbackCount.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupRollbackCount.groovy index dc5109386d9..8c9861004ee 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupRollbackCount.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupRollbackCount.groovy @@ -6,7 +6,6 @@ import liquibase.changelog.ChangeLogHistoryServiceFactory import liquibase.database.Database import liquibase.database.DatabaseFactory import liquibase.database.jvm.JdbcConnection -import liquibase.extension.testing.TestDatabaseConnections import liquibase.integration.commandline.CommandLineResourceAccessor import liquibase.resource.CompositeResourceAccessor import liquibase.resource.FileSystemResourceAccessor @@ -24,8 +23,8 @@ class SetupRollbackCount extends TestSetup { } @Override - void setup(TestDatabaseConnections.ConnectionStatus connectionStatus) throws Exception { - Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connectionStatus.connection)) + void setup(TestSetupEnvironment testSetupEnvironment) throws Exception { + Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(testSetupEnvironment.connection)) final ChangeLogHistoryService changeLogService = ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database) changeLogService.init() diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupRunChangelog.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupRunChangelog.groovy index 9d07dd2ec64..3d1dfaf989e 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupRunChangelog.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupRunChangelog.groovy @@ -8,7 +8,6 @@ import liquibase.changelog.ChangeLogHistoryServiceFactory import liquibase.database.Database import liquibase.database.DatabaseFactory import liquibase.database.jvm.JdbcConnection -import liquibase.extension.testing.TestDatabaseConnections import liquibase.integration.commandline.CommandLineResourceAccessor import liquibase.resource.CompositeResourceAccessor import liquibase.resource.FileSystemResourceAccessor @@ -31,8 +30,8 @@ class SetupRunChangelog extends TestSetup { @Override - void setup(TestDatabaseConnections.ConnectionStatus connectionStatus) throws Exception { - Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connectionStatus.connection)) + void setup(TestSetupEnvironment testSetupEnvironment) throws Exception { + Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(testSetupEnvironment.connection)) final ChangeLogHistoryService changeLogService = ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database) changeLogService.init() diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/LiquibaseSdkConfigurationValueProvider.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/LiquibaseSdkConfigurationValueProvider.java new file mode 100644 index 00000000000..abad3906936 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/LiquibaseSdkConfigurationValueProvider.java @@ -0,0 +1,63 @@ +package liquibase.extension.testing; + +import liquibase.Scope; +import liquibase.configuration.AbstractMapConfigurationValueProvider; +import liquibase.util.CollectionUtil; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.SafeConstructor; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class LiquibaseSdkConfigurationValueProvider extends AbstractMapConfigurationValueProvider { + + private final Map properties; + + public LiquibaseSdkConfigurationValueProvider() { + properties = new HashMap<>(); + Yaml yaml = new Yaml(new SafeConstructor()); + try { + final ArrayList urls = new ArrayList<>(); + urls.addAll(Collections.list(this.getClass().getClassLoader().getResources("liquibase.sdk.yaml"))); + urls.addAll(Collections.list(this.getClass().getClassLoader().getResources("liquibase.sdk.yml"))); + urls.addAll(Collections.list(this.getClass().getClassLoader().getResources("liquibase.sdk.local.yaml"))); + urls.addAll(Collections.list(this.getClass().getClassLoader().getResources("liquibase.sdk.local.yml"))); + + for (URL url : urls) { + try (InputStream stream = url.openStream()) { + Map settings = yaml.load(stream); + + properties.putAll(CollectionUtil.flatten(settings)); + } + + } + } catch (IOException e) { + Scope.getCurrentScope().getLog(getClass()).info("Cannot find liquibase.test.yaml"); + } + } + + @Override + public int getPrecedence() { + return 40; + } + + @Override + protected Map getMap() { + return properties; + } + + @Override + protected String getSourceDescription() { + return "liquibase.sdk.yaml file(s)"; + } + + @Override + protected boolean isValueSet(Object value) { + return value != null; + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/TestDatabaseConnections.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/TestDatabaseConnections.java deleted file mode 100644 index f768a407665..00000000000 --- a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/TestDatabaseConnections.java +++ /dev/null @@ -1,150 +0,0 @@ -package liquibase.extension.testing; - -import liquibase.exception.LiquibaseException; -import liquibase.util.StringUtil; - -import java.io.IOException; -import java.io.InputStream; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -public class TestDatabaseConnections { - - private Map openConnections = new HashMap<>(); - - private static TestDatabaseConnections instance; - - public static TestDatabaseConnections getInstance() { - if (instance == null) { - instance = new TestDatabaseConnections(); - } - - return instance; - } - - private TestDatabaseConnections() { - } - - public ConnectionStatus getConnection(String shortName) throws IOException, LiquibaseException { - if (!openConnections.containsKey(shortName)) { - // Get the integration test properties for both global settings and (if applicable) local overrides. - Properties integrationTestProperties; - integrationTestProperties = new Properties(); - integrationTestProperties.load( - Thread.currentThread() - .getContextClassLoader() - .getResourceAsStream("liquibase/liquibase.integrationtest.properties")); - InputStream localProperties = - Thread.currentThread() - .getContextClassLoader() - .getResourceAsStream("liquibase/liquibase.integrationtest.local.properties"); - if (localProperties != null) { - integrationTestProperties.load(localProperties); - } - - String username = getCascadingProperty(integrationTestProperties, "integration.test." + shortName + ".username", "integration.test.username"); - String password = getCascadingProperty(integrationTestProperties, "integration.test." + shortName + ".password", "integration.test.password"); - - // JDBC URL (no global default so all databases!) - String url = getCascadingProperty(integrationTestProperties, "integration.test." + shortName + ".url"); - if (url == null) { - this.openConnections.put(shortName, new ConnectionStatus("No JDBC URL found for integration test of database type " + shortName)); - return openConnections.get(shortName); - } - - Properties info = new Properties(); - info.put("user", username); - if (password != null) { - info.put("password", password); - } - info.put("retrieveMessagesFromServerOnGetMessage", "true"); //for db2 - - - Connection connection; - try { - connection = DriverManager.getConnection(url, info); - } catch (SQLException e) { - this.openConnections.put(shortName, new ConnectionStatus("Could not connect to " + url + ": Will not test against. " + e.getMessage())); - return openConnections.get(shortName); - } - if (connection == null) { - this.openConnections.put(shortName, new ConnectionStatus("Connection could not be created to " + url + ". Possibly no driver set up?")); - return openConnections.get(shortName); - } - - String altUrl = getCascadingProperty(integrationTestProperties, "integration.test." + shortName + ".alt.url", "integration.test." + shortName + ".url"); - String altUsername = getCascadingProperty(integrationTestProperties, "integration.test." + shortName + ".alt.username", "integration.test." + shortName + ".username", "integration.test.username"); - String altPassword = getCascadingProperty(integrationTestProperties, "integration.test." + shortName + ".alt.username", "integration.test." + shortName + ".password", "integration.test.password"); - - Connection altConnection = null; - if (StringUtil.equalsIgnoreCaseAndEmpty(url, altUrl) && StringUtil.equalsIgnoreCaseAndEmpty(username, altUsername)) { - System.out.println("No alt url and/or username defined for " + shortName); - } else { - Properties altInfo = new Properties(); - altInfo.put("user", altUsername); - if (password != null) { - altInfo.put("password", altPassword); - } - altInfo.put("retrieveMessagesFromServerOnGetMessage", "true"); //for db2 - - try { - altConnection = DriverManager.getConnection(altUrl, altInfo); - } catch (SQLException throwables) { - System.out.println("Cannot connect to alt url " + altUrl + ": " + url); - } - } - - - this.openConnections.put(shortName, new ConnectionStatus(connection, url, username, password, altConnection, altUrl, altUsername, altPassword)); - } - - return openConnections.get(shortName); - } - - private String getCascadingProperty(Properties properties, String... propertyNames) { - for (String property : propertyNames) { - String value = (String) properties.get(property); - if (value != null) { - return value.trim(); - } - } - - return null; - } - - public static class ConnectionStatus { - - public Connection connection; - public String errorMessage; - public String url; - public String username; - public String password; - - public Connection altConnection; - public String altUrl; - public String altUsername; - public String altPassword; - - public ConnectionStatus(Connection connection, String url, String username, String password, Connection altConnection, String altUrl, String altUsername, String altPassword) { - this.connection = connection; - this.url = url; - this.username = username; - this.password = password; - - this.altConnection = altConnection; - this.altUrl = altUrl; - this.altUsername = altUsername; - this.altPassword = altPassword; - } - - public ConnectionStatus(String errorMessage) { - this.errorMessage = errorMessage; - } - - } - -} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/setup/TestSetup.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/setup/TestSetup.java index 692c69db3f4..26037e5317b 100644 --- a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/setup/TestSetup.java +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/setup/TestSetup.java @@ -1,10 +1,8 @@ package liquibase.extension.testing.setup; -import liquibase.extension.testing.TestDatabaseConnections; - public abstract class TestSetup { - public abstract void setup(TestDatabaseConnections.ConnectionStatus connectionStatus) throws Exception; + public abstract void setup(TestSetupEnvironment testSetupEnvironment) throws Exception; public void cleanup() { } diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/setup/TestSetupEnvironment.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/setup/TestSetupEnvironment.java new file mode 100644 index 00000000000..b13c7bd5a45 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/setup/TestSetupEnvironment.java @@ -0,0 +1,35 @@ +package liquibase.extension.testing.setup; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class TestSetupEnvironment { + + public final String username; + public final String url; + public final String password; + public final Connection connection; + + public final Connection altConnection; + public final String altUsername; + public final String altUrl; + public final String altPassword; + + public String errorMessage; + + public TestSetupEnvironment(DatabaseTestSystem testSystem, DatabaseTestSystem altSystem) throws SQLException { + this.connection = testSystem.getConnection(); + this.url = testSystem.getConnectionUrl(); + this.username = testSystem.getUsername(); + this.password = testSystem.getPassword(); + + this.altUrl = this.url.replace(testSystem.getCatalog(), testSystem.getAltCatalog()); + this.altUsername = this.username; + this.altPassword = this.password; + this.altConnection = DriverManager.getConnection(this.altUrl, this.altUsername, this.altPassword); + + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/DatabaseTestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/DatabaseTestSystem.java new file mode 100644 index 00000000000..504dea722ca --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/DatabaseTestSystem.java @@ -0,0 +1,309 @@ +package liquibase.extension.testing.testsystem; + +import liquibase.Scope; +import liquibase.configuration.ConfigurationValueConverter; +import liquibase.configuration.LiquibaseConfiguration; +import liquibase.exception.UnexpectedLiquibaseException; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.JdbcDatabaseWrapper; +import liquibase.extension.testing.util.DownloadUtil; +import liquibase.logging.Logger; +import liquibase.util.CollectionUtil; +import liquibase.util.ObjectUtil; +import liquibase.util.StringUtil; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.sql.*; +import java.util.*; + +/** + * Base class for {@link TestSystem}s for databases. + */ +public abstract class DatabaseTestSystem extends TestSystem { + + private final SortedSet configurationKeys = new TreeSet<>(Arrays.asList("username", "password", "setup.username", "setup.password", "driverJar", + "catalog", "altCatalog", "schema", "altSchema", "altTablespace", "version", "imageName", "url")); + + protected DatabaseWrapper wrapper; + + private final Map connections = new HashMap<>(); + + public DatabaseTestSystem(String shortName) { + super(shortName); + } + + public DatabaseTestSystem(Definition definition) { + super(definition); + } + + @NotNull + protected DatabaseWrapper createWrapper() throws Exception { + String url = getConfiguredValue("url", String.class); + String imageName = getImageName(); + + if (url == null && imageName == null) { + throw new IllegalArgumentException("Either url or imageName must be configured for " + getDefinition()); + } + if (url != null && imageName != null) { + throw new IllegalArgumentException("Only url OR imageName must be configured for " + getDefinition() + ". Not both."); + } + + final Logger log = Scope.getCurrentScope().getLog(getClass()); + if (url != null) { + log.fine("Creating JDBC wrapper for " + url); + return createJdbcWrapper(url); + } + + return createContainerWrapper(); + } + + @NotNull + protected JdbcDatabaseWrapper createJdbcWrapper(String url) throws SQLException { + return new JdbcDatabaseWrapper(url, getUsername(), getPassword()); + } + + protected abstract DatabaseWrapper createContainerWrapper() throws Exception; + + /** + * Default implementation uses {@link #createWrapper()} to manage the external system. + * Multiple calls to start() will be no-ops. + * Calls {@link #setup()} after starting the wrapper. + */ + @Override + public void start() throws Exception { + if (wrapper != null) { + return; + } + + wrapper = createWrapper(); + + Scope.getCurrentScope().getUI().sendMessage("Starting database '" + this.getDefinition() + "'"); + + wrapper.start(); + + Scope.getCurrentScope().getLog(getClass()).info("Configuring database '" + this.getDefinition() + "'"); + setup(); + + Scope.getCurrentScope().getUI().sendMessage("Database '" + getDefinition() + "' details:\n" + StringUtil.indent(wrapper.describe())); + + Scope.getCurrentScope().getUI().sendMessage("Database '" + getDefinition() + "' connection information:\n" + + " url: " + this.getConnectionUrl() + "\n" + + " username: " + this.getUsername() + "\n" + + " password: " + this.getPassword() + "\n" + ); + } + + /** + * Default implementation uses {@link #createWrapper()} to manage the external system, and calls {@link DatabaseWrapper#stop()} + */ + @Override + public void stop() throws Exception { + if (wrapper == null) { + wrapper = createWrapper(); + } + + Scope.getCurrentScope().getUI().sendMessage("Stopping database wrapper: " + wrapper); + Scope.getCurrentScope().getLog(getClass()).info("Stopping database wrapper: " + wrapper); + wrapper.stop(); + Scope.getCurrentScope().getLog(getClass()).info("Stopped database wrapper: " + wrapper); + } + + /** + * Returns the driver library to use. Supports maven-style coordinate, like "com.h2database:h2:1.4.200". + * Default implementation uses the "driverJar" testSystem configuration value. + */ + public String getDriverJar() { + return getConfiguredValue("driverJar", String.class); + } + + /** + * Opens a connection to the given url, username, and password. This is not an end-user facing function because the url to use should be + * managed by the DatabaseWrapper, not the user. + */ + protected Connection getConnection(String url, String username, String password) throws SQLException { + Driver driver = getDriver(url); + + Properties properties = new Properties(); + properties.put("user", username); + properties.put("password", password); + return driver.connect(url, properties); + } + + /** + * @return the Driver instance to use for the given url. + * Default implementation uses the value in {@link #getDriverJar()} as needed and will download the library and manage the classloader as needed. + */ + protected Driver getDriver(String url) throws SQLException { + try { + Scope.getCurrentScope().getLog(getClass()).fine("Loading driver for " + url); + String driverJar = getDriverJar(); + Driver driver; + + if (driverJar == null) { + Scope.getCurrentScope().getLog(getClass()).fine("Using driver from standard classloader"); + driver = DriverManager.getDriver(url); + } else { + Scope.getCurrentScope().getLog(getClass()).fine("Using driver from " + driverJar); + Path driverPath = DownloadUtil.downloadMavenArtifact(driverJar); + final URLClassLoader isolatedClassloader = new URLClassLoader(new URL[]{ + driverPath.toUri().toURL(), + }, null); + + final Class isolatedDriverManager = Class.forName(DriverManager.class.getName(), true, isolatedClassloader); + final Method getDriverMethod = isolatedDriverManager.getMethod("getDriver", String.class); + + final Driver driverClass = (Driver) getDriverMethod.invoke(null, url); + driver = (Driver) Class.forName(driverClass.getClass().getName(), true, isolatedClassloader).newInstance(); + } + return driver; + } catch (SQLException e) { + throw e; + } catch (Exception e) { + throw new UnexpectedLiquibaseException(e); + } + } + + /** + * Opens a connection with valid permissions for the {@link #setup()} logic. + */ + protected Connection openSetupConnection() throws SQLException { + return getConnection(wrapper.getUrl(), getSetupUsername(), getSetupPassword()); + } + + /** + * Returns the connection to this database. Will reuse a single connection rather than continually open new ones. + * Convenience method for {@link #getConnection(String, String)} using {@link #getUsername()} and {@link #getPassword()} + */ + public Connection getConnection() throws SQLException { + return getConnection(getUsername(), getPassword()); + } + + /** + * Returns the connection to this database. Will reuse a single connection for each username/password combo rather than continually open new ones. + */ + public Connection getConnection(String username, String password) throws SQLException { + final String key = username + ":" + password; + + Connection connection = connections.get(key); + if (connection == null || connection.isClosed()) { + connection = getConnection(getConnectionUrl(), username, password); + connections.put(key, connection); + } + return connection; + } + + /** + * Return the url used to connect to this database. + * NOTE: this may be different than the 'url' configured value because the TestSystem implementations are free to tweak and control this URL based on other settings. + */ + public String getConnectionUrl() { + return wrapper.getUrl(); + } + + /** + * Standard username to use when connecting. Returns "username" test system configuration. + */ + public String getUsername() { + return getConfiguredValue("username", String.class); + } + + /** + * Standard password to use when connecting. Returns "password" test system configuration. + */ + public String getPassword() { + return getConfiguredValue("password", String.class); + } + + /** + * Standard "catalog" to use for testing. Returns "catalog" test system configuration. + */ + public String getCatalog() { + return getConfiguredValue("catalog", String.class); + } + + /** + * Standard alt catalog to use for testing. Returns "altCatalog" test system configuration. + */ + public String getAltCatalog() { + return getConfiguredValue("altCatalog", String.class); + } + + /** + * Standard alt schema to use for testing. Returns "altSchema" test system configuration. + */ + public String getAltSchema() { + return getConfiguredValue("altSchema", String.class); + } + + /** + * Standard alt tablespace to use for testing. Returns "username" test system configuration. + */ + public String getAltTablespace() { + return getConfiguredValue("altTablespace", String.class); + } + + /** + * "Privileged" username to use for {@link #setup()}. Returns "setup.username" or "username" test system configuration. + */ + protected String getSetupUsername() { + return ObjectUtil.defaultIfNull(getConfiguredValue("setup.username", String.class), getUsername()); + } + + /** + * "Privileged" password to use for {@link #setup()}. Returns "setup.password" or "password" test system configuration. + */ + protected String getSetupPassword() { + return ObjectUtil.defaultIfNull(getConfiguredValue("setup.password", String.class), getPassword()); + } + + /** + * Version of the database to test against."Privileged" username to use for {@link #setup()}. Returns "version" test system configuration. + */ + protected String getVersion() { + return getConfiguredValue("version", String.class); + } + + /** + * Docker image of the database to test against. Returns "imageName" test system configuration. + */ + protected String getImageName() { + return getConfiguredValue("imageName", String.class); + } + + /** + * Sets up any needed catalogs/schemas/usernames/etc. + */ + protected void setup() throws SQLException { + final Logger log = Scope.getCurrentScope().getLog(getClass()); + try (final Connection connection = openSetupConnection(); + final Statement statement = connection.createStatement()) { + for (String sql : CollectionUtil.createIfNull(getSetupSql())) { + log.info("Running setup SQL: " + sql); + try { + statement.execute(sql); + } catch (SQLException e) { + log.info("Error running setup SQL " + sql + ": " + e.getMessage() + ". Continuing on"); + log.fine(e.getMessage(), e); + + if (!connection.getAutoCommit()) { + connection.rollback(); + } + } + } + + if (!connection.getAutoCommit()) { + connection.commit(); + } + } + } + + /** + * Define SQL to run by {@link #setup()} + */ + protected abstract String[] getSetupSql(); + +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/TestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/TestSystem.java new file mode 100644 index 00000000000..244cca9241d --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/TestSystem.java @@ -0,0 +1,346 @@ +package liquibase.extension.testing.testsystem; + +import liquibase.Scope; +import liquibase.configuration.ConfigurationValueConverter; +import liquibase.configuration.ConfiguredValue; +import liquibase.configuration.LiquibaseConfiguration; +import liquibase.exception.UnexpectedLiquibaseException; +import liquibase.plugin.Plugin; +import liquibase.util.ObjectUtil; +import liquibase.util.StringUtil; +import org.junit.Assume; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.MultipleFailureException; +import org.junit.runners.model.Statement; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * TestSystem implementations define and manage a connection to an external system to test. + * Ideally the implementation can start and stop the test systems, but that is not necessary. + *

+ * This implements {@link TestRule} so it can control start/stop of the TestSystem in JUnit, but that may be removed as tests get converted to spock. + *

+ * Instances should not be created directly, but via {@link TestSystemFactory} + */ +public abstract class TestSystem implements TestRule, Plugin { + + private static final SortedSet testSystems = new TreeSet<>(); + private static final String configuredTestSystems; + + private final Definition definition; + + private final SortedSet configurationKeys = new TreeSet<>(Arrays.asList( + "keepRunning" + )); + + static { + //cache configured test systems for faster lookup + configuredTestSystems = Scope.getCurrentScope().getSingleton(LiquibaseConfiguration.class).getCurrentConfiguredValue(ConfigurationValueConverter.STRING, null, "liquibase.sdk.testSystem.test").getValue(); + if (configuredTestSystems != null) { + for (String definition : StringUtil.splitAndTrim(configuredTestSystems, ",")) + testSystems.add(TestSystem.Definition.parse(definition)); + } + } + + /** + * Empty constructor for ServiceLocator to use + */ + protected TestSystem(String name) { + this(new Definition(name)); + } + + /** + * Constructor for {@link TestSystemFactory} to use + */ + protected TestSystem(Definition definition) { + this.definition = definition; + } + + /** + * Return configuration keys supported by this testSystem + */ + public SortedSet getConfigurationKeys() { + return configurationKeys; + } + + /** + * Allows test system to be auto-controlled by JUnit tests. + *

+ * If the liquibase.sdk.testSystem.test configuration does NOT include the name of this test system, it skips the test.
+ * If the liquibase.sdk.testSystem.test configuration DOES include the name of the test system, it will connect to a matching system if available or will start it as possible. + * If it starts the system, it will not stop it until JVM shutdown. + *

+ * Example: + *

+     * \@Rule
+     * TestSystem testSystem = Scope.getCurrentScope().getSingleton(TestSystemFactory.class).getTestSystem("mysql")
+     * 
+ */ + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + Assume.assumeTrue("Not running test against " + TestSystem.this.getDefinition() + ": liquibase.sdk.testSystem.test is " + configuredTestSystems, shouldTest()); + + List errors = new ArrayList<>(); + + try { + TestSystem.this.start(); + base.evaluate(); + } catch (Throwable e) { + errors.add(e); + } + + MultipleFailureException.assertEmpty(errors); + } + }; + } + + /** + * Default implementation returns PRIORITY_DEFAULT if the name matches the given definition, without taking any profiles etc. into account. + */ + public int getPriority(Definition definition) { + if (definition.getName().equals(getDefinition().getName())) { + return PRIORITY_DEFAULT; + } + + return PRIORITY_NOT_APPLICABLE; + } + + /** + * @return true if this TestSystem should have automated tests run against it + */ + public boolean shouldTest() { + return testSystems.contains(TestSystem.this.getDefinition()); + } + + /** + * Return the definition of this test system. + */ + public Definition getDefinition() { + return definition; + } + + /** + * Return whether this testSystem should/will keep running after the JVM interacting with it exits. + * Default implementation returns the `keepRunning` test system configured value. + */ + public boolean getKeepRunning() { + return getConfiguredValue("keepRunning", Boolean.class); + } + + /** + * Convenience method for {@link #getConfiguredValue(String, ConfigurationValueConverter, boolean)} + */ + public T getConfiguredValue(String propertyName, Class type) { + return getConfiguredValue(propertyName, type, false); + } + + /** + * Convenience method for {@link #getConfiguredValue(String, ConfigurationValueConverter, boolean)} + */ + public T getConfiguredValue(String propertyName, Class type, boolean required) { + ConfigurationValueConverter converter = null; + if (type.equals(Class.class)) { + converter = (ConfigurationValueConverter) ConfigurationValueConverter.CLASS; + } else if (type.equals(String.class)) { + converter = (ConfigurationValueConverter) ConfigurationValueConverter.STRING; + } + + return getConfiguredValue(propertyName, converter, required); + } + + /** + * Returns the configured value for the given propertyName. It will check (in priority order): + *
    + *
  1. properties set directly on this object
  2. + *
  3. liquibase.sdk.testSystem.[name].[profile(s)].propertyName in the order the profiles are set on this object
  4. + *
  5. liquibase.sdk.testSystem.[name].propertyName
  6. + *
  7. liquibase.sdk.testSystem.default.propertyName
  8. + *
+ *
+ * If a value is not found, it will return null or throw an {@link UnexpectedLiquibaseException} if 'required' is true. + */ + public T getConfiguredValue(String propertyName, ConfigurationValueConverter converter, boolean required) { + ConfigurationValueConverter finalConverter = value -> { + if (value instanceof String && ((String) value).contains("${")) { + final Matcher matcher = Pattern.compile("(\\$\\{.+?})").matcher((String) value); + while (matcher.find()) { + final String config = matcher.group(1).replace("${", "").replace("}", "").trim(); + value = ((String) value).replace(matcher.group(1), getConfiguredValue(config, String.class)); + } + } + + if (converter == null) { + return (T) value; + } else { + return converter.convert(value); + } + }; + + final SortedMap properties = definition.getProperties(); + if (properties.containsKey(propertyName)) { + return finalConverter.convert(properties.get(propertyName)); + } + + final LiquibaseConfiguration config = Scope.getCurrentScope().getSingleton(LiquibaseConfiguration.class); + + ConfiguredValue configuredValue; + //first check profiles + for (String profile : definition.getProfiles()) { + configuredValue = config.getCurrentConfiguredValue(finalConverter, null, "liquibase.sdk.testSystem." + getDefinition().getName() + ".profiles." + profile + "." + propertyName); + + if (configuredValue.found()) { + return configuredValue.getValue(); + } + } + + configuredValue = config.getCurrentConfiguredValue(finalConverter, null, "liquibase.sdk.testSystem." + getDefinition().getName() + "." + propertyName); + + if (configuredValue.found()) { + return configuredValue.getValue(); + } + + //fall back to "default" setup + configuredValue = config.getCurrentConfiguredValue(finalConverter, null, "liquibase.sdk.testSystem.default." + propertyName); + if (configuredValue.found()) { + return configuredValue.getValue(); + } + + if (required) { + throw new UnexpectedLiquibaseException("No required liquibase.sdk.testSystem configuration for " + getDefinition().getName() + " of " + propertyName + " set"); + } + + return null; + } + + /** + * Starts the system if possible. + * Does not return until test system is reachable. + * If connecting to a running system, ensure the system can be reached. + * The lifetime of the started system should respect the {@link #getKeepRunning()} configuration. + * If the keepRunning flag has an invalid value for this test system, throw an {@link IllegalArgumentException}. + * + * @throws Exception if the system cannot be started or reached. + */ + public abstract void start() throws Exception; + + /** + * Stops the system if possible. + * Does not return until test system is down. + */ + public abstract void stop() throws Exception; + + @Override + public String toString() { + return definition.toString(); + } + + public static class Definition implements Comparable { + private static final Pattern namePattern = Pattern.compile("^([^:?]+)"); + private static final Pattern profilePattern = Pattern.compile(":([^?]*)"); + private static final Pattern propertiesPattern = Pattern.compile("\\?(.*)"); + + private final String name; + private final String[] profiles; + private final SortedMap properties = new TreeMap<>(); + + public static Definition parse(String definition) { + if (definition == null) { + return null; + } + + String name; + + final Matcher nameMatcher = namePattern.matcher(definition); + if (!nameMatcher.find()) { + throw new IllegalArgumentException("Cannot parse name from " + definition); + } + name = nameMatcher.group(1); + + + String[] profiles = null; + final Matcher profileMatcher = profilePattern.matcher(definition); + if (profileMatcher.find()) { + profiles = StringUtil.splitAndTrim(profileMatcher.group(1), ",").toArray(new String[0]); + } + + Definition returnObj = new Definition(name, profiles); + + final Matcher propertiesMatcher = propertiesPattern.matcher(definition); + if (propertiesMatcher.find()) { + String propertiesString = propertiesMatcher.group(1); + for (String keyValue : propertiesString.split("&")) { + final String[] split = keyValue.split("="); + returnObj.properties.put(split[0], split[1]); + } + } + + return returnObj; + } + + private Definition(String name, String... profiles) { + this.name = name; + this.profiles = profiles; + } + + public String getName() { + return name; + } + + /** + * @return the profiles associated with this testSystem. Returns an empty array if no profiles are defined + */ + public String[] getProfiles() { + if (profiles == null) { + return new String[0]; + } + return Arrays.copyOf(profiles, profiles.length); + } + + /** + * @return read-only copy of the local properties in this test system + */ + public SortedMap getProperties() { + return Collections.unmodifiableSortedMap(properties); + } + + @Override + public String toString() { + String returnString = getName(); + if (profiles != null && profiles.length > 0) { + returnString += ":" + StringUtil.join(profiles, ","); + } + + if (properties.size() > 0) { + returnString += "?" + StringUtil.join(properties, "&"); + } + return returnString; + } + + @Override + public int hashCode() { + return toString().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof TestSystem.Definition && this.toString().equals(obj.toString()); + } + + @Override + public int compareTo(TestSystem.Definition o) { + if (o == null) { + return 1; + } + return this.toString().compareTo(o.toString()); + } + } + + +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/TestSystemFactory.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/TestSystemFactory.java new file mode 100644 index 00000000000..4ac92c9ffd7 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/TestSystemFactory.java @@ -0,0 +1,104 @@ +package liquibase.extension.testing.testsystem; + +import liquibase.Scope; +import liquibase.configuration.ConfigurationValueConverter; +import liquibase.configuration.LiquibaseConfiguration; +import liquibase.database.Database; +import liquibase.exception.UnexpectedLiquibaseException; +import liquibase.plugin.AbstractPluginFactory; +import liquibase.util.CollectionUtil; +import liquibase.util.StringUtil; + +import java.lang.reflect.Constructor; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +/** + * Factory for getting {@link TestSystem} implementations. + */ +public class TestSystemFactory extends AbstractPluginFactory { + + private Map systems = new HashMap<>(); + + @Override + protected Class getPluginClass() { + return TestSystem.class; + } + + @Override + protected int getPriority(TestSystem testSystem, Object... args) { + return testSystem.getPriority((TestSystem.Definition) args[0]); + } + + /** + * Return the {@link TestSystem} for the given {@link liquibase.extension.testing.testsystem.TestSystem.Definition}. + * Returns singleton instances for equal definitions. + */ + public TestSystem getTestSystem(TestSystem.Definition definition) { + return systems.computeIfAbsent(definition, passedDefinition -> { + final TestSystem singleton = TestSystemFactory.this.getPlugin(passedDefinition); + + if (singleton == null) { + throw new UnexpectedLiquibaseException("No test system: " + passedDefinition); + } + + try { + TestSystem.Definition finalDefinition = passedDefinition; + if (passedDefinition.getProfiles().length == 0 && passedDefinition.getProperties().size() == 0) { + //see if we're configured to use a specific setting instead + for (String testSystem : getConfiguredTestSystems()) { + final TestSystem.Definition testSystemDef = TestSystem.Definition.parse(testSystem); + if (testSystemDef.getName().equals(passedDefinition.getName())) { + finalDefinition = testSystemDef; + break; + } + } + } + + final Constructor pluginConstructor = singleton.getClass().getConstructor(TestSystem.Definition.class); + return pluginConstructor.newInstance(finalDefinition); + } catch (ReflectiveOperationException e) { + throw new UnexpectedLiquibaseException(e); + } + }); + } + + private List getConfiguredTestSystems() { + final String testSystems = Scope.getCurrentScope().getSingleton(LiquibaseConfiguration.class).getCurrentConfiguredValue(ConfigurationValueConverter.STRING, null, "liquibase.sdk.testSystem.test").getValue(); + + return CollectionUtil.createIfNull(StringUtil.splitAndTrim(testSystems, ",")); + } + + /** + * Conveniene method for {@link #getTestSystem(TestSystem.Definition)} without having to parse the definition yourself. + */ + public TestSystem getTestSystem(String definition) { + return getTestSystem(TestSystem.Definition.parse(definition)); + } + + public Set getTestSystemNames() { + return super.findAllInstances().stream() + .map(testSystem -> testSystem.getDefinition().getName()) + .collect(Collectors.toSet()); + } + + @Override + protected synchronized Collection findAllInstances() { + return super.findAllInstances(); + } + + public List getAvailable(Class testSystemType) { + List returnList = new ArrayList<>(); + for (String testSystemDef : getConfiguredTestSystems()) { + final TestSystem testSystem = this.getTestSystem(testSystemDef); + + if (testSystemType.isAssignableFrom(testSystem.getClass())) { + returnList.add((T) testSystem); + } + } + + return returnList; + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/command/TestSystemDownCommand.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/command/TestSystemDownCommand.java new file mode 100644 index 00000000000..909040a7bf9 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/command/TestSystemDownCommand.java @@ -0,0 +1,37 @@ +package liquibase.extension.testing.testsystem.command; + +import liquibase.command.AbstractCommandStep; +import liquibase.command.CommandArgumentDefinition; +import liquibase.command.CommandBuilder; +import liquibase.command.CommandResultsBuilder; +import liquibase.extension.testing.testsystem.TestSystem; +import liquibase.extension.testing.testsystem.TestSystemFactory; + +public class TestSystemDownCommand extends AbstractCommandStep { + + public static final String[] COMMAND_NAME = {"sdk", "system", "down"}; + + public static final CommandArgumentDefinition NAME; + + static { + CommandBuilder builder = new CommandBuilder(COMMAND_NAME); + + NAME = builder.argument("name", String.class).required() + .description("The name of the system to").build(); + } + + @Override + public String[][] defineCommandNames() { + return new String[][]{ + COMMAND_NAME + }; + } + + @Override + public void run(CommandResultsBuilder resultsBuilder) throws Exception { + + final TestSystem env = new TestSystemFactory().getTestSystem(resultsBuilder.getCommandScope().getConfiguredValue(NAME).getValue()); + + env.stop(); + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/command/TestSystemUpCommand.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/command/TestSystemUpCommand.java new file mode 100644 index 00000000000..7f93eccaa81 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/command/TestSystemUpCommand.java @@ -0,0 +1,70 @@ +package liquibase.extension.testing.testsystem.command; + +import liquibase.Scope; +import liquibase.command.AbstractCommandStep; +import liquibase.command.CommandArgumentDefinition; +import liquibase.command.CommandBuilder; +import liquibase.command.CommandResultsBuilder; +import liquibase.extension.testing.testsystem.TestSystem; +import liquibase.extension.testing.testsystem.TestSystemFactory; + +import java.util.HashMap; +import java.util.Map; + +public class TestSystemUpCommand extends AbstractCommandStep { + + public static final String[] COMMAND_NAME = {"sdk", "system", "up"}; + + public static final CommandArgumentDefinition NAME; + public static final CommandArgumentDefinition VERSION; + public static final CommandArgumentDefinition PROFILES; + public static final CommandArgumentDefinition ACCEPT_LICENSES; + + static { + CommandBuilder builder = new CommandBuilder(COMMAND_NAME); + + NAME = builder.argument("name", String.class).required() + .description("The name of the system to").build(); + VERSION = builder.argument("version", String.class) + .description("Override version to use").build(); + PROFILES = builder.argument("profiles", String.class) + .description("Set profile(s)").build(); + ACCEPT_LICENSES = builder.argument("acceptLicense", Boolean.class) + .description("Accept licenses for any systems used/accessed") + .defaultValue(false) + .build(); + } + + @Override + public String[][] defineCommandNames() { + return new String[][]{ + COMMAND_NAME + }; + } + + @Override + public void run(CommandResultsBuilder resultsBuilder) throws Exception { + + final String name = resultsBuilder.getCommandScope().getConfiguredValue(NAME).getValue(); + final String version = resultsBuilder.getCommandScope().getConfiguredValue(VERSION).getValue(); + final String profiles = resultsBuilder.getCommandScope().getConfiguredValue(PROFILES).getValue(); + final Boolean acceptLicenses = resultsBuilder.getCommandScope().getConfiguredValue(ACCEPT_LICENSES).getValue(); + + String definition = name; + if (profiles != null) { + definition = name + ":" + profiles; + } + if (version != null) { + definition += "?version=" + version; + } + final TestSystem testSystem = new TestSystemFactory().getTestSystem(definition); + + Map scopeValues = new HashMap<>(); + if (acceptLicenses) { + scopeValues.put("liquibase.sdk.testSystem.acceptLicenses", testSystem.getDefinition().getName()); + } + + Scope.child(scopeValues, testSystem::start); + + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/CockroachTestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/CockroachTestSystem.java new file mode 100644 index 00000000000..d3531255802 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/CockroachTestSystem.java @@ -0,0 +1,55 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.DockerDatabaseWrapper; +import liquibase.util.StringUtil; +import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.CockroachContainer; +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.utility.DockerImageName; + +public class CockroachTestSystem extends DatabaseTestSystem { + + public CockroachTestSystem() { + super("cockroachdb"); + } + + public CockroachTestSystem(Definition definition) { + super(definition); + } + + @Override + protected @NotNull DatabaseWrapper createContainerWrapper() { + return new DockerDatabaseWrapper(new CockroachContainer( + DockerImageName.parse(getImageName()).withTag(getVersion())), + this + ); + } + + @Override + public String getConnectionUrl() { + final JdbcDatabaseContainer container = ((DockerDatabaseWrapper) wrapper).getContainer(); + + return "jdbc:postgresql://" + container.getHost() + ":" + container.getMappedPort(26257) + "/" + getCatalog(); + } + + @Override + protected String[] getSetupSql() { + String passwordClause = ""; + if (StringUtil.trimToNull(getPassword()) != null) { + passwordClause = " PASSWORD '" + StringUtil.trimToEmpty(getPassword()) + "'"; + } + + return new String[]{ + "CREATE USER IF NOT EXISTS " + getUsername() + passwordClause, + "CREATE DATABASE IF NOT EXISTS " + getCatalog(), + "CREATE DATABASE IF NOT EXISTS " + getAltCatalog(), + "CREATE SCHEMA IF NOT EXISTS " + getCatalog() + "." + getAltSchema(), + "GRANT ALL ON DATABASE " + getCatalog() + " TO " + getUsername(), + "GRANT ALL ON DATABASE " + getAltCatalog() + " TO " + getUsername(), + "GRANT ALL ON SCHEMA " + getCatalog() + "." + getAltSchema() + " TO " + getUsername(), + }; + } + +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/DB2TestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/DB2TestSystem.java new file mode 100644 index 00000000000..cc2233a27ad --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/DB2TestSystem.java @@ -0,0 +1,44 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.DockerDatabaseWrapper; +import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.Db2Container; +import org.testcontainers.utility.DockerImageName; + +public class DB2TestSystem extends DatabaseTestSystem { + + public DB2TestSystem() { + super("db2"); + } + + public DB2TestSystem(Definition definition) { + super(definition); + } + + @SuppressWarnings("java:S2095") + @Override + protected @NotNull DatabaseWrapper createContainerWrapper() { + return new DockerDatabaseWrapper( + new Db2Container(DockerImageName.parse(getImageName()).withTag(getVersion())) + .withUsername(getUsername()) + .withPassword(getPassword()) + .withDatabaseName(getCatalog()) + .withUrlParam("retrieveMessagesFromServerOnGetMessage", "true"), + this + ) { + @Override + public Runnable requireLicense() { + return ((Db2Container) this.getContainer())::acceptLicense; + } + }; + } + + @Override + protected String[] getSetupSql() { + return new String[]{ + "CREATE TABLESPACE "+getAltTablespace() + }; + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/DB2zTestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/DB2zTestSystem.java new file mode 100644 index 00000000000..0587bdc5f46 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/DB2zTestSystem.java @@ -0,0 +1,27 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.UnimplementedWrapper; +import org.jetbrains.annotations.NotNull; + +public class DB2zTestSystem extends DatabaseTestSystem { + + public DB2zTestSystem() { + super("db2z"); + } + + public DB2zTestSystem(Definition definition) { + super(definition); + } + + @Override + protected @NotNull DatabaseWrapper createContainerWrapper() throws Exception { + return new UnimplementedWrapper(); + } + + @Override + protected String[] getSetupSql() { + return new String[0]; + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/DerbyTestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/DerbyTestSystem.java new file mode 100644 index 00000000000..83ac48ce7a5 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/DerbyTestSystem.java @@ -0,0 +1,40 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.JdbcDatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.UnimplementedWrapper; +import org.jetbrains.annotations.NotNull; + +import java.sql.SQLException; + +public class DerbyTestSystem extends DatabaseTestSystem { + + public DerbyTestSystem() { + super("derby"); + } + + public DerbyTestSystem(Definition definition) { + super(definition); + } + + @Override + protected @NotNull DatabaseWrapper createContainerWrapper() throws Exception{ + throw new IllegalArgumentException("Cannot create container for derby. Use URL"); + } + + @Override + protected String[] getSetupSql() { + return new String[] { + "create schema "+getAltSchema(), + }; + } + + // @Override +// public Connection openConnection() throws SQLException { +// return DriverManager.getConnection("jdbc:h2:mem:"+getProperty("catalog", String.class), +// getProperty("username", String.class, true), +// getProperty("password", String.class) +// ); +// } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/FirebirdTestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/FirebirdTestSystem.java new file mode 100644 index 00000000000..a4d70f44394 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/FirebirdTestSystem.java @@ -0,0 +1,27 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.UnimplementedWrapper; +import org.jetbrains.annotations.NotNull; + +public class FirebirdTestSystem extends DatabaseTestSystem { + + public FirebirdTestSystem() { + super("firebird"); + } + + public FirebirdTestSystem(Definition definition) { + super(definition); + } + + @Override + protected DatabaseWrapper createContainerWrapper() throws Exception { + return new UnimplementedWrapper(); + } + + @Override + protected String[] getSetupSql() { + return new String[0]; + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/H2TestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/H2TestSystem.java new file mode 100644 index 00000000000..21d4aa37824 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/H2TestSystem.java @@ -0,0 +1,41 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; + +public class H2TestSystem extends DatabaseTestSystem { + + public H2TestSystem() { + super("h2"); + } + + public H2TestSystem(Definition definition) { + super(definition); + } + + @Override + public String getDriverJar() { + String driver = super.getDriverJar(); + if (driver == null) { + final String version = getVersion(); + if (version != null) { + driver = "com.h2database:h2:" + version; + } + } + + return driver; + } + + @Override + protected DatabaseWrapper createContainerWrapper() throws Exception { + throw new IllegalArgumentException("Cannot create container for h2. Use url"); + } + + @Override + protected String[] getSetupSql() { + return new String[]{ + "create schema " + getAltSchema(), + "grant all on schema " + getAltSchema() + " to " + getUsername(), + }; + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/HsqlTestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/HsqlTestSystem.java new file mode 100644 index 00000000000..29c6ccf8551 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/HsqlTestSystem.java @@ -0,0 +1,39 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.JdbcDatabaseWrapper; +import org.jetbrains.annotations.NotNull; + +import java.sql.SQLException; + +public class HsqlTestSystem extends DatabaseTestSystem { + + public HsqlTestSystem() { + super("hsqldb"); + } + + public HsqlTestSystem(Definition definition) { + super(definition); + } + + @Override + protected DatabaseWrapper createContainerWrapper() throws Exception { + throw new IllegalArgumentException("Cannot create container for hsql. Use url"); + } + + @Override + protected String[] getSetupSql() { + return new String[] { + "create schema "+getAltSchema(), + }; + } + + // @Override +// public Connection openConnection() throws SQLException { +// return DriverManager.getConnection("jdbc:h2:mem:"+getProperty("catalog", String.class), +// getProperty("username", String.class, true), +// getProperty("password", String.class) +// ); +// } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/HubTestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/HubTestSystem.java new file mode 100644 index 00000000000..ab8449ecbde --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/HubTestSystem.java @@ -0,0 +1,54 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.TestSystem; + +import java.util.Arrays; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.UUID; + +public class HubTestSystem extends TestSystem { + + private final SortedSet configurationKeys = new TreeSet<>(Arrays.asList("apiKey", "url", "username", "orgId")); + + public HubTestSystem() { + super("hub"); + } + + public HubTestSystem(Definition definition) { + super(definition); + } + + @Override + public void start() { + + } + + @Override + public void stop() { + + } + + public String getApiKey() { + return getConfiguredValue("apiKey", String.class); + } + + public String getUrl() { + return getConfiguredValue("url", String.class); + } + + public String getUsername() { + return getConfiguredValue("username", String.class); + } + + public UUID getOrgId() { + return UUID.fromString(getConfiguredValue("orgId", String.class)); + } + + @Override + public SortedSet getConfigurationKeys() { + final SortedSet returnKeys = super.getConfigurationKeys(); + returnKeys.addAll(this.configurationKeys); + return returnKeys; + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/InformixTestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/InformixTestSystem.java new file mode 100644 index 00000000000..8a020fb0ecd --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/InformixTestSystem.java @@ -0,0 +1,27 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.UnimplementedWrapper; +import org.jetbrains.annotations.NotNull; + +public class InformixTestSystem extends DatabaseTestSystem { + + public InformixTestSystem() { + super("informix"); + } + + public InformixTestSystem(Definition definition) { + super(definition); + } + + @Override + protected DatabaseWrapper createContainerWrapper() throws Exception { + return new UnimplementedWrapper(); + } + + @Override + protected String[] getSetupSql() { + return new String[0]; + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/MSSQLTestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/MSSQLTestSystem.java new file mode 100644 index 00000000000..2833c0985cd --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/MSSQLTestSystem.java @@ -0,0 +1,56 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.DockerDatabaseWrapper; +import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.containers.MSSQLServerContainer; +import org.testcontainers.utility.DockerImageName; + +public class MSSQLTestSystem extends DatabaseTestSystem { + + public MSSQLTestSystem() { + super("mssql"); + } + + public MSSQLTestSystem(Definition definition) { + super(definition); + } + + @Override + protected @NotNull DatabaseWrapper createContainerWrapper() { + return new DockerDatabaseWrapper(new MSSQLServerContainer( + DockerImageName.parse(getImageName()).withTag(getVersion())), + this + ) { + @Override + protected Runnable requireLicense() { + return ((MSSQLServerContainer) getContainer())::acceptLicense; + } + }; + } + + @Override + public String getConnectionUrl() { + final JdbcDatabaseContainer container = ((DockerDatabaseWrapper) wrapper).getContainer(); + + return "jdbc:sqlserver://" + container.getHost() + ":" + container.getMappedPort(MSSQLServerContainer.MS_SQL_SERVER_PORT)+";databaseName="+getCatalog(); + } + @Override + protected String[] getSetupSql() { + return new String[]{ + "CREATE LOGIN [" + getUsername() + "] with password=N'" + getPassword() + "', CHECK_EXPIRATION=OFF", + + "CREATE DATABASE " + getCatalog(), + "EXEC lbcat..sp_addsrvrolemember @loginame = N'" + getUsername() + "', @rolename = N'sysadmin'", + + "USE [" + getCatalog() + "]", +// "ALTER DATABASE ["+getCatalog()+"] MODIFY FILEGROUP [PRIMARY] DEFAULT", + "ALTER DATABASE [" + getCatalog() + "] ADD FILEGROUP [" + getAltTablespace() + "]", + + "ALTER DATABASE [" + getCatalog() + "] ADD FILE ( NAME = N'" + getAltTablespace() + "', FILENAME = N'/tmp/" + getAltTablespace() + ".ndf' , SIZE = 8192KB , FILEGROWTH = 65536KB ) TO FILEGROUP [" + getAltTablespace() + "]", + "CREATE SCHEMA [" + getAltSchema() + "] AUTHORIZATION [dbo]", + }; + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/MariaDBTestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/MariaDBTestSystem.java new file mode 100644 index 00000000000..9d3c100b112 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/MariaDBTestSystem.java @@ -0,0 +1,44 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.DockerDatabaseWrapper; +import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.MariaDBContainer; +import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.utility.DockerImageName; + +public class MariaDBTestSystem extends DatabaseTestSystem { + + public MariaDBTestSystem() { + super("mariadb"); + } + + public MariaDBTestSystem(Definition definition) { + super(definition); + } + + @SuppressWarnings("java:S2095") + @Override + protected DatabaseWrapper createContainerWrapper() throws Exception { + return new DockerDatabaseWrapper( + new MariaDBContainer(DockerImageName.parse(getImageName()).withTag(getVersion())) + .withUsername(getUsername()) + .withPassword(getPassword()) + .withDatabaseName(getCatalog()), + this + ); + } + + @Override + protected String[] getSetupSql() { + return new String[]{ + "create database if not exists " + getAltCatalog(), + "create schema if not exists " + getAltSchema(), + "create database if not exists " + getAltSchema(), //create altSchema as a catalog since some integration tests don't handle the difference + "GRANT ALL PRIVILEGES ON " + getCatalog() + ".* TO '" + getUsername() + "'@'%'", + "GRANT ALL PRIVILEGES ON " + getAltCatalog() + ".* TO '" + getUsername() + "'@'%'", + "GRANT ALL PRIVILEGES ON " + getAltSchema() + ".* TO '" + getUsername() + "'@'%'" + }; + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/MySQLTestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/MySQLTestSystem.java new file mode 100644 index 00000000000..48eb5a9485c --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/MySQLTestSystem.java @@ -0,0 +1,44 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.DockerDatabaseWrapper; +import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.utility.DockerImageName; + +public class MySQLTestSystem extends DatabaseTestSystem { + + public MySQLTestSystem() { + super("mysql"); + } + + public MySQLTestSystem(Definition definition) { + super(definition); + } + + @SuppressWarnings("java:S2095") + @Override + protected @NotNull DatabaseWrapper createContainerWrapper() { + return new DockerDatabaseWrapper( + new MySQLContainer(DockerImageName.parse(getImageName()).withTag(getVersion())) + .withUsername(getUsername()) + .withPassword(getPassword()) + .withDatabaseName(getCatalog()) + .withUrlParam("useSSL", "false") + .withUrlParam("allowPublicKeyRetrieval", "true"), + this + ); + } + + @Override + protected String[] getSetupSql() { + return new String[]{ + "create database if not exists " + getAltCatalog(), + "create database if not exists " + getAltSchema(), //create altSchema as a catalog since some integration tests don't handle the difference + "GRANT ALL PRIVILEGES ON " + getCatalog() + ".* TO '" + getUsername() + "'@'%'", + "GRANT ALL PRIVILEGES ON " + getAltCatalog() + ".* TO '" + getUsername() + "'@'%'", + "GRANT ALL PRIVILEGES ON " + getAltSchema() + ".* TO '" + getUsername() + "'@'%'" + }; + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/OracleTestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/OracleTestSystem.java new file mode 100644 index 00000000000..849d5be55e1 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/OracleTestSystem.java @@ -0,0 +1,43 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.DockerDatabaseWrapper; +import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.OracleContainer; +import org.testcontainers.utility.DockerImageName; + +public class OracleTestSystem extends DatabaseTestSystem { + + public OracleTestSystem() { + super("oracle"); + } + + public OracleTestSystem(Definition definition) { + super(definition); + } + + @SuppressWarnings("java:S2095") + @Override + protected @NotNull DatabaseWrapper createContainerWrapper() { + return new DockerDatabaseWrapper( + new OracleContainer(DockerImageName.parse(getImageName()).withTag(getVersion())) + .withUsername(getUsername()) + .withPassword(getPassword()) + .withDatabaseName(getCatalog()), + this + ); + } + + @Override + protected String[] getSetupSql() { + return new String[]{ + "CREATE TABLESPACE " + getAltTablespace() + " DATAFILE '" + getAltTablespace() + ".dat' SIZE 1M AUTOEXTEND ON", + "CREATE USER "+getAltCatalog()+" IDENTIFIED BY "+getPassword()+" DEFAULT TABLESPACE USERS TEMPORARY TABLESPACE TEMP ACCOUNT UNLOCK", + "GRANT ALL PRIVILEGES TO "+getUsername(), + "GRANT UNLIMITED TABLESPACE TO " + getUsername(), + "GRANT UNLIMITED TABLESPACE TO " + getAltCatalog(), + + }; + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/PostgresTestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/PostgresTestSystem.java new file mode 100644 index 00000000000..7c8029a0ce2 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/PostgresTestSystem.java @@ -0,0 +1,46 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.DockerDatabaseWrapper; +import liquibase.util.StringUtil; +import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.utility.DockerImageName; + +public class PostgresTestSystem extends DatabaseTestSystem { + + public PostgresTestSystem() { + super("postgresql"); + } + + public PostgresTestSystem(Definition definition) { + super(definition); + } + + @SuppressWarnings("java:S2095") + @Override + protected @NotNull DatabaseWrapper createContainerWrapper() { + return new DockerDatabaseWrapper(new PostgreSQLContainer( + DockerImageName.parse(getImageName()).withTag(getVersion())) + .withUsername(getUsername()) + .withPassword(getPassword()) + .withDatabaseName(getCatalog()), + this + ); + } + + @Override + protected String[] getSetupSql() { + return new String[]{ + "CREATE DATABASE " + getAltCatalog(), + "CREATE SCHEMA IF NOT EXISTS " + getAltSchema(), + "COPY (SELECT 1) TO PROGRAM 'mkdir -p /tmp/" + getAltTablespace() + "'", + "CREATE TABLESPACE " + getAltTablespace() + " OWNER lbuser LOCATION '/tmp/" + getAltTablespace() + "'", + "GRANT ALL PRIVILEGES ON DATABASE " + getCatalog() + " TO " + getUsername(), + "GRANT ALL PRIVILEGES ON DATABASE " + getAltCatalog() + " TO " + getUsername(), + "GRANT ALL PRIVILEGES ON SCHEMA " + getAltSchema() + " TO " + getUsername() + }; + } + +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/SQLiteTestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/SQLiteTestSystem.java new file mode 100644 index 00000000000..8e34b374fa2 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/SQLiteTestSystem.java @@ -0,0 +1,31 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.JdbcDatabaseWrapper; +import org.jetbrains.annotations.NotNull; + +import java.sql.SQLException; + +public class SQLiteTestSystem extends DatabaseTestSystem { + + public SQLiteTestSystem() { + super("sqlite"); + } + + public SQLiteTestSystem(Definition definition) { + super(definition); + } + + @Override + protected @NotNull DatabaseWrapper createContainerWrapper() throws Exception { + throw new IllegalArgumentException("Cannot create sqlite container. Use url"); + } + + @Override + protected String[] getSetupSql() { + return new String[]{ +// "create schema "+getAltSchema(), + }; + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/SybaseASATestSystem.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/SybaseASATestSystem.java new file mode 100644 index 00000000000..ae1ff347f92 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/core/SybaseASATestSystem.java @@ -0,0 +1,27 @@ +package liquibase.extension.testing.testsystem.core; + +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.wrapper.DatabaseWrapper; +import liquibase.extension.testing.testsystem.wrapper.UnimplementedWrapper; +import org.jetbrains.annotations.NotNull; + +public class SybaseASATestSystem extends DatabaseTestSystem { + + public SybaseASATestSystem() { + super("asany"); + } + + public SybaseASATestSystem(Definition definition) { + super(definition); + } + + @Override + protected @NotNull DatabaseWrapper createContainerWrapper() throws Exception { + return new UnimplementedWrapper(); + } + + @Override + protected String[] getSetupSql() { + return new String[0]; + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/spock/LiquibaseIntegrationMethodInterceptor.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/spock/LiquibaseIntegrationMethodInterceptor.java new file mode 100644 index 00000000000..c8c3163a4c2 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/spock/LiquibaseIntegrationMethodInterceptor.java @@ -0,0 +1,75 @@ +package liquibase.extension.testing.testsystem.spock; + +import liquibase.Scope; +import liquibase.extension.testing.testsystem.TestSystem; +import org.spockframework.runtime.extension.AbstractMethodInterceptor; +import org.spockframework.runtime.extension.IMethodInvocation; +import org.spockframework.runtime.model.FieldInfo; +import org.spockframework.runtime.model.SpecInfo; + +import java.util.ArrayList; +import java.util.List; + +public class LiquibaseIntegrationMethodInterceptor extends AbstractMethodInterceptor { + + private final SpecInfo spec; + private final LiquibaseIntegrationTestExtension.ErrorListener errorListener; + + LiquibaseIntegrationMethodInterceptor(SpecInfo spec, LiquibaseIntegrationTestExtension.ErrorListener errorListener) { + this.spec = spec; + this.errorListener = errorListener; + } + + @Override + public void interceptSetupSpecMethod(IMethodInvocation invocation) throws Throwable { + final List containers = findAllContainers(); + startContainers(containers, invocation); + + invocation.proceed(); + } + + @Override + public void interceptCleanupSpecMethod(IMethodInvocation invocation) throws Throwable { + final List containers = findAllContainers(); + stopContainers(containers, invocation); + + invocation.proceed(); + } + + + private List findAllContainers() { + List returnList = new ArrayList<>(); + for (FieldInfo fieldInfo : spec.getAllFields()) { + if (TestSystem.class.isAssignableFrom(fieldInfo.getType())) { + assert fieldInfo.isShared() : "TestEnvironment field " + fieldInfo.getName() + " must be @Shared"; + returnList.add(fieldInfo); + } + } + return returnList; + } + + private static void startContainers(List containers, IMethodInvocation invocation) throws Exception { + for (FieldInfo field : containers) { + TestSystem env = readContainerFromField(field, invocation); + env.start(); + + } + } + + private void stopContainers(List containers, IMethodInvocation invocation) throws Exception { + for (FieldInfo field : containers) { + TestSystem testSystem = readContainerFromField(field, invocation); + + try { + testSystem.stop(); + } catch (Exception e) { + Scope.getCurrentScope().getLog(getClass()).warning("Cannot stop "+testSystem.getDefinition()); + } + + } + } + + private static TestSystem readContainerFromField(FieldInfo f, IMethodInvocation invocation) { + return (TestSystem) f.readValue(invocation.getInstance()); + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/spock/LiquibaseIntegrationTest.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/spock/LiquibaseIntegrationTest.java new file mode 100644 index 00000000000..f803db43443 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/spock/LiquibaseIntegrationTest.java @@ -0,0 +1,12 @@ +package liquibase.extension.testing.testsystem.spock; + +import org.spockframework.runtime.extension.ExtensionAnnotation; + +import java.lang.annotation.*; + +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +@ExtensionAnnotation(LiquibaseIntegrationTestExtension.class) +public @interface LiquibaseIntegrationTest { +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/spock/LiquibaseIntegrationTestExtension.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/spock/LiquibaseIntegrationTestExtension.java new file mode 100644 index 00000000000..ac483a16587 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/spock/LiquibaseIntegrationTestExtension.java @@ -0,0 +1,39 @@ +package liquibase.extension.testing.testsystem.spock; + +import org.spockframework.runtime.AbstractRunListener; +import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension; +import org.spockframework.runtime.model.ErrorInfo; +import org.spockframework.runtime.model.SpecInfo; + +import java.util.ArrayList; +import java.util.List; + +public class LiquibaseIntegrationTestExtension extends AbstractAnnotationDrivenExtension { + @Override + public void visitSpecAnnotation(LiquibaseIntegrationTest annotation, SpecInfo spec) { + final ErrorListener listener = new ErrorListener(); + + LiquibaseIntegrationMethodInterceptor interceptor = new LiquibaseIntegrationMethodInterceptor(spec, listener); + spec.addSetupSpecInterceptor(interceptor); + spec.addCleanupSpecInterceptor(interceptor); + spec.addSetupInterceptor(interceptor); + spec.addCleanupInterceptor(interceptor); + + spec.addListener(listener); + + } + + public static class ErrorListener extends AbstractRunListener { + + private final List errors = new ArrayList<>(); + + @Override + public void error(ErrorInfo error) { + errors.add(error); + } + + public List getErrors() { + return errors; + } + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/wrapper/DatabaseWrapper.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/wrapper/DatabaseWrapper.java new file mode 100644 index 00000000000..f74b26194a5 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/wrapper/DatabaseWrapper.java @@ -0,0 +1,31 @@ +package liquibase.extension.testing.testsystem.wrapper; + +/** + * Wraps the external database used by {@link liquibase.extension.testing.testsystem.DatabaseTestSystem} + * so that HOW the database is interacted with is indepenent of WHAT we do with that connection. + * For example, the same setup logic can be applied regardless of whether the wrapped database is accessed via {@link JdbcDatabaseWrapper} or {@link DockerDatabaseWrapper}. + */ +public abstract class DatabaseWrapper { + + /** + * Start the database if possible and ensure it can be connected to. + * If the database is managed externally, just ensure it can be connected to. + */ + public abstract void start() throws Exception; + + + /** + * Stop the database if possible. + * If the database is managed externally, do not actually stop it. + */ + public abstract void stop() throws Exception; + + public abstract String getUsername(); + + public abstract String getUrl(); + + /** + * Describes the configuration of this wrapper. Used in outputting to user how this connection is configured. + */ + public abstract String describe(); +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/wrapper/DockerDatabaseWrapper.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/wrapper/DockerDatabaseWrapper.java new file mode 100644 index 00000000000..7b2ce7318ff --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/wrapper/DockerDatabaseWrapper.java @@ -0,0 +1,165 @@ +package liquibase.extension.testing.testsystem.wrapper; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.CreateContainerCmd; +import com.github.dockerjava.api.model.*; +import liquibase.Scope; +import liquibase.configuration.ConfigurationValueConverter; +import liquibase.configuration.ConfiguredValue; +import liquibase.configuration.LiquibaseConfiguration; +import liquibase.exception.UnexpectedLiquibaseException; +import liquibase.extension.testing.testsystem.TestSystem; +import liquibase.util.CollectionUtil; +import liquibase.util.StringUtil; +import org.testcontainers.containers.JdbcDatabaseContainer; + +import java.util.*; +import java.util.function.Consumer; + +/** + * Implementation of {@link DatabaseWrapper} for databases that are managed via docker in {@link JdbcDatabaseContainer}s. + */ +public class DockerDatabaseWrapper extends DatabaseWrapper { + + public static final String TEST_SYSTEM_LABEL = "org.liquibase.testSystem"; + + private final JdbcDatabaseContainer container; + private final TestSystem testSystem; + + private static final Set alreadyRunningContainerIds = new HashSet<>(); + + public DockerDatabaseWrapper(JdbcDatabaseContainer container, TestSystem testSystem) { + this.container = container; + this.testSystem = testSystem; + } + + @Override + public String describe() { + return "Docker image: " + container.getDockerImageName() + "\n" + + "Container name: " + container.getContainerName() + "\n" + + "Exposed ports: " + StringUtil.join(container.getExposedPorts(), ",") + "\n" + + "Reusable: " + container.isShouldBeReused(); + + } + + @Override + public void start() throws Exception { + if (container.isRunning()) { + return; + } + + final DockerClient dockerClient = container.getDockerClient(); + for (Container container : dockerClient.listContainersCmd().exec()) { + final String containerTestSystem = container.getLabels().get(TEST_SYSTEM_LABEL); + if (containerTestSystem != null && containerTestSystem.equals(testSystem.getDefinition().toString())) { + break; + } + } + + container.withReuse(testSystem.getKeepRunning()); + + if (testSystem.getKeepRunning()) { + mapPorts(container); + } + + container.withLabel(TEST_SYSTEM_LABEL, testSystem.getDefinition().toString()); + + final Runnable licenseAccept = this.requireLicense(); + if (licenseAccept != null) { + final ConfiguredValue acceptedLicenses = Scope.getCurrentScope().getSingleton(LiquibaseConfiguration.class).getCurrentConfiguredValue(ConfigurationValueConverter.STRING, null, "liquibase.sdk.testSystem.acceptLicenses"); + + Set accepted = new HashSet<>(); + if (acceptedLicenses != null) { + accepted.addAll(CollectionUtil.createIfNull(StringUtil.splitAndTrim(acceptedLicenses.getValue(), ","))); + } + + if (accepted.contains(this.testSystem.getDefinition().getName())) { + Scope.getCurrentScope().getLog(getClass()).fine("User accepted license for "+container.getDockerImageName()+" in liquibase.sdk.testSystem.acceptLicenses"); + licenseAccept.run(); + } else { + throw new UnexpectedLiquibaseException("Container "+container.getDockerImageName()+" requires accepting a license. If you accept its license, add '"+testSystem.getDefinition().getName()+"' to liquibase.sdk.testSystem.acceptLicenses or use the --accept-license flag in the `liquibase sdk system up` CLI"); + } + + } + + container.start(); + + Scope.getCurrentScope().getLog(getClass()).info("Running " + testSystem.getDefinition() + " as container " + container.getContainerName()); + } + + /** + * If the container requires the user accepting a license, return a Runnable which will accept it. + * @return a non-null value if {@link #start()} should call the Runnable if the user accepted the license. + */ + protected Runnable requireLicense() { + return null; + } + + protected void mapPorts(JdbcDatabaseContainer container) { + int[] ports = testSystem.getConfiguredValue("ports", value -> { + if (value == null) { + return null; + } + final String[] portStrings = String.valueOf(value).split("\\s*,\\s*"); + + int[] returnValue = new int[portStrings.length]; + for (int i = 0; i < portStrings.length; i++) { + returnValue[i] = Integer.parseInt(portStrings[i]); + } + + return returnValue; + }, false); + + if (ports == null) { + final List exposedPorts = container.getExposedPorts(); + ports = new int[exposedPorts.size()]; + for (int i = 0; i < exposedPorts.size(); i++) { + ports[i] = exposedPorts.get(i); + + } + } + + if (ports != null) { + List portBindings = new ArrayList<>(); + for (int port : ports) { + portBindings.add(new PortBinding(Ports.Binding.bindPort(port), new ExposedPort(port))); + } + + container.withCreateContainerCmdModifier((Consumer) cmd -> + cmd.withHostConfig(new HostConfig().withPortBindings(portBindings)) + ); + } + } + + @Override + public void stop() throws Exception { + if (container.isRunning()) { + container.stop(); + } else { + final DockerClient dockerClient = container.getDockerClient(); + final List containers = dockerClient.listContainersCmd().withLabelFilter(Collections.singletonMap(TEST_SYSTEM_LABEL, testSystem.getDefinition().toString())).exec(); + if (containers.size() == 0) { + throw new UnexpectedLiquibaseException("Cannot find running container " + testSystem.getDefinition().getName()); + } else { + for (Container container : containers) { + Scope.getCurrentScope().getLog(getClass()).info("Stopping container " + container.getId()); + dockerClient.stopContainerCmd(container.getId()).exec(); + } + } + } + } + + @Override + public String getUrl() { + return container.getJdbcUrl(); + } + + public JdbcDatabaseContainer getContainer() { + return container; + } + + @Override + public String getUsername() { + return container.getUsername(); + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/wrapper/JdbcDatabaseWrapper.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/wrapper/JdbcDatabaseWrapper.java new file mode 100644 index 00000000000..47bdff676c1 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/wrapper/JdbcDatabaseWrapper.java @@ -0,0 +1,50 @@ +package liquibase.extension.testing.testsystem.wrapper; + +import liquibase.exception.UnexpectedLiquibaseException; + +import java.sql.SQLException; + +/** + * Implementation of {@link DatabaseWrapper} for databases that are connected to via a JDBC url and are not "started" in a traditional sense. + */ +public class JdbcDatabaseWrapper extends DatabaseWrapper { + + private final String url; + private final String username; + private final String password; + + public JdbcDatabaseWrapper(String url, String username, String password) throws SQLException { + this.url = url; + this.username = username; + this.password = password; + } + + @Override + public String describe() { + return "Wrapped URL: " + url + "\n"; + } + + @Override + public void start() throws Exception { + + } + + @Override + public void stop() throws Exception { + throw new UnexpectedLiquibaseException("Cannot stop externally-managed database " + url); + } + + @Override + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + @Override + public String getUrl() { + return url; + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/wrapper/UnimplementedWrapper.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/wrapper/UnimplementedWrapper.java new file mode 100644 index 00000000000..a5f3d96606e --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/testsystem/wrapper/UnimplementedWrapper.java @@ -0,0 +1,36 @@ +package liquibase.extension.testing.testsystem.wrapper; + +import liquibase.exception.UnexpectedLiquibaseException; + +/** + * Wrapper for databases that are not yet implemented. + * + * @deprecated will remove when all TestSystems are created. + */ +public class UnimplementedWrapper extends DatabaseWrapper { + @Override + public void start() throws Exception { + throw new UnexpectedLiquibaseException("Unimplemented"); + } + + @Override + public void stop() throws Exception { + throw new UnexpectedLiquibaseException("Unimplemented"); + + } + + @Override + public String describe() { + throw new UnexpectedLiquibaseException("Unimplemented"); + } + + @Override + public String getUsername() { + throw new UnexpectedLiquibaseException("Unimplemented"); + } + + @Override + public String getUrl() { + throw new UnexpectedLiquibaseException("Unimplemented"); + } +} diff --git a/liquibase-extension-testing/src/main/java/liquibase/extension/testing/util/DownloadUtil.java b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/util/DownloadUtil.java new file mode 100644 index 00000000000..4e23510cd33 --- /dev/null +++ b/liquibase-extension-testing/src/main/java/liquibase/extension/testing/util/DownloadUtil.java @@ -0,0 +1,80 @@ +package liquibase.extension.testing.util; + +import liquibase.Scope; +import liquibase.exception.UnexpectedLiquibaseException; +import liquibase.logging.Logger; +import liquibase.util.StreamUtil; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class DownloadUtil { + + private DownloadUtil() { + + } + + public static Path downloadMavenArtifact(String coordinate) { + final String[] split = coordinate.split(":"); + if (split.length != 3) { + throw new IllegalArgumentException("Maven coordinates must be in the form groupId:artifactId:version"); + } + return downloadMavenArtifact(split[0], split[1], split[2]); + + } + + public static Path downloadMavenArtifact(String groupId, String artifactId, String version) { + final Logger log = Scope.getCurrentScope().getLog(DownloadUtil.class); + + Path path = Paths.get(System.getProperty("user.home"), + ".m2", + "repository", + groupId.replace(".", "/"), + artifactId, + version, + artifactId + "-" + version + ".jar"); + + final String url = "https://repo1.maven.org/maven2/" + groupId.replace(".", "/") + "/" + artifactId + "/" + version + "/" + artifactId + "-" + version + ".jar"; + + final File pathAsFile = path.toFile(); + if (pathAsFile.exists()) { + log.fine("Artifact " + groupId + ":" + artifactId + ":" + version + " is available at " + path); + } else { + log.info("Downloading " + url + " to " + path); + + try { + final HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setRequestMethod("GET"); + + if (!pathAsFile.getParentFile().exists() && !pathAsFile.getParentFile().mkdirs()) { + throw new UnexpectedLiquibaseException("Could not create "+pathAsFile.getAbsolutePath()+" directory"); + } + + try (final InputStream response = connection.getInputStream(); + FileOutputStream file = new FileOutputStream(pathAsFile)) { + StreamUtil.copy(response, file); + } + log.fine("Saved " + url + " to " + pathAsFile.getAbsolutePath()); + + path = pathAsFile.toPath(); + } catch (Exception e) { + throw new UnexpectedLiquibaseException("Error downloading from " + url + ": " + e.getMessage(), e); + } + } + + try { + System.out.println(path.toUri().toURL()); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + + return path; + + } +} diff --git a/liquibase-extension-testing/src/main/resources/META-INF/services/liquibase.command.CommandStep b/liquibase-extension-testing/src/main/resources/META-INF/services/liquibase.command.CommandStep new file mode 100644 index 00000000000..25a7168aec7 --- /dev/null +++ b/liquibase-extension-testing/src/main/resources/META-INF/services/liquibase.command.CommandStep @@ -0,0 +1,2 @@ +liquibase.extension.testing.testsystem.command.TestSystemDownCommand +liquibase.extension.testing.testsystem.command.TestSystemUpCommand diff --git a/liquibase-extension-testing/src/main/resources/META-INF/services/liquibase.configuration.ConfigurationValueProvider b/liquibase-extension-testing/src/main/resources/META-INF/services/liquibase.configuration.ConfigurationValueProvider new file mode 100644 index 00000000000..6af0add4385 --- /dev/null +++ b/liquibase-extension-testing/src/main/resources/META-INF/services/liquibase.configuration.ConfigurationValueProvider @@ -0,0 +1 @@ +liquibase.extension.testing.LiquibaseSdkConfigurationValueProvider diff --git a/liquibase-extension-testing/src/main/resources/META-INF/services/liquibase.extension.testing.testsystem.TestSystem b/liquibase-extension-testing/src/main/resources/META-INF/services/liquibase.extension.testing.testsystem.TestSystem new file mode 100644 index 00000000000..96cb48814e5 --- /dev/null +++ b/liquibase-extension-testing/src/main/resources/META-INF/services/liquibase.extension.testing.testsystem.TestSystem @@ -0,0 +1,16 @@ +liquibase.extension.testing.testsystem.core.CockroachTestSystem +liquibase.extension.testing.testsystem.core.DB2TestSystem +liquibase.extension.testing.testsystem.core.DB2zTestSystem +liquibase.extension.testing.testsystem.core.DerbyTestSystem +liquibase.extension.testing.testsystem.core.FirebirdTestSystem +liquibase.extension.testing.testsystem.core.H2TestSystem +liquibase.extension.testing.testsystem.core.HsqlTestSystem +liquibase.extension.testing.testsystem.core.HubTestSystem +liquibase.extension.testing.testsystem.core.InformixTestSystem +liquibase.extension.testing.testsystem.core.MariaDBTestSystem +liquibase.extension.testing.testsystem.core.MSSQLTestSystem +liquibase.extension.testing.testsystem.core.MySQLTestSystem +liquibase.extension.testing.testsystem.core.OracleTestSystem +liquibase.extension.testing.testsystem.core.PostgresTestSystem +liquibase.extension.testing.testsystem.core.SQLiteTestSystem +liquibase.extension.testing.testsystem.core.SybaseASATestSystem diff --git a/liquibase-extension-testing/src/main/resources/liquibase.sdk.yaml b/liquibase-extension-testing/src/main/resources/liquibase.sdk.yaml new file mode 100644 index 00000000000..4f016ed4735 --- /dev/null +++ b/liquibase-extension-testing/src/main/resources/liquibase.sdk.yaml @@ -0,0 +1,85 @@ +######## +## This file defines the default settings to connect to test systems understood by Liquibase core. +## +## To override values for your particular environment, create a liquibase.sdk.local.yaml file in the classpath and set +## whatever settings you want to be different than this default. +######## + +liquibase: + sdk: + testSystem: + default: + username: lbuser + password: LiquibasePass1 + catalog: lbcat + altCatalog: lbcat2 + altSchema: lbschem2 + altTablespace: liquibase2 + keepRunning: true + + test: hub,h2,hsqldb,sqlite + + hub: + url: http://localhost:8888 + + cockroachdb: + version: latest-v21.2 + imageName: cockroachdb/cockroach + password: "" + setup: + username: root + + derby: + url: jdbc:derby:memory:${catalog} + + db2: + imageName: ibmcom/db2 + version: 11.5.6.0a + + h2: + url: jdbc:h2:mem:${catalog};DB_CLOSE_DELAY=-1 + profiles: + "1.4": + version: 1.4.200 + "2.0": + + hsqldb: + url: jdbc:hsqldb:mem:${catalog} + + mariadb: + imageName: mariadb + version: 10.3 + setup: + username: root + + mssql: + imageName: mcr.microsoft.com/mssql/server + version: 2017-CU12 + setup: + username: SA + password: A_Str0ng_Required_Password + + + mysql: + version: 5.7 + imageName: mysql + setup: + username: root + profiles: + "5.7": + "8.0": + version: 8.0 + + oracle: + imageName: gvenzl/oracle-xe + version: 18-slim + setup: + username: system + + postgresql: + imageName: postgres + version: 9.6 + + sqlite: + url: "jdbc:sqlite::memory:" + diff --git a/liquibase-extension-testing/src/test/groovy/liquibase/extension/testing/testsystem/TestSystemDefinitionTest.groovy b/liquibase-extension-testing/src/test/groovy/liquibase/extension/testing/testsystem/TestSystemDefinitionTest.groovy new file mode 100644 index 00000000000..a33c8437dfd --- /dev/null +++ b/liquibase-extension-testing/src/test/groovy/liquibase/extension/testing/testsystem/TestSystemDefinitionTest.groovy @@ -0,0 +1,74 @@ +package liquibase.extension.testing.testsystem; + +import spock.lang.Specification +import spock.lang.Unroll; + +class TestSystemDefinitionTest extends Specification { + + @Unroll + def parse() { + expect: + TestSystem.Definition.parse(input).toString() == expected + + where: + input | expected + "test" | "test" + "test:prof1" | "test:prof1" + "test:prof1,prof2" | "test:prof1,prof2" + "test:prof2,prof1" | "test:prof2,prof1" + "test?key1=val1" | "test?key1=val1" + "test?key1=val1&key2=val2" | "test?key1=val1&key2=val2" + "test?key2=val2&key1=val1" | "test?key1=val1&key2=val2" + "test:prof1?key2=val2&key1=val1" | "test:prof1?key1=val1&key2=val2" + "test:prof1,prof2?key2=val2&key1=val1" | "test:prof1,prof2?key1=val1&key2=val2" + } + + def "parse null"() { + expect: + TestSystem.Definition.parse(null) == null + } + + @Unroll + def "equals, compareTo, and hashCode"() { + when: + def obj1 = TestSystem.Definition.parse(def1) + def obj2 = TestSystem.Definition.parse(def2) + + then: + obj1.equals(obj2) == equal + + if (equal) { + assert obj1.compareTo(obj2) == 0 + assert obj1.hashCode() == obj2.hashCode() + } else { + assert obj1.compareTo(obj2) != 0 + if (obj2 != null) { + assert obj1.hashCode() != obj2.hashCode() + } + } + + where: + def1 | def2 | equal + "test" | "test" | true + "test:x" | "test:x" | true + "test" | "test:x" | false + "test:x" | "test" | false + "test:x,y" | "test:y,x" | false + "test?k=v" | "test?k=v" | true + "test:x?k=v" | "test:x?k=v" | true + "test:x?k=X" | "test:x?k=v" | false + "test" | null | false + } + + def getProfiles() { + when: + def xyDefinition = TestSystem.Definition.parse("test:x,y") + + then: + TestSystem.Definition.parse("test").getProfiles().size() == 0 + xyDefinition.getProfiles().toArrayString() == "[x, y]" + + !xyDefinition.getProfiles().is(xyDefinition.getProfiles()) + + } +} diff --git a/liquibase-extension-testing/src/test/groovy/liquibase/extension/testing/testsystem/TestSystemFactoryTest.groovy b/liquibase-extension-testing/src/test/groovy/liquibase/extension/testing/testsystem/TestSystemFactoryTest.groovy new file mode 100644 index 00000000000..f54a782850c --- /dev/null +++ b/liquibase-extension-testing/src/test/groovy/liquibase/extension/testing/testsystem/TestSystemFactoryTest.groovy @@ -0,0 +1,37 @@ +package liquibase.extension.testing.testsystem + +import liquibase.Scope +import spock.lang.Specification +import spock.lang.Unroll + +class TestSystemFactoryTest extends Specification { + + def getTestSystem() { + when: + def factory = Scope.currentScope.getSingleton(TestSystemFactory) + + then: + factory.getTestSystem("mysql").is(factory.getTestSystem("mysql")) + factory.getTestSystem("mysql:x").is(factory.getTestSystem("mysql:x")) + !factory.getTestSystem("mysql").is(factory.getTestSystem("mysql:x")) + factory.getTestSystem("mysql?version=1.2").is(factory.getTestSystem("mysql?version=1.2")) + + factory.getTestSystem("mysql").toString() == "mysql" + factory.getTestSystem("mysql:x").toString() == "mysql:x" + } + + @Unroll + def "can construct all defined types: #name"() { + expect: + assert Scope.currentScope.getSingleton(TestSystemFactory).getTestSystem(name).getDefinition().getName() == name + + where: + name << Scope.currentScope.getSingleton(TestSystemFactory).getTestSystemNames() + + } + + def "getTestSystemNames"() { + expect: + Scope.currentScope.getSingleton(TestSystemFactory).getTestSystemNames().contains("mysql") + } +} diff --git a/liquibase-extension-testing/src/test/groovy/liquibase/extension/testing/testsystem/TestSystemTest.groovy b/liquibase-extension-testing/src/test/groovy/liquibase/extension/testing/testsystem/TestSystemTest.groovy new file mode 100644 index 00000000000..c5a0d34f6e2 --- /dev/null +++ b/liquibase-extension-testing/src/test/groovy/liquibase/extension/testing/testsystem/TestSystemTest.groovy @@ -0,0 +1,100 @@ +package liquibase.extension.testing.testsystem + +import liquibase.Scope +import liquibase.extension.testing.testsystem.core.H2TestSystem +import liquibase.plugin.Plugin +import spock.lang.Specification + +import java.sql.SQLException + +class TestSystemTest extends Specification { + + def shouldTest() { + expect: + !new ThisTestSystem().shouldTest() + new H2TestSystem().shouldTest() + } + + def getPriority() { + expect: + new ThisTestSystem().getPriority(TestSystem.Definition.parse("invalid")) == Plugin.PRIORITY_NOT_APPLICABLE + new ThisTestSystem().getPriority(TestSystem.Definition.parse("testing")) == Plugin.PRIORITY_DEFAULT + new ThisTestSystem().getPriority(TestSystem.Definition.parse("testing:profile")) == Plugin.PRIORITY_DEFAULT + } + + def getConfiguredValue() { + expect: + Scope.child([ + "liquibase.sdk.testSystem.other.prop1" : "other scoped val", + "liquibase.sdk.testSystem.testing.prop1": "system scoped val", + "liquibase.sdk.testSystem.default.prop1": "default scoped val", + ], { -> + assert new ThisTestSystem().getConfiguredValue("prop1", String, false) == "system scoped val" + } as Scope.ScopedRunner) + + Scope.child([ + "liquibase.sdk.testSystem.other.prop1" : "other scoped val", + "liquibase.sdk.testSystem.default.prop1": "default scoped val", + ], { -> + assert new ThisTestSystem().getConfiguredValue("prop1", String, false) == "default scoped val" + } as Scope.ScopedRunner) + + Scope.child([ + "liquibase.sdk.testSystem.other.prop1" : "other scoped val", + "liquibase.sdk.testSystem.testing.prop1" : "system scoped val", + "liquibase.sdk.testSystem.testing.profiles.x.prop1": "x scoped val", + "liquibase.sdk.testSystem.default.prop1" : "default scoped val", + ], { -> + + def system = new ThisTestSystem(TestSystem.Definition.parse("testing:x")) + assert system.getConfiguredValue("prop1", String, false) == "x scoped val" + } as Scope.ScopedRunner) + + Scope.child([ + "liquibase.sdk.testSystem.other.prop1" : "other scoped val", + "liquibase.sdk.testSystem.testing.prop1" : "system scoped val", + "liquibase.sdk.testSystem.testing.profiles.x.prop1": "x scoped val", + "liquibase.sdk.testSystem.default.prop1" : "default scoped val", + ], { -> + + def system = new ThisTestSystem(TestSystem.Definition.parse("testing:x?prop1=val1")) + assert system.getConfiguredValue("prop1", String, false) == "val1" + } as Scope.ScopedRunner) + + + Scope.child([ + "liquibase.sdk.testSystem.testing.prop1" : "val1", + "liquibase.sdk.testSystem.testing.prop2": "I see \${prop1}", + "liquibase.sdk.testSystem.testing.prop3": "I see \${ prop1 }", + ], { -> + + def system = new ThisTestSystem(TestSystem.Definition.parse("testing")) + assert system.getConfiguredValue("prop1", String) == "val1" + assert system.getConfiguredValue("prop2", String) == "I see val1" + assert system.getConfiguredValue("prop3", String) == "I see val1" + } as Scope.ScopedRunner) + } + + private static class ThisTestSystem extends TestSystem { + + private boolean running = false; + + ThisTestSystem() { + super("testing") + } + + ThisTestSystem(TestSystem.Definition definition) { + super(definition) + } + + @Override + void start() throws SQLException, Exception { + running = true + } + + @Override + void stop() throws Exception { + running = false + } + } +} diff --git a/liquibase-integration-tests/docker/docker-compose.yml b/liquibase-integration-tests/docker/docker-compose.yml deleted file mode 100644 index 94a47e2d2d8..00000000000 --- a/liquibase-integration-tests/docker/docker-compose.yml +++ /dev/null @@ -1,80 +0,0 @@ -version: '3.3' - -services: - mysql: - image: mysql:5 - ports: - - "33061:3306" - restart: always - environment: - MYSQL_ROOT_PASSWORD: LbRootPass1 - MYSQL_DATABASE: lbcat - MYSQL_USER: lbuser - MYSQL_PASSWORD: LiquibasePass1 - networks: - - integration - volumes: - - "./mysql-init.sql:/docker-entrypoint-initdb.d/mysql-init.sql" - - postgres-9: - image: postgres:9 - ports: - - "5432:5432" - restart: always - environment: - POSTGRES_PASSWORD: LbRootPass1 - POSTGRES_DB: lbcat - networks: - - integration - volumes: - - "./postgres-init.sh:/docker-entrypoint-initdb.d/postgres-init.sh" - - # cockroachdb: - # image: cockroachdb/cockroach-unstable:v20.2.0-rc.3 - # command: start-single-node --logtostderr --insecure - # ports: - # - "26257:26257" - # - "8080:8080" - - mssql: - container_name: sql-server-db-2019 - image: mcr.microsoft.com/mssql/server:2019-latest - ports: - - "14333:1433" - environment: - SA_PASSWORD: "LiquibasePass1" - ACCEPT_EULA: "Y" - networks: - - integration - volumes: - - "./mssql-init.sh:/docker-entrypoint-initdb.d/mssql-init.sh" - - "./mssql-init.sql:/docker-entrypoint-initdb.d/mssql-init.sql" - entrypoint: /bin/bash /docker-entrypoint-initdb.d/mssql-init.sh - - mariadb: - image: mariadb:10.5 - ports: - - "33066:3306" - restart: always - environment: - MYSQL_ROOT_PASSWORD: LbRootPass1 - MYSQL_DATABASE: lbcat - MYSQL_USER: lbuser - MYSQL_PASSWORD: LiquibasePass1 - networks: - - integration - volumes: - - "./mariadb-init.sql:/docker-entrypoint-initdb.d/mariadb-init.sql" - - oracle: - image: docker-dev.artifactory.datical.net/datical/oracle-ee:19.3.0-local - networks: - - integration - volumes: - - "./scripts_local/18_19:/opt/oracle/scripts/startup" - ports: - - "1521:1521" - restart: always - -networks: - integration: diff --git a/liquibase-integration-tests/docker/mariadb-init.sql b/liquibase-integration-tests/docker/mariadb-init.sql deleted file mode 100644 index 08f5ebca29e..00000000000 --- a/liquibase-integration-tests/docker/mariadb-init.sql +++ /dev/null @@ -1,4 +0,0 @@ -create database lbcat2; - -GRANT ALL PRIVILEGES ON lbcat.* TO 'lbuser'@'%'; -GRANT ALL PRIVILEGES ON lbcat2.* TO 'lbuser'@'%'; \ No newline at end of file diff --git a/liquibase-integration-tests/docker/mssql-init.sh b/liquibase-integration-tests/docker/mssql-init.sh deleted file mode 100644 index e95af4264f6..00000000000 --- a/liquibase-integration-tests/docker/mssql-init.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -/opt/mssql/bin/sqlservr & - -echo "Waiting for server to start...." -#do this in a loop because the timing for when the SQL instance is ready is indeterminate -for i in {1..50}; -do - /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P ${SA_PASSWORD} -d master -i /docker-entrypoint-initdb.d/mssql-init.sql - if [ $? -eq 0 ] - then - echo "mssql-init.sh completed" - break - else - echo "not ready yet..." - sleep 5 - fi -done - -sleep infinity \ No newline at end of file diff --git a/liquibase-integration-tests/docker/mssql-init.sql b/liquibase-integration-tests/docker/mssql-init.sql deleted file mode 100644 index ce32b12f90e..00000000000 --- a/liquibase-integration-tests/docker/mssql-init.sql +++ /dev/null @@ -1,40 +0,0 @@ -DECLARE @dataPath varchar(256); -DECLARE @logPath varchar(256); -SET @dataPath=(SELECT CAST(serverproperty('InstanceDefaultDataPath') AS varchar(256))); -SET @logPath=(SELECT CAST(serverproperty('InstanceDefaultLogPath') AS varchar(256))); - -CREATE LOGIN [lbuser] with password=N'LiquibasePass1', CHECK_EXPIRATION=OFF; -GO - -CREATE DATABASE lbcat; -GO - -EXEC lbcat..sp_addsrvrolemember @loginame = N'lbuser', @rolename = N'sysadmin' -GO - -/* By default, we set the compatibility level to the oldest version we are officially supporting. Note that there - * are differences in behaviour, e.g. with implicit conversions of date and time values. See - * https://docs.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql for details. - */ -ALTER DATABASE [lbcat] SET COMPATIBILITY_LEVEL = 100 - GO - - USE [lbcat] - GO -ALTER DATABASE [lbcat] MODIFY FILEGROUP [PRIMARY] DEFAULT - GO -ALTER DATABASE [lbcat] ADD FILEGROUP [liquibase2] - GO - -DECLARE @dataPath varchar(256); -DECLARE @logPath varchar(256); -SET @dataPath=(SELECT CAST(serverproperty('InstanceDefaultDataPath') AS varchar(256))); -SET @logPath=(SELECT CAST(serverproperty('InstanceDefaultLogPath') AS varchar(256))); - -DECLARE @createSql varchar(2000); -SET @createSql = (SELECT 'ALTER DATABASE [lbcat] ADD FILE ( NAME = N''liquibase2'', FILENAME = N''' + @dataPath + 'liquibase2.ndf'' , SIZE = 8192KB , FILEGROWTH = 65536KB ) TO FILEGROUP [liquibase2]'); -EXECUTE(@createSql); -GO - -CREATE SCHEMA [lbcat2] AUTHORIZATION [dbo] - GO diff --git a/liquibase-integration-tests/docker/mysql-init.sql b/liquibase-integration-tests/docker/mysql-init.sql deleted file mode 100644 index dc5fe15f417..00000000000 --- a/liquibase-integration-tests/docker/mysql-init.sql +++ /dev/null @@ -1,5 +0,0 @@ -create database lbcat2; - -GRANT ALL PRIVILEGES ON lbcat.* TO 'lbuser'@'%'; -GRANT ALL PRIVILEGES ON lbcat2.* TO 'lbuser'@'%'; - diff --git a/liquibase-integration-tests/docker/postgres-init.sh b/liquibase-integration-tests/docker/postgres-init.sh deleted file mode 100644 index e7f1b1fc057..00000000000 --- a/liquibase-integration-tests/docker/postgres-init.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -set -e - -mkdir -p /tmp/tablespace/liquibase2 - -psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL - CREATE USER lbuser WITH PASSWORD 'LiquibasePass1'; - CREATE DATABASE lbcat2; - CREATE SCHEMA lbcat2; - CREATE TABLESPACE liquibase2 OWNER lbuser LOCATION '/tmp/tablespace/liquibase2'; - GRANT ALL PRIVILEGES ON DATABASE lbcat TO lbuser; - GRANT ALL PRIVILEGES ON DATABASE lbcat2 TO lbuser; - GRANT ALL PRIVILEGES ON SCHEMA lbcat2 TO lbuser; -EOSQL \ No newline at end of file diff --git a/liquibase-integration-tests/docker/scripts_local/18_19/01_updateSystem.sql b/liquibase-integration-tests/docker/scripts_local/18_19/01_updateSystem.sql deleted file mode 100644 index f681cd2518f..00000000000 --- a/liquibase-integration-tests/docker/scripts_local/18_19/01_updateSystem.sql +++ /dev/null @@ -1,13 +0,0 @@ -SET ECHO ON; -WHENEVER SQLERROR EXIT SQL.SQLCODE; - -ALTER USER SYSTEM IDENTIFIED BY "R2B09T6iC4zxBTZ8"; - --- set undo tablespace to unlimited -ALTER DATABASE datafile '/opt/oracle/oradata/ORCLCDB/undotbs01.dbf' autoextend ON maxsize UNLIMITED; - -ALTER SYSTEM SET recyclebin = OFF DEFERRED; -ALTER SYSTEM SET "_cdb_disable_pdb_limit" =true scope=SPFILE; - -SHUTDOWN IMMEDIATE; -STARTUP; \ No newline at end of file diff --git a/liquibase-integration-tests/docker/scripts_local/18_19/02_addPdbs.sql b/liquibase-integration-tests/docker/scripts_local/18_19/02_addPdbs.sql deleted file mode 100644 index ed2d1f0ac66..00000000000 --- a/liquibase-integration-tests/docker/scripts_local/18_19/02_addPdbs.sql +++ /dev/null @@ -1,36 +0,0 @@ -SET ECHO ON; -WHENEVER SQLERROR EXIT SQL.SQLCODE; --- --- Check that PDB's are running --- -COLUMN CON_ID FORMAT 9999999; -COLUMN NAME FORMAT A20; -COLUMN OPEN_MODE FORMAT A20; -SELECT CON_ID, NAME, OPEN_MODE FROM V$PDBS; - - -SET ECHO ON; --- --- Clone the SEED database and create the pluggable databases --- - --- Creating PDB lbcat -ALTER SESSION SET PDB_FILE_NAME_CONVERT='/opt/oracle/oradata/ORCLCDB/pdbseed', '/opt/oracle/oradata/ORCLCDB/lbcat', '/opt/oracle/oradata/ORCLCDB/pdbseed/system01.dbf', '/opt/oracle/oradata/ORCLCDB/lbcat/system01.dbf'; -CREATE PLUGGABLE DATABASE lbcat ADMIN USER DATICAL_ADMIN IDENTIFIED BY DATICAL_ADMIN_PW; -ALTER PLUGGABLE DATABASE lbcat OPEN READ WRITE; -ALTER PLUGGABLE DATABASE lbcat SAVE STATE; - - --- Creating PDB lbcat2 -ALTER SESSION SET PDB_FILE_NAME_CONVERT='/opt/oracle/oradata/ORCLCDB/pdbseed', '/opt/oracle/oradata/ORCLCDB/lbcat2', '/opt/oracle/oradata/ORCLCDB/pdbseed/system01.dbf', '/opt/oracle/oradata/ORCLCDB/lbcat2/system01.dbf'; -CREATE PLUGGABLE DATABASE lbcat2 ADMIN USER DATICAL_ADMIN IDENTIFIED BY DATICAL_ADMIN_PW; -ALTER PLUGGABLE DATABASE lbcat2 OPEN READ WRITE; -ALTER PLUGGABLE DATABASE lbcat2 SAVE STATE; - --- --- Check that PDB's are running --- -COLUMN CON_ID FORMAT 9999999; -COLUMN NAME FORMAT A20; -COLUMN OPEN_MODE FORMAT A20; -SELECT CON_ID, NAME, OPEN_MODE FROM V$PDBS; \ No newline at end of file diff --git a/liquibase-integration-tests/docker/scripts_local/18_19/03_addUsers.sql b/liquibase-integration-tests/docker/scripts_local/18_19/03_addUsers.sql deleted file mode 100644 index 1f7b491c235..00000000000 --- a/liquibase-integration-tests/docker/scripts_local/18_19/03_addUsers.sql +++ /dev/null @@ -1,43 +0,0 @@ -SET ECHO ON; -WHENEVER SQLERROR EXIT SQL.SQLCODE; - --- Set the session container to lbcat -PROMPT "configuring lbcat" -ALTER SESSION SET container=lbcat; - --- Create lbcat USERS tablespace -CREATE TABLESPACE USERS - DATAFILE '/opt/oracle/oradata/ORCLCDB/lbcat/users01.dbf' SIZE 256K - AUTOEXTEND ON NEXT 256K; -ALTER PLUGGABLE DATABASE DEFAULT TABLESPACE USERS; - -CREATE TABLESPACE LIQUIBASE2 - DATAFILE '/opt/oracle/oradata/ORCLCDB/lbcat/liquibase2.dbf' SIZE 20M - AUTOEXTEND ON; - --- Create roles and "lbuser" user -@/opt/oracle/scripts/startup/support/roles.sql lbcat - --- ----------------------------------------------------------------------------- - --- Set the session container to lbcat2 -PROMPT "configuring lbcat2" -ALTER SESSION SET container=lbcat2; - --- Create lbcat2 USERS tablespace -CREATE TABLESPACE USERS - DATAFILE '/opt/oracle/oradata/ORCLCDB/lbcat2/users01.dbf' SIZE 256K - AUTOEXTEND ON NEXT 256K; -ALTER PLUGGABLE DATABASE DEFAULT TABLESPACE USERS; - -CREATE TABLESPACE LIQUIBASE2 - DATAFILE '/opt/oracle/oradata/ORCLCDB/lbcat2/liquibase2.dbf' SIZE 20M - AUTOEXTEND ON; - --- Create necessary roles and users -@/opt/oracle/scripts/startup/support/roles.sql lbcat2 - --- --- Reset the session container --- -ALTER SESSION SET CONTAINER=CDB$ROOT; \ No newline at end of file diff --git a/liquibase-integration-tests/docker/scripts_local/18_19/04_misc.sql b/liquibase-integration-tests/docker/scripts_local/18_19/04_misc.sql deleted file mode 100644 index 88160252f15..00000000000 --- a/liquibase-integration-tests/docker/scripts_local/18_19/04_misc.sql +++ /dev/null @@ -1,19 +0,0 @@ -SET ECHO ON; -WHENEVER SQLERROR EXIT SQL.SQLCODE; - - -DROP PUBLIC SYNONYM LOCAL_CHUNK_TYPES; -DROP PUBLIC SYNONYM LOCAL_CHUNKS; -DROP PUBLIC SYNONYM SHA_DATABASES; -DROP PUBLIC SYNONYM LOCAL_CHUNK_COLUMNS; - --- set processes to higher limit to avoid ORA-39014, ORA-39029, ORA-31671, ORA-00600 -ALTER SYSTEM SET open_cursors=1024 scope=both sid='*'; -ALTER SYSTEM SET "_optimizer_cost_based_transformation"=off scope=both sid='*'; ---ALTER SYSTEM SET processes=1000 scope=spfile; ---ALTER SYSTEM SET sessions=665 scope=spfile; ---ALTER SYSTEM SET transactions=700 scope=spfile; - --- Restart database after setting higher process limit -SHUTDOWN IMMEDIATE; -STARTUP; \ No newline at end of file diff --git a/liquibase-integration-tests/docker/scripts_local/18_19/support/roles.sql b/liquibase-integration-tests/docker/scripts_local/18_19/support/roles.sql deleted file mode 100644 index 11e4e5f6b63..00000000000 --- a/liquibase-integration-tests/docker/scripts_local/18_19/support/roles.sql +++ /dev/null @@ -1,127 +0,0 @@ -SET ECHO ON; -WHENEVER SQLERROR EXIT SQL.SQLCODE; -PROMPT "Starting roles.sql" - --- Value passed in from 03_addUsers.sql -SELECT '&1' FROM DUAL; - ---CREATE Roles ---Create LIQUIBASE_ROLE -CREATE ROLE LIQUIBASE_ROLE NOT IDENTIFIED; -GRANT ADMINISTER DATABASE TRIGGER TO LIQUIBASE_ROLE; -GRANT ALTER ANY INDEX TO LIQUIBASE_ROLE; -GRANT ALTER ANY MATERIALIZED VIEW TO LIQUIBASE_ROLE; -GRANT ALTER ANY PROCEDURE TO LIQUIBASE_ROLE; -GRANT ALTER ANY SEQUENCE TO LIQUIBASE_ROLE; -GRANT ALTER ANY TABLE TO LIQUIBASE_ROLE; -GRANT ALTER ANY TRIGGER TO LIQUIBASE_ROLE; -GRANT ALTER ANY TYPE TO LIQUIBASE_ROLE; -GRANT ALTER SESSION TO LIQUIBASE_ROLE; -GRANT GRANT ANY OBJECT PRIVILEGE TO LIQUIBASE_ROLE; -GRANT CREATE ANY DIRECTORY TO LIQUIBASE_ROLE; -GRANT CREATE ANY INDEX TO LIQUIBASE_ROLE; --- Oracle Oddity: Remember to grant "create table" to the schema owner, or Datical will not be able to create materialized views in that schema. --- ex., GRANT CREATE TABLE TO ; -GRANT CREATE ANY MATERIALIZED VIEW TO LIQUIBASE_ROLE; -GRANT CREATE ANY PROCEDURE TO LIQUIBASE_ROLE; -GRANT CREATE ANY SEQUENCE TO LIQUIBASE_ROLE; -GRANT CREATE ANY SYNONYM TO LIQUIBASE_ROLE; -GRANT CREATE ANY TABLE TO LIQUIBASE_ROLE; -GRANT CREATE ANY TRIGGER TO LIQUIBASE_ROLE; -GRANT CREATE ANY TYPE TO LIQUIBASE_ROLE; -GRANT CREATE ANY VIEW TO LIQUIBASE_ROLE; -GRANT CREATE SESSION TO LIQUIBASE_ROLE; -GRANT DELETE ANY TABLE TO LIQUIBASE_ROLE WITH ADMIN OPTION; -GRANT DROP ANY DIRECTORY TO LIQUIBASE_ROLE; -GRANT DROP ANY INDEX TO LIQUIBASE_ROLE; -GRANT DROP ANY MATERIALIZED VIEW TO LIQUIBASE_ROLE; -GRANT DROP ANY PROCEDURE TO LIQUIBASE_ROLE; -GRANT DROP ANY SEQUENCE TO LIQUIBASE_ROLE; -GRANT DROP ANY SYNONYM TO LIQUIBASE_ROLE; -GRANT DROP ANY TABLE TO LIQUIBASE_ROLE; -GRANT DROP ANY TRIGGER TO LIQUIBASE_ROLE; -GRANT DROP ANY TYPE TO LIQUIBASE_ROLE; -GRANT DROP ANY VIEW TO LIQUIBASE_ROLE; -GRANT EXECUTE ANY PROCEDURE TO LIQUIBASE_ROLE; -GRANT INSERT ANY TABLE TO LIQUIBASE_ROLE; -GRANT SELECT ANY DICTIONARY TO LIQUIBASE_ROLE; -GRANT SELECT ANY SEQUENCE TO LIQUIBASE_ROLE; -GRANT SELECT ANY TABLE TO LIQUIBASE_ROLE; -GRANT UPDATE ANY TABLE TO LIQUIBASE_ROLE ; -GRANT COMMENT ANY TABLE TO LIQUIBASE_ROLE; --- The following permission are needed for SNAPSHOT, COMPARE, FORECAST on all available schemas -GRANT SELECT ANY TABLE TO LIQUIBASE_ROLE; -GRANT SELECT ANY SEQUENCE TO LIQUIBASE_ROLE; -GRANT EXECUTE ANY PROCEDURE TO LIQUIBASE_ROLE; -GRANT CREATE ANY TRIGGER TO LIQUIBASE_ROLE; --- The following permissions are needed for auto generation of permissions -GRANT SELECT ANY DICTIONARY TO LIQUIBASE_ROLE; --- The following permissions are needed for working with EDITIONING VIEWS -GRANT CREATE ANY EDITION TO LIQUIBASE_ROLE; -GRANT DROP ANY EDITION TO LIQUIBASE_ROLE; --- The following permissions are needed for management of database scoped triggers -GRANT ADMINISTER DATABASE TRIGGER TO LIQUIBASE_ROLE; - --- The following must be run as sysdba --- SQL > conn / as sysdba --- The following permissions are needed to capture storage options for SNAPSHOT -GRANT EXECUTE on SYS.DBMS_METADATA TO LIQUIBASE_ROLE; -GRANT EXECUTE ON SYS.UTL_FILE TO LIQUIBASE_ROLE; - - --- Create lbuser USER -CREATE USER lbuser IDENTIFIED BY LiquibasePass1 -DEFAULT TABLESPACE "USERS" -TEMPORARY TABLESPACE "TEMP" -ACCOUNT UNLOCK; -ALTER USER lbuser QUOTA 100M ON USERS; -GRANT UNLIMITED TABLESPACE TO lbuser WITH ADMIN OPTION; --- Oracle Oddity: Remember to grant "create table" to the schema owner, or Datical will not be able to create materialized views in that schema. -GRANT CREATE TABLE TO lbuser WITH ADMIN OPTION; ---Grant Section --- Grant LIQUIBASE_ROLE role to the Datical User -GRANT LIQUIBASE_ROLE to lbuser with ADMIN OPTION; - --- Granting to the specific schema (not just the role) is required --- GRANT ALL ON DIRECTORY does not work with Oracle RDS use the following instead -GRANT READ ON DIRECTORY DATA_PUMP_DIR TO lbuser with GRANT OPTION; -GRANT WRITE ON DIRECTORY DATA_PUMP_DIR TO lbuser with GRANT OPTION; -GRANT CREATE SESSION TO lbuser WITH ADMIN OPTION; -GRANT CONNECT, RESOURCE TO lbuser WITH ADMIN OPTION; -GRANT EXP_FULL_DATABASE TO lbuser WITH ADMIN OPTION; -GRANT IMP_FULL_DATABASE TO lbuser WITH ADMIN OPTION; -GRANT SELECT ANY dictionary TO lbuser WITH ADMIN OPTION; -GRANT ALTER SESSION TO lbuser WITH ADMIN OPTION; -GRANT "DBA" TO lbuser; - - --- Create lbcat2 USER --- not sure why this user is needed, but not having it will result in an error. Setting grants the same as lbuser -CREATE USER lbcat2 IDENTIFIED BY LiquibasePass1 -DEFAULT TABLESPACE "USERS" -TEMPORARY TABLESPACE "TEMP" -ACCOUNT UNLOCK; -ALTER USER lbcat2 QUOTA 100M ON USERS; -GRANT UNLIMITED TABLESPACE TO lbcat2 WITH ADMIN OPTION; --- Oracle Oddity: Remember to grant "create table" to the schema owner, or Datical will not be able to create materialized views in that schema. -GRANT CREATE TABLE TO lbcat2 WITH ADMIN OPTION; ---Grant Section --- Grant LIQUIBASE_ROLE role to the Datical User -GRANT LIQUIBASE_ROLE to lbcat2 with ADMIN OPTION; - --- Granting to the specific schema (not just the role) is required --- GRANT ALL ON DIRECTORY does not work with Oracle RDS use the following instead -GRANT READ ON DIRECTORY DATA_PUMP_DIR TO lbcat2 with GRANT OPTION; -GRANT WRITE ON DIRECTORY DATA_PUMP_DIR TO lbcat2 with GRANT OPTION; -GRANT CREATE SESSION TO lbcat2 WITH ADMIN OPTION; -GRANT CONNECT, RESOURCE TO lbcat2 WITH ADMIN OPTION; -GRANT EXP_FULL_DATABASE TO lbcat2 WITH ADMIN OPTION; -GRANT IMP_FULL_DATABASE TO lbcat2 WITH ADMIN OPTION; -GRANT SELECT ANY dictionary TO lbcat2 WITH ADMIN OPTION; -GRANT ALTER SESSION TO lbcat2 WITH ADMIN OPTION; - --- try adding dba back to lbcat2 to fix things: -GRANT "DBA" TO lbcat2; - - -PROMPT "roles.sql complete" \ No newline at end of file diff --git a/liquibase-integration-tests/pom.xml b/liquibase-integration-tests/pom.xml index f178e12e4d7..f384032a398 100644 --- a/liquibase-integration-tests/pom.xml +++ b/liquibase-integration-tests/pom.xml @@ -47,73 +47,4 @@ - - - - oracle - - - com.oracle.jdbc - ojdbc7 - 12.1.0.2 - - - - - - db2 - - - com.ibm.db2.jcc - db2jcc4 - 4.22.29 - test - - - - - - sap-jconnect - - - com.sybase.jdbc4.utils - jconnect - 16.0.SP02.PL05 - test - - - - - - diff --git a/liquibase-integration-tests/src/test/groovy/liquibase/hub/StandardHubServiceTest.groovy b/liquibase-integration-tests/src/test/groovy/liquibase/hub/StandardHubServiceTest.groovy index a23ff624f97..c37cbd91834 100644 --- a/liquibase-integration-tests/src/test/groovy/liquibase/hub/StandardHubServiceTest.groovy +++ b/liquibase-integration-tests/src/test/groovy/liquibase/hub/StandardHubServiceTest.groovy @@ -1,7 +1,8 @@ package liquibase.hub import liquibase.Scope -import liquibase.configuration.LiquibaseConfiguration +import liquibase.extension.testing.testsystem.TestSystemFactory +import liquibase.extension.testing.testsystem.core.HubTestSystem import liquibase.hub.core.StandardHubService import liquibase.hub.model.Connection import spock.lang.Specification @@ -11,8 +12,7 @@ import static org.junit.Assume.assumeTrue class StandardHubServiceTest extends Specification { private StandardHubService hubService - - private Properties integrationTestProperties + private HubTestSystem hubTestSystem = (HubTestSystem) Scope.getCurrentScope().getSingleton(TestSystemFactory.class).getTestSystem("hub") private UUID knownProjectId = UUID.fromString("ce1a237e-e005-4288-a243-4856823a25a6") private UUID knownConnectionId = UUID.fromString("d92e6505-cd0f-4e91-bb66-b12e6a285184") @@ -20,21 +20,10 @@ class StandardHubServiceTest extends Specification { private String testScopeId def setup() { - if (integrationTestProperties == null) { - integrationTestProperties = new Properties() - integrationTestProperties.load((InputStream) Thread.currentThread().getContextClassLoader().getResourceAsStream("liquibase/liquibase.integrationtest.properties")) - - def localFileStream = (InputStream) Thread.currentThread().getContextClassLoader().getResourceAsStream("liquibase/liquibase.integrationtest.local.properties") - if (localFileStream != null) { - integrationTestProperties.load(localFileStream) - } - - testScopeId = Scope.enter([ - (HubConfiguration.LIQUIBASE_HUB_API_KEY.getKey()):integrationTestProperties.get("integration.test.hub.apikey"), - (HubConfiguration.LIQUIBASE_HUB_URL.getKey()):integrationTestProperties.get("integration.test.hub.url"), - ]) - - } + testScopeId = Scope.enter([ + (HubConfiguration.LIQUIBASE_HUB_API_KEY.getKey()): hubTestSystem.getApiKey(), + (HubConfiguration.LIQUIBASE_HUB_URL.getKey()) : hubTestSystem.getUrl(), + ]) hubService = new StandardHubService() assumeTrue("Liquibase Hub is not available for testing", hubService.isHubAvailable()) @@ -46,7 +35,7 @@ class StandardHubServiceTest extends Specification { def getMe() { when: - def hubUser = integrationTestProperties.get("integration.test.hub.userName") + def hubUser = hubTestSystem.getUsername() def me = hubService.getMe() then: @@ -57,7 +46,7 @@ class StandardHubServiceTest extends Specification { def getOrganization() { when: - def hubOrgId = UUID.fromString(integrationTestProperties.get("integration.test.hub.orgId")) + def hubOrgId = hubTestSystem.getOrgId() def org = hubService.getOrganization() then: @@ -107,8 +96,8 @@ class StandardHubServiceTest extends Specification { def "getConnections can return all connections"() { when: String jdbcUrl = integrationTestProperties.get("integration.test.oracle.url") - jdbcUrl = jdbcUrl.replaceAll("//","") - def hubUrl = integrationTestProperties.get("integration.test.hub.url") + jdbcUrl = jdbcUrl.replaceAll("//", "") + def hubUrl = hubTestSystem.getUrl() def connections = hubService.getConnections(null) then: diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/AbstractIntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/AbstractIntegrationTest.java index 17650a06d01..5b57e48c597 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/AbstractIntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/AbstractIntegrationTest.java @@ -26,6 +26,9 @@ import liquibase.exception.ValidationFailedException; import liquibase.executor.Executor; import liquibase.executor.ExecutorService; +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.TestSystemFactory; +import liquibase.extension.testing.testsystem.core.HubTestSystem; import liquibase.hub.HubConfiguration; import liquibase.listener.SqlListener; import liquibase.lockservice.LockService; @@ -41,8 +44,6 @@ import liquibase.statement.core.CreateTableStatement; import liquibase.statement.core.DropTableStatement; import liquibase.structure.core.*; -import liquibase.test.DatabaseTestContext; -import liquibase.test.DatabaseTestURL; import liquibase.test.DiffResultAssert; import liquibase.test.JUnitResourceAccessor; import liquibase.util.RegexMatcher; @@ -69,12 +70,20 @@ */ public abstract class AbstractIntegrationTest { + public static final String ALT_TABLESPACE = "LIQUIBASE2"; + public static final String ALT_SCHEMA = "LBSCHEM2"; + public static final String ALT_CATALOG = "LBCAT2"; + + @Rule + public DatabaseTestSystem testSystem; + + @Rule + public HubTestSystem hubEnvironment; + @Rule public TemporaryFolder tempDirectory = new TemporaryFolder(); protected String completeChangeLog; protected String contexts = "test, context-b"; - protected String username; - protected String password; Set emptySchemas = new TreeSet<>(); Logger logger; private String rollbackChangeLog; @@ -86,10 +95,11 @@ public abstract class AbstractIntegrationTest { private String invalidReferenceChangeLog; private String objectQuotingStrategyChangeLog; private Database database; - private String jdbcUrl; private String defaultSchemaName; protected AbstractIntegrationTest(String changelogDir, Database dbms) throws Exception { + this.testSystem = (DatabaseTestSystem) Scope.getCurrentScope().getSingleton(TestSystemFactory.class).getTestSystem(dbms.getShortName()); + this.completeChangeLog = "changelogs/" + changelogDir + "/complete/root.changelog.xml"; this.rollbackChangeLog = "changelogs/" + changelogDir + "/rollback/rollbackable.changelog.xml"; this.includedChangeLog = "changelogs/" + changelogDir + "/complete/included.changelog.xml"; @@ -101,93 +111,21 @@ protected AbstractIntegrationTest(String changelogDir, Database dbms) throws Exc this.objectQuotingStrategyChangeLog = "changelogs/common/object.quoting.strategy.changelog.xml"; logger = Scope.getCurrentScope().getLog(getClass()); - // Get the integration test properties for both global settings and (if applicable) local overrides. - Properties integrationTestProperties; - integrationTestProperties = new Properties(); - integrationTestProperties.load( - Thread.currentThread() - .getContextClassLoader() - .getResourceAsStream("liquibase/liquibase.integrationtest.properties")); - InputStream localProperties= - Thread.currentThread() - .getContextClassLoader() - .getResourceAsStream("liquibase/liquibase.integrationtest.local.properties"); - if(localProperties!=null) - integrationTestProperties.load(localProperties); - - // Login username - String username = integrationTestProperties.getProperty("integration.test." + dbms.getShortName() + ".username"); - if(username==null) - username=integrationTestProperties.getProperty("integration.test.username"); - this.setUsername(username); - - // Login password - String password = integrationTestProperties.getProperty("integration.test." + dbms.getShortName() + ".password"); - if(password==null) - password=integrationTestProperties.getProperty("integration.test.password"); - this.setPassword(password); - - // JDBC URL (no global default so all databases!) - String url = integrationTestProperties.getProperty("integration.test." + dbms.getShortName() + ".url"); - if ((url == null) || ((url.length()) == 0)) { - throw new LiquibaseException("No JDBC URL found for integration test of database type " + dbms.getShortName()); - } - this.setJdbcUrl(url); + this.hubEnvironment = (HubTestSystem) Scope.getCurrentScope().getSingleton(TestSystemFactory.class).getTestSystem("hub"); - String testHubApiKey = integrationTestProperties.getProperty("integration.test.hub.apiKey"); - if (testHubApiKey != null) { - System.setProperty(HubConfiguration.LIQUIBASE_HUB_API_KEY.getKey(), testHubApiKey); - String testHubUrl = integrationTestProperties.getProperty("integration.test.hub.url"); - System.setProperty(HubConfiguration.LIQUIBASE_HUB_URL.getKey(), testHubUrl); + if (hubEnvironment.getApiKey() != null) { + System.setProperty(HubConfiguration.LIQUIBASE_HUB_API_KEY.getKey(), hubEnvironment.getApiKey()); + System.setProperty(HubConfiguration.LIQUIBASE_HUB_URL.getKey(), hubEnvironment.getUrl()); } Scope.setScopeManager(new TestScopeManager()); } - public static DatabaseTestURL getDatabaseTestURL(String databaseManager) { - try { - Properties integrationTestProperties; - integrationTestProperties = new Properties(); - integrationTestProperties.load( - Thread.currentThread() - .getContextClassLoader() - .getResourceAsStream("liquibase/liquibase.integrationtest.properties")); - InputStream localProperties = - Thread.currentThread() - .getContextClassLoader() - .getResourceAsStream("liquibase/liquibase.integrationtest.local.properties"); - if (localProperties != null) - integrationTestProperties.load(localProperties); - - String url = integrationTestProperties.getProperty("integration.test." + databaseManager + ".jdbcUrl"); - if (url == null) - return null; - - DatabaseTestURL testUrl = new DatabaseTestURL(databaseManager, url, - // These may be set to null if not defined as properties. - integrationTestProperties.getProperty("integration.test." + databaseManager + ".username"), - integrationTestProperties.getProperty("integration.test." + databaseManager + ".password") - ); - return testUrl; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private void openConnection(String url, String username, String password) throws Exception { - DatabaseConnection connection = DatabaseTestContext.getInstance().getConnection(url, username, password); + private void openConnection() throws Exception { + DatabaseConnection connection = new JdbcConnection(testSystem.getConnection()); - if (connection != null) { - database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection); - } + database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection); } - /** - * @return true if this database is provided by the Travis CI build on GitHub. - * - * See https://docs.travis-ci.com/user/database-setup/ - */ - protected abstract boolean isDatabaseProvidedByTravisCI(); - /** * Reset database connection and internal objects before each test. * CAUTION! Does not wipe the schemas - if you want that, you must call {@link #clearDatabase()} . @@ -195,14 +133,14 @@ private void openConnection(String url, String username, String password) throws @Before public void setUp() throws Exception { // Try to get a connection to the DBMS (or start the embedded DBMS types) - openConnection(getJdbcUrl(), getUsername(), getPassword()); + openConnection(); // Do not count the test as successful if we skip it because of a failed login. Count it as skipped instead. org.junit.Assume.assumeTrue(database != null); if (database.supportsTablespaces()) { // Use the opportunity to test if the DATABASECHANGELOG table is placed in the correct tablespace - database.setLiquibaseTablespaceName(DatabaseTestContext.ALT_TABLESPACE); + database.setLiquibaseTablespaceName(ALT_TABLESPACE); } if (!database.getConnection().getAutoCommit()) { database.rollback(); @@ -256,11 +194,11 @@ protected void wipeDatabase() { SnapshotGeneratorFactory factory = SnapshotGeneratorFactory.getInstance(); if (database.supportsSchemas()) { - emptyTestSchema(null, DatabaseTestContext.ALT_SCHEMA, database); + emptyTestSchema(null, ALT_SCHEMA, database); } if (supportsAltCatalogTests()) { if (database.supportsSchemas() && database.supportsCatalogs()) { - emptyTestSchema(DatabaseTestContext.ALT_CATALOG, DatabaseTestContext.ALT_SCHEMA, database); + emptyTestSchema(ALT_CATALOG, ALT_SCHEMA, database); } } @@ -273,8 +211,8 @@ protected void wipeDatabase() { * are doing a thorough cleanup. */ CatalogAndSchema[] alternativeLocations = new CatalogAndSchema[]{ - new CatalogAndSchema(DatabaseTestContext.ALT_CATALOG, null), - new CatalogAndSchema(null, DatabaseTestContext.ALT_SCHEMA), + new CatalogAndSchema(ALT_CATALOG, null), + new CatalogAndSchema(null, ALT_SCHEMA), new CatalogAndSchema("LBCAT2", database.getDefaultSchemaName()), new CatalogAndSchema(null, "LBCAT2"), new CatalogAndSchema("lbcat2", database.getDefaultSchemaName()), @@ -364,22 +302,6 @@ public void testBatchInsert() throws Exception { // ChangeLog already contains the verification code } - - @Test - public void testDatabaseIsReachableIfRequired() { - if (isDatabaseProvidedByTravisCI()) { - assertNotNull( - "This integration test is expected to pass on Travis CI.\n" + - "If you are running on a dev machine and do not have the required\n" + - "database installed, you may choose to ignore this failed test.\n" + - "To run this test on a dev machine, you will need to install the corresponding\n" + - "database and configure liquibase.integrationtest.local.properties", - getDatabase()); - } else { - assumeNotNull(this.getDatabase()); - } - } - @Test @SuppressWarnings("squid:S2699") // Successful execution qualifies as test success. public void testRunChangeLog() throws Exception { @@ -398,7 +320,7 @@ protected void runChangeLogFile(String changeLogFile) throws Exception { //run again to test changelog testing logic liquibase = createLiquibase(changeLogFile); - liquibase.setChangeLogParameter( "loginuser", getUsername()); + liquibase.setChangeLogParameter( "loginuser", testSystem.getUsername()); try { liquibase.update(this.contexts); @@ -453,7 +375,7 @@ public void testRunUpdateOnOldChangelogTableFormat() throws Exception { conn.setAutoCommit(savedAcSetting); liquibase = createLiquibase(completeChangeLog); - liquibase.setChangeLogParameter( "loginuser", getUsername()); + liquibase.setChangeLogParameter( "loginuser", testSystem.getUsername()); liquibase.update(this.contexts); } @@ -467,7 +389,7 @@ public void testOutputChangeLog() throws Exception { clearDatabase(); liquibase = createLiquibase(completeChangeLog); - liquibase.setChangeLogParameter("loginuser", getUsername()); + liquibase.setChangeLogParameter("loginuser", testSystem.getUsername()); liquibase.update(this.contexts, output); String outputResult = output.getBuffer().toString(); @@ -573,7 +495,7 @@ public void testUpdateTwice() throws Exception { clearDatabase(); liquibase = createLiquibase(completeChangeLog); - liquibase.setChangeLogParameter( "loginuser", getUsername()); + liquibase.setChangeLogParameter( "loginuser", testSystem.getUsername()); liquibase.update(this.contexts); liquibase.update(this.contexts); } @@ -587,12 +509,12 @@ public void testUpdateClearUpdate() throws Exception { clearDatabase(); liquibase = createLiquibase(completeChangeLog); - liquibase.setChangeLogParameter( "loginuser", getUsername()); + liquibase.setChangeLogParameter( "loginuser", testSystem.getUsername()); liquibase.update(this.contexts); clearDatabase(); liquibase = createLiquibase(completeChangeLog); - liquibase.setChangeLogParameter( "loginuser", getUsername()); + liquibase.setChangeLogParameter( "loginuser", testSystem.getUsername()); liquibase.update(this.contexts); } @@ -657,7 +579,7 @@ public void testTag() throws Exception { clearDatabase(); liquibase = createLiquibase(completeChangeLog); - liquibase.setChangeLogParameter( "loginuser", getUsername()); + liquibase.setChangeLogParameter( "loginuser", testSystem.getUsername()); liquibase.update(this.contexts); liquibase.tag("Test Tag"); @@ -788,7 +710,7 @@ public void testRerunDiffChangeLogAltSchema() throws Exception { } Liquibase liquibase = createLiquibase(includedChangeLog); - database.setDefaultSchemaName("lbcat2"); + database.setDefaultSchemaName("lbschem2"); clearDatabase(); @@ -803,7 +725,7 @@ public void testRerunDiffChangeLogAltSchema() throws Exception { new CompareControl.SchemaComparison[]{ new CompareControl.SchemaComparison( CatalogAndSchema.DEFAULT, - new CatalogAndSchema(null, "lbcat2") + new CatalogAndSchema("lbcat2", null) ) }, originalSnapshot.getSnapshotControl().getTypesToInclude() @@ -826,20 +748,20 @@ public void testRerunDiffChangeLogAltSchema() throws Exception { //run again to test changelog testing logic Executor executor = Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", database); try { - executor.execute(new DropTableStatement("lbcat2", "lbcat2", database.getDatabaseChangeLogTableName(), false)); + executor.execute(new DropTableStatement("lbcat2", null, database.getDatabaseChangeLogTableName(), false)); } catch (DatabaseException e) { //ok } try { - executor.execute(new DropTableStatement("lbcat2", "lbcat2", database.getDatabaseChangeLogLockTableName(), false)); + executor.execute(new DropTableStatement("lbcat2", null, database.getDatabaseChangeLogLockTableName(), false)); } catch (DatabaseException e) { //ok } database.commit(); - DatabaseConnection connection = DatabaseTestContext.getInstance().getConnection(getJdbcUrl(), username, password); + DatabaseConnection connection = new JdbcConnection(testSystem.getConnection()); database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection); - database.setDefaultSchemaName("lbcat2"); + database.setDefaultSchemaName("lbschem2"); liquibase = createLiquibase(tempFile.getName()); try { liquibase.update(this.contexts); @@ -873,7 +795,7 @@ public void testClearChecksums() throws Exception { clearDatabase(); liquibase = createLiquibase(completeChangeLog); - liquibase.setChangeLogParameter( "loginuser", getUsername()); + liquibase.setChangeLogParameter( "loginuser", testSystem.getUsername()); liquibase.update(this.contexts); liquibase.clearCheckSums(); @@ -903,11 +825,11 @@ public void testUnrunChangeSetsEmptyDatabase() throws Exception { assumeNotNull(this.getDatabase()); Liquibase liquibase = createLiquibase(completeChangeLog); - liquibase.setChangeLogParameter( "loginuser", getUsername()); + liquibase.setChangeLogParameter( "loginuser", testSystem.getUsername()); clearDatabase(); liquibase = createLiquibase(completeChangeLog); - liquibase.setChangeLogParameter( "loginuser", getUsername()); + liquibase.setChangeLogParameter( "loginuser", testSystem.getUsername()); List list = liquibase.listUnrunChangeSets(new Contexts(this.contexts), new LabelExpression()); assertTrue("querying the changelog table on an empty target should return at least 1 un-run change set", !list.isEmpty()); @@ -980,14 +902,14 @@ public void testDbDoc() throws Exception { clearDatabase(); liquibase = createLiquibase(completeChangeLog); - liquibase.setChangeLogParameter( "loginuser", getUsername()); + liquibase.setChangeLogParameter( "loginuser", testSystem.getUsername()); liquibase.update(this.contexts); Path outputDir = tempDirectory.newFolder().toPath().normalize(); logger.fine("Database documentation will be written to this temporary folder: " + outputDir); liquibase = createLiquibase(completeChangeLog); - liquibase.setChangeLogParameter( "loginuser", getUsername()); + liquibase.setChangeLogParameter( "loginuser", testSystem.getUsername()); liquibase.generateDocumentation(outputDir.toAbsolutePath().toString(), this.contexts); } @@ -1152,30 +1074,6 @@ protected Database getDatabase(){ return database; } - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - protected String getJdbcUrl() { - return jdbcUrl; - } - - protected void setJdbcUrl(String jdbcUrl) { - this.jdbcUrl = jdbcUrl; - } - public String getDefaultSchemaName() { return defaultSchemaName; } diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/asany/SybaseASAIntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/asany/SybaseASAIntegrationTest.java index c0c9693f197..4d0251ca7a1 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/asany/SybaseASAIntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/asany/SybaseASAIntegrationTest.java @@ -9,12 +9,6 @@ public SybaseASAIntegrationTest() throws Exception { super( "asany", DatabaseFactory.getInstance().getDatabase("asany")); } - @Override - protected boolean isDatabaseProvidedByTravisCI() { - // Seems unlikely to ever be provided by Travis, as it's not free - return false; - } - @Override protected boolean shouldRollBack() { return false; diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/cockroachdb/CockroachDBIntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/cockroachdb/CockroachDBIntegrationTest.java index 077075300cf..e34b7177e8a 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/cockroachdb/CockroachDBIntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/cockroachdb/CockroachDBIntegrationTest.java @@ -2,7 +2,6 @@ import liquibase.Scope; import liquibase.database.DatabaseFactory; -import liquibase.database.jvm.JdbcConnection; import liquibase.dbtest.AbstractIntegrationTest; import liquibase.executor.Executor; import liquibase.executor.ExecutorService; @@ -13,7 +12,6 @@ import liquibase.structure.core.Column; import liquibase.structure.core.PrimaryKey; import liquibase.structure.core.Table; -import org.junit.Before; import org.junit.Test; import java.util.List; @@ -28,44 +26,6 @@ public CockroachDBIntegrationTest() throws Exception { super("cockroachdb", DatabaseFactory.getInstance().getDatabase("cockroachdb")); } - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - - ((JdbcConnection) getDatabase().getConnection()).getUnderlyingConnection().createStatement().executeUpdate( - "CREATE USER IF NOT EXISTS lbuser" - ); - ((JdbcConnection) getDatabase().getConnection()).getUnderlyingConnection().createStatement().executeUpdate( - "CREATE DATABASE IF NOT EXISTS lbcat" - ); - ((JdbcConnection) getDatabase().getConnection()).getUnderlyingConnection().createStatement().executeUpdate( - "CREATE DATABASE IF NOT EXISTS lbcat2" - ); - // Create schemas for tests testRerunDiffChangeLogAltSchema - ((JdbcConnection) getDatabase().getConnection()).getUnderlyingConnection().createStatement().executeUpdate( - "CREATE SCHEMA IF NOT EXISTS lbcat2" - ); - ((JdbcConnection) getDatabase().getConnection()).getUnderlyingConnection().createStatement().executeUpdate( - "GRANT ALL ON DATABASE lbcat TO lbuser" - );// - ((JdbcConnection) getDatabase().getConnection()).getUnderlyingConnection().createStatement().executeUpdate( - "GRANT ALL ON DATABASE lbcat2 TO lbuser" - ); - // Create schemas for tests testRerunDiffChangeLogAltSchema - ((JdbcConnection) getDatabase().getConnection()).getUnderlyingConnection().createStatement().executeUpdate( - "GRANT ALL ON SCHEMA lbcat2 TO lbuser" - ); - - getDatabase().commit(); - - } - - @Override - protected boolean isDatabaseProvidedByTravisCI() { - return true; - } - @Test public void snapshot() throws Exception { if (getDatabase() == null) { diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/db2/DB2IntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/db2/DB2IntegrationTest.java index d2f4184ba94..1abfaa4ee71 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/db2/DB2IntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/db2/DB2IntegrationTest.java @@ -9,11 +9,6 @@ public class DB2IntegrationTest extends AbstractIntegrationTest { - @Override - protected boolean isDatabaseProvidedByTravisCI() { - return false; - } - public DB2IntegrationTest() throws Exception { super("db2", DatabaseFactory.getInstance().getDatabase("db2")); } diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/db2z/DB2zIntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/db2z/DB2zIntegrationTest.java index abb8f560580..cb0b4ffe9bf 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/db2z/DB2zIntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/db2z/DB2zIntegrationTest.java @@ -9,11 +9,6 @@ public class DB2zIntegrationTest extends AbstractIntegrationTest { - @Override - protected boolean isDatabaseProvidedByTravisCI() { - return false; - } - public DB2zIntegrationTest() throws Exception { super("db2z", DatabaseFactory.getInstance().getDatabase("db2z")); } diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/derby/DerbyIntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/derby/DerbyIntegrationTest.java index 9a4ce0540d7..5a53bfd244a 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/derby/DerbyIntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/derby/DerbyIntegrationTest.java @@ -14,12 +14,6 @@ public DerbyIntegrationTest() throws Exception { super("derby", DatabaseFactory.getInstance().getDatabase("derby")); } - @Override - protected boolean isDatabaseProvidedByTravisCI() { - // Derby is an in-process database - return true; - } - @Override protected boolean shouldRollBack() { return false; diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/firebird/FirebirdIntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/firebird/FirebirdIntegrationTest.java index 93154eb47e8..1ec6c2a7e0a 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/firebird/FirebirdIntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/firebird/FirebirdIntegrationTest.java @@ -19,12 +19,6 @@ public FirebirdIntegrationTest() throws Exception { super("firebird", DatabaseFactory.getInstance().getDatabase("firebird")); } - @Override - protected boolean isDatabaseProvidedByTravisCI() { - // Seems unlikely to ever be provided by Travis, as it's not free - return false; - } - @Override protected boolean shouldRollBack() { return false; diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/h2/H2IntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/h2/H2IntegrationTest.java index b77c5247c73..7f3b2ffc9c0 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/h2/H2IntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/h2/H2IntegrationTest.java @@ -10,6 +10,7 @@ import liquibase.diff.output.DiffOutputControl; import liquibase.diff.output.changelog.DiffToChangeLog; import liquibase.diff.output.report.DiffToReport; +import liquibase.exception.DatabaseException; import liquibase.exception.ValidationFailedException; import liquibase.snapshot.DatabaseSnapshot; import liquibase.snapshot.SnapshotControl; @@ -32,12 +33,6 @@ public H2IntegrationTest() throws Exception { this.dbmsExcludeChangelog = "changelogs/h2/complete/dbms.exclude.changelog.xml"; } - @Override - protected boolean isDatabaseProvidedByTravisCI() { - // H2 is an in-process database - return true; - } - @Test public void diffToPrintStream() throws Exception{ if (getDatabase() == null) { @@ -105,7 +100,7 @@ public void runYamlChangelog() throws Exception { //run again to test changelog testing logic liquibase = createLiquibase("changelogs/yaml/common.tests.changelog.yaml"); - liquibase.setChangeLogParameter("loginuser", getUsername()); + liquibase.setChangeLogParameter("loginuser", testSystem.getUsername()); try { liquibase.update(this.contexts); @@ -128,7 +123,7 @@ public void runJsonChangelog() throws Exception { //run again to test changelog testing logic liquibase = createLiquibase("changelogs/json/common.tests.changelog.json"); - liquibase.setChangeLogParameter("loginuser", getUsername()); + liquibase.setChangeLogParameter("loginuser", testSystem.getUsername()); try { liquibase.update(this.contexts); diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/hsqldb/HsqlIntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/hsqldb/HsqlIntegrationTest.java index 1b579b20606..6a9c2a2757f 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/hsqldb/HsqlIntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/hsqldb/HsqlIntegrationTest.java @@ -14,12 +14,6 @@ public HsqlIntegrationTest() throws Exception { super("hsqldb", DatabaseFactory.getInstance().getDatabase("hsqldb")); } - @Override - protected boolean isDatabaseProvidedByTravisCI() { - // Hsqldb is an in-process database - return true; - } - @Override public void setUp() throws Exception { diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/informix/InformixIntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/informix/InformixIntegrationTest.java index 8d3931d9ab7..5585566ca28 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/informix/InformixIntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/informix/InformixIntegrationTest.java @@ -15,9 +15,4 @@ public InformixIntegrationTest() throws Exception { super("informix", DatabaseFactory.getInstance().getDatabase("informix")); } - @Override - protected boolean isDatabaseProvidedByTravisCI() { - // Seems unlikely to ever be provided by Travis, as it's not free - return false; - } } diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/mariadb/MariaDBIntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/mariadb/MariaDBIntegrationTest.java index 82a99eb6ac8..8a9ffa7d052 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/mariadb/MariaDBIntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/mariadb/MariaDBIntegrationTest.java @@ -27,11 +27,6 @@ public MariaDBIntegrationTest() throws Exception { super("mariadb", DatabaseFactory.getInstance().getDatabase("mariadb")); } - @Override - protected boolean isDatabaseProvidedByTravisCI() { - return true; - } - @Test @Override public void testRunChangeLog() throws Exception { diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/mssql/AbstractMssqlIntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/mssql/AbstractMssqlIntegrationTest.java index 4322933184a..a054ef5e907 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/mssql/AbstractMssqlIntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/mssql/AbstractMssqlIntegrationTest.java @@ -21,12 +21,6 @@ public AbstractMssqlIntegrationTest(String changelogDir, Database dbms) throws E super(changelogDir, dbms); } - @Override - protected boolean isDatabaseProvidedByTravisCI() { - // Seems unlikely to ever be provided by Travis, as it's not free - return false; - } - @Override protected boolean shouldRollBack() { return false; diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/mysql/MySQLIntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/mysql/MySQLIntegrationTest.java index cef4a8e5760..f063362343f 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/mysql/MySQLIntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/mysql/MySQLIntegrationTest.java @@ -47,11 +47,6 @@ public MySQLIntegrationTest() throws Exception { super("mysql", DatabaseFactory.getInstance().getDatabase("mysql")); } - @Override - protected boolean isDatabaseProvidedByTravisCI() { - return true; - } - @Test @Override public void testRunChangeLog() throws Exception { diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/oracle/OracleIntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/oracle/OracleIntegrationTest.java index 51eb9f340fd..2dbb3bc47bf 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/oracle/OracleIntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/oracle/OracleIntegrationTest.java @@ -8,22 +8,13 @@ import liquibase.database.DatabaseFactory; import liquibase.database.jvm.JdbcConnection; import liquibase.dbtest.AbstractIntegrationTest; -import liquibase.exception.DatabaseException; import liquibase.exception.ValidationFailedException; -import liquibase.executor.Executor; -import liquibase.executor.ExecutorService; -import liquibase.logging.LogService; -import liquibase.logging.Logger; import liquibase.sql.visitor.AbstractSqlVisitor; -import liquibase.sql.visitor.SqlVisitor; -import liquibase.statement.core.DropTableStatement; import org.junit.Test; import java.sql.ResultSet; import java.sql.Statement; -import java.util.ArrayList; import java.util.Date; -import java.util.List; import static junit.framework.TestCase.assertEquals; import static org.junit.Assert.assertTrue; @@ -50,12 +41,6 @@ public OracleIntegrationTest() throws Exception { System.setProperty("oracle.net.tns_admin",System.getenv("TNS_ADMIN")); } - @Override - protected boolean isDatabaseProvidedByTravisCI() { - // Seems unlikely to ever be provided by Travis, as it's not free - return false; - } - @Override @Test public void testRunChangeLog() throws Exception { diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/pgsql/PostgreSQLIntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/pgsql/PostgreSQLIntegrationTest.java index 532fa8882e5..8c94f5126e2 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/pgsql/PostgreSQLIntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/pgsql/PostgreSQLIntegrationTest.java @@ -46,11 +46,6 @@ public void tearDown() throws Exception { } } - @Override - protected boolean isDatabaseProvidedByTravisCI() { - return true; - } - @Test public void testDependenciesInGenerateChangeLog() throws Exception { assumeNotNull(this.getDatabase()); diff --git a/liquibase-integration-tests/src/test/java/liquibase/dbtest/sqlite/SQLiteIntegrationTest.java b/liquibase-integration-tests/src/test/java/liquibase/dbtest/sqlite/SQLiteIntegrationTest.java index e22d70a757c..ef859063e6e 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/dbtest/sqlite/SQLiteIntegrationTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/dbtest/sqlite/SQLiteIntegrationTest.java @@ -24,11 +24,6 @@ public SQLiteIntegrationTest() throws Exception { } } - @Override - protected boolean isDatabaseProvidedByTravisCI() { - return true; - } - @Override @Test public void testRunChangeLog() throws Exception { diff --git a/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AbstractExecuteTest.java b/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AbstractExecuteTest.java index 30d6b690ded..eeb679ea0fe 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AbstractExecuteTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AbstractExecuteTest.java @@ -5,6 +5,7 @@ import liquibase.changelog.ChangeLogHistoryServiceFactory; import liquibase.database.Database; import liquibase.database.DatabaseConnection; +import liquibase.database.DatabaseFactory; import liquibase.database.core.UnsupportedDatabase; import liquibase.database.example.ExampleCustomDatabase; import liquibase.database.jvm.JdbcConnection; @@ -12,6 +13,9 @@ import liquibase.exception.DatabaseException; import liquibase.exception.UnexpectedLiquibaseException; import liquibase.executor.ExecutorService; +import liquibase.extension.testing.testsystem.DatabaseTestSystem; +import liquibase.extension.testing.testsystem.TestSystem; +import liquibase.extension.testing.testsystem.TestSystemFactory; import liquibase.listener.SqlListener; import liquibase.lockservice.LockServiceFactory; import liquibase.database.core.MockDatabase; @@ -20,7 +24,6 @@ import liquibase.sqlgenerator.SqlGeneratorFactory; import liquibase.statement.SqlStatement; import liquibase.structure.core.Table; -import liquibase.test.DatabaseTestContext; import liquibase.test.TestContext; import org.junit.After; @@ -119,17 +122,21 @@ private void test(String[] expectedSql, Class[] includeDatab } resetAvailableDatabases(); - for (Database availableDatabase : DatabaseTestContext.getInstance().getAvailableDatabases()) { - Statement statement = ((JdbcConnection) availableDatabase.getConnection()).getUnderlyingConnection().createStatement(); - if (shouldTestDatabase(availableDatabase, includeDatabases, excludeDatabases)) { - String sqlToRun = SqlGeneratorFactory.getInstance().generateSql(statementUnderTest, availableDatabase)[0].toSql(); + for (DatabaseTestSystem testSystem : Scope.getCurrentScope().getSingleton(TestSystemFactory.class).getAvailable(DatabaseTestSystem.class)) { + testSystem.start(); + + Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(testSystem.getConnection())); + + Statement statement = ((JdbcConnection) database.getConnection()).getUnderlyingConnection().createStatement(); + if (shouldTestDatabase(database, includeDatabases, excludeDatabases)) { + String sqlToRun = SqlGeneratorFactory.getInstance().generateSql(statementUnderTest, database)[0].toSql(); try { for (SqlListener listener : Scope.getCurrentScope().getListeners(SqlListener.class)) { listener.writeSqlWillRun(sqlToRun); } statement.execute(sqlToRun); } catch (Exception e) { - System.out.println("Failed to execute against " + availableDatabase.getShortName() + ": " + sqlToRun); + System.out.println("Failed to execute against " + database.getShortName() + ": " + sqlToRun); throw e; } @@ -200,9 +207,12 @@ private String replaceEscaping(String expectedSql, Database database) { } public void resetAvailableDatabases() throws Exception { - for (Database database : DatabaseTestContext.getInstance().getAvailableDatabases()) { + for (DatabaseTestSystem testSystem : Scope.getCurrentScope().getSingleton(TestSystemFactory.class).getAvailable(DatabaseTestSystem.class)) { + testSystem.start(); + Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(testSystem.getConnection())); DatabaseConnection connection = database.getConnection(); Statement connectionStatement = ((JdbcConnection) connection).getUnderlyingConnection().createStatement(); + connection.commit(); try { database.dropDatabaseObjects(CatalogAndSchema.DEFAULT); @@ -221,17 +231,17 @@ public void resetAvailableDatabases() throws Exception { connection.commit(); if (database.supportsSchemas()) { - database.dropDatabaseObjects(new CatalogAndSchema(DatabaseTestContext.ALT_CATALOG, DatabaseTestContext.ALT_SCHEMA)); + database.dropDatabaseObjects(new CatalogAndSchema(null, testSystem.getAltSchema())); connection.commit(); try { - connectionStatement.executeUpdate("drop table " + database.escapeTableName(DatabaseTestContext.ALT_CATALOG, DatabaseTestContext.ALT_SCHEMA, database.getDatabaseChangeLogLockTableName())); + connectionStatement.executeUpdate("drop table " + database.escapeTableName(testSystem.getAltCatalog(), testSystem.getAltSchema(), database.getDatabaseChangeLogLockTableName())); } catch (SQLException e) { //ok } connection.commit(); try { - connectionStatement.executeUpdate("drop table " + database.escapeTableName(DatabaseTestContext.ALT_CATALOG, DatabaseTestContext.ALT_SCHEMA, database.getDatabaseChangeLogTableName())); + connectionStatement.executeUpdate("drop table " + database.escapeTableName(testSystem.getAltCatalog(), testSystem.getAltSchema(), database.getDatabaseChangeLogTableName())); } catch (SQLException e) { //ok } diff --git a/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AddAutoIncrementExecuteTest.java b/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AddAutoIncrementExecuteTest.java index 1dc002cb409..457b5033ede 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AddAutoIncrementExecuteTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AddAutoIncrementExecuteTest.java @@ -3,12 +3,12 @@ import liquibase.database.Database; import liquibase.database.core.*; import liquibase.datatype.DataTypeFactory; +import liquibase.dbtest.AbstractIntegrationTest; import liquibase.statement.ColumnConstraint; import liquibase.statement.NotNullConstraint; import liquibase.statement.SqlStatement; import liquibase.statement.core.AddAutoIncrementStatement; import liquibase.statement.core.CreateTableStatement; -import liquibase.test.DatabaseTestContext; import org.junit.Test; import java.util.ArrayList; @@ -32,7 +32,7 @@ protected List setupStatements(Database database) { statements.add(table); if (database.supportsSchemas()) { - table = new CreateTableStatement(DatabaseTestContext.ALT_CATALOG, DatabaseTestContext.ALT_SCHEMA, TABLE_NAME); + table = new CreateTableStatement(AbstractIntegrationTest.ALT_CATALOG, AbstractIntegrationTest.ALT_SCHEMA, TABLE_NAME); table.addColumn("id", DataTypeFactory.getInstance().fromDescription("int", database), null, new ColumnConstraint[]{new NotNullConstraint()}); statements.add(table); } diff --git a/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AddColumnExecutorTest.java b/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AddColumnExecutorTest.java index b486e3dc667..e209d56fffc 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AddColumnExecutorTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AddColumnExecutorTest.java @@ -3,10 +3,10 @@ import liquibase.database.Database; import liquibase.database.core.*; import liquibase.datatype.DataTypeFactory; +import liquibase.dbtest.AbstractIntegrationTest; import liquibase.statement.*; import liquibase.statement.core.AddColumnStatement; import liquibase.statement.core.CreateTableStatement; -import liquibase.test.DatabaseTestContext; import org.junit.Test; import java.util.ArrayList; @@ -24,7 +24,7 @@ protected List setupStatements(Database database) { statements.add(table); if (database.supportsSchemas()) { - table = new CreateTableStatement(DatabaseTestContext.ALT_CATALOG, DatabaseTestContext.ALT_SCHEMA, TABLE_NAME); + table = new CreateTableStatement(AbstractIntegrationTest.ALT_CATALOG, AbstractIntegrationTest.ALT_SCHEMA, TABLE_NAME); table.addColumn("id", DataTypeFactory.getInstance().fromDescription("int", database), null, new ColumnConstraint[]{ new NotNullConstraint()}); statements.add(table); } diff --git a/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AddUniqueConstraintExecutorTest.java b/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AddUniqueConstraintExecutorTest.java index 54f4101e716..740b1423eee 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AddUniqueConstraintExecutorTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/statementexecute/AddUniqueConstraintExecutorTest.java @@ -4,12 +4,12 @@ import liquibase.database.Database; import liquibase.database.core.*; import liquibase.datatype.DataTypeFactory; +import liquibase.dbtest.AbstractIntegrationTest; import liquibase.statement.ColumnConstraint; import liquibase.statement.NotNullConstraint; import liquibase.statement.SqlStatement; import liquibase.statement.core.AddUniqueConstraintStatement; import liquibase.statement.core.CreateTableStatement; -import liquibase.test.DatabaseTestContext; import org.junit.Test; import java.util.ArrayList; @@ -32,7 +32,7 @@ protected List setupStatements(Database database) { statements.add(table); if (database.supportsSchemas()) { - table = new CreateTableStatement(DatabaseTestContext.ALT_CATALOG, DatabaseTestContext.ALT_SCHEMA, TABLE_NAME); + table = new CreateTableStatement(AbstractIntegrationTest.ALT_CATALOG, AbstractIntegrationTest.ALT_SCHEMA, TABLE_NAME); table .addColumn("id", DataTypeFactory.getInstance().fromDescription("int", database), null, new ColumnConstraint[]{ new NotNullConstraint() }) .addColumn(COLUMN_NAME, DataTypeFactory.getInstance().fromDescription("int", database), null, new ColumnConstraint[]{ new NotNullConstraint() }); @@ -126,15 +126,15 @@ public void execute_noConstraintName() throws Exception { @Test public void execute_withSchema() throws Exception { statementUnderTest = new AddUniqueConstraintStatement( - DatabaseTestContext.ALT_CATALOG, - DatabaseTestContext.ALT_SCHEMA, + AbstractIntegrationTest.ALT_CATALOG, + AbstractIntegrationTest.ALT_SCHEMA, TABLE_NAME, new ColumnConfig[] {new ColumnConfig().setName(COLUMN_NAME)}, CONSTRAINT_NAME ); - assertCorrect("ALTER TABLE liquibasec.adduqtest ADD CONSTRAINT uq_test UNIQUE (coltomakeuq)", MySQLDatabase + assertCorrect("ALTER TABLE lbcat2.adduqtest ADD CONSTRAINT uq_test UNIQUE (coltomakeuq)", MySQLDatabase .class); /* * In Informix, this test case is actually impossible. While it is allowed to cross-select data from @@ -142,24 +142,24 @@ public void execute_withSchema() throws Exception { * different database (even if the database is on the same instance). So, even as the following * statement is semantically false, it is syntactically correct. */ - assertCorrect("ALTER TABLE liquibasec:liquibaseb.adduqtest ADD CONSTRAINT UNIQUE (coltomakeuq) CONSTRAINT " + + assertCorrect("ALTER TABLE lbcat2:lbschem2.adduqtest ADD CONSTRAINT UNIQUE (coltomakeuq) CONSTRAINT " + "uq_test", InformixDatabase.class); - assertCorrect("alter table liquibasec.adduqtest add constraint uq_test unique (coltomakeuq)", OracleDatabase.class); - assertCorrect("alter table liquibaseb.\"adduqtest\" add constraint uq_test unique (\"coltomakeuq\")", PostgresDatabase.class, CockroachDatabase.class,EnterpriseDBDatabase.class); - assertCorrect("alter table liquibasec.adduqtest add constraint uq_test unique (coltomakeuq)", DerbyDatabase + assertCorrect("alter table lbcat2.adduqtest add constraint uq_test unique (coltomakeuq)", OracleDatabase.class); + assertCorrect("alter table lbschem2.\"adduqtest\" add constraint uq_test unique (\"coltomakeuq\")", PostgresDatabase.class, CockroachDatabase.class,EnterpriseDBDatabase.class); + assertCorrect("alter table lbcat2.adduqtest add constraint uq_test unique (coltomakeuq)", DerbyDatabase .class); - assertCorrect("alter table [liquibaseb].[adduqtest] add constraint [uq_test] unique ([coltomakeuq])", + assertCorrect("alter table [lbschem2].[adduqtest] add constraint [uq_test] unique ([coltomakeuq])", SybaseASADatabase.class, SybaseDatabase.class); - assertCorrect("alter table [liquibasec].[liquibaseb].[adduqtest] add constraint [uq_test] unique " + + assertCorrect("alter table [lbcat2].[lbschem2].[adduqtest] add constraint [uq_test] unique " + "([coltomakeuq])", MSSQLDatabase.class); assertCorrect("alter table [adduqtest] add constraint [uq_test] unique ([coltomakeuq])", FirebirdDatabase.class, Firebird3Database.class); - assertCorrect("alter table [liquibaseb].[adduqtest] add constraint [uq_test] unique ([coltomakeuq])", HsqlDatabase.class); - assertCorrect("alter table \"liquibasec\".[adduqtest] add constraint [uq_test] unique ([coltomakeuq])", DB2Database.class, Db2zDatabase.class); - assertCorrect("alter table [liquibaseb].[adduqtest] add constraint [uq_test] unique ([coltomakeuq])", H2Database.class); - assertCorrect("alter table [liquibaseb].[adduqtest] add constraint [uq_test] unique ([coltomakeuq])", Ingres9Database.class); - assertCorrectOnRest("alter table [liquibasec].[adduqtest] add constraint [uq_test] unique ([coltomakeuq])"); + assertCorrect("alter table [lbschem2].[adduqtest] add constraint [uq_test] unique ([coltomakeuq])", HsqlDatabase.class); + assertCorrect("alter table \"lbcat2\".[adduqtest] add constraint [uq_test] unique ([coltomakeuq])", DB2Database.class, Db2zDatabase.class); + assertCorrect("alter table [lbschem2].[adduqtest] add constraint [uq_test] unique ([coltomakeuq])", H2Database.class); + assertCorrect("alter table [lbschem2].[adduqtest] add constraint [uq_test] unique ([coltomakeuq])", Ingres9Database.class); + assertCorrectOnRest("alter table [lbcat2].[adduqtest] add constraint [uq_test] unique ([coltomakeuq])"); } diff --git a/liquibase-integration-tests/src/test/java/liquibase/statementexecute/RenameColumnExecuteTest.java b/liquibase-integration-tests/src/test/java/liquibase/statementexecute/RenameColumnExecuteTest.java index 35c7337b912..7a788e75b4c 100644 --- a/liquibase-integration-tests/src/test/java/liquibase/statementexecute/RenameColumnExecuteTest.java +++ b/liquibase-integration-tests/src/test/java/liquibase/statementexecute/RenameColumnExecuteTest.java @@ -3,12 +3,12 @@ import liquibase.database.Database; import liquibase.database.core.*; import liquibase.datatype.DataTypeFactory; +import liquibase.dbtest.AbstractIntegrationTest; import liquibase.statement.ColumnConstraint; import liquibase.statement.NotNullConstraint; import liquibase.statement.SqlStatement; import liquibase.statement.core.CreateTableStatement; import liquibase.statement.core.RenameColumnStatement; -import liquibase.test.DatabaseTestContext; import org.junit.Test; import java.util.ArrayList; @@ -33,7 +33,7 @@ protected List setupStatements(Database database) { statements.add(table); if (database.supportsSchemas()) { - table = new CreateTableStatement(DatabaseTestContext.ALT_CATALOG, DatabaseTestContext.ALT_SCHEMA, TABLE_NAME); + table = new CreateTableStatement(AbstractIntegrationTest.ALT_CATALOG, AbstractIntegrationTest.ALT_SCHEMA, TABLE_NAME); table .addColumn("id", DataTypeFactory.getInstance().fromDescription("int", database), null, new ColumnConstraint[]{ new NotNullConstraint() }); statements.add(table); diff --git a/liquibase-integration-tests/src/test/java/liquibase/statementexecute/SelectFromDatabaseChangeLogLockExecutorTest.java b/liquibase-integration-tests/src/test/java/liquibase/statementexecute/SelectFromDatabaseChangeLogLockExecutorTest.java deleted file mode 100644 index 45e6dfe0d65..00000000000 --- a/liquibase-integration-tests/src/test/java/liquibase/statementexecute/SelectFromDatabaseChangeLogLockExecutorTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package liquibase.statementexecute; - -import liquibase.change.ColumnConfig; -import liquibase.database.Database; -import liquibase.database.core.MSSQLDatabase; -import liquibase.database.core.OracleDatabase; -import liquibase.database.core.SybaseASADatabase; -import liquibase.database.core.SybaseDatabase; -import liquibase.statement.SqlStatement; -import liquibase.statement.core.CreateDatabaseChangeLogLockTableStatement; -import liquibase.statement.core.SelectFromDatabaseChangeLogLockStatement; -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; - -public class SelectFromDatabaseChangeLogLockExecutorTest extends AbstractExecuteTest { - - @Override - protected List setupStatements(Database database) { - return Arrays.asList(new CreateDatabaseChangeLogLockTableStatement()); - } - - @Test - public void generateSql() throws Exception { - this.statementUnderTest = new SelectFromDatabaseChangeLogLockStatement("LOCKED"); - assertCorrect("select [locked] from [databasechangeloglock] where [id]=1", MSSQLDatabase.class, SybaseDatabase.class); - assertCorrect("select [locked] from [databasechangeloglock] where [id]=1", SybaseASADatabase.class); - assertCorrect("select [locked] from [databasechangeloglock] where [id]=1 for update", OracleDatabase.class); - assertCorrectOnRest("select [locked] from [databasechangeloglock] where [id]=1"); - } - - @Test - public void generateSql_count() throws Exception { - this.statementUnderTest = new SelectFromDatabaseChangeLogLockStatement(new ColumnConfig().setName("COUNT(*)", true)); - assertCorrect("select count(*) from [databasechangeloglock] where [id]=1", MSSQLDatabase.class, SybaseDatabase.class); - assertCorrect("select count(*) from [databasechangeloglock] where [id]=1", MSSQLDatabase.class, SybaseASADatabase.class); - assertCorrect("select count(*) from [databasechangeloglock] where [id]=1 for update", OracleDatabase.class); - assertCorrectOnRest("select count(*) from [databasechangeloglock] where [id]=1"); - } - - @Test - public void generateSql_multicolumn() throws Exception { - this.statementUnderTest = new SelectFromDatabaseChangeLogLockStatement("LOCKED", "LOCKEDBY"); - assertCorrect("select [locked],[lockedby] from [databasechangeloglock] where [id]=1", MSSQLDatabase.class, SybaseDatabase.class); - assertCorrect("select [locked],[lockedby] from [databasechangeloglock] where [id]=1", MSSQLDatabase.class, SybaseASADatabase.class); - assertCorrect("select [locked],[lockedby] from [databasechangeloglock] where [id]=1 for update", OracleDatabase.class); - assertCorrectOnRest("select [locked],[lockedby] from [databasechangeloglock] where [id]=1"); - } - -} diff --git a/liquibase-integration-tests/src/test/java/liquibase/statementexecute/UnlockDatabaseChangeLogExecuteTest.java b/liquibase-integration-tests/src/test/java/liquibase/statementexecute/UnlockDatabaseChangeLogExecuteTest.java deleted file mode 100644 index db64b40c8b9..00000000000 --- a/liquibase-integration-tests/src/test/java/liquibase/statementexecute/UnlockDatabaseChangeLogExecuteTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package liquibase.statementexecute; - -import liquibase.database.Database; -import liquibase.database.core.*; -import liquibase.statement.SqlStatement; -import liquibase.statement.core.CreateDatabaseChangeLogLockTableStatement; -import liquibase.statement.core.UnlockDatabaseChangeLogStatement; -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; - -public class UnlockDatabaseChangeLogExecuteTest extends AbstractExecuteTest { - @Override - protected List setupStatements(Database database) { - return Arrays.asList(new CreateDatabaseChangeLogLockTableStatement()); - } - - @Test - public void generateSql() throws Exception { - this.statementUnderTest = new UnlockDatabaseChangeLogStatement(); - assertCorrect("update [databasechangeloglock] set [locked] = 0, [lockedby] = null, [lockgranted] = null where [id] = 1", MSSQLDatabase.class, SybaseDatabase.class); - assertCorrect("update [databasechangeloglock] set [locked] = 0, [lockedby] = null, [lockgranted] = null where [id] = 1", MSSQLDatabase.class, SybaseASADatabase.class); - assertCorrect("update [databasechangeloglock] set [locked] = 'f', [lockedby] = null, [lockgranted] = null where [id] = 1", InformixDatabase.class); - assertCorrect("update [databasechangeloglock] set [locked] = false, [lockedby] = null, [lockgranted] = null where [id] = 1", PostgresDatabase.class, CockroachDatabase.class, EnterpriseDBDatabase.class, HsqlDatabase.class, H2Database.class, Ingres9Database.class, Firebird3Database.class); - assertCorrectOnRest("update [databasechangeloglock] set [locked] = 0, [lockedby] = null, [lockgranted] = null where [id] = 1"); - } -} diff --git a/liquibase-integration-tests/src/test/java/liquibase/test/DatabaseTest.java b/liquibase-integration-tests/src/test/java/liquibase/test/DatabaseTest.java deleted file mode 100644 index 38e8a52a242..00000000000 --- a/liquibase-integration-tests/src/test/java/liquibase/test/DatabaseTest.java +++ /dev/null @@ -1,7 +0,0 @@ -package liquibase.test; - -import liquibase.database.Database; - -public interface DatabaseTest { - public void performTest(Database database) throws Exception; -} diff --git a/liquibase-integration-tests/src/test/java/liquibase/test/DatabaseTestContext.java b/liquibase-integration-tests/src/test/java/liquibase/test/DatabaseTestContext.java deleted file mode 100644 index ca2f7bf784b..00000000000 --- a/liquibase-integration-tests/src/test/java/liquibase/test/DatabaseTestContext.java +++ /dev/null @@ -1,324 +0,0 @@ -package liquibase.test; - -import liquibase.Scope; -import liquibase.database.Database; -import liquibase.database.DatabaseConnection; -import liquibase.database.DatabaseFactory; -import liquibase.database.core.AbstractDb2Database; -import liquibase.database.core.MockDatabase; -import liquibase.database.core.SQLiteDatabase; -import liquibase.database.example.ExampleCustomDatabase; -import liquibase.database.jvm.JdbcConnection; -import liquibase.exception.DatabaseException; -import liquibase.listener.SqlListener; -import liquibase.resource.ResourceAccessor; - -import java.sql.Connection; -import java.sql.Driver; -import java.sql.SQLException; -import java.util.*; - -public class DatabaseTestContext { - public static final String ALT_CATALOG = "LIQUIBASEC"; - public static final String ALT_SCHEMA = "LIQUIBASEB"; - public static final String ALT_TABLESPACE = "LIQUIBASE2"; - private static final String TEST_DATABASES_PROPERTY = "test.databases"; - private static DatabaseTestContext instance = new DatabaseTestContext(); - private final DatabaseTestURL[] DEFAULT_TEST_DATABASES = new DatabaseTestURL[]{ - /* @todo Extract all remaining connection string examples into liquibase.integrationtest.properties, then delete this code block. */ - /* - new DatabaseTestURL("Cache","jdbc:Cache://"+AbstractIntegrationTest.getDatabaseServerHostname("Cache")+":1972/liquibase"), - new DatabaseTestURL("DB2","jdbc:db2://"+AbstractIntegrationTest.getDatabaseServerHostname("DB2")+":50000/liquibas"), - new DatabaseTestURL("Derby","jdbc:derby:liquibase;create=true"), - new DatabaseTestURL("FireBird","jdbc:firebirdsql:"+AbstractIntegrationTest.getDatabaseServerHostname("Firebird")+"/3050:c:\\firebird\\liquibase.fdb"), - new DatabaseTestURL("H2","jdbc:h2:mem:liquibase"), - new DatabaseTestURL("Hsql","jdbc:hsqldb:mem:liquibase"), - new DatabaseTestURL("MssqlJtds","jdbc:jtds:sqlserver://"+AbstractIntegrationTest.getDatabaseServerHostname("MSSQL")+";databaseName=liquibase"), - // "jdbc:sqlserver://localhost;databaseName=liquibase", - new DatabaseTestURL("MySQL","jdbc:mysql://"+AbstractIntegrationTest.getDatabaseServerHostname("mysql")+"/liquibase"), - new DatabaseTestURL("Oracle","jdbc:oracle:thin:@"+AbstractIntegrationTest.getDatabaseServerHostname("oracle")+"/XE"), - // "jdbc:jtds:sybase://localhost/nathan:5000", - // "jdbc:sybase:Tds:"+ InetAddress.getLocalHost().getHostName()+":5000/liquibase", - new DatabaseTestURL("SAPDB","jdbc:sapdb://"+AbstractIntegrationTest.getDatabaseServerHostname("sapdb")+"/liquibas"), - new DatabaseTestURL("SQLite","jdbc:sqlite:/liquibase.db"), - new DatabaseTestURL("SybaseJtds","jdbc:sybase:Tds:"+AbstractIntegrationTest.getDatabaseServerHostname("sybase")+":9810/servicename=prior") - */ - }; - private Set availableDatabases = new HashSet(); - private Set allDatabases; - private Set availableConnections; - private Map connectionsByUrl = new HashMap(); - private Map connectionsAttempted = new HashMap(); - private ResourceAccessor resourceAccessor; - - public static DatabaseTestContext getInstance() { - return instance; - } - - /** - * Makes a best effort to gracefully shut down a (possible open) databaseConnection and ignores any - * errors that happen during that process. - * - * @param databaseConnection - */ - private static void shutdownConnection(JdbcConnection databaseConnection) { - try { - try { - if (!databaseConnection.getUnderlyingConnection().getAutoCommit()) { - databaseConnection.getUnderlyingConnection().rollback(); - } - } catch (SQLException e) { - // Ignore. If rollback fails or is impossible, there is nothing we can do about it. - } - - // Close the JDBC connection - databaseConnection.getUnderlyingConnection().close(); - } catch (SQLException e) { - Scope.getCurrentScope().getLog(DatabaseTestContext.class).warning("Could not close the following connection: " + databaseConnection.getURL(), e); - } - } - - /** - * Insert the temp dir path and ensure our replacement ends with / - */ - private static String replaceTempDirPlaceholder(String givenUrl) { - String tempDir = System.getProperty("java.io.tmpdir"); - if (!tempDir.endsWith(System.getProperty("file.separator"))) - tempDir += System.getProperty("file.separator"); - - return givenUrl.replace("***TEMPDIR***/", tempDir); - } - - /** - * Replace the ***RANDOM*** placeholder in a database jdbc url with a random string. - */ - private static String replaceRandomPlaceholder(String givenUrl) { - return givenUrl.replace("***RANDOM***", UUID.randomUUID().toString()); - } - - /** - * Returns a DatabaseConnection for a givenUrl is one is already open. If not, attempts to create it, but only - * if a previous attempt at creating the connection has NOT failed (to prevent unnecessary connection attempts - * during the integration tests). - * - * @param givenUrl The JDBC URL to connect to - * @param username the user name to use to log in to the instance (may be null, esp. for embedded DBMS) - * @param password the password for the username (may be null) - * @return a DatabaseConnection if one has been established or fetched from the cache successfully, null otherwise - * @throws Exception if an error occurs while trying to get the connection - */ - private DatabaseConnection openConnection(final String givenUrl, - final String username, final String password) throws Exception { - final String url = replaceRandomPlaceholder(replaceTempDirPlaceholder(givenUrl)); - - if (connectionsAttempted.containsKey(url)) { - JdbcConnection connection = (JdbcConnection) connectionsByUrl.get(url); - if (connection == null) { - return null; - } else if (connection.getUnderlyingConnection().isClosed()) { - connectionsByUrl.put(url, openDatabaseConnection(url, username, password)); - } - return connectionsByUrl.get(url); - } - connectionsAttempted.put(url, Boolean.TRUE); - - if (System.getProperty(TEST_DATABASES_PROPERTY) != null) { - boolean shouldTest = false; - String[] databasesToTest = System.getProperty(TEST_DATABASES_PROPERTY).split("\\s*,\\s*"); - for (String database : databasesToTest) { - if (url.indexOf(database) >= 0) { - shouldTest = true; - } - } - if (!shouldTest) { - System.out.println("test.databases system property forbids testing against " + url); - return null; - } else { - System.out.println("Will be tested against " + url); - } - } - - DatabaseConnection connection = openDatabaseConnection(url, username, password); - if (connection == null) { - return null; - } - - Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection); - final DatabaseConnection databaseConnection = database.getConnection(); - - if (databaseConnection.getAutoCommit()) { - databaseConnection.setAutoCommit(false); - } - - try { - if (url.startsWith("jdbc:hsql")) { - String sql = "CREATE SCHEMA " + ALT_SCHEMA + " AUTHORIZATION DBA"; - for (SqlListener listener : Scope.getCurrentScope().getListeners(SqlListener.class)) { - listener.writeSqlWillRun(sql); - } - ((JdbcConnection) databaseConnection).getUnderlyingConnection().createStatement().execute(sql); - } else if (url.startsWith("jdbc:sqlserver") - || url.startsWith("jdbc:postgresql") - || url.startsWith("jdbc:h2")) { - String sql = "CREATE SCHEMA " + ALT_SCHEMA; - for (SqlListener listener : Scope.getCurrentScope().getListeners(SqlListener.class)) { - listener.writeSqlWillRun(sql); - } - ((JdbcConnection) databaseConnection).getUnderlyingConnection().createStatement().execute(sql); - } - if (!databaseConnection.getAutoCommit()) { - databaseConnection.commit(); - } - } catch (SQLException e) { - // schema already exists - } finally { - try { - databaseConnection.rollback(); - } catch (DatabaseException e) { - if (database instanceof AbstractDb2Database) { -// expected, there is a problem with it - } else { - throw e; - } - } - } - - connectionsByUrl.put(url, databaseConnection); - - Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { - - @Override - public void run() { - shutdownConnection((JdbcConnection) databaseConnection); - } - })); - - return databaseConnection; - } - - /** - * Ensures that the next attempt to call openConnection for the given JDBC URL returns a fresh connection. - * - * @param url The JDBC connection URL to remove from the cache pool. - */ - public void closeConnection(String url) { - JdbcConnection conn = (JdbcConnection) connectionsByUrl.get(url); - if (conn != null) { - shutdownConnection(conn); - connectionsByUrl.remove(url); - connectionsAttempted.remove(url); - } - } - - public DatabaseConnection openDatabaseConnection(String url, - String username, String password) throws Exception { - - JUnitJDBCDriverClassLoader jdbcDriverLoader = JUnitJDBCDriverClassLoader.getInstance(); - final Driver driver; - try { - driver = (Driver) Class.forName(DatabaseFactory.getInstance().findDefaultDriver(url), true, jdbcDriverLoader).getConstructor().newInstance(); - } catch (Exception e) { - System.out.println("Could not connect to " + url + ": Will not test against. " + e.getMessage()); - return null; //could not connect - } - - Properties info = new Properties(); - info.put("user", username); - if (password != null) { - info.put("password", password); - } - info.put("retrieveMessagesFromServerOnGetMessage", "true"); //for db2 - - - Connection connection; - try { - connection = driver.connect(url, info); - } catch (SQLException e) { - System.out.println("Could not connect to " + url + ": Will not test against. " + e.getMessage()); - return null; //could not connect - } - if (connection == null) { - throw new DatabaseException("Connection could not be created to " + url + " with driver " + driver.getClass().getName() + ". Possibly the wrong driver for the given database URL"); - } - - return new JdbcConnection(connection); - } - - public DatabaseTestURL[] getTestUrls() { - return DEFAULT_TEST_DATABASES; - } - - public Set getAllDatabases() { - if (allDatabases == null) { - allDatabases = new HashSet(); - - allDatabases.addAll(DatabaseFactory.getInstance().getImplementedDatabases()); - - List toRemove = new ArrayList(); - for (Database database : allDatabases) { - if ((database instanceof SQLiteDatabase) //todo: re-enable sqlite testing - || (database instanceof MockDatabase) || (database instanceof ExampleCustomDatabase)) { - toRemove.add(database); - } - } - allDatabases.removeAll(toRemove); - } - return allDatabases; - } - - public Set getAvailableDatabases() throws Exception { - if (availableDatabases.isEmpty()) { - for (DatabaseConnection conn : getAvailableConnections()) { - availableDatabases.add(DatabaseFactory.getInstance().findCorrectDatabaseImplementation(conn)); - } - } - //Check to don't return closed databases - Iterator iter = availableDatabases.iterator(); - while (iter.hasNext()) { - Database database = iter.next(); - if (database.getConnection().isClosed()) - iter.remove(); - } - - - return availableDatabases; - } - - - public Set getAvailableConnections() throws Exception { - if (availableConnections == null) { - availableConnections = new HashSet(); - for (DatabaseTestURL url : getTestUrls()) { - DatabaseConnection connection = openConnection(url.getUrl(), url.getUsername(), url.getPassword()); - - if (connection != null) { - availableConnections.add(connection); - } - } - } - - //Check to don't return closed connections - Iterator iter = availableConnections.iterator(); - while (iter.hasNext()) { - DatabaseConnection connection = iter.next(); - if (connection.isClosed()) - iter.remove(); - } - - return availableConnections; - } - - public DatabaseConnection getConnection(String url, String username, String password) throws Exception { - return openConnection(url, username, password); - } - - public String getTestUrl(Database database) throws Exception { - for (DatabaseTestURL turl : getTestUrls()) { - String url = turl.getUrl(); - if (database.getDefaultDriver(url) != null) { - return url; - } - } - throw new RuntimeException("Could not find url for " + database); - } -} diff --git a/liquibase-integration-tests/src/test/java/liquibase/test/DatabaseTestTemplate.java b/liquibase-integration-tests/src/test/java/liquibase/test/DatabaseTestTemplate.java deleted file mode 100644 index 60f7d4ca555..00000000000 --- a/liquibase-integration-tests/src/test/java/liquibase/test/DatabaseTestTemplate.java +++ /dev/null @@ -1,80 +0,0 @@ -package liquibase.test; - -import liquibase.Scope; -import liquibase.database.Database; -import liquibase.database.core.SQLiteDatabase; -import liquibase.exception.MigrationFailedException; -import liquibase.executor.ExecutorService; -import liquibase.executor.jvm.JdbcExecutor; -import liquibase.lockservice.LockService; -import liquibase.lockservice.LockServiceFactory; -import org.junit.ComparisonFailure; - -import java.util.Set; - -public class DatabaseTestTemplate { - public void testOnAvailableDatabases(DatabaseTest test) throws Exception { - test(test, DatabaseTestContext.getInstance().getAvailableDatabases()); - } - - public void testOnAllDatabases(DatabaseTest test) throws Exception { - test(test, TestContext.getInstance().getAllDatabases()); - } - - private void test(DatabaseTest test, Set databasesToTestOn) throws Exception { - for (Database database : databasesToTestOn) { - if (database instanceof SQLiteDatabase) { - continue; //todo: find how to get tests to run correctly on SQLite - } - JdbcExecutor writeExecutor = new JdbcExecutor(); - writeExecutor.setDatabase(database); - Scope.getCurrentScope().getSingleton(ExecutorService.class).setExecutor("jdbc", database, writeExecutor); - LockService lockService = LockServiceFactory.getInstance().getLockService(database); - lockService.reset(); - if (database.getConnection() != null) { - lockService.forceReleaseLock(); - } - - try { - test.performTest(database); - } catch (ComparisonFailure e) { - String newMessage = "Database Test Failure on " + database; - if (e.getMessage() != null) { - newMessage += ": " + e.getMessage(); - } - - ComparisonFailure newError = new ComparisonFailure(newMessage, e.getExpected(), e.getActual()); - newError.setStackTrace(e.getStackTrace()); - throw newError; - } catch (AssertionError | MigrationFailedException e) { - e.printStackTrace(); - String newMessage = "Database Test Failure on " + database; - if (e.getMessage() != null) { - newMessage += ": " + e.getMessage(); - } - - AssertionError newError = new AssertionError(newMessage); - newError.setStackTrace(e.getStackTrace()); - throw newError; - } catch (Exception e) { - e.printStackTrace(); - String newMessage = "Database Test Exception on " + database; - if (e.getMessage() != null) { - newMessage += ": " + e.getMessage(); - } - - Exception newError = e.getClass().getConstructor(String.class).newInstance(newMessage); - if (e.getCause() == null) { - newError.setStackTrace(e.getStackTrace()); - } else { - newError.setStackTrace(e.getCause().getStackTrace()); - } - throw newError; - } finally { - if ((database.getConnection() != null) && !database.getAutoCommitMode()) { - database.rollback(); - } - } - } - } -} diff --git a/liquibase-integration-tests/src/test/java/liquibase/test/DatabaseTestURL.java b/liquibase-integration-tests/src/test/java/liquibase/test/DatabaseTestURL.java deleted file mode 100644 index 005d59b50e8..00000000000 --- a/liquibase-integration-tests/src/test/java/liquibase/test/DatabaseTestURL.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package liquibase.test; - -import liquibase.dbtest.AbstractIntegrationTest; - -/** - * - * @author lujop - */ -public class DatabaseTestURL { - private String url; - private String username; - private String password; - - private String databaseManager; - - public DatabaseTestURL(String databaseManager, String defaultUrl) { - // Use a specific URL / username / password if given in the test configuration properties. - DatabaseTestURL configuredUrl = AbstractIntegrationTest.getDatabaseTestURL(databaseManager); - if (configuredUrl == null) { - this.url = defaultUrl; - } else { - this.url = configuredUrl.getUrl(); - this.username = configuredUrl.getUsername(); - this.password = configuredUrl.getPassword(); - } - this.databaseManager = databaseManager; - } - - public DatabaseTestURL(String databaseManager, String url, String username, String password) { - this.url = url; - this.username = username; - this.password = password; - this.databaseManager = databaseManager; - } - - public String getDatabaseManager() { - return databaseManager; - } - - public String getUrl() { - return url; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } -} diff --git a/liquibase-integration-tests/src/test/java/liquibase/test/JUnitJDBCDriverClassLoader.java b/liquibase-integration-tests/src/test/java/liquibase/test/JUnitJDBCDriverClassLoader.java deleted file mode 100644 index 98c72aac617..00000000000 --- a/liquibase-integration-tests/src/test/java/liquibase/test/JUnitJDBCDriverClassLoader.java +++ /dev/null @@ -1,72 +0,0 @@ -package liquibase.test; - -import java.io.File; -import java.io.FileFilter; -import java.io.FilenameFilter; -import java.net.URI; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.List; - -/** - * Class Loader for loading JDBC drivers in unit tests. It was orginally not a singleton, but - * would instead take the directory of the particular driver you wanted and create a new - * class loader with just those jar files. Unfortunatley, the class loaders were never cleaned up by - * the JVM even though there were no references to them and the permgen space requirements would skyrocket. - * It was re-implemented as a singleton to solve that problem. If we ever need to make different unit tests that use - * the same driver class name but different jars (versions) we will need to re-address the issue. - */ -public class JUnitJDBCDriverClassLoader extends URLClassLoader { - - private static final JUnitJDBCDriverClassLoader instance = new JUnitJDBCDriverClassLoader(); - - private JUnitJDBCDriverClassLoader() { - super(getDriverClasspath()); - } - - public static JUnitJDBCDriverClassLoader getInstance() { - return instance; - } - - private static URL[] getDriverClasspath() { - try { - List urls = new ArrayList(); - - return urls.toArray(new URL[urls.size()]); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static void addUrlsFromPath(List addTo,String path) throws Exception { - File thisClassFile = new File(new URI(Thread.currentThread().getContextClassLoader().getResource("liquibase/test/JUnitJDBCDriverClassLoader.class") - .toExternalForm())); - File jdbcLib = new File(thisClassFile.getParentFile().getParentFile().getParentFile(),path); - if (!jdbcLib.exists()) { - throw new RuntimeException("JDBC driver directory "+jdbcLib.getAbsolutePath()+" does not exist"); - } - File[] files = jdbcLib.listFiles(new FileFilter() { - @Override - public boolean accept(File pathname) { - return pathname.isDirectory(); - } - }); - if(files == null) { - files = new File[]{}; - } - for (File driverDir : files) { - File[] driverJars = driverDir.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.endsWith("jar"); - } - }); - - for (File jar : driverJars) { - addTo.add(jar.toURI().toURL()); - } - - } - } -} diff --git a/liquibase-integration-tests/src/test/resources/changelogs/cockroachdb/complete/root.changelog.xml b/liquibase-integration-tests/src/test/resources/changelogs/cockroachdb/complete/root.changelog.xml index e270007f0cc..b38e9657c48 100644 --- a/liquibase-integration-tests/src/test/resources/changelogs/cockroachdb/complete/root.changelog.xml +++ b/liquibase-integration-tests/src/test/resources/changelogs/cockroachdb/complete/root.changelog.xml @@ -89,9 +89,15 @@ - + + + + + @@ -369,4 +375,4 @@ Auto increment the primary keys - \ No newline at end of file + diff --git a/liquibase-integration-tests/src/test/resources/changelogs/common/externalfk.init.changelog.xml b/liquibase-integration-tests/src/test/resources/changelogs/common/externalfk.init.changelog.xml index 8ecf90bae09..1a8bafb3614 100644 --- a/liquibase-integration-tests/src/test/resources/changelogs/common/externalfk.init.changelog.xml +++ b/liquibase-integration-tests/src/test/resources/changelogs/common/externalfk.init.changelog.xml @@ -6,7 +6,7 @@ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd"> - + @@ -26,7 +26,7 @@ diff --git a/liquibase-integration-tests/src/test/resources/changelogs/db2/complete/root.changelog.xml b/liquibase-integration-tests/src/test/resources/changelogs/db2/complete/root.changelog.xml index f35ce4d8bbe..75a8c9429ab 100644 --- a/liquibase-integration-tests/src/test/resources/changelogs/db2/complete/root.changelog.xml +++ b/liquibase-integration-tests/src/test/resources/changelogs/db2/complete/root.changelog.xml @@ -154,7 +154,7 @@ - select * from liquibase.person + select * from lbuser.person @@ -335,4 +335,4 @@ - \ No newline at end of file + diff --git a/liquibase-integration-tests/src/test/resources/changelogs/mssql/complete/person_view.sql b/liquibase-integration-tests/src/test/resources/changelogs/mssql/complete/person_view.sql index c99c6fda50f..eab0ef29de6 100644 --- a/liquibase-integration-tests/src/test/resources/changelogs/mssql/complete/person_view.sql +++ b/liquibase-integration-tests/src/test/resources/changelogs/mssql/complete/person_view.sql @@ -1,5 +1,5 @@ -CREATE VIEW [lbcat2].[PERSON_VIEW] +CREATE VIEW [lbschem2].[PERSON_VIEW] AS SELECT * FROM person -GO \ No newline at end of file +GO diff --git a/liquibase-integration-tests/src/test/resources/changelogs/mssql/complete/root.changelog.xml b/liquibase-integration-tests/src/test/resources/changelogs/mssql/complete/root.changelog.xml index 248f0d8f2f5..fcfd5719490 100644 --- a/liquibase-integration-tests/src/test/resources/changelogs/mssql/complete/root.changelog.xml +++ b/liquibase-integration-tests/src/test/resources/changelogs/mssql/complete/root.changelog.xml @@ -305,7 +305,7 @@ - + Checking if person_view present, and adding if not @@ -342,4 +342,4 @@ - \ No newline at end of file + diff --git a/liquibase-integration-tests/src/test/resources/liquibase/liquibase.integrationtest.jenkins.properties b/liquibase-integration-tests/src/test/resources/liquibase/liquibase.integrationtest.jenkins.properties deleted file mode 100644 index 3bb4f238811..00000000000 --- a/liquibase-integration-tests/src/test/resources/liquibase/liquibase.integrationtest.jenkins.properties +++ /dev/null @@ -1,90 +0,0 @@ -# This file is used by Jenkins. -# Jenkins runs the maven build in another container, so the databases must be accessed by their hostname -# Additionally, the maven container and the docker databases must also be on the same docker network -# As part of the Jenkins pipeline this template will replace the default liquibase.integrationtest.properties file - -# These assume that the servers (if applicable, some DBMSs are embedded) are running on localhost. -# For the majority of the "big" DBMS like MSSQL and Oracle, this will not be practical due to their resource -# hunger; in this case, please use liquibase.integrationtest.local.properties to point to your local testing server -# instead of changing this file. - -# It is possible to specify different JDBC URLs, usernames and passwords -# for each database type during tests. For databases other than Oracle Database, -# copy the following block and replace "Oracle" with the name of a supported DBMS, -# e.g. DB2, Informix, MSSQL, MySQL, Postgres etc. -# integration.test.Oracle.url=jdbc:oracle:thin:@LIQUIBASE_INTEGRATION_TEST -# integration.test.Oracle.username=liquibase -# integration.test.Oracle.password=liquibase -# Note: The String # ***TEMPDIR*** is replaced with the contents of System.getProperty("java.io.tmpdir") -# at runtime, typically /tmp on Unix and a folder inside the user's profile on Windows. -# "Fallback" username and password. If no username or password is set for a given test, -# we use these: -integration.test.username=lbuser -integration.test.password=LiquibasePass1 - -# Oracle RDBMS running on localhost on 1521/tcp -# integration.test.oracle.username=liquibase -# integration.test.oracle.password=liquibase -integration.test.oracle.url=jdbc:oracle:thin:@//oracle:1521/lbcat - -# SAP (formerly Siebel) Adaptive SQL Anywhere -integration.test.asany.username=liquibase -integration.test.asany.password=liquibase -integration.test.asany.url=jdbc:sybase:Tds:localhost:2638?ServiceName=liquibase - -# IBM DB2 LUW (LUW = Version for Linux, Unix and Windows) -integration.test.db2.url=jdbc:db2://localhost:50000/liquibas:currentSchema=LIQUIBASE; -integration.test.db2z.url=jdbc:db2://localhost:50000/liquibas:currentSchema=LIQUIBASE; - -# Apache Derby embedded SQL database -integration.test.derby.username=liquibase -integration.test.derby.password=liquibase -integration.test.derby.url=jdbc:derby:liquibase;create=true - -# Firebird SQL -# WARNING: The 3.0.0 JDBC driver for Firebird does not support encryption. You must set WireCrypt = Enabled -# in your firebird.conf and restart the database server for this to work. -# To use the 3.0.0 JDBC driver, you must specify the charSet attribute (UTF-8 should work for most users). -integration.test.firebird.url=jdbc:firebirdsql:localhost/3050:***TEMPDIR***/liquibase.fdb?charSet=utf-8 - -# H2 embedded SQL database -integration.test.h2.url=jdbc:h2:mem:liquibase -integration.test.h2.alt.url=jdbc:h2:mem:liquibase-alt -#integration.test.h2.url=jdbc:h2:***TEMPDIR***/liquibase - -# Hyper SQL (hsqldb) embedded SQL database -integration.test.hsqldb.url=jdbc:hsqldb:mem:liquibase -# hsqldb is special in that we always need to use user "sa" with no password. -integration.test.hsqldb.username=sa -integration.test.hsqldb.password= -integration.test.hsqldb.alt.url=jdbc:hsqldb:mem:alt-liquibase - -# IBM Informix SQL Dynamic Server. Note the special requirements for Unicode (DB_LOCALE) and fixed setting for -# DATE literals (we need this for specifying DATE column default values that are literals). -integration.test.informix.username=liquibase -integration.test.informix.password=liquibase -integration.test.informix.url=jdbc:informix-sqli://localhost:9090/liquibase:informixserver=ol_informix1210;\ - DB_LOCALE=en_us.utf8;GL_DATE=%iY-%m-%d - -# Microsoft SQL Server - Not currently working -# @todo There are two special integration tests for MSSQL Server: MssqlCaseSensitive and MssqlJtds. We need an extension to this naming mechanism to allow these tests to use different URLs. -integration.test.mssql.url=jdbc:sqlserver://mssql:1433;databaseName=lbcat -# Postgres Community -integration.test.postgresql.url=jdbc:postgresql://postgres-9:5432/lbcat -# SQLite Database -integration.test.sqlite.url=jdbc:sqlite:***TEMPDIR***/liquibase.db - -# MySQL and MariaDB -# Because both MySQL and MariaDB claim TCP port 3306 during a default installation, I decided to give 3306 to neither -# of them to avoid confusion. If you want to run integration tests on MySQL and MariaDB and do not want this, -# please create liquibase.integrationtests.local.properties choose your own port(s) for them. -integration.test.mysql.url=jdbc:mysql://mysql:3306/lbcat?useSSL=false -integration.test.mariadb.url=jdbc:mariadb://mariadb:3306/lbcat - -# CockroachDB -integration.test.cockroachdb.username=root -integration.test.cockroachdb.password= -integration.test.cockroachdb.url=jdbc:postgresql://localhost:26257/defaultdb?sslmode=disable - -# Liquibase Hub -integration.test.hub.url=http://localhost:8888 \ No newline at end of file diff --git a/liquibase-integration-tests/src/test/resources/liquibase/liquibase.integrationtest.properties b/liquibase-integration-tests/src/test/resources/liquibase/liquibase.integrationtest.properties deleted file mode 100644 index 1dfe8391181..00000000000 --- a/liquibase-integration-tests/src/test/resources/liquibase/liquibase.integrationtest.properties +++ /dev/null @@ -1,85 +0,0 @@ -# This file is the default and is used by Github Actions. -# Github Actions by default has the runner on the VM, so connecting to localhost on the appropriate db port is correct. - -# Default test URLs for DBMSs. -# These assume that the servers (if applicable, some DBMSs are embedded) are running on localhost. -# For the majority of the "big" DBMS like MSSQL and Oracle, this will not be practical due to their resource -# hunger; in this case, please use liquibase.integrationtest.local.properties to point to your local testing server -# instead of changing this file. - -# It is possible to specify different JDBC URLs, usernames and passwords -# for each database type during tests. For databases other than Oracle Database, -# copy the following block and replace "Oracle" with the name of a supported DBMS, -# e.g. DB2, Informix, MSSQL, MySQL, Postgres etc. -# integration.test.Oracle.url=jdbc:oracle:thin:@LIQUIBASE_INTEGRATION_TEST -# integration.test.Oracle.username=liquibase -# integration.test.Oracle.password=liquibase -# Note: The String # ***TEMPDIR*** is replaced with the contents of System.getProperty("java.io.tmpdir") -# at runtime, typically /tmp on Unix and a folder inside the user's profile on Windows. -# "Fallback" username and password. If no username or password is set for a given test, -# we use these: -integration.test.username=lbuser -integration.test.password=LiquibasePass1 - -# Oracle RDBMS running on localhost on 1521/tcp -# integration.test.oracle.username=liquibase -# integration.test.oracle.password=liquibase -integration.test.oracle.url=jdbc:oracle:thin:@//localhost:1521/lbcat - -# SAP (formerly Siebel) Adaptive SQL Anywhere -integration.test.asany.username=liquibase -integration.test.asany.password=liquibase -integration.test.asany.url=jdbc:sybase:Tds:localhost:2638?ServiceName=liquibase -# IBM DB2 LUW (LUW = Version for Linux, Unix and Windows) -integration.test.db2.url=jdbc:db2://localhost:50000/liquibas:currentSchema=LIQUIBASE; -integration.test.db2z.url=jdbc:db2://localhost:50000/liquibas:currentSchema=LIQUIBASE; -# Apache Derby embedded SQL database -integration.test.derby.username=liquibase -integration.test.derby.password=liquibase -integration.test.derby.url=jdbc:derby:liquibase;create=true -# Firebird SQL -# WARNING: The 3.0.0 JDBC driver for Firebird does not support encryption. You must set WireCrypt = Enabled -# in your firebird.conf and restart the database server for this to work. -# To use the 3.0.0 JDBC driver, you must specify the charSet attribute (UTF-8 should work for most users). -integration.test.firebird.url=jdbc:firebirdsql:localhost/3050:***TEMPDIR***/liquibase.fdb?charSet=utf-8 -# H2 embedded SQL database -integration.test.h2.url=jdbc:h2:mem:liquibase -integration.test.h2.alt.url=jdbc:h2:mem:liquibase-alt -#integration.test.h2.url=jdbc:h2:***TEMPDIR***/liquibase -integration.test.h2.username= -integration.test.h2.password= - -# Hyper SQL (hsqldb) embedded SQL database -integration.test.hsqldb.url=jdbc:hsqldb:mem:liquibase -# hsqldb is special in that we always need to use user "sa" with no password. -integration.test.hsqldb.username=sa -integration.test.hsqldb.password= -integration.test.hsqldb.alt.url=jdbc:hsqldb:mem:alt-liquibase - -# IBM Informix SQL Dynamic Server. Note the special requirements for Unicode (DB_LOCALE) and fixed setting for -# DATE literals (we need this for specifying DATE column default values that are literals). -integration.test.informix.username=liquibase -integration.test.informix.password=liquibase -integration.test.informix.url=jdbc:informix-sqli://localhost:9090/liquibase:informixserver=ol_informix1210;\ - DB_LOCALE=en_us.utf8;GL_DATE=%iY-%m-%d -# Microsoft SQL Server -# @todo There are two special integration tests for MSSQL Server: MssqlCaseSensitive and MssqlJtds. We need an extension to this naming mechanism to allow these tests to use different URLs. -integration.test.mssql.url=jdbc:sqlserver://localhost:14333;databaseName=lbcat -# Postgres Community -integration.test.postgresql.url=jdbc:postgresql://localhost:5432/lbcat -# SQLite Database -# The ***RANDOM*** string is replaced with a UUID during runtime. -integration.test.sqlite.url=jdbc:sqlite:***TEMPDIR***/liquibase-***RANDOM***.db - -# MySQL and MariaDB -# Because both MySQL and MariaDB claim TCP port 3306 during a default installation, I decided to give 3306 to neither -# of them to avoid confusion. If you want to run integration tests on MySQL and MariaDB and do not want this, -# please create liquibase.integrationtests.local.properties choose your own port(s) for them. -integration.test.mysql.url=jdbc:mysql://localhost:33061/lbcat?useSSL=false -integration.test.mariadb.url=jdbc:mariadb://localhost:33066/lbcat - -integration.test.cockroachdb.username=root -integration.test.cockroachdb.password= -integration.test.cockroachdb.url=jdbc:postgresql://localhost:26257/defaultdb?sslmode=disable - -integration.test.hub.url=http://localhost:8888 \ No newline at end of file diff --git a/liquibase-integration-tests/src/test/resources/travis-ci/mysql-setup.sql b/liquibase-integration-tests/src/test/resources/travis-ci/mysql-setup.sql deleted file mode 100644 index fe9dd6e20ed..00000000000 --- a/liquibase-integration-tests/src/test/resources/travis-ci/mysql-setup.sql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE user lbuser@localhost identified by 'lbuser'; - -CREATE DATABASE lbcat; -GRANT ALL PRIVILEGES ON lbcat.* TO 'lbuser'@'localhost'; - -CREATE DATABASE lbcat2; -GRANT ALL PRIVILEGES ON lbcat2.* TO 'lbuser'@'localhost'; - -FLUSH privileges; diff --git a/liquibase-integration-tests/src/test/resources/vagrant/.gitignore b/liquibase-integration-tests/src/test/resources/vagrant/.gitignore deleted file mode 100644 index 62c2cc54a0a..00000000000 --- a/liquibase-integration-tests/src/test/resources/vagrant/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -install-files/*/*.zip -*/.vagrant \ No newline at end of file diff --git a/liquibase-integration-tests/src/test/resources/vagrant/install-files/oracle/README.md b/liquibase-integration-tests/src/test/resources/vagrant/install-files/oracle/README.md deleted file mode 100644 index 58c2e2ab08d..00000000000 --- a/liquibase-integration-tests/src/test/resources/vagrant/install-files/oracle/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## install-files/oracle - -Place oracle install files here. See ../../README.md for more info \ No newline at end of file diff --git a/liquibase-integration-tests/src/test/resources/vagrant/linux-standard/Puppetfile b/liquibase-integration-tests/src/test/resources/vagrant/linux-standard/Puppetfile deleted file mode 100644 index b22fb6e52df..00000000000 --- a/liquibase-integration-tests/src/test/resources/vagrant/linux-standard/Puppetfile +++ /dev/null @@ -1,7 +0,0 @@ -forge "http://forge.puppetlabs.com" - -mod "puppetlabs/apt" -mod "puppetlabs/mysql" -mod "puppetlabs/postgresql" -mod "puppetlabs/firewall" -mod "biemond/oradb" diff --git a/liquibase-integration-tests/src/test/resources/vagrant/linux-standard/Vagrantfile b/liquibase-integration-tests/src/test/resources/vagrant/linux-standard/Vagrantfile deleted file mode 100644 index 974ee41d276..00000000000 --- a/liquibase-integration-tests/src/test/resources/vagrant/linux-standard/Vagrantfile +++ /dev/null @@ -1,117 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! -VAGRANTFILE_API_VERSION = "2" - -Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| - # All Vagrant configuration is done here. The most common configuration - # options are documented and commented below. For a complete reference, - # please see the online documentation at vagrantup.com. - - # Every Vagrant virtual environment requires a box to build off of. - config.vm.box = "CentOS-6.4-x86_64-v20130427" - - # The url from where the 'config.vm.box' box will be fetched if it - # doesn't already exist on the user's system. - config.vm.box_url = "http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-x86_64-v20130427.box" - - #config.vm.hostname = "liquibase-test.example.com" - - # Create a forwarded port mapping which allows access to a specific port - # within the machine from a port on the host machine. In the example below, - # accessing "localhost:8080" will access port 80 on the guest machine. - # config.vm.network :forwarded_port, guest: 80, host: 8080 - - # Create a private network, which allows host-only access to the machine - # using a specific IP. - config.vm.network :private_network, ip: "10.10.100.100" - - # Create a public network, which generally matched to bridged network. - # Bridged networks make the machine appear as another physical device on - # your network. - # config.vm.network :public_network - - # If true, then any SSH connections made will enable agent forwarding. - # Default value: false - # config.ssh.forward_agent = true - - # Share an additional folder to the guest VM. The first argument is - # the path on the host to the actual folder. The second argument is - # the path on the guest to mount the folder. And the optional third - # argument is a set of non-required options. - config.vm.synced_folder "../install-files", "/vagrant-install-files" - - # Provider-specific configuration so you can fine-tune various - # backing providers for Vagrant. These expose provider-specific options. - # Example for VirtualBox: - # - config.vm.provider :virtualbox do |vb| - # # Don't boot with headless mode - # vb.gui = true - # - # # Use VBoxManage to customize the VM. For example to change memory: - vb.customize ["modifyvm", :id, "--memory", "8192"] - end - # - # View the documentation for the provider you're using for more - # information on available options. - - ##### UNCOMMENT THIS WHEN RUNNING THE FIRST TIME ### - config.vm.provision :shell do |shell| - shell.inline = "echo 'configuring librarian-puppet'; - cp /vagrant/Puppetfile /etc/puppet; - sudo gem install librarian-puppet --no-rdoc --no-ri; - cd /etc/puppet; - librarian-puppet install; - sudo mkdir /install; - echo 'Copying vagrant-install-files...'; - cp -rn /vagrant-install-files/* /install; - echo 'Copying vagrant-install-files done'; - " -end - - config.vm.provision :puppet, :module_path => "modules" do |puppet| - puppet.manifests_path = "manifests" - puppet.manifest_file = "init.pp" - #puppet.options = "--debug --verbose" - end - - # Enable provisioning with chef solo, specifying a cookbooks path, roles - # path, and data_bags path (all relative to this Vagrantfile), and adding - # some recipes and/or roles. - # - # config.vm.provision :chef_solo do |chef| - # chef.cookbooks_path = "../my-recipes/cookbooks" - # chef.roles_path = "../my-recipes/roles" - # chef.data_bags_path = "../my-recipes/data_bags" - # chef.add_recipe "mysql" - # chef.add_role "web" - # - # # You may also specify custom JSON attributes: - # chef.json = { :mysql_password => "foo" } - # end - - # Enable provisioning with chef server, specifying the chef server URL, - # and the path to the validation key (relative to this Vagrantfile). - # - # The Opscode Platform uses HTTPS. Substitute your organization for - # ORGNAME in the URL and validation key. - # - # If you have your own Chef Server, use the appropriate URL, which may be - # HTTP instead of HTTPS depending on your configuration. Also change the - # validation key to validation.pem. - # - # config.vm.provision :chef_client do |chef| - # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME" - # chef.validation_key_path = "ORGNAME-validator.pem" - # end - # - # If you're using the Opscode platform, your validator client is - # ORGNAME-validator, replacing ORGNAME with your organization name. - # - # If you have your own Chef Server, the default validation client name is - # chef-validator, unless you changed the configuration. - # - # chef.validation_client_name = "ORGNAME-validator" -end diff --git a/liquibase-integration-tests/src/test/resources/vagrant/linux-standard/manifests/init.pp b/liquibase-integration-tests/src/test/resources/vagrant/linux-standard/manifests/init.pp deleted file mode 100644 index 2b4e9ec1f4f..00000000000 --- a/liquibase-integration-tests/src/test/resources/vagrant/linux-standard/manifests/init.pp +++ /dev/null @@ -1,138 +0,0 @@ -package { "unzip": - ensure => "installed" -} - -resources { "firewall": - purge => true -} - -Firewall { - before => Class['my_firewall::post'], - require => Class['my_firewall::pre'], -} - -class { ['my_firewall::pre', 'my_firewall::post']: } - -class { 'firewall': } - -class { 'mysql::server': - config_hash => { - 'root_password' => 'root', - 'bind_address' => '0.0.0.0', } -} - -mysql::db { 'liquibase': - user => 'liquibase', - password => 'liquibase', - host => '%', - grant => ['all'], -} - -mysql::db { 'liquibaseb': - user => 'liquibase', - password => 'liquibase', - host => '%', - grant => ['all'], -} - - -class { 'postgresql::server': - config_hash => { - 'ip_mask_deny_postgres_user' => '0.0.0.0/32', - 'ip_mask_allow_all_users' => '0.0.0.0/0', - 'listen_addresses' => '*', - 'ipv4acls' => ['host all liquibase 0.0.0.0/0 password'], - 'postgres_password' => 'postgres' - }, -} - -postgresql::db { 'liquibase': - user => 'liquibase', - password => 'liquibase' -} - - -$oracle_packages = [ -"binutils", -"compat-libcap1", -"gcc", -"gcc-c++", -"glibc", -"glibc-devel", -"ksh", -"libgcc", -"libstdc++", -"libstdc++-devel", -"libaio", -"libaio-devel", -"libXext", -"libX11", -"libXau", -"libxcb", -"libXi", -"make", -"sysstat", -] - -package { $oracle_packages: ensure => "installed" } - -oradb::installdb{ '12.1.0.1_Linux-x86-64': - version => '12.1.0.1', - file => 'linuxamd64_12c_database', - databaseType => 'SE', - oracleBase => '/oracle', - oracleHome => '/oracle/product/12.1/db', - user => 'oracle', - group => 'dba', - downloadDir => '/install/oracle/', - puppetDownloadMntPoint => '/install/oracle/' -} - -oradb::database{ 'liquibase': - oracleBase => '/oracle', - oracleHome => '/oracle/product/12.1/db', - version => "12.1", - user => 'oracle', - group => 'dba', - downloadDir => '/install/oracle/', - action => 'create', - dbName => 'liquibase', - dbDomain => 'liquibase.org', - sysPassword => 'liquibase', - systemPassword => 'liquibase', - dataFileDestination => "/oracle/oradata", - recoveryAreaDestination => "/oracle/flash_recovery_area", - characterSet => "AL32UTF8", - nationalCharacterSet => "UTF8", - initParams => "open_cursors=1000,processes=600,job_queue_processes=4", - sampleSchema => 'FALSE', - memoryPercentage => "40", - memoryTotal => "800", - databaseType => "MULTIPURPOSE", - require => Oradb::InstallDb['12.1.0.1_Linux-x86-64'], -} - - oradb::net{ 'config net8': - oracleHome => '/oracle/product/12.1/db', - version => "12.1", - user => 'oracle', - group => 'dba', - downloadDir => '/install/oracle/', - require => Oradb::Database['liquibase'], - } - -oradb::listener{'start listener': - oracleBase => '/oracle', - oracleHome => '/oracle/product/12.1/db', - user => 'oracle', - group => 'dba', - action => 'start', - require => Oradb::Net['config net8'], - } - -oradb::autostartdatabase{ 'autostart oracle': - oracleHome => '/oracle/product/12.1/db', - user => 'oracle', - dbName => 'liquibase', - require => Oradb::Database['liquibase'], -} \ No newline at end of file diff --git a/liquibase-integration-tests/src/test/resources/vagrant/linux-standard/modules/my_firewall/manifests/post.pp b/liquibase-integration-tests/src/test/resources/vagrant/linux-standard/modules/my_firewall/manifests/post.pp deleted file mode 100644 index 650ff368c86..00000000000 --- a/liquibase-integration-tests/src/test/resources/vagrant/linux-standard/modules/my_firewall/manifests/post.pp +++ /dev/null @@ -1,7 +0,0 @@ -class my_firewall::post { - firewall { '999 drop all': - proto => 'all', - action => 'drop', - before => undef, - } -} \ No newline at end of file diff --git a/liquibase-integration-tests/src/test/resources/vagrant/linux-standard/modules/my_firewall/manifests/pre.pp b/liquibase-integration-tests/src/test/resources/vagrant/linux-standard/modules/my_firewall/manifests/pre.pp deleted file mode 100644 index dd954f8f5e0..00000000000 --- a/liquibase-integration-tests/src/test/resources/vagrant/linux-standard/modules/my_firewall/manifests/pre.pp +++ /dev/null @@ -1,25 +0,0 @@ -class my_firewall::pre { - Firewall { - require => undef, - } - - # Default firewall rules - firewall { '000 accept all icmp': - proto => 'icmp', - action => 'accept', - }-> - firewall { '001 accept all to lo interface': - proto => 'all', - iniface => 'lo', - action => 'accept', - }-> - firewall { '002 accept all to tcp interface': - proto => 'tcp', - action => 'accept', - }-> - firewall { '003 accept related established rules': - proto => 'all', - state => ['RELATED', 'ESTABLISHED'], - action => 'accept', - } -} \ No newline at end of file diff --git a/pom.xml b/pom.xml index a3a8159059a..aeea66fe67d 100644 --- a/pom.xml +++ b/pom.xml @@ -197,6 +197,28 @@ test + + com.ibm.db2 + jcc + 11.5.6.0 + test + + + + com.oracle.database.jdbc + ojdbc8 + 18.3.0.0 + test + + + + org.xerial + sqlite-jdbc + 3.34.0 + test + + + javax.xml.bind