/**
* Copyright (c) Codice Foundation
* <p/>
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or any later version.
* <p/>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*/
package ddf.catalog.metacard.validation;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.theInstance;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentMatcher;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import ddf.catalog.data.Attribute;
import ddf.catalog.data.Metacard;
import ddf.catalog.data.impl.AttributeImpl;
import ddf.catalog.data.impl.MetacardImpl;
import ddf.catalog.data.types.Validation;
import ddf.catalog.operation.CreateRequest;
import ddf.catalog.operation.DeleteRequest;
import ddf.catalog.operation.Request;
import ddf.catalog.operation.UpdateRequest;
import ddf.catalog.operation.impl.CreateRequestImpl;
import ddf.catalog.operation.impl.DeleteRequestImpl;
import ddf.catalog.operation.impl.UpdateRequestImpl;
import ddf.catalog.plugin.PluginExecutionException;
import ddf.catalog.plugin.StopProcessingException;
import ddf.catalog.util.Describable;
import ddf.catalog.validation.MetacardValidator;
import ddf.catalog.validation.ValidationException;
public class MetacardValidityMarkerPluginTest {
private static final String ID = "ID";
private static final String SAMPLE_WARNING = "sample warning";
private static final String SAMPLE_ERROR = "sample error";
private static final String FIRST = "first";
private static final String SECOND = "second";
private static final String VALID_TAG = "VALID";
private static final String INVALID_TAG = "INVALID";
private static final Map<String, Serializable> PROPERTIES = Collections.singletonMap("foo",
"bar");
private static final Set<String> DESTINATIONS = Sets.newHashSet("source 1", "source 2");
private final Consumer<Attribute> expectNone = attribute -> assertThat(attribute,
is(nullValue()));
private final Consumer<Attribute> expectError = attribute -> {
assertThat(attribute.getValues(), hasSize(1));
assertThat(attribute.getValues(), contains(SAMPLE_ERROR));
};
private final Consumer<Attribute> expectWarning = attribute -> {
assertThat(attribute.getValues(), hasSize(1));
assertThat(attribute.getValues(), contains(SAMPLE_WARNING));
};
private MetacardValidityMarkerPlugin plugin;
private List<MetacardValidator> metacardValidators;
private List<String> enforcedMetacardValidators;
@Before
public void setUp() {
metacardValidators = new ArrayList<>();
enforcedMetacardValidators = new ArrayList<>();
plugin = new MetacardValidityMarkerPlugin();
plugin.setMetacardValidators(metacardValidators);
plugin.setEnforcedMetacardValidators(enforcedMetacardValidators);
}
private List<Metacard> getUpdatedMetacards(UpdateRequest updateRequest) {
return updateRequest.getUpdates()
.stream()
.map(Map.Entry::getValue)
.collect(Collectors.toList());
}
private void verifyCreate(CreateRequest originalRequest, Consumer<Attribute> errorExpectation,
Consumer<Attribute> warningExpectation, String expectedTag)
throws PluginExecutionException, StopProcessingException {
CreateRequest filteredRequest = plugin.process(originalRequest);
List<Metacard> filteredMetacards = filteredRequest.getMetacards();
verifyMetacardErrorsAndWarnings(filteredMetacards,
errorExpectation,
warningExpectation,
expectedTag);
verifyRequestPropertiesUnchanged(originalRequest, filteredRequest);
}
private void verifyUpdate(UpdateRequest originalRequest, Consumer<Attribute> errorExpectation,
Consumer<Attribute> warningExpectation, String expectedTag)
throws PluginExecutionException, StopProcessingException {
UpdateRequest filteredRequest = plugin.process(originalRequest);
List<Metacard> filteredMetacards = getUpdatedMetacards(filteredRequest);
verifyMetacardErrorsAndWarnings(filteredMetacards,
errorExpectation,
warningExpectation,
expectedTag);
verifyRequestPropertiesUnchanged(originalRequest, filteredRequest);
}
private void verifyMetacardErrorsAndWarnings(List<Metacard> filteredMetacards,
Consumer<Attribute> errorExpectation, Consumer<Attribute> warningExpectation,
String expectedTag) {
assertThat(filteredMetacards, hasSize(2));
filteredMetacards.forEach(metacard -> {
errorExpectation.accept(metacard.getAttribute(Validation.VALIDATION_ERRORS));
warningExpectation.accept(metacard.getAttribute(Validation.VALIDATION_WARNINGS));
assertThat(metacard.getTags(), hasItem(expectedTag));
});
}
private void verifyRequestPropertiesUnchanged(Request original, Request processed) {
assertThat(processed.getProperties(), is(original.getProperties()));
assertThat(processed.getStoreIds(), is(original.getStoreIds()));
}
@Test
public void testMultipleValidationTagsValid()
throws StopProcessingException, PluginExecutionException {
metacardValidators.add(getMockPassingValidator());
CreateRequest request = getMockCreateRequest();
Metacard m1 = request.getMetacards().get(0);
Set<String> tags = m1.getTags();
tags.add(INVALID_TAG);
m1.setAttribute(new AttributeImpl(Metacard.TAGS, new ArrayList<String>(tags)));
CreateRequest filteredRequest = plugin.process(request);
assertThat(filteredRequest.getMetacards().get(0).getTags(), hasItem(VALID_TAG));
assertThat(filteredRequest.getMetacards().get(0).getTags(), not(hasItem(INVALID_TAG)));
}
@Test
public void testMultipleValidationTagsInvalid()
throws StopProcessingException, PluginExecutionException, ValidationException {
metacardValidators.add(getMockFailingValidatorWithErrorsAndWarnings());
CreateRequest request = getMockCreateRequest();
Metacard m1 = request.getMetacards().get(0);
Set<String> tags = m1.getTags();
tags.add(VALID_TAG);
m1.setAttribute(new AttributeImpl(Metacard.TAGS, new ArrayList<String>(tags)));
CreateRequest filteredRequest = plugin.process(request);
assertThat(filteredRequest.getMetacards().get(0).getTags(), hasItem(INVALID_TAG));
assertThat(filteredRequest.getMetacards().get(0).getTags(), not(hasItem(VALID_TAG)));
}
@Test
public void testMarkMetacardValid() throws StopProcessingException, PluginExecutionException {
metacardValidators.add(getMockPassingValidator());
verifyCreate(getMockCreateRequest(), expectNone, expectNone, VALID_TAG);
verifyUpdate(getMockUpdateRequest(), expectNone, expectNone, VALID_TAG);
}
@Test
public void testMarkMetacardInvalidErrors()
throws ValidationException, StopProcessingException, PluginExecutionException {
metacardValidators.add(getMockFailingValidatorWithErrors());
verifyCreate(getMockCreateRequest(), expectError, expectNone, INVALID_TAG);
verifyUpdate(getMockUpdateRequest(), expectError, expectNone, INVALID_TAG);
}
@Test
public void testMarkMetacardInvalidWarnings()
throws ValidationException, StopProcessingException, PluginExecutionException {
metacardValidators.add(getMockFailingValidatorWithWarnings());
verifyCreate(getMockCreateRequest(), expectNone, expectWarning, INVALID_TAG);
verifyUpdate(getMockUpdateRequest(), expectNone, expectWarning, INVALID_TAG);
}
@Test
public void testMarkMetacardInvalidErrorsAndWarnings()
throws ValidationException, StopProcessingException, PluginExecutionException {
metacardValidators.add(getMockFailingValidatorWithErrorsAndWarnings());
verifyCreate(getMockCreateRequest(), expectError, expectWarning, INVALID_TAG);
verifyUpdate(getMockUpdateRequest(), expectError, expectWarning, INVALID_TAG);
}
@Test
public void testProcessDelete() throws StopProcessingException, PluginExecutionException {
DeleteRequestImpl deleteRequest = mock(DeleteRequestImpl.class);
DeleteRequest returnedDeleteRequest = plugin.process(deleteRequest);
assertThat(returnedDeleteRequest, is(theInstance(deleteRequest)));
}
private void verifyEnforcedCreate(CreateRequest originalRequest,
List<Metacard> expectedAllowedMetacards)
throws PluginExecutionException, StopProcessingException {
CreateRequest filteredRequest = plugin.process(originalRequest);
List<Metacard> filteredMetacards = filteredRequest.getMetacards();
verifyAllowedMetacards(filteredMetacards, expectedAllowedMetacards);
verifyRequestPropertiesUnchanged(originalRequest, filteredRequest);
}
private void verifyEnforcedUpdate(UpdateRequest originalRequest,
List<Metacard> expectedAllowedMetacards)
throws PluginExecutionException, StopProcessingException {
UpdateRequest filteredRequest = plugin.process(originalRequest);
List<Metacard> filteredMetacards = getUpdatedMetacards(filteredRequest);
verifyAllowedMetacards(filteredMetacards, expectedAllowedMetacards);
verifyRequestPropertiesUnchanged(originalRequest, filteredRequest);
}
private void verifyAllowedMetacards(List<Metacard> filteredMetacards,
List<Metacard> expectedAllowedMetacards) {
Matcher[] metacardMatchers = expectedAllowedMetacards.stream()
.map(Matchers::theInstance)
.toArray(Matcher[]::new);
assertThat(filteredMetacards, contains(metacardMatchers));
}
@Test
public void testMetacardPassesEnforcedValidators()
throws StopProcessingException, PluginExecutionException {
metacardValidators.add(getMockEnforcedPassingValidatorWithId(ID));
enforcedMetacardValidators.add(ID);
CreateRequest createRequest = getMockCreateRequest();
List<Metacard> createdMetacards = createRequest.getMetacards();
verifyEnforcedCreate(createRequest, createdMetacards);
UpdateRequest updateRequest = getMockUpdateRequest();
List<Metacard> updatedMetacards = getUpdatedMetacards(updateRequest);
verifyEnforcedUpdate(updateRequest, updatedMetacards);
}
@Test
public void testMetacardFailsEnforcedValidator()
throws ValidationException, StopProcessingException, PluginExecutionException {
metacardValidators.add(getMockEnforcedFailingValidatorWithId(ID));
enforcedMetacardValidators.add(ID);
CreateRequest createRequest = getMockCreateRequest();
List<Metacard> createdMetacards = createRequest.getMetacards();
verifyEnforcedCreate(createRequest, createdMetacards.subList(1, createdMetacards.size()));
UpdateRequest updateRequest = getMockUpdateRequest();
List<Metacard> updatedMetacards = getUpdatedMetacards(updateRequest);
verifyEnforcedUpdate(updateRequest, updatedMetacards.subList(1, updatedMetacards.size()));
}
@Test
public void testMetacardPassesEnforcedValidatorsNoDescribable()
throws StopProcessingException, PluginExecutionException {
MetacardValidator mockValidator = getMockPassingValidatorNoDescribable();
metacardValidators.add(mockValidator);
enforcedMetacardValidators.add(mockValidator.getClass()
.getCanonicalName());
CreateRequest createRequest = getMockCreateRequest();
List<Metacard> createdMetacards = createRequest.getMetacards();
verifyEnforcedCreate(createRequest, createdMetacards);
UpdateRequest updateRequest = getMockUpdateRequest();
List<Metacard> updatedMetacards = getUpdatedMetacards(updateRequest);
verifyEnforcedUpdate(updateRequest, updatedMetacards);
}
@Test
public void testMetacardFailsEnforcedValidatorNoDescribable()
throws ValidationException, StopProcessingException, PluginExecutionException {
MetacardValidator mockValidator = getMockFailingValidatorNoDescribable();
metacardValidators.add(mockValidator);
enforcedMetacardValidators.add(mockValidator.getClass()
.getCanonicalName());
CreateRequest createRequest = getMockCreateRequest();
List<Metacard> createdMetacards = createRequest.getMetacards();
verifyEnforcedCreate(createRequest, createdMetacards.subList(1, createdMetacards.size()));
UpdateRequest updateRequest = getMockUpdateRequest();
List<Metacard> updatedMetacards = getUpdatedMetacards(updateRequest);
verifyEnforcedUpdate(updateRequest, updatedMetacards.subList(1, updatedMetacards.size()));
}
@Test
public void testGetters() {
assertThat(plugin.getMetacardValidators(), is(empty()));
assertThat(plugin.getEnforcedMetacardValidators(), is(empty()));
}
@Test
public void testEnforceWarningsOnly() throws Exception {
markerPluginResponseHelper(getMockFailingValidatorWithWarnings(), false, true, 0);
markerPluginResponseHelper(getMockFailingValidatorWithErrors(), false, true, 2);
}
@Test
public void testEnforceErrorsOnly() throws Exception {
markerPluginResponseHelper(getMockFailingValidatorWithErrors(), true, false, 0);
markerPluginResponseHelper(getMockFailingValidatorWithWarnings(), true, false, 2);
}
@Test
public void testEnforceErrorsAndWarnings() throws Exception {
markerPluginResponseHelper(getMockFailingValidatorWithErrorsAndWarnings(), true, true, 0);
}
@Test
public void testNoEnforcement() throws Exception {
markerPluginResponseHelper(getMockFailingValidatorWithErrorsAndWarnings(), false, false, 2);
markerPluginResponseHelper(getMockFailingValidatorWithWarnings(), false, false, 2);
markerPluginResponseHelper(getMockFailingValidatorWithErrors(), false, false, 2);
}
@Test
public void testTrackingErrors() throws Exception {
testTrackingHelper(getMockFailingValidatorWithErrors(), true, false);
}
@Test
public void testTrackingWarnings() throws Exception {
testTrackingHelper(getMockFailingValidatorWithWarnings(), false, true);
}
@Test
public void testTrackingErrorsAndWarnings() throws Exception {
testTrackingHelper(getMockFailingValidatorWithErrorsAndWarnings(), true, true);
}
@Test
public void testTrackingClean() throws Exception {
testTrackingHelper(getMockPassingValidator(), false, false);
}
private void testTrackingHelper(MetacardValidator validator, boolean expectErrors,
boolean expectWarnings) throws Exception {
List<Metacard> metacards = markerPluginResponseHelper(validator, false, false, 2);
for (Metacard m : metacards) {
if (expectErrors) {
assertThat(m.getAttribute(Validation.FAILED_VALIDATORS_ERRORS)
.getValues()
.isEmpty(), is(false));
}
if (expectWarnings) {
assertThat(m.getAttribute(Validation.FAILED_VALIDATORS_WARNINGS)
.getValues()
.isEmpty(), is(false));
}
}
}
private Metacard metacardWithTitle(String title) {
MetacardImpl metacard = new MetacardImpl();
metacard.setTitle(title);
return metacard;
}
private CreateRequest getMockCreateRequest() {
List<Metacard> listMetacards = Lists.newArrayList(metacardWithTitle(FIRST),
metacardWithTitle(SECOND));
return new CreateRequestImpl(listMetacards, PROPERTIES, DESTINATIONS);
}
private UpdateRequest getMockUpdateRequest() {
List<Map.Entry<Serializable, Metacard>> updates = new ArrayList<>();
updates.add(new AbstractMap.SimpleEntry<>(FIRST, metacardWithTitle(FIRST)));
updates.add(new AbstractMap.SimpleEntry<>(SECOND, metacardWithTitle(SECOND)));
return new UpdateRequestImpl(updates, Metacard.TITLE, PROPERTIES, DESTINATIONS);
}
private MetacardValidator getMockPassingValidator() {
MetacardValidator mockValidator = mock(MetacardValidator.class,
withSettings().extraInterfaces(Describable.class));
return mockValidator;
}
private MetacardValidator getMockFailingValidatorWithErrors() throws ValidationException {
ValidationException validationException = mock(ValidationException.class);
when(validationException.getErrors()).thenReturn(Collections.singletonList(SAMPLE_ERROR));
MetacardValidator metacardValidator = mock(MetacardValidator.class,
withSettings().extraInterfaces(Describable.class));
doThrow(validationException).when(metacardValidator)
.validate(any(Metacard.class));
when(((Describable) metacardValidator).getId()).thenReturn(ID);
return metacardValidator;
}
private MetacardValidator getMockFailingValidatorWithWarnings() throws ValidationException {
ValidationException validationException = mock(ValidationException.class);
when(validationException.getWarnings()).thenReturn(Collections.singletonList(SAMPLE_WARNING));
MetacardValidator metacardValidator = mock(MetacardValidator.class,
withSettings().extraInterfaces(Describable.class));
doThrow(validationException).when(metacardValidator)
.validate(any(Metacard.class));
when(((Describable) metacardValidator).getId()).thenReturn(ID);
return metacardValidator;
}
private MetacardValidator getMockFailingValidatorWithErrorsAndWarnings()
throws ValidationException {
ValidationException validationException = mock(ValidationException.class);
when(validationException.getErrors()).thenReturn(Collections.singletonList(SAMPLE_ERROR));
when(validationException.getWarnings()).thenReturn(Collections.singletonList(SAMPLE_WARNING));
MetacardValidator metacardValidator = mock(MetacardValidator.class,
withSettings().extraInterfaces(Describable.class));
doThrow(validationException).when(metacardValidator)
.validate(any(Metacard.class));
when(((Describable) metacardValidator).getId()).thenReturn(ID);
return metacardValidator;
}
private MetacardValidator getMockEnforcedPassingValidatorWithId(String id) {
MetacardValidator metacardValidator = mock(MetacardValidator.class,
withSettings().extraInterfaces(Describable.class));
when(((Describable) metacardValidator).getId()).thenReturn(id);
return metacardValidator;
}
private MetacardValidator getMockEnforcedFailingValidatorWithId(String id)
throws ValidationException {
ValidationException validationException = mock(ValidationException.class);
when(validationException.getErrors()).thenReturn(Collections.singletonList(SAMPLE_ERROR));
MetacardValidator metacardValidator = mock(MetacardValidator.class,
withSettings().extraInterfaces(Describable.class));
doThrow(validationException).when(metacardValidator)
.validate(argThat(isMetacardWithTitle(FIRST)));
when(((Describable) metacardValidator).getId()).thenReturn(id);
return metacardValidator;
}
private IsMetacardWithTitle isMetacardWithTitle(String title) {
return new IsMetacardWithTitle(title);
}
private MetacardValidator getMockPassingValidatorNoDescribable() {
return mock(MetacardValidator.class);
}
private MetacardValidator getMockFailingValidatorNoDescribable() throws ValidationException {
MetacardValidator metacardValidator = mock(MetacardValidator.class);
ValidationException validationException = mock(ValidationException.class);
when(validationException.getErrors()).thenReturn(Collections.singletonList(SAMPLE_ERROR));
doThrow(validationException).when(metacardValidator)
.validate(argThat(isMetacardWithTitle(FIRST)));
return metacardValidator;
}
private class IsMetacardWithTitle extends ArgumentMatcher<Metacard> {
private final String title;
private IsMetacardWithTitle(String title) {
this.title = title;
}
@Override
public boolean matches(Object o) {
return ((Metacard) o).getTitle()
.equals(title);
}
}
private List<Metacard> markerPluginResponseHelper(MetacardValidator validator,
boolean enforceErrors, boolean enforceWarnings, int numNotFiltered) throws Exception {
String validatorName = plugin.getValidatorName(validator);
metacardValidators.add(validator);
enforcedMetacardValidators.add(validatorName);
plugin.setMetacardValidators(metacardValidators);
plugin.setEnforcedMetacardValidators(enforcedMetacardValidators);
plugin.setEnforceErrors(enforceErrors);
plugin.setEnforceWarnings(enforceWarnings);
CreateRequest createRequest = plugin.process(getMockCreateRequest());
List<Metacard> createdMetacards = createRequest.getMetacards();
assertThat(createdMetacards.size(), is(numNotFiltered));
//reset
metacardValidators.remove(validator);
enforcedMetacardValidators.remove(validatorName);
plugin.setEnforceErrors(true);
plugin.setEnforceWarnings(false);
return createdMetacards;
}
}