add validator for lists that ensure the element validator is called for empty entries

Signed-off-by: Matthew Burgess <mattyb149@apache.org>

This closes #4116
This commit is contained in:
Otto Fowler 2020-03-06 08:06:08 -05:00 committed by Matthew Burgess
parent 4a2a91135e
commit d9ac9e44a7
No known key found for this signature in database
GPG Key ID: 05D3DEB8126DAD24
2 changed files with 160 additions and 2 deletions

View File

@ -566,7 +566,14 @@ public class StandardValidators {
};
}
public static Validator createListValidator(boolean trimEntries, boolean excludeEmptyEntries, Validator validator) {
public static Validator createListValidator(boolean trimEntries, boolean excludeEmptyEntries,
Validator elementValidator){
return createListValidator(trimEntries,excludeEmptyEntries, elementValidator, false);
}
public static Validator createListValidator(boolean trimEntries, boolean excludeEmptyEntries,
Validator validator,
boolean ensureElementValidation) {
return (subject, input, context) -> {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
@ -576,7 +583,7 @@ public class StandardValidators {
return new ValidationResult.Builder().subject(subject).input(null).explanation("List must have at least one non-empty element").valid(false).build();
}
final String[] list = input.split(",");
final String[] list = ensureElementValidation ? input.split(",",-1) : input.split(",");
if (list.length == 0) {
return new ValidationResult.Builder().subject(subject).input(null).explanation("List must have at least one non-empty element").valid(false).build();
}

View File

@ -309,6 +309,157 @@ public class TestStandardValidators {
assertEquals(2, mockValidator.getValidateCallCount());
}
@Test
public void testListValidatorEnsuringElementValidation() {
// use the TestMockValidator to be sure the item validator get's called when we think it should
// note that it will reset the count after every get call.
// note that we fail fast, so if there are 3 items, and the second fails validation, the call
// count will be 2
InstrumentedStandardValidator mockValidator = new InstrumentedStandardValidator(StandardValidators.NON_EMPTY_VALIDATOR, true);
Validator val = StandardValidators.createListValidator(true, false, mockValidator, true);
ValidationResult vr;
final ValidationContext validationContext = Mockito.mock(ValidationContext.class);
vr = val.validate("List", null, validationContext);
assertFalse(vr.isValid());
assertEquals(0, mockValidator.getValidateCallCount());
vr = val.validate("List", "", validationContext);
assertFalse(vr.isValid());
assertEquals(1, mockValidator.getValidateCallCount());
// Whitespace will be trimmed
vr = val.validate("List", " ", validationContext);
assertFalse(vr.isValid());
assertEquals(1, mockValidator.getValidateCallCount());
// An empty list is the same as null, "" or " "
vr = val.validate("List", ",", validationContext);
assertFalse(vr.isValid());
assertEquals(1, mockValidator.getValidateCallCount());
vr = val.validate("List", " , ", validationContext);
assertFalse(vr.isValid());
assertEquals(1, mockValidator.getValidateCallCount());
// will evaluate to no entry
vr = val.validate("List", ",,,,", validationContext);
assertFalse(vr.isValid());
assertEquals(1, mockValidator.getValidateCallCount());
// will evaluate to an empty element
vr = val.validate("List", ",foo", validationContext);
assertFalse(vr.isValid());
assertEquals(1, mockValidator.getValidateCallCount());
vr = val.validate("List", "1", validationContext);
assertTrue(vr.isValid());
assertEquals(1, mockValidator.getValidateCallCount());
vr = val.validate("List", "1,2,3", validationContext);
assertTrue(vr.isValid());
assertEquals(3, mockValidator.getValidateCallCount());
// The parser will bother with whitespace after the last comma, as we ensure the inner validator is called
vr = val.validate("List", "a,", validationContext);
assertFalse(vr.isValid());
assertEquals(2, mockValidator.getValidateCallCount());
// However it will bother if there is an empty element in the list (two commas in a row, e.g.)
vr = val.validate("List", "a,,c", validationContext);
assertFalse(vr.isValid());
assertEquals(2, mockValidator.getValidateCallCount());
vr = val.validate("List", "a, ,c, ", validationContext);
assertFalse(vr.isValid());
assertEquals(2, mockValidator.getValidateCallCount());
// Try without trim and use a non-blank validator instead of a non-empty one
//
// use the TestMockValidator to be sure the item validator get's called when we think it should
// note that it will reset the count after every get call.
// note that we fail fast, so if there are 3 items, and the second fails validation, the call
// count will be 2
mockValidator = new InstrumentedStandardValidator(StandardValidators.NON_BLANK_VALIDATOR, true);
val = StandardValidators.createListValidator(false, true, mockValidator);
vr = val.validate("List", null, validationContext);
assertFalse(vr.isValid());
assertEquals(0, mockValidator.getValidateCallCount());
// List Validator will ignore empty entries
vr = val.validate("List", "", validationContext);
assertTrue(vr.isValid());
assertEquals(0, mockValidator.getValidateCallCount());
// Whitespace will not be trimmed, but it is still invalid because a non-blank validator is used
vr = val.validate("List", " ", validationContext);
assertFalse(vr.isValid());
assertEquals(1, mockValidator.getValidateCallCount());
vr = val.validate("List", "a,,c", validationContext);
assertTrue(vr.isValid());
assertEquals(2, mockValidator.getValidateCallCount());
vr = val.validate("List", "a, ,c, ", validationContext);
assertFalse(vr.isValid());
assertEquals(2, mockValidator.getValidateCallCount());
// Try without trim and use a non-empty validator
// use the TestMockValidator to be sure the item validator get's called when we think it should
// note that it will reset the count after every get call.
// note that we fail fast, so if there are 3 items, and the second fails validation, the call
// count will be 2
mockValidator = new InstrumentedStandardValidator(StandardValidators.NON_EMPTY_VALIDATOR, true);
val = StandardValidators.createListValidator(false, false, mockValidator);
vr = val.validate("List", null, validationContext);
assertFalse(vr.isValid());
assertEquals(0, mockValidator.getValidateCallCount());
vr = val.validate("List", "", validationContext);
assertFalse(vr.isValid());
assertEquals(1, mockValidator.getValidateCallCount());
// Whitespace will not be trimmed
vr = val.validate("List", " ", validationContext);
assertTrue(vr.isValid());
assertEquals(1, mockValidator.getValidateCallCount());
vr = val.validate("List", "a, ,c, ", validationContext);
assertTrue(vr.isValid());
assertEquals(4, mockValidator.getValidateCallCount());
// Try with trim and use a boolean validator // Try without trim and use a non-empty validator
// use the TestMockValidator to be sure the item validator get's called when we think it should
// note that it will reset the count after every get call.
// note that we fail fast, so if there are 3 items, and the second fails validation, the call
// count will be 2
mockValidator = new InstrumentedStandardValidator(StandardValidators.BOOLEAN_VALIDATOR, true);
val = StandardValidators.createListValidator(true, true, mockValidator);
vr = val.validate("List", "notbool", validationContext);
assertFalse(vr.isValid());
assertEquals(1, mockValidator.getValidateCallCount());
vr = val.validate("List", " notbool \n ", validationContext);
assertFalse(vr.isValid());
assertEquals(1, mockValidator.getValidateCallCount());
vr = val.validate("List", "true", validationContext);
assertTrue(vr.isValid());
assertEquals(1, mockValidator.getValidateCallCount());
vr = val.validate("List", " true \n ", validationContext);
assertTrue(vr.isValid());
assertEquals(1, mockValidator.getValidateCallCount());
vr = val.validate("List", " , false, true,\n", validationContext);
assertTrue(vr.isValid());
assertEquals(2, mockValidator.getValidateCallCount());
}
@Test
public void testCreateURLorFileValidator() {
Validator val = StandardValidators.createURLorFileValidator();