package nl.ipo.cds.etl.attributemapping;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import nl.idgis.commons.jobexecutor.Job;
import nl.idgis.commons.jobexecutor.JobLogger.LogLevel;
import nl.ipo.cds.attributemapping.operations.OperationInput;
import nl.ipo.cds.attributemapping.operations.OperationType;
import nl.ipo.cds.attributemapping.operations.discover.OperationDiscoverer;
import nl.ipo.cds.categories.IntegrationTests;
import nl.ipo.cds.dao.attributemapping.InputOperationDTO;
import nl.ipo.cds.dao.attributemapping.OperationDTO;
import nl.ipo.cds.dao.attributemapping.OperationInputDTO;
import nl.ipo.cds.dao.attributemapping.TransformOperationDTO;
import nl.ipo.cds.domain.AttributeType;
import nl.ipo.cds.domain.FeatureType;
import nl.ipo.cds.domain.FeatureTypeAttribute;
import nl.ipo.cds.domain.QName;
import nl.ipo.cds.etl.attributemapping.AttributeMappingValidator.MessageKey;
import nl.ipo.cds.etl.log.EventLogger;
import nl.ipo.cds.etl.operations.input.StringConstantInput;
import nl.ipo.cds.etl.operations.transform.ConditionalTransform;
import nl.ipo.cds.etl.operations.transform.ConditionalTransform.Operation;
import nl.ipo.cds.etl.operations.transform.MakeInspireIdTransform;
import nl.ipo.cds.etl.operations.transform.SplitStringTransform;
import nl.ipo.cds.etl.theme.AttributeDescriptor;
import nl.ipo.cds.etl.theme.ThemeConfig;
import nl.ipo.cds.etl.theme.ThemeDiscoverer;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@Category(IntegrationTests.class)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AttributeMappingValidatorTest.Config.class)
public class AttributeMappingValidatorTest extends AbstractAttributeMapperTest {
private static final String themeName = "Protected sites";
@Inject
private ThemeDiscoverer themeDiscoverer;
@Inject
private OperationDiscoverer operationDiscoverer;
private Map<String, AttributeDescriptor<?>> attributeDescriptors;
protected Map<MessageKey, List<String[]>> lines;
@Configuration
@ComponentScan (basePackageClasses = {
nl.ipo.cds.etl.theme.protectedSite.config.Package.class,
nl.ipo.cds.etl.theme.Package.class
})
public static class Config {
}
@Before
public void createLines () {
lines = new HashMap<MessageKey, List<String[]>> ();
}
@Before
public void findAttributeDescriptors () {
final ThemeConfig<?> themeConfig = themeDiscoverer.getThemeConfiguration (themeName);
if (themeConfig == null) {
throw new IllegalArgumentException (String.format ("Theme %s not found", themeName));
}
attributeDescriptors = new HashMap<String, AttributeDescriptor<?>> ();
for (final AttributeDescriptor<?> ad: themeConfig.getAttributeDescriptors ()) {
attributeDescriptors.put (ad.getName (), ad);
}
}
@Test
public void testNullOperation () {
assertFalse (validate (attributeDescriptors.get("geometry"), null));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_MISSING);
}
@Test
public void testRootIsNotTransform () {
assertFalse (validate (attributeDescriptors.get ("geometry"), new OperationDTO () {
@Override
public Object getOperationProperties() {
return null;
}
@Override
public List<OperationInput> getInputs() {
return null;
}
@Override
public OperationType getOperationType() {
return null;
}
}));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_TECHNICAL_ERROR);
}
@Test
public void testRootIsNotConditionalTransform () {
assertFalse (validate (
attributeDescriptors.get ("geometry"),
new TransformOperationDTO (
getOperationType (SplitStringTransform.class),
new ArrayList<OperationInputDTO> (),
null
)
));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_TECHNICAL_ERROR);
}
@Test
public void testRootConditionCountMismatch () {
final ConditionalTransform.Settings settings = new ConditionalTransform.Settings ();
final List<OperationInputDTO> inputs = new ArrayList<OperationInputDTO> ();
final ConditionalTransform.Condition condition = new ConditionalTransform.Condition ();
final List<ConditionalTransform.Condition> conditions = new ArrayList<ConditionalTransform.Condition> ();
conditions.add (condition);
settings.setConditions (conditions);
assertFalse (validate (
attributeDescriptors.get ("geometry"),
new TransformOperationDTO (
getOperationType (ConditionalTransform.class),
inputs,
settings
)
));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_TECHNICAL_ERROR);
}
@Test
public void testConditionInvalidAttribute () {
final ConditionalTransform.Settings settings = new ConditionalTransform.Settings ();
final ConditionalTransform.Condition condition = new ConditionalTransform.Condition ();
condition.setAttribute ("invalidAttribute");
condition.setOperation (Operation.IS_NULL);
settings.setConditions (Arrays.asList (condition));
// ATTRIBUTE_MAPPING_CONDITION_INVALID_ATTRIBUTE
assertFalse (validate (
attributeDescriptors.get ("geometry"),
new TransformOperationDTO (
getOperationType (ConditionalTransform.class),
Arrays.asList (constantTextInput (), constantTextInput ()),
settings
)
));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_CONDITION_INVALID_ATTRIBUTE);
assertNoTechnicalErrors ();
}
@Test
public void testConditionNoValue () {
final ConditionalTransform.Settings settings = new ConditionalTransform.Settings ();
final ConditionalTransform.Condition condition = new ConditionalTransform.Condition ();
condition.setAttribute ("geometry");
condition.setOperation (Operation.IN);
settings.setConditions (Arrays.asList (condition));
// ATTRIBUTE_MAPPING_CONDITION_INVALID_ATTRIBUTE
assertFalse (validate (
attributeDescriptors.get ("geometry"),
new TransformOperationDTO (
getOperationType (ConditionalTransform.class),
Arrays.asList (constantTextInput (), constantTextInput ()),
settings
)
));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_CONDITION_NO_VALUE);
assertNoTechnicalErrors ();
}
@Test
public void testConditionTypesIncompatible () {
final ConditionalTransform.Settings settings = new ConditionalTransform.Settings ();
final ConditionalTransform.Condition condition = new ConditionalTransform.Condition ();
condition.setAttribute ("geometry");
condition.setOperation (Operation.IS_NULL);
settings.setConditions (Arrays.asList (condition));
// Attempt to assign string to geometry:
assertFalse (validate (
attributeDescriptors.get ("geometry"),
new TransformOperationDTO (
getOperationType (ConditionalTransform.class),
Arrays.asList (constantTextInput (), constantTextInput ()),
settings
)
));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_TYPES_INCOMPATIBLE, 2);
assertNoTechnicalErrors ();
}
@Test
public void testTypesIncompatible () {
// ATTRIBUTE_MAPPING_TYPES_INCOMPATIBLE
final ConditionalTransform.Settings settings = new ConditionalTransform.Settings ();
// Constant text -> Split string -> Make inspire ID
// This assigns an array to a string and should fail.
final TransformOperationDTO constantText = new TransformOperationDTO (
getOperationType (StringConstantInput.class),
Arrays.<OperationInputDTO>asList (),
new StringConstantInput.Settings ()
);
final TransformOperationDTO splitString = new TransformOperationDTO (
getOperationType (SplitStringTransform.class),
Arrays.asList (new OperationInputDTO[] { new OperationInputDTO (constantText) }),
new SplitStringTransform.Settings ()
);
final TransformOperationDTO makeInspireID = new TransformOperationDTO (
getOperationType (MakeInspireIdTransform.class),
Arrays.asList (new OperationInputDTO[] { new OperationInputDTO (splitString), constantTextInput (), constantTextInput (), constantTextInput () }),
null
);
// Attempt to assign string to geometry:
assertFalse (validate (
attributeDescriptors.get ("inspireID"),
new TransformOperationDTO (
getOperationType (ConditionalTransform.class),
Arrays.asList (new OperationInputDTO (makeInspireID)),
settings
)
));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_TYPES_INCOMPATIBLE);
assertNoTechnicalErrors ();
}
@Test
public void testWrongOperationType () {
assertFalse (validate (
attributeDescriptors.get ("inspireID"),
new TransformOperationDTO (
getOperationType (ConditionalTransform.class),
Arrays.asList (new OperationInputDTO (new OperationDTO () {
@Override
public Object getOperationProperties() {
return null;
}
@Override
public List<OperationInput> getInputs() {
return null;
}
@Override
public OperationType getOperationType() {
return null;
}
})),
new ConditionalTransform.Settings ()
)
));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_TECHNICAL_ERROR);
}
@Test
public void testInputNotFound () {
// ATTRIBUTE_MAPPING_INPUT_NOT_FOUND
assertFalse (validate (
attributeDescriptors.get ("inspireID"),
new TransformOperationDTO (
getOperationType (ConditionalTransform.class),
Arrays.asList (input ("nonExistingInput", AttributeType.STRING)),
new ConditionalTransform.Settings ()
)
));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_INPUT_NOT_FOUND);
assertNoTechnicalErrors ();
}
@Test
public void testInputNoLongerAvailable () {
assertFalse (validate (
attributeDescriptors.get ("inspireID"),
new TransformOperationDTO (
getOperationType (ConditionalTransform.class),
Arrays.asList (input ("testAttribute", AttributeType.STRING, "testAttributeOld")),
new ConditionalTransform.Settings ()
)
));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_INPUT_NO_LONGER_AVAILABLE);
assertNoTechnicalErrors ();
}
@Test
public void testInputTypeChanged () {
assertFalse (validate (
attributeDescriptors.get ("inspireID"),
new TransformOperationDTO (
getOperationType (ConditionalTransform.class),
Arrays.asList (input ("testAttribute", AttributeType.INTEGER)),
new ConditionalTransform.Settings ()
)
));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_INPUT_TYPE_CHANGED);
assertNoTechnicalErrors ();
}
@Test
public void testInputCountInvalid () {
// ATTRIBUTE_MAPPING_TYPES_INCOMPATIBLE
final ConditionalTransform.Settings settings = new ConditionalTransform.Settings ();
// Constant text -> Split string -> Make inspire ID
// This assigns an array to a string and should fail.
final TransformOperationDTO makeInspireID = new TransformOperationDTO (
getOperationType (MakeInspireIdTransform.class),
Arrays.asList (new OperationInputDTO[] { constantTextInput (), constantTextInput (), constantTextInput () }),
null
);
// Attempt to assign string to geometry:
assertFalse (validate (
attributeDescriptors.get ("inspireID"),
new TransformOperationDTO (
getOperationType (ConditionalTransform.class),
Arrays.asList (new OperationInputDTO (makeInspireID)),
settings
)
));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_INPUT_COUNT_INVALID);
assertNoTechnicalErrors ();
}
@Test
public void testOperationWithoutOperationType () {
assertFalse (validate (
attributeDescriptors.get ("inspireID"),
new TransformOperationDTO (
getOperationType (ConditionalTransform.class),
Arrays.<OperationInputDTO>asList (new OperationInputDTO (new TransformOperationDTO (null, Arrays.<OperationInputDTO>asList (), null))),
new ConditionalTransform.Settings ()
)
));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_TECHNICAL_ERROR);
}
@Test
public void testOperationWithoutPropertiesObject () {
assertFalse (validate (
attributeDescriptors.get ("inspireID"),
new TransformOperationDTO (
getOperationType (ConditionalTransform.class),
Arrays.<OperationInputDTO>asList (new OperationInputDTO (new TransformOperationDTO (getOperationType (StringConstantInput.class), Arrays.<OperationInputDTO>asList (), null))),
new ConditionalTransform.Settings ()
)
));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_TECHNICAL_ERROR);
}
@Test
public void testOperationWithPropertiesObjectOfWrongType () {
assertFalse (validate (
attributeDescriptors.get ("inspireID"),
new TransformOperationDTO (
getOperationType (ConditionalTransform.class),
Arrays.<OperationInputDTO>asList (new OperationInputDTO (new TransformOperationDTO (getOperationType (StringConstantInput.class), Arrays.<OperationInputDTO>asList (), new ConditionalTransform.Settings ()))),
new ConditionalTransform.Settings ()
)
));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_TECHNICAL_ERROR);
}
@Test
public void testOperationWithNullInputs () {
assertFalse (validate (
attributeDescriptors.get ("inspireID"),
new TransformOperationDTO (
getOperationType (ConditionalTransform.class),
Arrays.<OperationInputDTO>asList(new OperationInputDTO (
new TransformOperationDTO (
getOperationType (MakeInspireIdTransform.class),
Arrays.<OperationInputDTO>asList (
new OperationInputDTO (new TransformOperationDTO (
getOperationType (StringConstantInput.class),
Arrays.<OperationInputDTO>asList (),
new StringConstantInput.Settings ()
)),
new OperationInputDTO (null),
new OperationInputDTO (null),
new OperationInputDTO (new TransformOperationDTO (
getOperationType (StringConstantInput.class),
Arrays.<OperationInputDTO>asList (),
new StringConstantInput.Settings ()
))
),
null
)
)),
new ConditionalTransform.Settings ()
)
));
assertHasMessage (MessageKey.ATTRIBUTE_MAPPING_INPUT_COUNT_INVALID);
}
private OperationInputDTO input (final String attributeName, final AttributeType attributeType) {
return input (attributeName, attributeType, attributeName);
}
private OperationInputDTO input (final String attributeName, final AttributeType attributeType, final String newName) {
final FeatureType featureType = getFeatureType ();
FeatureTypeAttribute attribute = null;
for (final FeatureTypeAttribute attr: featureType.getAttributes ()) {
if (attr.getName ().getLocalPart().equals (attributeName)) {
attribute = attr;
break;
}
}
return new OperationInputDTO (new InputOperationDTO (attribute, newName, attributeType));
}
private TransformOperationDTO constantText () {
return new TransformOperationDTO(getOperationType (StringConstantInput.class), Arrays.<OperationInputDTO>asList (), new StringConstantInput.Settings ());
}
private OperationInputDTO constantTextInput () {
return new OperationInputDTO (constantText ());
}
private void assertNoTechnicalErrors () {
if (lines.containsKey (MessageKey.ATTRIBUTE_MAPPING_TECHNICAL_ERROR)) {
final StringBuilder b = new StringBuilder ();
for (final String[] item: lines.get (MessageKey.ATTRIBUTE_MAPPING_TECHNICAL_ERROR)) {
if (b.length () > 0) {
b.append (",");
}
b.append (item[0]);
}
fail (b.toString ());
}
}
private void assertHasMessage (final MessageKey messageKey) {
assertHasMessage (messageKey, 1);
}
private void assertHasMessage (final MessageKey messageKey, final int count) {
final List<String[]> messages = lines.get (messageKey);
if ((messages == null ? 0 : messages.size ()) != count) {
fail (String.format ("Expected: %d instances of %s (found %s)", count, messageKey.toString (), getMessageKeys ()));
}
}
private String getMessageKeys () {
final StringBuilder builder = new StringBuilder ();
for (final MessageKey key: lines.keySet ()) {
if (builder.length () > 0) {
builder.append (",");
}
builder.append (String.format("%s:%d", key, lines.get (key).size ()));
if (lines.get(key).get (0).length > 0) {
builder.append (String.format("(%s)", lines.get(key).get(0)[0]));
}
}
return builder.toString ();
}
private boolean validate (final AttributeDescriptor<?> attributeDescriptor, final OperationDTO rootOperation) {
final EventLogger<AttributeMappingValidator.MessageKey> logger = new EventLogger<AttributeMappingValidator.MessageKey> () {
@Override
public String logEvent(final Job job, final MessageKey messageKey, final LogLevel logLevel, final String... messageValues) {
final List<String[]> list;
if (!lines.containsKey (messageKey)) {
list = new ArrayList<String[]> ();
lines.put (messageKey, list);
} else {
list = lines.get (messageKey);
}
list.add (messageValues);
return messageKey.toString ();
}
@Override
public String logEvent(Job job, MessageKey messageKey, LogLevel logLevel, double x, double y, String gmlId, String... messageValues) {
return logEvent (job, messageKey, logLevel, messageValues);
}
@Override
public String logEvent(Job job, MessageKey messageKey,
LogLevel logLevel, Map<String, Object> context,
String... messageValues) {
return logEvent (job, messageKey, logLevel, messageValues);
}
};
final AttributeMappingValidator validator = new AttributeMappingValidator (
attributeDescriptor,
getFeatureType (),
logger
);
return validator.isValid (null, rootOperation);
}
private FeatureType getFeatureType () {
final FeatureType featureType = new FeatureType() {
@Override
public QName getName() {
return new QName() {
@Override
public int compareTo(QName o) {
return getLocalPart ().compareTo (o.getLocalPart ());
}
@Override
public String getNamespace() {
return "http://www.idgis.nl";
}
@Override
public String getLocalPart() {
return "TestFeatureType";
}
};
}
@Override
public Set<FeatureTypeAttribute> getAttributes() {
final Set<FeatureTypeAttribute> attributes = new HashSet<FeatureTypeAttribute> ();
attributes.add (new FeatureTypeAttribute() {
@Override
public int compareTo (FeatureTypeAttribute o) {
return getName ().compareTo (o.getName ());
}
@Override
public AttributeType getType() {
return AttributeType.STRING;
}
@Override
public QName getName() {
return new QName () {
@Override
public int compareTo(QName o) {
return getLocalPart ().compareTo (o.getLocalPart ());
}
@Override
public String getNamespace() {
return "http://www.idgis.nl";
}
@Override
public String getLocalPart() {
return "testAttribute";
}
};
}
});
return attributes;
}
};
return featureType;
}
}