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
53
54
55
56
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
84
85
86
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 }