View Javadoc
1   package com.opencsv.validators;
2   
3   import com.opencsv.exceptions.CsvValidationException;
4   import org.junit.jupiter.api.Assertions;
5   import org.junit.jupiter.api.BeforeEach;
6   import org.junit.jupiter.api.DisplayName;
7   import org.junit.jupiter.api.Test;
8   import org.junit.jupiter.params.ParameterizedTest;
9   import org.junit.jupiter.params.provider.Arguments;
10  import org.junit.jupiter.params.provider.MethodSource;
11  
12  import java.util.ArrayList;
13  import java.util.List;
14  import java.util.function.Function;
15  import java.util.stream.Stream;
16  
17  import static org.junit.jupiter.api.Assertions.*;
18  import static org.mockito.Mockito.*;
19  
20  public class RowValidatorAggregatorTest {
21      private static final String[] GOOD_ROW = {"8675309", "test@email.name"};
22      private static final String[] ANOTHER_GOOD_ROW = {"1234567", "full.name@sourceforge.net"};
23      private static final String[] BAD_ROW = {"not a number", "not.an.email"};
24      private static final String[] LONG_ROW = {"2345678", "anemail@test.com", "extra column"};
25      private static final String[] SHORT_ROW = {"7654321"};
26      private static final String[] EMPTY_ROW = {};
27  
28      private RowValidatorAggregator aggregator;
29  
30      private RowMustHaveSameNumberOfColumnsAsFirstRowValidator columnCountValidator;
31  
32      private static final String FIRST_COLUMN_FAILURE_MESSAGE = "The first element of the row must be a seven digit number!";
33  
34      private static final Function<String[], Boolean> FIRST_ELEMENT_IS_A_NUMBER =
35              (x) -> x.length > 0 && x[0].matches("^[0-9]{7}$");
36      private static final RowValidator FIRST_ELEMENT_VALIDATOR = new RowFunctionValidator(FIRST_ELEMENT_IS_A_NUMBER, FIRST_COLUMN_FAILURE_MESSAGE);
37  
38      private static final String SECOND_COLUMN_FAILURE_MESSAGE = "The second element of the row must be an email address!";
39  
40      private static final Function<String[], Boolean> SECOND_ELEMENT_IS_AN_EMAIL_ADDRESS =
41              (x) -> x.length > 1 && x[1].matches("^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,5})$");
42      private static final RowValidator SECOND_ELEMENT_VALIDATOR = new RowFunctionValidator(SECOND_ELEMENT_IS_AN_EMAIL_ADDRESS, SECOND_COLUMN_FAILURE_MESSAGE);
43  
44  
45      @BeforeEach
46      public void setup() {
47          aggregator = new RowValidatorAggregator();
48  
49          columnCountValidator = new RowMustHaveSameNumberOfColumnsAsFirstRowValidator();
50      }
51  
52      // A Note on the Arguments of the test.  Because these variables are
53      // recreated on each run each run has a new validator.  That is why the
54      // LONG_ROW has true for the column count validator result
55      // because for each arguement it IS the first row.  The SHORT_ROW fails
56      // because there is not a second row to validate which is part of the validation regex.
57      private static Stream<Arguments> createIsValidArguments() {
58          return Stream.of(
59                  Arguments.of(GOOD_ROW, true),
60                  Arguments.of(null, false),
61                  Arguments.of(ANOTHER_GOOD_ROW, true),
62                  Arguments.of(EMPTY_ROW, false),
63                  Arguments.of(LONG_ROW, true),
64                  Arguments.of(SHORT_ROW, false),
65                  Arguments.of(BAD_ROW, false),
66                  Arguments.of(GOOD_ROW, true)
67          );
68      }
69  
70      @DisplayName("RowValidatorAggregator isValid")
71      @ParameterizedTest
72      @MethodSource("createIsValidArguments")
73      public void lineIsValid(String[] row, boolean valid) {
74          aggregator.addValidator(null);
75          aggregator.addValidator(columnCountValidator);
76          aggregator.addValidator(FIRST_ELEMENT_VALIDATOR);
77          aggregator.addValidator(SECOND_ELEMENT_VALIDATOR);
78  
79          assertEquals(valid, aggregator.isValid(row));
80      }
81  
82  
83      // A Note on the Arguments of the test.  Because these variables are
84      // recreated on each run each run has a new validator.  That is why the
85      // LONG_ROW and SHORT_ROW have true for the column count validator result
86      // because for each arguement it IS the first row.
87      private static Stream<Arguments> createValidateArguments() {
88          return Stream.of(
89                  Arguments.of(GOOD_ROW, true, true, true),
90                  Arguments.of(null, false, false, false),
91                  Arguments.of(ANOTHER_GOOD_ROW, true, true, true),
92                  Arguments.of(EMPTY_ROW, false, false, false),
93                  Arguments.of(LONG_ROW, true, true, true),
94                  Arguments.of(SHORT_ROW, true, true, false),
95                  Arguments.of(BAD_ROW, true, false, false),
96                  Arguments.of(GOOD_ROW, true, true, true)
97          );
98      }
99  
100     @DisplayName("RowValidatorAggregator validate")
101     @ParameterizedTest
102     @MethodSource("createValidateArguments")
103     public void lineValidate(String[] row, boolean columnCountValid, boolean firstElementValid, boolean secondElementValid) {
104         aggregator.addValidator(null);
105         aggregator.addValidator(columnCountValidator);
106         aggregator.addValidator(FIRST_ELEMENT_VALIDATOR);
107         aggregator.addValidator(SECOND_ELEMENT_VALIDATOR);
108 
109         boolean allValid = columnCountValid && firstElementValid && secondElementValid;
110 
111         try {
112             aggregator.validate(row);
113             if (!allValid) {
114                 fail("All validators passed but at least one should have failed!");
115             }
116         } catch (CsvValidationException cve) {
117             if (allValid) {
118                 fail("Validator should have passed");
119             }
120 
121             String exceptionMessage = cve.getMessage();
122 
123             Assertions.assertAll("Exception message is incorrect",
124                     () -> assertEquals(!columnCountValid, exceptionMessageContainsColumnCountMessage(exceptionMessage), "Supposed to have column count message."),
125                     () -> assertEquals(!firstElementValid, exceptionMessage.contains(FIRST_COLUMN_FAILURE_MESSAGE), "Exception message did not mention first column error."),
126                     () -> assertEquals(!secondElementValid, exceptionMessage.contains(SECOND_COLUMN_FAILURE_MESSAGE), "Exception message did not mention second column error."));
127         }
128     }
129 
130     private boolean exceptionMessageContainsColumnCountMessage(String exceptionMessage) {
131         return exceptionMessage.contains("First row should not be empty or null")
132                 || exceptionMessage.contains("Row should not be empty or null")
133                 || exceptionMessage.contains("Row was expected to have 2 elements but had ");
134     }
135 
136     @DisplayName("Short circuit if there are no validators present.")
137     @Test
138     public void shortCircuitIfNoValidators() throws CsvValidationException {
139         List<RowValidator> spyList = spy(new ArrayList<>());
140 
141         aggregator.setValidators(spyList);
142 
143         aggregator.validate(GOOD_ROW);
144 
145         verify(spyList).isEmpty();
146         verifyNoMoreInteractions(spyList);
147     }
148 }