package com.github.mygreen.supercsv.validation;
import static org.junit.Assert.*;
import static org.assertj.core.api.Assertions.*;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import org.junit.Before;
import org.junit.Test;
import org.supercsv.prefs.CsvPreference;
import com.github.mygreen.supercsv.annotation.CsvBean;
import com.github.mygreen.supercsv.annotation.CsvColumn;
import com.github.mygreen.supercsv.annotation.constraint.CsvNumberMin;
import com.github.mygreen.supercsv.annotation.constraint.CsvRequire;
import com.github.mygreen.supercsv.annotation.conversion.CsvTrim;
import com.github.mygreen.supercsv.annotation.format.CsvNumberFormat;
import com.github.mygreen.supercsv.builder.BeanMapping;
import com.github.mygreen.supercsv.builder.BeanMappingFactory;
import com.github.mygreen.supercsv.io.CsvAnnotationBeanReader;
import com.github.mygreen.supercsv.io.CsvAnnotationBeanWriter;
import com.github.mygreen.supercsv.localization.MessageResolver;
import com.github.mygreen.supercsv.localization.ResourceBundleMessageResolver;
/**
* {@link CsvValidator}のテスタ
*
* @since 2.0
* @author T.TSUCHIE
*
*/
public class CsvValidatorTest {
private BeanMappingFactory beanMappingFactory;
private CsvExceptionConverter exceptionConverter;
private MessageResolver testMessageResolver = new ResourceBundleMessageResolver(ResourceBundle.getBundle("TestMessages"));
@Before
public void setUp() throws Exception {
this.beanMappingFactory = new BeanMappingFactory();
this.exceptionConverter = new CsvExceptionConverter();
}
@CsvBean(header=true, validators=TestValidator.class)
public static class TestCsv {
@CsvColumn(number=1, label="名前")
@CsvRequire(considerBlank=true)
@CsvTrim
private String name;
@CsvColumn(number=2, label="年齢")
@CsvTrim
private Integer age;
@CsvColumn(number=3, label="給料")
@CsvTrim
@CsvNumberFormat(pattern="###,##0")
@CsvNumberMin("0")
private Integer salary;
public TestCsv() {
}
public TestCsv(final String name, final Integer age, final Integer salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getSalary() {
return salary;
}
public void setSalary(Integer salary) {
this.salary = salary;
}
}
public static class TestValidator implements CsvValidator<TestCsv> {
@Override
public void validate(final TestCsv record, final CsvBindingErrors bindingErrors,
final ValidationContext<TestCsv> validationContext) {
final CsvField<Integer> ageField = new CsvField<>(validationContext, record, "age");
final CsvField<Integer> salaryField = new CsvField<>(validationContext, record, "salary");
salaryField
.add(new CsvFieldValidator<Integer>() {
@Override
public void validate(final CsvBindingErrors bindingErrors, final CsvField<Integer> field) {
if(ageField.isEmpty()) {
return;
}
// カラム「age(年齢)」が20以上の場合、カラム「給料(salary)」が設定されているかチェックする。
if(ageField.isNotEmpty() && ageField.getValue() >= 20 && field.isEmpty()) {
// メッセージ中の変数の作成
final Map<String, Object> vars = createMessageVariables(field);
vars.put("maxAge", 20);
// ageに関するフィールドエラーの追加
bindingErrors.rejectValue(field.getName(), field.getType(), "age.required", vars);
}
}
})
.add(new MaxValidator(10_000_000))
.validate(bindingErrors);
}
}
public static class MaxValidator implements CsvFieldValidator<Integer> {
private final int max;
public MaxValidator(final int max) {
this.max = max;
}
@Override
public void validate(CsvBindingErrors bindingErrors, CsvField<Integer> field) {
if(field.isEmpty()) {
return;
}
if(field.getValue() > max) {
// メッセージ変数の組み立て
Map<String, Object> vars = createMessageVariables(field);
vars.put("max", max);
bindingErrors.rejectValue(field.getName(), field.getType(), "fieldError.max", vars);
}
}
}
@Test
public void testValidate_read() throws Exception {
String csv = "名前,年齢,給料"
+ "\r\n阿部真一, 10, "
+ "\r\n山田太郎, 20, \"200,000\""
+ "\r\n鈴木次郎, 30, " // 給料がnull
+ "\r\n林花子, 40, \"20,000,000\"" // 給料が上限値超え
+ "\r\n本田一郎, 50, abc" // 給料が数値
;
CsvAnnotationBeanReader<TestCsv> csvReader = new CsvAnnotationBeanReader<>(TestCsv.class, new StringReader(csv), CsvPreference.STANDARD_PREFERENCE);
exceptionConverter.setMessageResolver(testMessageResolver);
csvReader.setExceptionConverter(exceptionConverter);
List<TestCsv> list = csvReader.readAll(true);
csvReader.close();
assertThat(list).hasSize(2);
assertThat(list.get(0)).hasFieldOrPropertyWithValue("name", "阿部真一");
assertThat(list.get(1)).hasFieldOrPropertyWithValue("name", "山田太郎");
List<String> errorMessages = csvReader.getErrorMessages();
csvReader.getErrorMessages().stream().forEach(System.out::println);
assertThat(errorMessages).hasSize(3).containsExactly(
"[4行, 3列] : 項目「給料」は、年齢が20歳以上の場合には必須です。",
"[5行, 3列] : 項目「給料」の値(20,000,000)は、10,000,000以内で入力してください。",
"[6行, 3列] : 項目「給料」の値(abc)は、整数の書式「#,##0」として不正です。"
);
}
/**
* 書き込み時の入力値検証 - 検証のスキップ
*/
@Test
public void testValidate_write_skipValidate() throws Exception {
StringWriter writer = new StringWriter();
beanMappingFactory.getConfiguration().setSkipValidationOnWrite(true);
BeanMapping<TestCsv> beanMapping = beanMappingFactory.create(TestCsv.class);
CsvAnnotationBeanWriter<TestCsv> csvWriter = new CsvAnnotationBeanWriter<>(beanMapping, writer, CsvPreference.STANDARD_PREFERENCE);
exceptionConverter.setMessageResolver(testMessageResolver);
csvWriter.setExceptionConverter(exceptionConverter);
List<TestCsv> list = new ArrayList<>();
list.add(new TestCsv("阿部真一", 10, null));
list.add(new TestCsv("山田太郎", 20, 200_000));
list.add(new TestCsv("鈴木次郎", 30, null)); // 給料がnull
list.add(new TestCsv("林花子", 40, 20_000_000));
list.add(new TestCsv("橋本誠", 60, 400_000)); // 給料が上限値超え
csvWriter.writeAll(list, true);
csvWriter.flush();
csvWriter.close();
List<String> errorMessages = csvWriter.getErrorMessages();
assertThat(errorMessages).isEmpty();
String csv = writer.toString();
System.out.println(csv);
String expected = "名前,年齢,給料"
+ "\r\n阿部真一,10,"
+ "\r\n山田太郎,20,\"200,000\""
+ "\r\n鈴木次郎,30," // 給料がnull
+ "\r\n林花子,40,\"20,000,000\"" // 給料が上限値超え
+ "\r\n橋本誠,60,\"400,000\""
+ "\r\n"
;
assertThat(csv).isEqualTo(expected);
}
/**
* 書き込み時の入力値検証 - 検証の実行
*/
@Test
public void testValidate_write_validate() throws Exception {
StringWriter writer = new StringWriter();
CsvAnnotationBeanWriter<TestCsv> csvWriter = new CsvAnnotationBeanWriter<>(TestCsv.class, writer, CsvPreference.STANDARD_PREFERENCE);
exceptionConverter.setMessageResolver(testMessageResolver);
csvWriter.setExceptionConverter(exceptionConverter);
List<TestCsv> list = new ArrayList<>();
list.add(new TestCsv("阿部真一", 10, null));
list.add(new TestCsv("山田太郎", 20, 200_000));
list.add(new TestCsv("鈴木次郎", 30, null)); // 給料がnull
list.add(new TestCsv("林花子", 40, 20_000_000));
list.add(new TestCsv("橋本誠", 60, 400_000)); // 給料が上限値超え
csvWriter.writeAll(list, true);
csvWriter.flush();
csvWriter.close();
List<String> errorMessages = csvWriter.getErrorMessages();
assertThat(errorMessages).hasSize(2).containsExactly(
"[4行, 3列] : 項目「給料」は、年齢が20歳以上の場合には必須です。",
"[5行, 3列] : 項目「給料」の値(20,000,000)は、10,000,000以内で入力してください。"
);
String csv = writer.toString();
System.out.println(csv);
String expected = "名前,年齢,給料"
+ "\r\n阿部真一,10,"
+ "\r\n山田太郎,20,\"200,000\""
+ "\r\n橋本誠,60,\"400,000\""
+ "\r\n"
;
assertThat(csv).isEqualTo(expected);
}
}