diff options
Diffstat (limited to 'common-parameters/src/test/java/org/onap/policy/common/parameters/TestBeanValidator.java')
-rw-r--r-- | common-parameters/src/test/java/org/onap/policy/common/parameters/TestBeanValidator.java | 664 |
1 files changed, 664 insertions, 0 deletions
diff --git a/common-parameters/src/test/java/org/onap/policy/common/parameters/TestBeanValidator.java b/common-parameters/src/test/java/org/onap/policy/common/parameters/TestBeanValidator.java new file mode 100644 index 00000000..83d0a7f9 --- /dev/null +++ b/common-parameters/src/test/java/org/onap/policy/common/parameters/TestBeanValidator.java @@ -0,0 +1,664 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020-2021 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.parameters; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertTrue; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import lombok.Getter; +import org.junit.Before; +import org.junit.Test; +import org.onap.policy.common.parameters.annotations.ClassName; +import org.onap.policy.common.parameters.annotations.Max; +import org.onap.policy.common.parameters.annotations.Min; +import org.onap.policy.common.parameters.annotations.NotBlank; +import org.onap.policy.common.parameters.annotations.NotNull; +import org.onap.policy.common.parameters.annotations.Pattern; +import org.onap.policy.common.parameters.annotations.Size; +import org.onap.policy.common.parameters.annotations.Valid; + +public class TestBeanValidator { + private static final String TOP = "top"; + private static final String STR_FIELD = "strValue"; + private static final String INT_FIELD = "intValue"; + private static final String NUM_FIELD = "numValue"; + private static final String ITEMS_FIELD = "items"; + private static final String STRING_VALUE = "string value"; + private static final int INT_VALUE = 20; + + private BeanValidator validator; + + @Before + public void setUp() { + validator = new BeanValidator(); + } + + @Test + public void testValidateTop_testValidateFields() { + // validate null + assertTrue(validator.validateTop(TOP, null).isValid()); + + // validate something that has no annotations + assertTrue(validator.validateTop(TOP, validator).isValid()); + + @NotNull + @Getter + class Data { + String strValue; + } + + // one failure case + Data data = new Data(); + BeanValidationResult result = validator.validateTop(TOP, data); + assertInvalid("testValidateFields", result, STR_FIELD, "null"); + assertTrue(result.getResult().contains(TOP)); + + // one success case + data.strValue = STRING_VALUE; + assertTrue(validator.validateTop(TOP, data).isValid()); + + /** + * Repeat with a subclass. + */ + @Getter + class Derived extends Data { + @Min(10) + int intValue; + } + + Derived derived = new Derived(); + derived.strValue = STRING_VALUE; + derived.intValue = INT_VALUE; + + // success case + assertTrue(validator.validateTop(TOP, derived).isValid()); + + // failure cases + derived.strValue = null; + assertInvalid("testValidateFields", validator.validateTop(TOP, derived), STR_FIELD, "null"); + derived.strValue = STRING_VALUE; + + derived.intValue = 1; + assertInvalid("testValidateFields", validator.validateTop(TOP, derived), INT_FIELD, "minimum"); + derived.intValue = INT_VALUE; + + // both invalid + derived.strValue = null; + derived.intValue = 1; + result = validator.validateTop(TOP, derived); + assertInvalid("testValidateFields", result, STR_FIELD, "null"); + assertInvalid("testValidateFields", result, INT_FIELD, "minimum"); + derived.strValue = STRING_VALUE; + derived.intValue = INT_VALUE; + } + + @Test + public void testVerNotNull() { + class NotNullCheck { + @Getter + @Min(1) + @NotNull + Integer intValue; + } + + NotNullCheck notNullCheck = new NotNullCheck(); + assertInvalid("testVerNotNull", validator.validateTop(TOP, notNullCheck), INT_FIELD, "null"); + + notNullCheck.intValue = INT_VALUE; + assertTrue(validator.validateTop(TOP, notNullCheck).isValid()); + + notNullCheck.intValue = 0; + assertInvalid("testVerNotNull", validator.validateTop(TOP, notNullCheck), INT_FIELD, "minimum"); + } + + @Test + public void testVerNotBlank() { + class NotBlankCheck { + @Getter + @NotBlank + String strValue; + } + + NotBlankCheck notBlankCheck = new NotBlankCheck(); + + // null + assertTrue(validator.validateTop(TOP, notBlankCheck).isValid()); + + // empty + notBlankCheck.strValue = ""; + assertInvalid("testVerNotNull", validator.validateTop(TOP, notBlankCheck), STR_FIELD, "blank"); + + // spaces + notBlankCheck.strValue = " "; + assertInvalid("testVerNotNull", validator.validateTop(TOP, notBlankCheck), STR_FIELD, "blank"); + + // not blank + notBlankCheck.strValue = STRING_VALUE; + assertTrue(validator.validateTop(TOP, notBlankCheck).isValid()); + + /* + * Class with "blank" annotation on an integer. + */ + class NotBlankInt { + @Getter + @NotBlank + int intValue; + } + + NotBlankInt notBlankInt = new NotBlankInt(); + notBlankInt.intValue = 0; + assertTrue(validator.validateTop(TOP, notBlankInt).isValid()); + } + + /** + * Tests verSize with a collection. + */ + @Test + public void testVerSizeCollection() { + class CollectionSizeCheck { + @Getter + @Size(min = 3) + Collection<Integer> items; + } + + CollectionSizeCheck collCheck = new CollectionSizeCheck(); + + // valid length - exact + collCheck.items = List.of(1, 2, 3); + assertThat(validator.validateTop(TOP, collCheck).isValid()).isTrue(); + + // valid length - extra + collCheck.items = List.of(1, 2, 3, 4); + assertThat(validator.validateTop(TOP, collCheck).isValid()).isTrue(); + + // too few + collCheck.items = List.of(1, 2); + assertInvalid("testVerSize", validator.validateTop(TOP, collCheck), ITEMS_FIELD, "minimum", "3"); + + // null + collCheck.items = null; + assertThat(validator.validateTop(TOP, collCheck).isValid()).isTrue(); + } + + /** + * Tests verSize with a map. + */ + @Test + public void testVerSizeMap() { + class MapSizeCheck { + @Getter + @Size(min = 3) + Map<Integer, Integer> items; + } + + MapSizeCheck mapCheck = new MapSizeCheck(); + + // valid length - exact + mapCheck.items = Map.of(1, 10, 2, 20, 3, 30); + assertThat(validator.validateTop(TOP, mapCheck).isValid()).isTrue(); + + // valid length - extra + mapCheck.items = Map.of(1, 10, 2, 20, 3, 30, 4, 40); + assertThat(validator.validateTop(TOP, mapCheck).isValid()).isTrue(); + + // too few + mapCheck.items = Map.of(1, 10, 2, 20); + assertInvalid("testVerSize", validator.validateTop(TOP, mapCheck), ITEMS_FIELD, "minimum", "3"); + + // null + mapCheck.items = null; + assertThat(validator.validateTop(TOP, mapCheck).isValid()).isTrue(); + } + + /** + * Tests verSize with an object for which it doesn't apply. + */ + @Test + public void testVerSizeOther() { + class OtherSizeCheck { + @Getter + @Size(min = 3) + Integer items; + } + + OtherSizeCheck otherCheck = new OtherSizeCheck(); + + otherCheck.items = 10; + assertThat(validator.validateTop(TOP, otherCheck).isValid()).isTrue(); + } + + @Test + public void testVerRegex() { + class RegexCheck { + @Getter + @Pattern(regexp = "[a-f]*") + String strValue; + } + + RegexCheck regexCheck = new RegexCheck(); + + // does not match + regexCheck.strValue = "xyz"; + assertInvalid("testVerRegex", validator.validateTop(TOP, regexCheck), STR_FIELD, + "does not match regular expression [a-f]"); + + // matches + regexCheck.strValue = "abcabc"; + assertTrue(validator.validateTop(TOP, regexCheck).isValid()); + + // invalid regex + class InvalidRegexCheck { + @Getter + @Pattern(regexp = "[a-f") + String strValue; + } + + InvalidRegexCheck invalidRegexCheck = new InvalidRegexCheck(); + + // does not match + invalidRegexCheck.strValue = "abc"; + assertInvalid("testVerRegex", validator.validateTop(TOP, invalidRegexCheck), STR_FIELD, + "does not match regular expression [a-f"); + + // matches + regexCheck.strValue = "abcabc"; + assertTrue(validator.validateTop(TOP, regexCheck).isValid()); + + /* + * Class with "regex" annotation on an integer. + */ + class RegexInt { + @Getter + @Pattern(regexp = "[a-f]*") + int intValue; + } + + RegexInt regexInt = new RegexInt(); + regexInt.intValue = 0; + assertInvalid("testVerRegex", validator.validateTop(TOP, regexInt), INT_FIELD, + "does not match regular expression [a-f]"); + } + + @Test + public void testVerMax() { + /* + * Field is not a number. + */ + class NonNumeric { + @Getter + @Max(100) + String strValue; + } + + NonNumeric nonNumeric = new NonNumeric(); + nonNumeric.strValue = STRING_VALUE; + assertTrue(validator.validateTop(TOP, nonNumeric).isValid()); + + /* + * Integer field. + */ + class IntField { + @Getter + @Max(100) + Integer intValue; + } + + // ok value + IntField intField = new IntField(); + assertNumeric("testVerMax-integer", intField, value -> { + intField.intValue = value; + }, INT_FIELD, "maximum", INT_VALUE, 100, 101); + + /* + * Long field. + */ + class LongField { + @Getter + @Max(100) + Long numValue; + } + + // ok value + LongField longField = new LongField(); + assertNumeric("testVerMax-long", longField, value -> { + longField.numValue = (long) value; + }, NUM_FIELD, "maximum", INT_VALUE, 100, 101); + + /* + * Float field. + */ + class FloatField { + @Getter + @Max(100) + Float numValue; + } + + // ok value + FloatField floatField = new FloatField(); + assertNumeric("testVerMax-float", floatField, value -> { + floatField.numValue = (float) value; + }, NUM_FIELD, "maximum", INT_VALUE, 100, 101); + + /* + * Double field. + */ + class DoubleField { + @Getter + @Max(100) + Double numValue; + } + + // ok value + DoubleField doubleField = new DoubleField(); + assertNumeric("testVerMax-double", doubleField, value -> { + doubleField.numValue = (double) value; + }, NUM_FIELD, "maximum", INT_VALUE, 100, 101); + + /* + * Atomic Integer field (which is a subclass of Number). + */ + class AtomIntValue { + @Getter + @Max(100) + AtomicInteger numValue; + } + + // ok value + AtomIntValue atomIntField = new AtomIntValue(); + atomIntField.numValue = new AtomicInteger(INT_VALUE); + assertTrue(validator.validateTop(TOP, atomIntField).isValid()); + + // invalid value - should be OK, because it isn't an Integer + atomIntField.numValue.set(101); + assertTrue(validator.validateTop(TOP, atomIntField).isValid()); + } + + @Test + public void testVerMin() { + /* + * Field is not a number. + */ + class NonNumeric { + @Getter + @Min(10) + String strValue; + } + + NonNumeric nonNumeric = new NonNumeric(); + nonNumeric.strValue = STRING_VALUE; + assertTrue(validator.validateTop(TOP, nonNumeric).isValid()); + + /* + * Integer field. + */ + class IntField { + @Getter + @Min(10) + Integer intValue; + } + + // ok value + IntField intField = new IntField(); + assertNumeric("testVerMin-integer", intField, value -> { + intField.intValue = value; + }, INT_FIELD, "minimum", INT_VALUE, 10, 1); + + /* + * Long field. + */ + class LongField { + @Getter + @Min(10) + Long numValue; + } + + // ok value + LongField longField = new LongField(); + assertNumeric("testVerMin-long", longField, value -> { + longField.numValue = (long) value; + }, NUM_FIELD, "minimum", INT_VALUE, 10, 1); + + /* + * Float field. + */ + class FloatField { + @Getter + @Min(10) + Float numValue; + } + + // ok value + FloatField floatField = new FloatField(); + assertNumeric("testVerMin-float", floatField, value -> { + floatField.numValue = (float) value; + }, NUM_FIELD, "minimum", INT_VALUE, 10, 1); + + /* + * Double field. + */ + class DoubleField { + @Getter + @Min(10) + Double numValue; + } + + // ok value + DoubleField doubleField = new DoubleField(); + assertNumeric("testVerMin-double", doubleField, value -> { + doubleField.numValue = (double) value; + }, NUM_FIELD, "minimum", INT_VALUE, 10, 1); + + /* + * Atomic Integer field (which is a subclass of Number). + */ + class AtomIntValue { + @Getter + @Min(10) + AtomicInteger numValue; + } + + // ok value + AtomIntValue atomIntField = new AtomIntValue(); + atomIntField.numValue = new AtomicInteger(INT_VALUE); + assertTrue(validator.validateTop(TOP, atomIntField).isValid()); + + // invalid value - should be OK, because it isn't an Integer + atomIntField.numValue.set(101); + assertTrue(validator.validateTop(TOP, atomIntField).isValid()); + } + + @Test + public void testVerClassName() { + class ClassNameCheck { + @Getter + @ClassName + String strValue; + } + + ClassNameCheck classCheck = new ClassNameCheck(); + + // null should be OK + classCheck.strValue = null; + assertTrue(validator.validateTop(TOP, classCheck).isValid()); + + // valid class name + classCheck.strValue = getClass().getName(); + assertTrue(validator.validateTop(TOP, classCheck).isValid()); + + // invalid class name + classCheck.strValue = "<unknown class>"; + assertInvalid("testVerClassName", validator.validateTop(TOP, classCheck), + STR_FIELD, "class is not in the classpath"); + } + + @Test + public void testVerCascade() { + class Item { + @Getter + @NotNull + Integer intValue; + } + + @Getter + class Container { + @Valid + Item checked; + + Item unchecked; + + @Valid + List<Item> items; + + @Valid + Map<String, Item> itemMap; + } + + Container cont = new Container(); + cont.unchecked = new Item(); + cont.items = List.of(new Item()); + cont.itemMap = Map.of(STRING_VALUE, new Item()); + + cont.checked = null; + assertTrue(validator.validateTop(TOP, cont).isValid()); + + cont.checked = new Item(); + + assertInvalid("testVerCascade", validator.validateTop(TOP, cont), INT_FIELD, "null"); + + cont.checked.intValue = INT_VALUE; + assertTrue(validator.validateTop(TOP, cont).isValid()); + } + + @Test + public void testVerCollection() { + @Getter + class Container { + List<@Min(5) Integer> items; + + // not a collection - should not be checked + @Valid + String strValue; + + String noAnnotations; + } + + Container cont = new Container(); + cont.strValue = STRING_VALUE; + cont.noAnnotations = STRING_VALUE; + + // null collection - always valid + assertTrue(validator.validateTop(TOP, cont).isValid()); + + // empty collection - always valid + cont.items = List.of(); + assertTrue(validator.validateTop(TOP, cont).isValid()); + + cont.items = List.of(-10, -20); + assertThat(validator.validateTop(TOP, cont).getResult()).contains("\"0\"", "-10", "\"1\"", "-20", "minimum"); + + cont.items = List.of(10, -30); + assertThat(validator.validateTop(TOP, cont).getResult()).contains("\"1\"", "-30", "minimum") + .doesNotContain("\"0\""); + + cont.items = List.of(10, 20); + assertTrue(validator.validateTop(TOP, cont).isValid()); + } + + @Test + public void testVerMap() { + @Getter + class Container { + Map<String, @Min(5) Integer> items; + + // not a map + @NotBlank + String strValue; + + String noAnnotations; + } + + Container cont = new Container(); + cont.strValue = STRING_VALUE; + cont.noAnnotations = STRING_VALUE; + + // null map - always valid + assertTrue(validator.validateTop(TOP, cont).isValid()); + + // empty map - always valid + cont.items = Map.of(); + assertTrue(validator.validateTop(TOP, cont).isValid()); + + cont.items = Map.of("abc", -10, "def", -20); + assertThat(validator.validateTop(TOP, cont).getResult()).contains("abc", "-10", "def", "-20", "minimum"); + + cont.items = Map.of("abc", 10, "def", -30); + assertThat(validator.validateTop(TOP, cont).getResult()).contains("def", "-30", "minimum") + .doesNotContain("abc"); + + cont.items = Map.of("abc", 10, "def", 20); + assertTrue(validator.validateTop(TOP, cont).isValid()); + } + + @Test + public void testGetEntryName() { + assertThat(validator.getEntryName(makeEntry(null, 0))).isEmpty(); + assertThat(validator.getEntryName(makeEntry("", 0))).isEmpty(); + assertThat(validator.getEntryName(makeEntry(STRING_VALUE, 0))).isEqualTo(STRING_VALUE); + } + + /** + * Makes a Map entry with the given key and value. + * + * @param key desired key + * @param value desired value + * @return a new Map entry + */ + private Map.Entry<String, Integer> makeEntry(String key, int value) { + HashMap<String, Integer> map = new HashMap<>(); + map.put(key, value); + return map.entrySet().iterator().next(); + } + + private <T> void assertNumeric(String testName, T object, Consumer<Integer> setter, String fieldName, + String expectedText, int inside, int edge, int outside) { + setter.accept(inside); + assertTrue(validator.validateTop(TOP, object).isValid()); + + // on the edge + setter.accept(edge); + assertTrue(validator.validateTop(TOP, object).isValid()); + + // invalid + setter.accept(outside); + assertInvalid("testVerNotNull", validator.validateTop(TOP, object), fieldName, expectedText); + } + + + private void assertInvalid(String testName, BeanValidationResult result, String... text) { + assertThat(result.getResult()).describedAs(testName).contains(text); + } +} |