Java tutorial
/** * Copyright 2014 www.migratebird.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.migratebird.integrationtest; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import com.migratebird.DbUpdate; import com.migratebird.MainFactory; import com.migratebird.config.Config; import com.migratebird.config.ConfigrationLoader; import com.migratebird.config.MigratebirdProperties; import com.migratebird.database.*; import com.migratebird.database.impl.DefaultDatabaseConnectionManager; import com.migratebird.database.impl.DefaultSQLHandler; import com.migratebird.datasource.DataSourceFactory; import com.migratebird.datasource.impl.SimpleDataSourceFactory; import com.migratebird.script.ExecutedScript; import com.migratebird.script.executedscriptinfo.ExecutedScriptInfoSource; import com.migratebird.util.MigrateBirdException; import com.migratebird.util.SQLTestUtils; import org.junit.Before; import org.junit.Test; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.Properties; import java.util.Set; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.apache.commons.io.FileUtils.writeStringToFile; import static org.apache.commons.lang.StringUtils.*; import static com.migratebird.config.MigratebirdProperties.*; import static com.migratebird.integrationtest.MigrateBirdIntegrationTest.TestScript.*; import static com.migratebird.util.SQLTestUtils.dropTestTables; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; /** * Integration test for the migratebird: verifies the typical usage scenario's in an integrated way: The migratebird is * setup using properties, a real database is used and scripts are created on the file system. * */ public class MigrateBirdIntegrationTest { static enum TestScript { INCREMENTAL_1("01_incremental/01_incremental_1.sql"), INCREMENTAL_1_RENAMED( "01_incremental/01_incremental_1_renamed.sql"), INCREMENTAL_2( "01_incremental/02_incremental_2.sql"), INCREMENTAL_3( "01_incremental/03_incremental_3.sql"), INCREMENTAL_4( "02_incremental/04_incremental_4.sql"), INCREMENTAL_1_QUALIFIER1("01_incremental/01_#q1.sql"), INCREMENTAL_2_QUALIFIER2( "01_incremental/02_#q2.sql"), INCREMENTAL_2_UNKNOWN_QUALIFIER( "01_incremental/02_#unknown.sql"), INCREMENTAL_3_QUALIFIER1_QUALIFIER2( "01_incremental/03_#q1_#q2.sql"), REPEATABLE("repeatable/repeatable.sql"), REPEATABLE_RENAMED("repeatable/repeatable_renamed.sql"), POST_PROCESSING_INDEXED_1("postprocessing/postProcessing_indexed_1.sql"), POST_PROCESSING_INDEXED_2( "postprocessing/postProcessing_indexed_2.sql"), POST_PROCESSING_INDEXED_2_RENAMED( "postprocessing/postProcessing_indexed_2_renamed.sql"), POST_PROCESSING_NOTINDEXED( "postprocessing/postProcessing_notindexed.sql"); TestScript(String scriptName) { this(scriptName, null); } TestScript(String scriptName, String scriptContent) { this.scriptName = scriptName; this.scriptContent = scriptContent; } private String scriptName; private String scriptContent; } private static final String BEFORE_INITIAL_TABLE = "before_initial"; private File scriptsLocation; private Database defaultDatabase; private Properties properties; private Config config; @Before public void init() { scriptsLocation = new File("target", "migratebird-integrationtest/scripts"); initConfiguration(); clearScriptsDirectory(); clearTestDatabase(); } @Test public void initialScriptsExecuted() { createScripts(INCREMENTAL_1, INCREMENTAL_2, REPEATABLE); updateDatabase(); assertScriptsCorrectlyExecuted(INCREMENTAL_1, INCREMENTAL_2, REPEATABLE); updateDatabase(); } @Test public void addIncremental() { createScripts(INCREMENTAL_1, REPEATABLE); updateDatabase(); assertScriptsNotExecuted(INCREMENTAL_2); createScript(INCREMENTAL_2); updateDatabase(); assertScriptsCorrectlyExecuted(INCREMENTAL_2); } @Test public void addIncremental_dryRun() { createScripts(INCREMENTAL_1, REPEATABLE); updateDatabase(); assertScriptsNotExecuted(INCREMENTAL_2); createScript(INCREMENTAL_2); checkScriptUpdates(); assertScriptsNotExecuted(INCREMENTAL_2); } @Test public void updateIncremental_fromScratchEnabled() { enableFromScratch(); createScripts(INCREMENTAL_1, INCREMENTAL_2); updateDatabase(); updateScript(INCREMENTAL_1); updateDatabase(); assertScriptsNotExecuted(INCREMENTAL_1); assertUpdatedScriptsExecuted(INCREMENTAL_1); // Verify that, if we run updateDatabase again, scripts are not run over again. This to make sure we prevent // bug DBM-80 to happen again. boolean updatePerformed = updateDatabase(); assertFalse(updatePerformed); } @Test public void updateIncremental_fromScratchDisabled() { createScripts(INCREMENTAL_1, INCREMENTAL_2); updateDatabase(); updateScript(INCREMENTAL_1); try { updateDatabase(); } catch (MigrateBirdException e) { assertMessageContains(e.getMessage(), "updated", "indexed", getTableNameForScript(INCREMENTAL_1)); } } @Test public void updateIncremental_fromScratchDisabled_dryRun() { createScripts(INCREMENTAL_1, INCREMENTAL_2); updateDatabase(); updateScript(INCREMENTAL_1); try { checkScriptUpdates(); } catch (MigrateBirdException e) { assertMessageContains(e.getMessage(), "updated", "indexed", getTableNameForScript(INCREMENTAL_1)); } } @Test public void addIncrementalWithLowerIndex_fromScratchEnabled() { enableFromScratch(); createScripts(INCREMENTAL_2); updateDatabase(); createScripts(INCREMENTAL_1); updateDatabase(); assertScriptsCorrectlyExecuted(INCREMENTAL_1, INCREMENTAL_2); } @Test public void addIncrementalWithLowerIndex_fromScratchDisabled() { createScripts(INCREMENTAL_2); updateDatabase(); createScripts(INCREMENTAL_1); try { updateDatabase(); fail(); } catch (MigrateBirdException e) { assertMessageContains(e.getMessage(), "added", "incremental", "lower index", INCREMENTAL_1.scriptName); } } @Test public void removeExistingIncremental_fromScratchEnabled() { enableFromScratch(); createScripts(INCREMENTAL_1, INCREMENTAL_2); updateDatabase(); removeScript(INCREMENTAL_2); updateDatabase(); assertScriptsNotExecuted(INCREMENTAL_2); } @Test public void removeExistingIncremental_fromScratchDisabled() { createScripts(INCREMENTAL_1, INCREMENTAL_2); updateDatabase(); removeScript(INCREMENTAL_2); try { updateDatabase(); fail(); } catch (MigrateBirdException e) { assertMessageContains(e.getMessage(), "deleted", "indexed", INCREMENTAL_2.scriptName); } } @Test public void addRepeatable() { createScripts(INCREMENTAL_1); updateDatabase(); assertScriptsNotExecuted(REPEATABLE); createScripts(REPEATABLE); updateDatabase(); assertScriptsCorrectlyExecuted(REPEATABLE); } @Test public void addRepeatable_dryRun() { createScripts(INCREMENTAL_1); updateDatabase(); assertScriptsNotExecuted(REPEATABLE); createScripts(REPEATABLE); checkScriptUpdates(); assertScriptsNotExecuted(REPEATABLE); } @Test public void updateRepeatable() { createScripts(INCREMENTAL_1, REPEATABLE); updateDatabase(); updateRepeatableScript(REPEATABLE); updateDatabase(); assertUpdatedScriptsExecuted(REPEATABLE); } @Test public void updateRepeatable_dryRun() { createScripts(INCREMENTAL_1, REPEATABLE); updateDatabase(); updateRepeatableScript(REPEATABLE); checkScriptUpdates(); assertUpdatedScriptsNotExecuted(REPEATABLE); } @Test public void testDeleteRepeatable() { createScripts(INCREMENTAL_1, REPEATABLE); updateDatabase(); removeScript(REPEATABLE); assertInExecutedScripts(REPEATABLE); updateDatabase(); assertNotInExecutedScripts(REPEATABLE); } @Test public void deleteRepeatable_dryRun() { createScripts(INCREMENTAL_1, REPEATABLE); updateDatabase(); removeScript(REPEATABLE); assertInExecutedScripts(REPEATABLE); checkScriptUpdates(); assertInExecutedScripts(REPEATABLE); } /** * Test for adding a patch script that has an index smaller than an existing index. Out of sequence is * not allowed so the update should have failed. */ @Test(expected = MigrateBirdException.class) public void addPatchScript_outOfSequenceNotAllowed() { createScripts(INCREMENTAL_2); updateDatabase(); createPatchScript(INCREMENTAL_1); updateDatabase(); } /** * Test for adding a patch script that has an index smaller than an existing index. Out of sequence is * allowed, the patch script should have been executed (with a warning) */ @Test public void addPatchScript_outOfSequenceAllowed() { allowOutOfSequenceExecutionOfPatches(); createScripts(INCREMENTAL_2); updateDatabase(); createPatchScript(INCREMENTAL_1); updateDatabase(); } /** * Test for adding a patch script that has an index smaller than an existing index. Out of sequence is * allowed, but the patch has a sequence nr that was already used. This should fail. */ @Test(expected = MigrateBirdException.class) public void addPatchScript_identicalSequenceNr() { allowOutOfSequenceExecutionOfPatches(); createScripts(INCREMENTAL_1); updateDatabase(); createPatchScript(INCREMENTAL_1); updateDatabase(); } @Test public void errorInIncrementalScript() { enableFromScratch(); createScripts(INCREMENTAL_1, INCREMENTAL_2, REPEATABLE); errorInScript(INCREMENTAL_1); try { updateDatabase(); fail(); } catch (MigrateBirdException e) { assertMessageContains(e.getMessage(), "Could not perform database statement"); } assertScriptsNotExecuted(INCREMENTAL_1, INCREMENTAL_2, REPEATABLE); // Verify that an error is raised when we check for database updates try { checkScriptUpdates(); fail(); } catch (MigrateBirdException e) { assertMessageContains(e.getMessage(), "During the latest update", INCREMENTAL_1.scriptName); } // Try again without changing anything // No script is executed but an exception is raised indicating that the script that caused the error was not changed. try { updateDatabase(); fail(); } catch (MigrateBirdException e) { assertMessageContains(e.getMessage(), "During the latest update", INCREMENTAL_1.scriptName); } assertScriptsNotExecuted(INCREMENTAL_1, INCREMENTAL_2, REPEATABLE); // change the script and try again // the database should have been recreated from scratch and all the tables should have been re-created fixErrorInScript(INCREMENTAL_1); updateDatabase(); assertScriptsCorrectlyExecuted(INCREMENTAL_1, INCREMENTAL_2, REPEATABLE); } @Test public void errorInRepeatableScript() { enableFromScratch(); createScripts(INCREMENTAL_1, REPEATABLE); errorInScript(REPEATABLE); try { updateDatabase(); fail(); } catch (MigrateBirdException e) { assertMessageContains(e.getMessage(), "error", REPEATABLE.scriptName); } // Verify that an error is raised when we check for database updates try { checkScriptUpdates(); fail(); } catch (MigrateBirdException e) { assertMessageContains(e.getMessage(), "During the latest update", REPEATABLE.scriptName); } // Try again without changing anything // No script is executed but an exception is raised indicating that the script that caused the error was not changed. try { updateDatabase(); fail(); } catch (MigrateBirdException e) { assertMessageContains(e.getMessage(), "During the latest update", REPEATABLE.scriptName); } // Update an incremental script, without fixing the error // A from-scratch recreation is performed, but the error occurs again updateScript(INCREMENTAL_1); try { updateDatabase(); fail(); } catch (MigrateBirdException e) { assertMessageContains(e.getMessage(), "error", REPEATABLE.scriptName); } assertUpdatedScriptsExecuted(INCREMENTAL_1); // Fix the error and witness that the repeatable script is re-executed, and the update finishes successfully fixErrorInRepeatableScript(REPEATABLE); updateDatabase(); assertUpdatedScriptsExecuted(INCREMENTAL_1); assertScriptsCorrectlyExecuted(REPEATABLE); } @Test public void errorInRepeatableScript_fixByRemovingScript() { createScripts(INCREMENTAL_1, REPEATABLE); errorInScript(REPEATABLE); try { updateDatabase(); fail(); } catch (MigrateBirdException e) { assertMessageContains(e.getMessage(), "error", REPEATABLE.scriptName); } // Remove the script. Now the update should finish successfully. The record pointing to the previously failed // repeatable script must be removed from the migratebird_scripts table. removeScript(REPEATABLE); updateDatabase(); assertScriptsCorrectlyExecuted(INCREMENTAL_1); assertScriptsNotExecuted(REPEATABLE); assertNotInExecutedScripts(REPEATABLE); } /** * Verifies that, if the migratebird_scripts table doesn't exist yet, and the autoCreateExecutedScriptsInfoTable property is set to true, * we start with a from scratch update */ @Test public void initialFromScratchUpdate() { enableFromScratch(); createTable(BEFORE_INITIAL_TABLE); createScripts(INCREMENTAL_1); updateDatabase(); assertTableDoesntExist(BEFORE_INITIAL_TABLE); } /** * Verifies that, if the migratebird_scripts table doesn't exist yet, and the autoCreateExecutedScriptsInfoTable property is set to true, * we don't start with a from scratch update */ @Test public void noInitialFromScratchUpdateIfFromScratchDisabled() { disableFromScratch(); createTable(BEFORE_INITIAL_TABLE); createScripts(INCREMENTAL_1); updateDatabase(); assertTableExists(BEFORE_INITIAL_TABLE); } @Test public void executePostProcessingScriptsIfSomeScriptIsModified() { disableFromScratch(); createScripts(INCREMENTAL_1, REPEATABLE); createPostprocessingScripts(POST_PROCESSING_INDEXED_1); // Do an initial database setup and verify that the postprocessing scripts are executed updateDatabase(); assertScriptsCorrectlyExecuted(POST_PROCESSING_INDEXED_1); // Verify that the postprocessing scripts are executed when a new incremental script is added dropTestTables(defaultDatabase, getTableNameForScript(POST_PROCESSING_INDEXED_1)); createScript(INCREMENTAL_2); updateDatabase(); assertScriptsCorrectlyExecuted(POST_PROCESSING_INDEXED_1); // Verify that the postprocessing scripts are executed when a repeatable script is updated dropTestTables(defaultDatabase, getTableNameForScript(POST_PROCESSING_INDEXED_1)); updateScript(REPEATABLE); updateDatabase(); assertScriptsCorrectlyExecuted(POST_PROCESSING_INDEXED_1); } @Test public void reExecutePostProcessingScriptIfItFailedDuringAPreviousRunAndNoOtherChanges() { createScript("postprocessing/failing_script.sql", "xxxxx"); try { updateDatabase(); } catch (MigrateBirdException e) { // expected } try { updateDatabase(); } catch (MigrateBirdException e) { assertMessageContains(e.getMessage(), "postprocessing/failing_script.sql"); } } @Test public void reExecuteAllPostProcessingScriptsIfOneOfThemIsModified() { disableFromScratch(); createScripts(INCREMENTAL_1); createPostprocessingScripts(POST_PROCESSING_INDEXED_1, POST_PROCESSING_NOTINDEXED); updateDatabase(); assertScriptsCorrectlyExecuted(POST_PROCESSING_INDEXED_1, POST_PROCESSING_NOTINDEXED); // Verify that all postprocessing scripts are re-executed if a new one is added dropTestTables(defaultDatabase, getTableNameForScript(POST_PROCESSING_INDEXED_1)); createScripts(POST_PROCESSING_INDEXED_2); updateDatabase(); assertScriptsCorrectlyExecuted(POST_PROCESSING_INDEXED_1, POST_PROCESSING_INDEXED_2, POST_PROCESSING_NOTINDEXED); // Verify that all postprocessing scripts are re-executed if a not indexed postprocessing script is updated dropTestTables(defaultDatabase, getTableNameForScript(POST_PROCESSING_INDEXED_1)); updateRepeatableScript(POST_PROCESSING_NOTINDEXED); updateDatabase(); assertScriptsCorrectlyExecuted(POST_PROCESSING_INDEXED_1, POST_PROCESSING_INDEXED_2); assertUpdatedScriptsExecuted(POST_PROCESSING_NOTINDEXED); // Verify that all postprocessing scripts are re-executed if an indexed postprocessing script is updated dropTestTables(defaultDatabase, getUpdatedTableNameForScript(POST_PROCESSING_NOTINDEXED)); updateRepeatableScript(POST_PROCESSING_INDEXED_1); updateDatabase(); assertUpdatedScriptsExecuted(POST_PROCESSING_INDEXED_1, POST_PROCESSING_NOTINDEXED); // Verify that all postprocessing scripts are re-executed if one of them is renamed dropTestTables(defaultDatabase, getUpdatedTableNameForScript(POST_PROCESSING_INDEXED_1), getTableNameForScript(POST_PROCESSING_INDEXED_2), getUpdatedTableNameForScript(POST_PROCESSING_NOTINDEXED)); renameScript(POST_PROCESSING_INDEXED_2, POST_PROCESSING_INDEXED_2_RENAMED); updateDatabase(); assertScriptsCorrectlyExecuted(POST_PROCESSING_INDEXED_2); assertUpdatedScriptsExecuted(POST_PROCESSING_INDEXED_1, POST_PROCESSING_NOTINDEXED); assertNotInExecutedScripts(POST_PROCESSING_INDEXED_2); // Verify that all postprocessing scripts are re-executed if one of them is deleted dropTestTables(defaultDatabase, getUpdatedTableNameForScript(POST_PROCESSING_INDEXED_1), getTableNameForScript(POST_PROCESSING_INDEXED_2), getUpdatedTableNameForScript(POST_PROCESSING_NOTINDEXED)); removeScript(POST_PROCESSING_INDEXED_2_RENAMED); updateDatabase(); assertScriptsNotExecuted(POST_PROCESSING_INDEXED_2); assertUpdatedScriptsExecuted(POST_PROCESSING_INDEXED_1, POST_PROCESSING_NOTINDEXED); assertNotInExecutedScripts(POST_PROCESSING_INDEXED_2); } @Test public void handleRegularIndexedScriptRename() { disableFromScratch(); createScripts(INCREMENTAL_1, INCREMENTAL_2); updateDatabase(); // Rename an indexed script renameScript(INCREMENTAL_1, INCREMENTAL_1_RENAMED); updateDatabase(); assertNotInExecutedScripts(INCREMENTAL_1); assertInExecutedScripts(INCREMENTAL_1_RENAMED); } @Test public void handleRepeatableScriptRename() { disableFromScratch(); createScripts(INCREMENTAL_1, REPEATABLE); updateDatabase(); // Rename a repeatable script renameScript(REPEATABLE, REPEATABLE_RENAMED); updateDatabase(); assertNotInExecutedScripts(REPEATABLE); assertInExecutedScripts(REPEATABLE_RENAMED); } @Test public void handleScriptRenameThatChangesScriptSequence() { disableFromScratch(); createScripts(INCREMENTAL_2, INCREMENTAL_3); updateDatabase(); // Rename an indexed script renameScript(INCREMENTAL_3, INCREMENTAL_1); try { updateDatabase(); fail(); } catch (MigrateBirdException e) { assertMessageContains(e.getMessage(), "indexed", "renamed", "changes the sequence", INCREMENTAL_3.scriptName, INCREMENTAL_1.scriptName); } } @Test public void includedAndExcludedQualifiers() { enableFromScratch(); createScripts(INCREMENTAL_1_QUALIFIER1, INCREMENTAL_2_QUALIFIER2, INCREMENTAL_3_QUALIFIER1_QUALIFIER2, INCREMENTAL_4); setIncludedAndExcludedQualifiers("", "Q1"); updateDatabase(); assertScriptsCorrectlyExecuted(INCREMENTAL_2_QUALIFIER2, INCREMENTAL_4); assertScriptsNotExecuted(INCREMENTAL_1_QUALIFIER1, INCREMENTAL_3_QUALIFIER1_QUALIFIER2); setIncludedAndExcludedQualifiers("Q1", ""); updateDatabase(); assertScriptsCorrectlyExecuted(INCREMENTAL_1_QUALIFIER1, INCREMENTAL_3_QUALIFIER1_QUALIFIER2); assertScriptsNotExecuted(INCREMENTAL_2_QUALIFIER2, INCREMENTAL_4); setIncludedAndExcludedQualifiers("Q1", "Q2"); updateDatabase(); assertScriptsCorrectlyExecuted(INCREMENTAL_1_QUALIFIER1); assertScriptsNotExecuted(INCREMENTAL_2_QUALIFIER2, INCREMENTAL_3_QUALIFIER1_QUALIFIER2, INCREMENTAL_4); } @Test(expected = MigrateBirdException.class) public void testErrorInCaseOfUnknownQualifier() { createScripts(INCREMENTAL_2_UNKNOWN_QUALIFIER); updateDatabase(); } @Test public void noFromScratchUpdateIfBaseLineRevisionIsSet() { try { properties.setProperty(PROPERTY_BASELINE_REVISION, "1.2"); updateIncremental_fromScratchEnabled(); fail("Expected MigrateBirdException"); } catch (MigrateBirdException e) { assertEquals("Unable to recreate the database from scratch: a baseline revision is set.\n" + "After clearing the database only scripts starting from the baseline revision would have been executed. The other scripts would have been ignored resulting in an inconsistent database state.\n" + "Please clear the baseline revision if you want to perform a from scratch update.\n" + "Another option is to explicitly clear the database using the clear task and then performing the update.", e.getMessage()); } } @Test public void useScriptParameters() throws IOException { File propertiesFile = writePropertiesFile("param", "PARAMETERIZED_TABLE_NAME"); properties.put(PROPERTY_SCRIPT_PARAMETER_FILE, propertiesFile.getPath()); createScript("01_parameterizedScript.sql", "create table ${param} (test varchar(10));"); updateDatabase(); assertTrue(getTableNames().contains("PARAMETERIZED_TABLE_NAME")); } ////////////// PRIVATE HELPER METHODS //////////////////// private void createTable(String tableName) { SQLTestUtils.executeUpdate("create table " + tableName + " (test varchar(10))", defaultDatabase.getDataSource()); } private void errorInScript(TestScript script) { createScript(script, "this is an error;"); } private void fixErrorInScript(TestScript script) { createScript(script, getCreateTableStatement(getTableNameForScript(script))); } private void fixErrorInRepeatableScript(TestScript script) { createScript(script, getRecreateTableStatement(getTableNameForScript(script))); } private void enableFromScratch() { properties.put(PROPERTY_FROM_SCRATCH_ENABLED, "true"); } private void disableFromScratch() { properties.put(PROPERTY_FROM_SCRATCH_ENABLED, "false"); } private void allowOutOfSequenceExecutionOfPatches() { properties.put(MigratebirdProperties.PROPERTY_PATCH_ALLOWOUTOFSEQUENCEEXECUTION, "true"); } private void setIncludedAndExcludedQualifiers(String includedQualifiers, String excludedQualifiers) { properties.put(MigratebirdProperties.PROPERTY_EXCLUDED_QUALIFIERS, excludedQualifiers); properties.put(MigratebirdProperties.PROPERTY_INCLUDED_QUALIFIERS, includedQualifiers); } private void assertMessageContains(String message, String... subStrings) { for (String subString : subStrings) { assertTrue("Expected message to contain substring " + subString + ", but it doesn't.\nMessage was: " + message, message.toLowerCase().contains(subString.toLowerCase())); } } private void assertInExecutedScripts(TestScript script) { MainFactory mainFactory = new MainFactory(config); ExecutedScriptInfoSource executedScriptInfoSource = mainFactory.createExecutedScriptInfoSource(); Set<ExecutedScript> executedScripts = executedScriptInfoSource.getExecutedScripts(); for (ExecutedScript executedScript : executedScripts) { if (script.scriptName.equals(executedScript.getScript().getFileName())) { return; } } fail("Expected " + script + " to be part of the executed scripts, but it isn't"); } private void assertNotInExecutedScripts(TestScript script) { MainFactory mainFactory = new MainFactory(config); ExecutedScriptInfoSource executedScriptInfoSource = mainFactory.createExecutedScriptInfoSource(); Set<ExecutedScript> executedScripts = executedScriptInfoSource.getExecutedScripts(); for (ExecutedScript executedScript : executedScripts) { if (script.scriptName.equals(executedScript.getScript().getFileName())) { fail("Expected " + script + " not to be part of the executed scripts, but it is"); } } } private void assertScriptsCorrectlyExecuted(TestScript... scripts) { Set<String> tableNames = getTableNames(); for (TestScript script : scripts) { String tableName = getTableNameForScript(script); assertTrue("Table " + tableName + " does not exist, so the script " + script + " has not been executed", tableNames.contains(defaultDatabase.toCorrectCaseIdentifier(tableName))); } } private void assertUpdatedScriptsExecuted(TestScript... scripts) { Set<String> tableNames = getTableNames(); for (TestScript script : scripts) { String tableName = getUpdatedTableNameForScript(script); assertTrue( "Table " + tableName + " does not exist, so the updated script " + script + " has not been executed", tableNames.contains(defaultDatabase.toCorrectCaseIdentifier(tableName))); } } private void assertScriptsNotExecuted(TestScript... scripts) { Set<String> tableNames = getTableNames(); for (TestScript script : scripts) { String tableName = getTableNameForScript(script); assertFalse("Table " + tableName + " exists, so the script " + script + " has been executed", tableNames.contains(defaultDatabase.toCorrectCaseIdentifier(tableName))); } } private void assertUpdatedScriptsNotExecuted(TestScript... scripts) { Set<String> tableNames = getTableNames(); for (TestScript script : scripts) { String tableName = getUpdatedTableNameForScript(script); assertFalse("Table " + tableName + " exists, so the updated script " + script + " has been executed", tableNames.contains(defaultDatabase.toCorrectCaseIdentifier(tableName))); } } private void assertTableExists(String tableName) { assertTrue("Table " + tableName + " doesn't exist", getTableNames().contains(defaultDatabase.toCorrectCaseIdentifier(tableName))); } private void assertTableDoesntExist(String tableName) { assertFalse("Table " + tableName + " exists, while it shouldn't", getTableNames().contains(defaultDatabase.toCorrectCaseIdentifier(tableName))); } private boolean updateDatabase() { MainFactory mainFactory = new MainFactory(config); DbUpdate dbUpdate = mainFactory.createDbUpdate(); return dbUpdate.updateDatabase(false); } private void checkScriptUpdates() { MainFactory mainFactory = new MainFactory(config); DbUpdate dbUpdate = mainFactory.createDbUpdate(); dbUpdate.updateDatabase(true); } private void createScripts(TestScript... scripts) { for (TestScript scriptName : scripts) { createScript(scriptName); } } private void createScript(TestScript script) { if (isIndexedFileName(getShortFileName(script))) { createIncrementalScript(script); } else { createRepeatableScript(script); } } private void updateScript(TestScript script) { if (isIndexedFileName(getShortFileName(script))) { updateIncrementalScript(script); } else { updateRepeatableScript(script); } } private void renameScript(TestScript script, TestScript newScript) { removeScript(script); if (isIndexedFileName(script.scriptName)) { createScript(newScript, getCreateTableStatement(getTableNameForScript(script))); } else { createScript(newScript, getRecreateTableStatement(getTableNameForScript(script))); } } private void createIncrementalScript(TestScript script) { String content = script.scriptContent != null ? script.scriptContent : getCreateTableStatement(getTableNameForScript(script)); createScript(script, content); } private void updateIncrementalScript(TestScript script) { createScript(script, getCreateTableStatement(getUpdatedTableNameForScript(script))); } private void createPostprocessingScripts(TestScript... scripts) { for (TestScript script : scripts) { createPostprocessingScript(script); } } private void createPostprocessingScript(TestScript script) { createRepeatableScript(script); } private void createRepeatableScript(TestScript script) { createScript(script, getRecreateTableStatement(getTableNameForScript(script))); } private void updateRepeatableScript(TestScript scriptName) { createScript(scriptName, getDropTableStatement(getTableNameForScript(scriptName)) + getRecreateTableStatement(getUpdatedTableNameForScript(scriptName))); } private void createPatchScript(TestScript script) { String patchScriptName = getPatchScriptName(script); createScript(patchScriptName, getCreateTableStatement(getTableNameForScript(script))); } private String getPatchScriptName(TestScript script) { String directory = substringBeforeLast(script.scriptName, "/"); String scriptNameWithoutDirectory = substringAfterLast(script.scriptName, "/"); String index = substringBefore(scriptNameWithoutDirectory, "_"); String scriptNameAfterIndex = substringAfter(scriptNameWithoutDirectory, "_"); return directory + "/" + index + "_#patch_" + scriptNameAfterIndex; } private String getTableNameForScript(TestScript scriptName) { String shortFileName = getShortFileName(scriptName); if (isIndexedFileName(shortFileName)) shortFileName = substringAfter(shortFileName, "_"); shortFileName = StringUtils.remove(shortFileName, "#"); return shortFileName; } private String getShortFileName(TestScript script) { return substringBeforeLast(substringAfterLast(script.scriptName, "/"), ".sql"); } private boolean isIndexedFileName(String shortFileName) { return isNumeric(substringBefore(shortFileName, "_")); } private String getUpdatedTableNameForScript(TestScript script) { return getTableNameForScript(script) + "_updated"; } private String getCreateTableStatement(String tableName) { return "create table " + tableName + " (test varchar(10)); "; } private String getDropTableStatement(String tableName) { return "drop table " + tableName + " if exists; "; } private String getRecreateTableStatement(String tableName) { return getDropTableStatement(tableName) + getCreateTableStatement(tableName); } private void createScript(TestScript script, String scriptContent) { createScript(script.scriptName, scriptContent); } private void createScript(String scriptName, String scriptContent) { File scriptFile = new File(scriptsLocation.getAbsolutePath(), scriptName); scriptFile.getParentFile().mkdirs(); writeContentToFile(scriptFile, scriptContent); } private void writeContentToFile(File scriptFile, String scriptContent) { try { writeStringToFile(scriptFile, scriptContent); } catch (IOException e) { throw new RuntimeException(e); } } private void removeScript(TestScript script) { File scriptFile = new File(scriptsLocation.getAbsolutePath(), script.scriptName); scriptFile.delete(); } private void clearScriptsDirectory() { try { FileUtils.cleanDirectory(scriptsLocation); } catch (IOException e) { throw new MigrateBirdException(e); } catch (IllegalArgumentException e) { // Ignored } } private void clearTestDatabase() { dropTestTables(defaultDatabase, "migratebird_scripts", BEFORE_INITIAL_TABLE); for (TestScript script : TestScript.values()) { dropTestTables(defaultDatabase, getTableNameForScript(script)); dropTestTables(defaultDatabase, getUpdatedTableNameForScript(script)); } } private Set<String> getTableNames() { return defaultDatabase.getTableNames("PUBLIC"); } private File writePropertiesFile(String propertyName, String propertyValue) throws IOException { File propertiesFile = File.createTempFile("scriptParams", "properties"); Properties properties = new Properties(); properties.put(propertyName, propertyValue); properties.store(new FileWriter(propertiesFile), null); return propertiesFile; } private void initConfiguration() { properties = new ConfigrationLoader().loadDefaultConfiguration(); properties.put("database.dialect", "hsqldb"); properties.put("database.driverClassName", "org.hsqldb.jdbcDriver"); properties.put("database.url", "jdbc:hsqldb:mem:migratebird"); properties.put("database.userName", "sa"); properties.put("database.password", ""); properties.put("database.schemaNames", "PUBLIC"); properties.put(PROPERTY_AUTO_CREATE_MIGRATEBIRD_SCRIPTS_TABLE, "true"); properties.put(PROPERTY_SCRIPT_LOCATIONS, scriptsLocation.getAbsolutePath()); properties.put(PROPERTY_USESCRIPTFILELASTMODIFICATIONDATES, "false"); properties.put(PROPERTY_FROM_SCRATCH_ENABLED, "false"); properties.put(PROPERTY_QUALIFIERS, "special,q1,q2"); config = new Config(properties); SQLHandler sqlHandler = new DefaultSQLHandler(); DataSourceFactory dataSourceFactory = new SimpleDataSourceFactory(); DatabaseConnectionManager databaseConnectionManager = new DefaultDatabaseConnectionManager(config, sqlHandler, dataSourceFactory); DatabasesFactory databasesFactory = new DatabasesFactory(config, databaseConnectionManager); Databases databases = databasesFactory.createDatabases(); defaultDatabase = databases.getDefaultDatabase(); } }