package liquibase.integration.commandline; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; import java.io.StringReader; import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; import java.util.Properties; import liquibase.exception.CommandLineParsingException; import liquibase.util.StringUtils; import org.junit.Test; /** * Tests for {@link Main} */ public class MainTest { @Test public void migrateWithAllParameters() throws Exception { String[] args = new String[]{ "--driver=DRIVER", "--username=USERNAME", "--password=PASSWORD", "--url=URL", "--changeLogFile=FILE", "--classpath=CLASSPATH;CLASSPATH2", "--contexts=CONTEXT1,CONTEXT2", "--promptForNonLocalDatabase=true", "--changeExecListenerClass=MockChangeExecListener", "--changeExecListenerPropertiesFile=PROPS", "update", }; Main cli = new Main(); cli.parseOptions(args); assertEquals("DRIVER", cli.driver); assertEquals("USERNAME", cli.username); assertEquals("PASSWORD", cli.password); assertEquals("URL", cli.url); assertEquals("FILE", cli.changeLogFile); assertEquals("CLASSPATH;CLASSPATH2", cli.classpath); assertEquals("CONTEXT1,CONTEXT2", cli.contexts); assertEquals(Boolean.TRUE, cli.promptForNonLocalDatabase); assertEquals("update", cli.command); assertEquals("MockChangeExecListener", cli.changeExecListenerClass); assertEquals("PROPS", cli.changeExecListenerPropertiesFile); } @Test public void falseBooleanParameters() throws Exception { String[] args = new String[]{ "--promptForNonLocalDatabase=false", "update", }; Main cli = new Main(); cli.parseOptions(args); assertEquals(Boolean.FALSE, cli.promptForNonLocalDatabase); assertEquals("update", cli.command); } @Test public void convertMigrateToUpdate() throws Exception { String[] args = new String[]{ "--promptForNonLocalDatabase=false", "migrate", }; Main cli = new Main(); cli.parseOptions(args); assertEquals("update", cli.command); } @Test public void trueBooleanParameters() throws Exception { String[] args = new String[]{ "--promptForNonLocalDatabase=true", "update", }; Main cli = new Main(); cli.parseOptions(args); assertEquals(Boolean.TRUE, cli.promptForNonLocalDatabase); assertEquals("update", cli.command); } @Test(expected = CommandLineParsingException.class) public void parameterWithoutDash() throws Exception { String[] args = new String[]{ "promptForNonLocalDatabase=true", "update", }; Main cli = new Main(); cli.parseOptions(args); } @Test(expected = CommandLineParsingException.class) public void unknownParameter() throws Exception { String[] args = new String[]{ "--promptForNonLocalDatabase=true", "--badParam=here", "migrate", }; Main cli = new Main(); cli.parseOptions(args); } @Test(expected = CommandLineParsingException.class) public void configureNonExistantClassloaderLocation() throws Exception { Main cli = new Main(); cli.classpath = "badClasspathLocation"; cli.configureClassLoader(); } @Test public void windowsConfigureClassLoaderLocation() throws Exception { Main cli = new Main(); if (cli.isWindows()) { System.setProperty("os.name", "Windows XP"); cli.classpath = "c:\\;c:\\windows\\"; cli.applyDefaults(); cli.configureClassLoader(); URL[] classloaderURLs = ((URLClassLoader) cli.classLoader).getURLs(); assertEquals(2, classloaderURLs.length); assertEquals("file:/c:/", classloaderURLs[0].toExternalForm()); assertEquals("file:/c:/windows/", classloaderURLs[1].toExternalForm()); } } @Test public void unixConfigureClassLoaderLocation() throws Exception { Main cli = new Main(); if (!cli.isWindows()) { System.setProperty("os.name", "Linux"); cli.classpath = "/tmp:/"; cli.applyDefaults(); cli.configureClassLoader(); URL[] classloaderURLs = ((URLClassLoader) cli.classLoader).getURLs(); assertEquals(2, classloaderURLs.length); assertEquals("file:/tmp/", classloaderURLs[0].toExternalForm()); assertEquals("file:/", classloaderURLs[1].toExternalForm()); } } @Test public void propertiesFileWithNoOtherArgs() throws Exception { Main cli = new Main(); Properties props = new Properties(); props.setProperty("driver", "DRIVER"); props.setProperty("username", "USERNAME"); props.setProperty("password", "PASSWD"); props.setProperty("url", "URL"); props.setProperty("changeLogFile", "FILE"); props.setProperty("classpath", "CLASSPAHT"); props.setProperty("contexts", "CONTEXTS"); props.setProperty("promptForNonLocalDatabase", "TRUE"); ByteArrayOutputStream propFile = new ByteArrayOutputStream(); props.store(propFile, ""); cli.parsePropertiesFile(new ByteArrayInputStream(propFile.toByteArray())); assertEquals("DRIVER", cli.driver); assertEquals("USERNAME", cli.username); assertEquals("PASSWD", cli.password); assertEquals("URL", cli.url); assertEquals("FILE", cli.changeLogFile); assertEquals("CLASSPAHT", cli.classpath); assertEquals("CONTEXTS", cli.contexts); assertEquals(Boolean.TRUE, cli.promptForNonLocalDatabase); } @Test public void propertiesFileWithOtherArgs() throws Exception { Main cli = new Main(); cli.username = "PASSED USERNAME"; cli.password = "PASSED PASSWD"; Properties props = new Properties(); props.setProperty("driver", "DRIVER"); props.setProperty("username", "USERNAME"); props.setProperty("password", "PASSWD"); props.setProperty("url", "URL"); props.setProperty("changeLogFile", "FILE"); props.setProperty("classpath", "CLASSPAHT"); props.setProperty("contexts", "CONTEXTS"); props.setProperty("promptForNonLocalDatabase", "TRUE"); ByteArrayOutputStream propFile = new ByteArrayOutputStream(); props.store(propFile, ""); cli.parsePropertiesFile(new ByteArrayInputStream(propFile.toByteArray())); assertEquals("DRIVER", cli.driver); assertEquals("PASSED USERNAME", cli.username); assertEquals("PASSED PASSWD", cli.password); assertEquals("URL", cli.url); assertEquals("FILE", cli.changeLogFile); assertEquals("CLASSPAHT", cli.classpath); assertEquals("CONTEXTS", cli.contexts); assertEquals(Boolean.TRUE, cli.promptForNonLocalDatabase); } @Test public void propertiesFileParsingShouldIgnoreUnknownArgumentsIfStrictFalseIsInFile() throws Exception { Main cli = new Main(); Properties props = new Properties(); props.setProperty("driver", "DRIVER"); props.setProperty("unknown.property", "UnknownValue"); props.setProperty("strict", "false"); ByteArrayOutputStream propFile = new ByteArrayOutputStream(); props.store(propFile, ""); cli.parsePropertiesFile(new ByteArrayInputStream(propFile.toByteArray())); assertEquals("DRIVER", cli.driver); } @Test public void propertiesFileParsingShouldIgnoreUnknownArgumentsIfStrictModeIsFalse() throws Exception { Main cli = new Main(); String[] args = new String[]{"--strict=false"}; cli.parseOptions(args); Properties props = new Properties(); props.setProperty("driver", "DRIVER"); props.setProperty("unknown.property", "UnknownValue"); ByteArrayOutputStream propFile = new ByteArrayOutputStream(); props.store(propFile, ""); cli.parsePropertiesFile(new ByteArrayInputStream(propFile.toByteArray())); assertEquals("DRIVER", cli.driver); } @Test(expected = CommandLineParsingException.class) public void propertiesFileParsingShouldFailOnUnknownArgumentsIfStrictMode() throws Exception { Main cli = new Main(); Properties props = new Properties(); props.setProperty("driver", "DRIVER"); props.setProperty("unknown.property", "UnknownValue"); props.setProperty("strict", "true"); ByteArrayOutputStream propFile = new ByteArrayOutputStream(); props.store(propFile, ""); cli.parsePropertiesFile(new ByteArrayInputStream(propFile.toByteArray())); } @Test public void applyDefaults() { Main cli = new Main(); cli.promptForNonLocalDatabase = Boolean.TRUE; cli.applyDefaults(); assertEquals(Boolean.TRUE, cli.promptForNonLocalDatabase); cli.promptForNonLocalDatabase = Boolean.FALSE; cli.applyDefaults(); assertEquals(Boolean.FALSE, cli.promptForNonLocalDatabase); cli.promptForNonLocalDatabase = null; cli.applyDefaults(); assertEquals(Boolean.FALSE, cli.promptForNonLocalDatabase); } @Test(expected = CommandLineParsingException.class) public void propertiesFileWithBadArgs() throws Exception { Main cli = new Main(); Properties props = new Properties(); props.setProperty("driver", "DRIVER"); props.setProperty("username", "USERNAME"); props.setProperty("badArg", "ARG"); ByteArrayOutputStream propFile = new ByteArrayOutputStream(); props.store(propFile, ""); cli.parsePropertiesFile(new ByteArrayInputStream(propFile.toByteArray())); } @Test public void checkSetup() { Main cli = new Main(); assertTrue(cli.checkSetup().size() > 0); cli.driver = "driver"; cli.username = "username"; cli.password = "pwd"; cli.url = "url"; cli.changeLogFile = "file"; cli.classpath = "classpath"; assertTrue(cli.checkSetup().size() > 0); cli.command = "BadCommand"; assertTrue(cli.checkSetup().size() > 0); cli.command = "migrate"; assertEquals(0, cli.checkSetup().size()); String[] noArgCommand = { "migrate", "migrateSQL", "update", "updateSQL", "futureRollbackSQL", "updateTestingRollback", "listLocks", "dropAll", "releaseLocks", "validate", "help", "clearCheckSums", "changelogSync", "changelogSyncSQL", "markNextChangeSetRan", "markNextChangeSetRanSQL" }; cli.commandParams.clear(); cli.commandParams.add("--logLevel=debug"); // verify unexpected parameter for(int i=0; i<noArgCommand.length; i++) { cli.command = noArgCommand[i]; assertEquals(1, cli.checkSetup().size()); } // test update cmd with -D parameter cli.command = "update"; cli.commandParams.clear(); cli.changeLogParameters.clear(); cli.changeLogParameters.put("engine", "myisam"); assertEquals(0, cli.checkSetup().size()); // verify normal case - comand w/o command parameters cli.commandParams.clear(); for(int i=0; i<noArgCommand.length; i++) { cli.command = noArgCommand[i]; assertEquals(0, cli.checkSetup().size()); } String[] singleArgCommand = { "updateCount", "updateCountSQL", "tag", "dbDoc" }; // verify unexpected parameter for single arg commands cli.commandParams.add("--logLevel=debug"); for(int i=0; i<singleArgCommand.length; i++) { cli.command = singleArgCommand[i]; assertEquals(1, cli.checkSetup().size()); } // verify normal case - comand with string command parameter cli.commandParams.clear(); cli.commandParams.add("someCommandValue"); for(int i=0; i<singleArgCommand.length; i++) { cli.command = singleArgCommand[i]; assertEquals(0, cli.checkSetup().size()); } // status w/o parameter cli.command = "status"; cli.commandParams.clear(); assertEquals(0, cli.checkSetup().size()); // status w/--verbose cli.commandParams.add("--verbose"); assertEquals(0, cli.checkSetup().size()); cli.commandParams.clear(); cli.commandParams.add("--logLevel=debug"); assertEquals(1, cli.checkSetup().size()); String[] multiArgCommand = { "diff", "diffChangeLog" }; //first verify diff cmds w/o args cli.commandParams.clear(); for(int i=0; i<multiArgCommand.length; i++) { cli.command = multiArgCommand[i]; assertEquals(0, cli.checkSetup().size()); } // next verify with all parms String[] cmdParms = { "--referenceUsername=USERNAME", "--referencePassword=PASSWORD", "--referenceUrl=URL", "--referenceDriver=DRIVER"}; // load all parms for (String param : cmdParms) { cli.commandParams.add(param); } assertEquals(0, cli.checkSetup().size()); // now add an unexpected parm cli.commandParams.add("--logLevel=debug"); assertEquals(1, cli.checkSetup().size()); } @Test public void printHelp() throws Exception { ByteArrayOutputStream stream = new ByteArrayOutputStream(); Main cli = new Main(); cli.printHelp(new PrintStream(stream)); BufferedReader reader = new BufferedReader(new StringReader(new String(stream.toByteArray()))); String line; while ((line = reader.readLine()) != null) { //noinspection MagicNumber if (line.length() > 80) { fail("'" + line + "' is longer than 80 chars"); } } } @Test public void tag() throws Exception { String[] args = new String[]{ "--driver=DRIVER", "--username=USERNAME", "--password=PASSWORD", "--url=URL", "--changeLogFile=FILE", "--classpath=CLASSPATH;CLASSPATH2", "--contexts=CONTEXT1,CONTEXT2", "tag", "TagHere" }; Main cli = new Main(); cli.parseOptions(args); assertEquals("DRIVER", cli.driver); assertEquals("USERNAME", cli.username); assertEquals("PASSWORD", cli.password); assertEquals("URL", cli.url); assertEquals("FILE", cli.changeLogFile); assertEquals("CLASSPATH;CLASSPATH2", cli.classpath); assertEquals("CONTEXT1,CONTEXT2", cli.contexts); assertEquals("tag", cli.command); assertEquals("TagHere", cli.commandParams.iterator().next()); } @Test public void migrateWithEqualsInParams() throws Exception { String url = "dbc:sqlserver://127.0.0.1;DatabaseName=dev_nn;user=ffdatabase;password=p!88worD"; String[] args = new String[]{ "--url=" + url, "migrate", }; Main cli = new Main(); cli.parseOptions(args); assertEquals(url, cli.url); } @Test public void fixArgs() { Main liquibase = new Main(); String[] fixedArgs = liquibase.fixupArgs(new String[] {"--defaultsFile","liquibase.properties", "migrate"}); assertEquals("--defaultsFile=liquibase.properties migrate", StringUtils.join(Arrays.asList(fixedArgs), " ")); fixedArgs = liquibase.fixupArgs(new String[] {"--defaultsFile=liquibase.properties", "migrate"}); assertEquals("--defaultsFile=liquibase.properties migrate", StringUtils.join(Arrays.asList(fixedArgs), " ")); fixedArgs = liquibase.fixupArgs(new String[] {"--driver=DRIVER", "--username=USERNAME", "--password=PASSWORD", "--url=URL", "--changeLogFile=FILE", "--classpath=CLASSPATH;CLASSPATH2", "--contexts=CONTEXT1,CONTEXT2", "--promptForNonLocalDatabase=true", "migrate" }); assertEquals("--driver=DRIVER --username=USERNAME --password=PASSWORD --url=URL --changeLogFile=FILE --classpath=CLASSPATH;CLASSPATH2 --contexts=CONTEXT1,CONTEXT2 --promptForNonLocalDatabase=true migrate", StringUtils.join(Arrays.asList(fixedArgs), " ")); } @Test public void testVersionArg() throws Exception { Main.run(new String[] {"--version"}); } @Test public void testSplitArgWithValueEndingByEqualSing() throws CommandLineParsingException { final String argName = "password"; final String argValue = "s3-cr3t="; Main tested = new Main(); tested.parseOptions(new String[] { "--" + argName + "=" + argValue }); assertEquals(argValue, tested.password); } @Test public void testDatabaseChangeLogTableName_Properties() throws IOException, CommandLineParsingException { Main main = new Main(); Properties props = new Properties(); props.setProperty("databaseChangeLogTableName", "PROPSCHANGELOG"); props.setProperty("databaseChangeLogLockTableName", "PROPSCHANGELOGLOCK"); ByteArrayOutputStream propFile = new ByteArrayOutputStream(); props.store(propFile, ""); main.parsePropertiesFile(new ByteArrayInputStream(propFile.toByteArray())); assertEquals("PROPSCHANGELOG", main.databaseChangeLogTableName); assertEquals("PROPSCHANGELOGLOCK", main.databaseChangeLogLockTableName); } @Test public void testDatabaseChangeLogTableName_Options() throws IOException, CommandLineParsingException { Main main = new Main(); String[] opts = { "--databaseChangeLogTableName=OPTSCHANGELOG", "--databaseChangeLogLockTableName=OPTSCHANGELOGLOCK"}; main.parseOptions(opts); assertEquals("OPTSCHANGELOG", main.databaseChangeLogTableName); assertEquals("OPTSCHANGELOGLOCK", main.databaseChangeLogLockTableName); } }