package liquibase.verify.change; import liquibase.change.Change; import liquibase.change.ChangeFactory; import liquibase.change.ChangeMetaData; import liquibase.change.ChangeParameterMetaData; import liquibase.database.Database; import liquibase.database.DatabaseFactory; import liquibase.exception.ValidationErrors; import liquibase.serializer.LiquibaseSerializable; import liquibase.serializer.core.string.StringChangeLogSerializer; import liquibase.sql.Sql; import liquibase.sqlgenerator.SqlGeneratorFactory; import liquibase.statement.SqlStatement; import liquibase.test.JUnitResourceAccessor; import liquibase.util.StringUtils; import liquibase.verify.AbstractVerifyTest; import org.junit.Ignore; import org.junit.Test; import java.util.*; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class VerifyChangeClassesTest extends AbstractVerifyTest { @Ignore @Test public void minimumRequiredIsValidSql() throws Exception { ChangeFactory changeFactory = ChangeFactory.getInstance(); for (String changeName : changeFactory.getDefinedChanges()) { if (changeName.equals("addDefaultValue")) { continue; //need to better handle strange "one of defaultValue* is required" logic } if (changeName.equals("changeWithNestedTags") || changeName.equals("sampleChange")) { continue; //not a real change } for (Database database : DatabaseFactory.getInstance().getImplementedDatabases()) { if (database.getShortName() == null) { continue; } TestState state = new TestState(name.getMethodName(), changeName, database.getShortName(), TestState.Type.SQL); state.addComment("Database: " + database.getShortName()); Change change = changeFactory.create(changeName); if (!change.supports(database)) { continue; } if (change.generateStatementsVolatile(database)) { continue; } ChangeMetaData changeMetaData = ChangeFactory.getInstance().getChangeMetaData(change); change.setResourceAccessor(new JUnitResourceAccessor()); for (String paramName : new TreeSet<String>(changeMetaData.getRequiredParameters(database).keySet())) { ChangeParameterMetaData param = changeMetaData.getParameters().get(paramName); Object paramValue = param.getExampleValue(database); String serializedValue; serializedValue = formatParameter(paramValue); state.addComment("Change Parameter: " + param.getParameterName() + "=" + serializedValue); param.setValue(change, paramValue); } ValidationErrors errors = change.validate(database); assertFalse("Validation errors for " + changeMetaData.getName() + " on " + database.getShortName() + ": " + errors.toString(), errors.hasErrors()); SqlStatement[] sqlStatements = change.generateStatements(database); for (SqlStatement statement : sqlStatements) { Sql[] sql = SqlGeneratorFactory.getInstance().generateSql(statement, database); if (sql == null) { System.out.println("Null sql for " + statement + " on " + database.getShortName()); } else { for (Sql line : sql) { String sqlLine = line.toSql(); assertFalse("Change "+changeMetaData.getName()+" contains 'null' for "+database.getShortName()+": "+sqlLine, sqlLine.contains(" null ")); state.addValue(sqlLine + ";"); } } } state.test(); } } } @Test public void lessThanMinimumFails() throws Exception { ChangeFactory changeFactory = ChangeFactory.getInstance(); for (String changeName : changeFactory.getDefinedChanges()) { for (Database database : DatabaseFactory.getInstance().getImplementedDatabases()) { if (database.getShortName() == null) { continue; } Change change = changeFactory.create(changeName); if (!change.supports(database)) { continue; } if (change.generateStatementsVolatile(database)) { continue; } ChangeMetaData changeMetaData = ChangeFactory.getInstance().getChangeMetaData(change); change.setResourceAccessor(new JUnitResourceAccessor()); ArrayList<String> requiredParams = new ArrayList<String>(changeMetaData.getRequiredParameters(database).keySet()); for (String paramName : requiredParams) { ChangeParameterMetaData param = changeMetaData.getParameters().get(paramName); Object paramValue = param.getExampleValue(database); param.setValue(change, paramValue); } for (int i = 0; i < requiredParams.size(); i++) { String paramToRemove = requiredParams.get(i); ChangeParameterMetaData paramToRemoveMetadata = changeMetaData.getParameters().get(paramToRemove); Object currentValue = paramToRemoveMetadata.getCurrentValue(change); paramToRemoveMetadata.setValue(change, null); assertTrue("No errors even with "+changeMetaData.getName()+" with a null "+paramToRemove+" on "+database.getShortName(), change.validate(database).hasErrors()); paramToRemoveMetadata.setValue(change, currentValue); } } } } @Ignore @Test public void extraParamsIsValidSql() throws Exception { ChangeFactory changeFactory = ChangeFactory.getInstance(); for (String changeName : changeFactory.getDefinedChanges()) { if (changeName.equals("addDefaultValue")) { continue; //need to better handle strange "one of defaultValue* is required" logic } if (changeName.equals("createProcedure")) { continue; //need to better handle strange "one of path or body is required" logic } for (Database database : DatabaseFactory.getInstance().getImplementedDatabases()) { if (database.getShortName() == null) { continue; } TestState state = new TestState(name.getMethodName(), changeName, database.getShortName(), TestState.Type.SQL); state.addComment("Database: " + database.getShortName()); Change baseChange = changeFactory.create(changeName); if (!baseChange.supports(database)) { continue; } if (baseChange.generateStatementsVolatile(database)) { continue; } ChangeMetaData changeMetaData = ChangeFactory.getInstance().getChangeMetaData(baseChange); ArrayList<String> optionalParameters = new ArrayList<String>(changeMetaData.getOptionalParameters(database).keySet()); Collections.sort(optionalParameters); List<List<String>> paramLists = powerSet(optionalParameters); Collections.sort(paramLists, new Comparator<List<String>>() { @Override public int compare(List<String> o1, List<String> o2) { int comp = Integer.valueOf(o1.size()).compareTo(o2.size()); if (comp == 0) { comp = StringUtils.join(o1,",").compareTo(StringUtils.join(o2, ",")); } return comp; } }); for (List<String> permutation : paramLists) { Change change = changeFactory.create(changeName); change.setResourceAccessor(new JUnitResourceAccessor()); // for (String paramName : new TreeSet<String>(changeMetaData.getRequiredParameters(database).keySet())) { ChangeParameterMetaData param = changeMetaData.getParameters().get(paramName); Object paramValue = param.getExampleValue(database); String serializedValue; serializedValue = formatParameter(paramValue); state.addComment("Required Change Parameter: "+ param.getParameterName()+"="+ serializedValue); param.setValue(change, paramValue); } for (String paramName : permutation) { ChangeParameterMetaData param = changeMetaData.getParameters().get(paramName); if (!param.supports(database)) { continue; } Object paramValue = param.getExampleValue(database); String serializedValue; serializedValue = formatParameter(paramValue); state.addComment("Optional Change Parameter: "+ param.getParameterName()+"="+ serializedValue); param.setValue(change, paramValue); } ValidationErrors errors = change.validate(database); assertFalse("Validation errors for " + changeMetaData.getName() + " on "+database.getShortName()+": " +errors.toString(), errors.hasErrors()); // // SqlStatement[] sqlStatements = change.generateStatements(database); // for (SqlStatement statement : sqlStatements) { // Sql[] sql = SqlGeneratorFactory.getInstance().generateSql(statement, database); // if (sql == null) { // System.out.println("Null sql for "+statement+" on "+database.getShortName()); // } else { // for (Sql line : sql) { // state.addValue(line.toSql()+";"); // } // } // } // state.test(); } } } } private List<List<String>> powerSet(List<String> baseSet) { List<List<String>> returnList = new LinkedList<List<String>>(); if (baseSet.isEmpty()) { returnList.add(new ArrayList<String>()); return returnList; } List<String> list = new ArrayList<String>(baseSet); String head = list.get(0); List<String> rest = new ArrayList<String>(list.subList(1, list.size())); for (List<String> set : powerSet(rest)) { List<String> newSet = new ArrayList<String>(); newSet.add(head); newSet.addAll(set); returnList.add(newSet); returnList.add(set); } return returnList; } private String formatParameter(Object paramValue) { String serializedValue; if (paramValue instanceof Collection) { serializedValue = "["; for (Object obj : (Collection) paramValue) { serializedValue += formatParameter(obj) + ", "; } serializedValue += "]"; } else if (paramValue instanceof LiquibaseSerializable) { serializedValue = new StringChangeLogSerializer().serialize(((LiquibaseSerializable) paramValue), true); } else { serializedValue = paramValue.toString(); } return serializedValue; } // @Test // public void volitileIsCorrect() { // // } }