/*
* Copyright 2008, Unitils.org
*
* 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 org.unitils.dbmaintainer.script.impl;
import static java.util.Arrays.asList;
import static org.junit.Assert.*;
import static org.unitils.thirdparty.org.apache.commons.io.FileUtils.copyDirectory;
import static org.unitils.thirdparty.org.apache.commons.io.FileUtils.copyFile;
import static org.unitils.thirdparty.org.apache.commons.io.FileUtils.forceDeleteOnExit;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import javax.sql.DataSource;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.unitils.UnitilsJUnit4;
import org.unitils.core.UnitilsException;
import org.unitils.database.annotations.TestDataSource;
import org.unitils.dbmaintainer.script.ExecutedScript;
import org.unitils.dbmaintainer.script.Script;
import org.unitils.dbmaintainer.version.Version;
import org.unitils.reflectionassert.ReflectionAssert;
import org.unitils.reflectionassert.ReflectionComparatorMode;
/**
* Tests the DefaultScriptSource
*
* @author Tim Ducheyne
* @author Filip Neven
*/
@RunWith(BlockJUnit4ClassRunner.class)
public class DefaultScriptSourceTest extends UnitilsJUnit4 {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
/* DataSource for the test database */
@TestDataSource
DataSource dataSource = null;
/* Tested object */
DefaultScriptSource scriptSource;
String scriptsDirName;
List<ExecutedScript> alreadyExecutedScripts;
Date executionDate;
private List<String> schemas;
private String dialect;
private Properties configuration;
/**
* Cleans test directory and copies test files to it. Initializes test objects
*/
@Before
public void setUp() throws Exception {
executionDate = new Date();
dialect = "oracle";
// Create test directories
String tmpDir = System.getProperty("java.io.tmpdir");
if(!tmpDir.endsWith("/")) {
tmpDir += "/";
}
scriptsDirName = tmpDir + "DefaultScriptSourceTest";
forceDeleteOnExit(new File(scriptsDirName));
// Copy test files
copyDirectory(new File(getClass().getResource("DefaultScriptSourceTest").toURI()), new File(scriptsDirName));
alreadyExecutedScripts = new ArrayList<ExecutedScript>(asList(
getExecutedScript("1_scripts/001_scriptA.sql"),
getExecutedScript("1_scripts/002_scriptB.sql"),
getExecutedScript("2_scripts/002_scriptE.sql"),
getExecutedScript("2_scripts/scriptF.sql"),
getExecutedScript("2_scripts/subfolder/001_scriptG.sql"),
getExecutedScript("2_scripts/subfolder/scriptH.sql"),
getExecutedScript("scripts/001_scriptI.sql"),
getExecutedScript("scripts/scriptJ.sql")
));
// Initialize FileScriptSource object
configuration = new Properties();
String scriptsLocations = scriptsDirName + "/test_scripts";
configuration.setProperty(DefaultScriptSource.PROPKEY_SCRIPT_LOCATIONS, scriptsLocations);
configuration.setProperty(DefaultScriptSource.PROPKEY_SCRIPT_EXTENSIONS, "sql");
configuration.setProperty(DefaultScriptSource.PROPKEY_POSTPROCESSINGSCRIPT_DIRNAME, "postprocessing");
configuration.setProperty(DefaultScriptSource.PROPKEY_USESCRIPTFILELASTMODIFICATIONDATES, "false");
configuration.setProperty("dbMaintainer.generateDataSetStructure.enabled", "true");
configuration.setProperty(DefaultScriptSource.PROPKEY_QUALIFIERS, "include1, include2, include3, exclude1, exclude2, exclude3");
configuration.setProperty(DefaultScriptSource.PROPKEY_EXCLUDE_QUALIFIERS, "exclude1, exclude2, exclude3");
scriptSource = new DefaultScriptSource();
scriptSource.init(configuration);
schemas = new ArrayList<String>();
schemas.add("public");
}
private ExecutedScript getExecutedScript(String scriptFileName) throws NoSuchAlgorithmException, IOException {
return new ExecutedScript(new Script(scriptFileName, 0L, getCheckSum(scriptFileName)), executionDate, true);
}
private String getCheckSum(String fileName) throws NoSuchAlgorithmException, IOException {
MessageDigest digest = MessageDigest.getInstance("MD5");
InputStream is = new DigestInputStream(new FileInputStream(scriptsDirName + "/test_scripts/" + fileName), digest);
while (is.read() != -1);
return getHexPresentation(digest.digest());
}
private String getHexPresentation(byte[] byteArray) {
StringBuffer result = new StringBuffer();
for (int i = 0; i < byteArray.length; i++) {
result.append(Integer.toString((byteArray[i] & 0xff) + 0x100, 16).substring(1));
}
return result.toString();
}
/**
* Tests getting all scripts in the correct order.
*/
@Test
public void testGetAllUpdateScripts() {
List<Script> scripts = scriptSource.getAllUpdateScripts(dialect, schemas.get(0), true);
assertEquals("1_scripts/001_scriptA.sql", scripts.get(0).getFileName()); // x.1.1
assertEquals("1_scripts/002_scriptB.sql", scripts.get(1).getFileName()); // x.1.2
assertEquals("1_scripts/scriptD.sql", scripts.get(2).getFileName()); // x.1.x
assertEquals("2_scripts/002_scriptE.sql", scripts.get(3).getFileName()); // x.2.2
assertEquals("2_scripts/scriptF.sql", scripts.get(4).getFileName()); // x.2.x
assertEquals("2_scripts/subfolder/001_scriptG.sql", scripts.get(5).getFileName()); // x.2.x.1
assertEquals("2_scripts/subfolder/scriptH.sql", scripts.get(6).getFileName()); // x.2.x.x
assertEquals("scripts/001_scriptI.sql", scripts.get(7).getFileName()); // x.x.1
assertEquals("scripts/scriptJ.sql", scripts.get(8).getFileName()); // x.x.x
}
@Test
public void testDuplicateIndex() throws Exception {
File duplicateIndexScript = null;
try {
File scriptA = new File(scriptsDirName
+ "/test_scripts/1_scripts/001_scriptA.sql");
duplicateIndexScript = new File(scriptsDirName
+ "/test_scripts/1_scripts/001_duplicateIndexScript.sql");
copyFile(scriptA, duplicateIndexScript);
try {
scriptSource.getAllUpdateScripts(dialect, schemas.get(0), true);
fail("Expected a UnitilsException because of a duplicate script");
} catch (UnitilsException e) {
// expected
}
} finally {
try {
duplicateIndexScript.delete();
} catch(Exception e) {
// Safely ignore NPE or any IOException...
}
}
}
/**
* Tests getting all scripts that have an index higher than the highest of the already executed scripts or
* whose content has changed.
*/
@Test
public void testGetNewScripts() {
alreadyExecutedScripts.set(5, new ExecutedScript(new Script("2_scripts/subfolder/scriptH.sql", 0L, "xxx"), executionDate, true));
List<Script> scripts = scriptSource.getNewScripts(new Version("2.x.1"), new HashSet<ExecutedScript>(alreadyExecutedScripts),dialect, schemas.get(0), true);
assertEquals("1_scripts/scriptD.sql", scripts.get(0).getFileName()); // x.1.x was added
assertEquals("2_scripts/subfolder/scriptH.sql", scripts.get(1).getFileName()); // x.2.x.x was changed
assertEquals("scripts/001_scriptI.sql", scripts.get(2).getFileName()); // x.x.1 higher version
}
@Test
public void testIsExistingScriptsModfied_noModifications() {
assertFalse(scriptSource.isExistingIndexedScriptModified(new Version("x.x.x"), new HashSet<ExecutedScript>(alreadyExecutedScripts), dialect, schemas.get(0), true));
}
@Test
public void testIsExistingScriptsModfied_modifiedScript() {
alreadyExecutedScripts.set(1, new ExecutedScript(new Script("1_scripts/002_scriptB.sql", 0L, "xxx"), executionDate, true));
assertTrue(scriptSource.isExistingIndexedScriptModified(new Version("x.x.x"), new HashSet<ExecutedScript>(alreadyExecutedScripts),dialect, schemas.get(0), true));
}
@Test
public void testIsExistingScriptsModfied_scriptAdded() {
alreadyExecutedScripts.remove(1);
assertTrue(scriptSource.isExistingIndexedScriptModified(new Version("x.x.x"), new HashSet<ExecutedScript>(alreadyExecutedScripts), dialect, schemas.get(0), true));
}
@Test
public void testIsExistingScriptsModfied_scriptRemoved() {
alreadyExecutedScripts.add(new ExecutedScript(new Script("1_scripts/003_scriptB.sql", 0L, "xxx"), executionDate, true));
assertTrue(scriptSource.isExistingIndexedScriptModified(new Version("x.x.x"), new HashSet<ExecutedScript>(alreadyExecutedScripts), dialect, schemas.get(0), true));
}
@Test
public void testIsExistingScriptsModfied_newScript() {
alreadyExecutedScripts.remove(1);
assertFalse(scriptSource.isExistingIndexedScriptModified(new Version("1.1"), new HashSet<ExecutedScript>(alreadyExecutedScripts), dialect, schemas.get(0), true));
}
@Test
public void testIsExistingScriptsModfied_higherIndexScriptModified() {
alreadyExecutedScripts.set(1, new ExecutedScript(new Script("1_scripts/002_scriptB.sql", 0L, "xxx"), executionDate, true));
assertFalse(scriptSource.isExistingIndexedScriptModified(new Version("1.1"), new HashSet<ExecutedScript>(alreadyExecutedScripts), dialect, schemas.get(0), true));
assertTrue(scriptSource.isExistingIndexedScriptModified(new Version("1.2"), new HashSet<ExecutedScript>(alreadyExecutedScripts), dialect, schemas.get(0), true));
}
/**
* Test whether an existing script was modified script but all scripts have a higher version than the current version.
*/
@Test
public void testIsExistingScriptsModfied_noLowerIndex() {
boolean result = scriptSource.isExistingIndexedScriptModified(new Version("0"), new HashSet<ExecutedScript>(alreadyExecutedScripts), dialect, schemas.get(0), true);
assertFalse(result);
}
/**
* Test getting the post processing scripts.
*/
@Test
public void testGetPostProcessingScripts() {
List<Script> scripts = scriptSource.getPostProcessingScripts(dialect, schemas.get(0), true);
assertEquals("postprocessing/post-scriptA.sql", scripts.get(0).getFileName());
assertEquals("postprocessing/post-scriptB.sql", scripts.get(1).getFileName());
}
@Test
public void testCheckIfFileMustBeAddedToScriptList() throws Exception {
String schema1 = "USERS";
String schema2 = "pEoplE";
Assert.assertFalse(scriptSource.checkIfScriptContainsCorrectDatabaseName("test123.sql", "public", false));
Assert.assertTrue(scriptSource.checkIfScriptContainsCorrectDatabaseName("test123.sql", "public", true));
Assert.assertTrue(scriptSource.checkIfScriptContainsCorrectDatabaseName("@users_addusers.sql", schema1, true));
Assert.assertFalse(scriptSource.checkIfScriptContainsCorrectDatabaseName("@usersaddusers.sql", schema1, true));
Assert.assertFalse(scriptSource.checkIfScriptContainsCorrectDatabaseName("1@users_addusers.sql", schema1, true));
Assert.assertTrue(scriptSource.checkIfScriptContainsCorrectDatabaseName("1_@users_addusers.sql", schema1, true));
Assert.assertTrue(scriptSource.checkIfScriptContainsCorrectDatabaseName("01_@users_addusers.sql", schema1, true));
Assert.assertFalse(scriptSource.checkIfScriptContainsCorrectDatabaseName("01@users_addusers.sql", schema1, true));
Assert.assertFalse(scriptSource.checkIfScriptContainsCorrectDatabaseName("01@users_addpeople.sql", schema2, true));
Assert.assertFalse(scriptSource.checkIfScriptContainsCorrectDatabaseName("1@people_addusers.sql", schema1, true));
Assert.assertTrue(scriptSource.checkIfScriptContainsCorrectDatabaseName("01_@people_addusers.sql", schema2, true));
Assert.assertTrue(scriptSource.checkIfScriptContainsCorrectDatabaseName("01_@people_addUsers.sql", schema2, true));
}
/**
* test {@link DefaultScriptSource#checkIfThereAreNoQualifiers(String)}
* @throws Exception
*/
@Test
public void testCheckIfThereAreNoQualifiers() throws Exception {
Assert.assertTrue(scriptSource.checkIfThereAreNoQualifiers("01_products.sql"));
Assert.assertFalse(scriptSource.checkIfThereAreNoQualifiers("01_#refdata_#postgres_products.sql"));
Assert.assertFalse(scriptSource.checkIfThereAreNoQualifiers("#refdata_#postgres_products.sql"));
}
@Test
public void testContainsOneOfQualifiers_withoutIncludes() throws Exception {
scriptSource = new DefaultScriptSource();
Properties configuration = new Properties();
configuration.setProperty(DefaultScriptSource.PROPKEY_QUALIFIERS, "include1, include2, include3, exclude1, exclude2, exclude3");
configuration.setProperty(DefaultScriptSource.PROPKEY_EXCLUDE_QUALIFIERS, "exclude1, exclude2, exclude3");
scriptSource.init(configuration);
Assert.assertTrue(scriptSource.containsOneOfQualifiers("01_products.sql"));
Assert.assertTrue(scriptSource.containsOneOfQualifiers("01_#include1_products.sql"));
Assert.assertTrue(scriptSource.containsOneOfQualifiers("01_#include1_#include2_products.sql"));
Assert.assertTrue(scriptSource.containsOneOfQualifiers("#include1_#include2_products.sql"));
Assert.assertFalse(scriptSource.containsOneOfQualifiers("01_#refdata_#postgres_products.sql"));
Assert.assertFalse(scriptSource.containsOneOfQualifiers("#refdata_#postgres_products.sql"));
Assert.assertFalse(scriptSource.containsOneOfQualifiers("01_#include1_#exclude2_products.sql"));
Assert.assertFalse(scriptSource.containsOneOfQualifiers("01_#exclude1_products.sql"));
}
@Test
public void testContainsOneOfQualifiers_withIncludes() throws Exception {
scriptSource = new DefaultScriptSource();
Properties configuration = new Properties();
configuration.setProperty(DefaultScriptSource.PROPKEY_QUALIFIERS, "include1, include2, include3, exclude1, exclude2, exclude3");
configuration.setProperty(DefaultScriptSource.PROPKEY_INCLUDE_QUALIFIERS, "include1, include2, include3");
configuration.setProperty(DefaultScriptSource.PROPKEY_EXCLUDE_QUALIFIERS, "exclude1, exclude2, exclude3");
scriptSource.init(configuration);
Assert.assertFalse(scriptSource.containsOneOfQualifiers("01_products.sql"));
Assert.assertTrue(scriptSource.containsOneOfQualifiers("01_#include1_products.sql"));
Assert.assertTrue(scriptSource.containsOneOfQualifiers("01_#include1_#include2_products.sql"));
Assert.assertTrue(scriptSource.containsOneOfQualifiers("#include1_#include2_products.sql"));
Assert.assertFalse(scriptSource.containsOneOfQualifiers("01_#refdata_#postgres_products.sql"));
Assert.assertFalse(scriptSource.containsOneOfQualifiers("#refdata_#postgres_products.sql"));
Assert.assertFalse(scriptSource.containsOneOfQualifiers("01_#include1_#exclude2_products.sql"));
Assert.assertFalse(scriptSource.containsOneOfQualifiers("01_#exclude1_products.sql"));
}
@Test
public void testGetScriptsAt_multiUserSupport() throws Exception {
File parentFile = tempFolder.newFolder("test1");
tempFolder.newFile("test1/file1.txt");
tempFolder.newFile("test1/file2.sql");
tempFolder.newFile("test1/@users_addusers.sql");
tempFolder.newFile("test1/01_@users_addusers.sql");
tempFolder.newFile("test1/1@people_addusers.sql");
List<Script> actual = new ArrayList<Script>();
scriptSource.getScriptsAt(actual, parentFile.getParentFile().getAbsolutePath(), "test1", "users", true);
List<String> actualNames = new ArrayList<String>();
for (Script script : actual) {
actualNames.add(script.getFileName());
}
Assert.assertEquals(3, actual.size());
ReflectionAssert.assertReflectionEquals(Arrays.asList("test1/file2.sql", "test1/@users_addusers.sql", "test1/01_@users_addusers.sql"), actualNames, ReflectionComparatorMode.LENIENT_ORDER);
}
@Test
public void testGetScriptsAt_qualifiers() throws Exception {
String nameFolder = "getscriptsat_qualifiers";
File parentFile = tempFolder.newFolder(nameFolder);
tempFolder.newFile(nameFolder + "/01_#include1_products.sql");
tempFolder.newFile(nameFolder + "/#include1_#include2_products.sql");
tempFolder.newFile(nameFolder + "/01_#include1_#include2_products.sql");
tempFolder.newFile(nameFolder + "/01_#refdata_#postgres_products.sql");
tempFolder.newFile(nameFolder + "/01_#include1_#exclude2_products.sql");
scriptSource = new DefaultScriptSource();
configuration.setProperty(DefaultScriptSource.PROPKEY_INCLUDE_QUALIFIERS, "include1, include2, include3");
scriptSource.init(configuration);
List<Script> actual = new ArrayList<Script>();
scriptSource.getScriptsAt(actual, parentFile.getParentFile().getAbsolutePath(), nameFolder, "users", true);
List<String> actualNames = new ArrayList<String>();
for (Script script : actual) {
actualNames.add(script.getFileName());
}
Assert.assertEquals(3, actualNames.size());
ReflectionAssert.assertLenientEquals(Arrays.asList(nameFolder + "/01_#include1_products.sql", nameFolder + "/#include1_#include2_products.sql", nameFolder + "/01_#include1_#include2_products.sql"), actualNames);
}
@Test
public void testGetScriptsAt_qualifiersAndMultiUserSupport_defaultDatabase() throws Exception {
String nameFolder = "getscriptsat";
File parentFile = tempFolder.newFolder(nameFolder);
tempFolder.newFile(nameFolder + "/01_#include1_products.sql");
tempFolder.newFile(nameFolder + "/01_#include2_@users_products.sql");
tempFolder.newFile(nameFolder + "/01_#include2_@people_products.sql");
tempFolder.newFile(nameFolder + "/#include1_@people_#include2_products.sql");
tempFolder.newFile(nameFolder + "/@users_#include1_#include2_products.sql");
tempFolder.newFile(nameFolder + "/#include1_#include2_products.sql");
tempFolder.newFile(nameFolder + "/01_#include1_#include2_products.sql");
tempFolder.newFile(nameFolder + "/01_#refdata_#postgres_products.sql");
tempFolder.newFile(nameFolder + "/01_#include1_#exclude2_products.sql");
List<Script> actual = new ArrayList<Script>();
scriptSource = new DefaultScriptSource();
configuration.setProperty(DefaultScriptSource.PROPKEY_INCLUDE_QUALIFIERS, "include1, include2, include3");
scriptSource.init(configuration);
scriptSource.getScriptsAt(actual, parentFile.getParentFile().getAbsolutePath(), nameFolder, "users", true);
List<String> actualNames = new ArrayList<String>();
for (Script script : actual) {
actualNames.add(script.getFileName());
}
List<String> expected = new ArrayList<String>();
expected.add(nameFolder + "/01_#include1_products.sql");
expected.add(nameFolder + "/01_#include2_@users_products.sql");
expected.add(nameFolder + "/@users_#include1_#include2_products.sql");
expected.add(nameFolder + "/#include1_#include2_products.sql");
expected.add(nameFolder + "/01_#include1_#include2_products.sql");
Assert.assertEquals(5, actual.size());
ReflectionAssert.assertReflectionEquals(expected, actualNames, ReflectionComparatorMode.LENIENT_ORDER);
}
}