package com.github.mygreen.supercsv.cellprocessor.constraint;
import static org.junit.Assert.*;
import static org.assertj.core.api.Assertions.*;
import static com.github.mygreen.supercsv.tool.TestUtils.*;
import static com.github.mygreen.supercsv.tool.HasCellProcessorAssert.*;
import java.lang.annotation.Annotation;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.supercsv.cellprocessor.ift.CellProcessor;
import com.github.mygreen.supercsv.annotation.CsvBean;
import com.github.mygreen.supercsv.annotation.CsvColumn;
import com.github.mygreen.supercsv.annotation.constraint.CsvDateTimeRange;
import com.github.mygreen.supercsv.annotation.format.CsvDateTimeFormat;
import com.github.mygreen.supercsv.builder.ProcessorBuilderResolver;
import com.github.mygreen.supercsv.builder.BeanMapping;
import com.github.mygreen.supercsv.builder.BeanMappingFactory;
import com.github.mygreen.supercsv.builder.BuildCase;
import com.github.mygreen.supercsv.builder.ColumnMapping;
import com.github.mygreen.supercsv.builder.Configuration;
import com.github.mygreen.supercsv.builder.FieldAccessor;
import com.github.mygreen.supercsv.builder.standard.DateProcessorBuilder;
import com.github.mygreen.supercsv.cellprocessor.format.TextFormatter;
import com.github.mygreen.supercsv.exception.SuperCsvInvalidAnnotationException;
import com.github.mygreen.supercsv.exception.SuperCsvValidationException;
import com.github.mygreen.supercsv.validation.CsvExceptionConverter;
/**
* {@link DateTimeRangeFactory}のテスタ
*
* @since 2.0
* @author T.TSUCHIE
*
*/
public class DateTimeRangeFactoryTest {
@Rule
public TestName name = new TestName();
private DateTimeRangeFactory<Date> factory;
private Configuration config;
private Comparator<Annotation> comparator;
private ProcessorBuilderResolver builderResolver;
private BeanMappingFactory beanMappingFactory;
private CsvExceptionConverter exceptionConverter;
private final Class<?>[] groupEmpty = new Class[]{};
@Before
public void setUp() throws Exception {
this.factory = new DateTimeRangeFactory<Date>();
this.config = new Configuration();
this.beanMappingFactory = new BeanMappingFactory();
beanMappingFactory.setConfiguration(config);
this.exceptionConverter = new CsvExceptionConverter();
this.comparator = config.getAnnoationComparator();
this.builderResolver = config.getBuilderResolver();
}
private static final Date TEST_VALUE_MIN_OBJ = toDate(2000, 1, 1);
private static final String TEST_VALUE_MIN_FORMATTED = "2000/01/01";
private static final Date TEST_VALUE_MAX_OBJ = toDate(2000, 1, 5);
private static final String TEST_VALUE_MAX_FORMATTED = "2000/01/05";
@CsvBean
private static class TestCsv {
@CsvColumn(number=1, label="カラム1")
@CsvDateTimeFormat(pattern="yyyy/MM/dd")
@CsvDateTimeRange(min=TEST_VALUE_MIN_FORMATTED, max=TEST_VALUE_MAX_FORMATTED)
private Date col_default;
@CsvColumn(number=2)
@CsvDateTimeFormat(pattern="yyyy/MM/dd")
@CsvDateTimeRange(min=TEST_VALUE_MIN_FORMATTED, max=TEST_VALUE_MAX_FORMATTED, inclusive=false)
private Date col_inclusive_false;
@CsvColumn(number=10)
@CsvDateTimeFormat(pattern="yyyy/MM/dd")
@CsvDateTimeRange(min=TEST_VALUE_MIN_FORMATTED, max=TEST_VALUE_MAX_FORMATTED, message="テストメッセージ")
private Date col_message;
@CsvColumn(number=11)
@CsvDateTimeFormat(pattern="yyyy/MM/dd")
@CsvDateTimeRange(min=TEST_VALUE_MIN_FORMATTED, max=TEST_VALUE_MAX_FORMATTED, message="")
private Date col_message_empty;
@CsvColumn(number=12)
@CsvDateTimeFormat(pattern="yyyy/MM/dd")
@CsvDateTimeRange(min=TEST_VALUE_MIN_FORMATTED, max=TEST_VALUE_MAX_FORMATTED,
message="lineNumber={lineNumber}, rowNumber={rowNumber}, columnNumber={columnNumber}, label={label}, validatedValue=${printer.print(validatedValue)}, min=${printer.print(min)}, max=${printer.print(max)}, inclusive={inclusive}")
private Date col_message_variables;
}
@CsvBean
private static class ErrorCsv {
@CsvColumn(number=1, label="カラム1")
@CsvDateTimeFormat(pattern="yyyy/MM/dd")
@CsvDateTimeRange(min="aaaa", max=TEST_VALUE_MAX_FORMATTED)
private Date col_min_wrong;
@CsvColumn(number=2, label="カラム2")
@CsvDateTimeFormat(pattern="yyyy/MM/dd")
@CsvDateTimeRange(min=TEST_VALUE_MIN_FORMATTED, max="aaaa")
private Date col_max_wrong;
@CsvColumn(number=3, label="カラム3")
@CsvDateTimeFormat(pattern="yyyy/MM/dd")
@CsvDateTimeRange(min=TEST_VALUE_MAX_FORMATTED, max=TEST_VALUE_MIN_FORMATTED)
private Date col_min_max_wrong;
}
@Test
public void testCreate_default() {
FieldAccessor field = getFieldAccessor(TestCsv.class, "col_default", comparator);
DateProcessorBuilder builder = (DateProcessorBuilder) builderResolver.resolve(Date.class);
TextFormatter<Date> formatter = builder.getFormatter(field, config);
CsvDateTimeRange anno = field.getAnnotationsByGroup(CsvDateTimeRange.class, groupEmpty).get(0);
{
//next null
Optional<CellProcessor> processor = factory.create(anno, Optional.empty(), field, formatter, config);
printCellProcessorChain(processor.get(), name.getMethodName());
assertThat(processor.get()).isInstanceOf(DateTimeRange.class);
DateTimeRange<Date> actual = (DateTimeRange<Date>)processor.get();
assertThat(actual.getMin()).isEqualTo(TEST_VALUE_MIN_OBJ);
assertThat(actual.getMax()).isEqualTo(TEST_VALUE_MAX_OBJ);
assertThat(actual.isInclusive()).isEqualTo(true);
assertThat(actual.getPrinter()).isEqualTo(formatter);
{
// valid input
Date input = TEST_VALUE_MIN_OBJ;
assertThat((Object)actual.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(input);
}
{
// wrong input
Date input = minusDays(TEST_VALUE_MIN_OBJ, 1);
assertThatThrownBy(() -> actual.execute(input, ANONYMOUS_CSVCONTEXT)).isInstanceOf(SuperCsvValidationException.class);
}
assertThat(actual.getValidationMessage()).isEqualTo("{com.github.mygreen.supercsv.annotation.constraint.CsvDateTimeRange.message}");
}
{
//next exist
Optional<CellProcessor> processor = factory.create(anno, Optional.of(new NextCellProcessor()), field, formatter, config);
printCellProcessorChain(processor.get(), name.getMethodName());
assertThat(processor.get()).isInstanceOf(DateTimeRange.class);
DateTimeRange<Date> actual = (DateTimeRange<Date>)processor.get();
assertThat(actual.getMin()).isEqualTo(TEST_VALUE_MIN_OBJ);
assertThat(actual.getMax()).isEqualTo(TEST_VALUE_MAX_OBJ);
assertThat(actual.isInclusive()).isEqualTo(true);
assertThat(actual.getPrinter()).isEqualTo(formatter);
{
// valid input
Date input = TEST_VALUE_MIN_OBJ;
assertThat((Object)actual.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(input);
}
{
// wrong input
Date input = minusDays(TEST_VALUE_MIN_OBJ, 1);
assertThatThrownBy(() -> actual.execute(input, ANONYMOUS_CSVCONTEXT)).isInstanceOf(SuperCsvValidationException.class);
}
assertThat(actual.getValidationMessage()).isEqualTo("{com.github.mygreen.supercsv.annotation.constraint.CsvDateTimeRange.message}");
}
}
/**
* 属性 inclusive = false の場合
*/
@Test
public void testCreate_attrInclusive_false() {
FieldAccessor field = getFieldAccessor(TestCsv.class, "col_inclusive_false", comparator);
DateProcessorBuilder builder = (DateProcessorBuilder) builderResolver.resolve(Date.class);
TextFormatter<Date> formatter = builder.getFormatter(field, config);
CsvDateTimeRange anno = field.getAnnotationsByGroup(CsvDateTimeRange.class, groupEmpty).get(0);
//next null
Optional<CellProcessor> processor = factory.create(anno, Optional.empty(), field, formatter, config);
printCellProcessorChain(processor.get(), name.getMethodName());
assertThat(processor.get()).isInstanceOf(DateTimeRange.class);
DateTimeRange<Date> actual = (DateTimeRange<Date>)processor.get();
assertThat(actual.getMin()).isEqualTo(TEST_VALUE_MIN_OBJ);
assertThat(actual.getMax()).isEqualTo(TEST_VALUE_MAX_OBJ);
assertThat(actual.isInclusive()).isEqualTo(false);
assertThat(actual.getPrinter()).isEqualTo(formatter);
{
// valid input
Date input = minusDays(TEST_VALUE_MIN_OBJ, 1);
assertThatThrownBy(() -> actual.execute(input, ANONYMOUS_CSVCONTEXT)).isInstanceOf(SuperCsvValidationException.class);
}
{
// wrong input
Date input = TEST_VALUE_MIN_OBJ;
assertThatThrownBy(() -> actual.execute(input, ANONYMOUS_CSVCONTEXT)).isInstanceOf(SuperCsvValidationException.class);
}
{
// wrong input
Date input = plusDays(TEST_VALUE_MIN_OBJ, 1);
assertThat((Object)actual.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(input);
}
{
// valid input
Date input = plusDays(TEST_VALUE_MAX_OBJ, 1);
assertThatThrownBy(() -> actual.execute(input, ANONYMOUS_CSVCONTEXT)).isInstanceOf(SuperCsvValidationException.class);
}
{
// wrong input
Date input = TEST_VALUE_MAX_OBJ;
assertThatThrownBy(() -> actual.execute(input, ANONYMOUS_CSVCONTEXT)).isInstanceOf(SuperCsvValidationException.class);
}
{
// wrong input
Date input = minusDays(TEST_VALUE_MAX_OBJ, 1);
assertThat((Object)actual.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(input);
}
}
/**
* 属性messageのテスト
*/
@Test
public void testCreate_attrMessage() {
FieldAccessor field = getFieldAccessor(TestCsv.class, "col_message", comparator);
DateProcessorBuilder builder = (DateProcessorBuilder) builderResolver.resolve(Date.class);
TextFormatter<Date> formatter = builder.getFormatter(field, config);
CsvDateTimeRange anno = field.getAnnotationsByGroup(CsvDateTimeRange.class, groupEmpty).get(0);
Optional<CellProcessor> processor = factory.create(anno, Optional.empty(), field, formatter, config);
printCellProcessorChain(processor.get(), name.getMethodName());
assertThat(processor.get()).isInstanceOf(DateTimeRange.class);
DateTimeRange<Date> actual = (DateTimeRange<Date>)processor.get();
assertThat(actual.getValidationMessage()).isEqualTo("テストメッセージ");
}
/**
* 属性minの書式が不正な場合
*/
@Test
public void testCreate_attrMin_wrong() {
FieldAccessor field = getFieldAccessor(ErrorCsv.class, "col_min_wrong", comparator);
DateProcessorBuilder builder = (DateProcessorBuilder) builderResolver.resolve(Date.class);
TextFormatter<Date> formatter = builder.getFormatter(field, config);
CsvDateTimeRange anno = field.getAnnotationsByGroup(CsvDateTimeRange.class, groupEmpty).get(0);
try {
factory.create(anno, Optional.empty(), field, formatter, config);
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvInvalidAnnotationException.class)
.hasMessage("'%s' において、アノテーション @CsvDateTimeRange の属性 'min' の値(aaaa)は、java.util.Date (yyyy/MM/dd)として不正です。",
field.getNameWithClass());
}
}
/**
* 属性maxの書式が不正な場合
*/
@Test
public void testCreate_attrMax_wrong() {
FieldAccessor field = getFieldAccessor(ErrorCsv.class, "col_max_wrong", comparator);
DateProcessorBuilder builder = (DateProcessorBuilder) builderResolver.resolve(Date.class);
TextFormatter<Date> formatter = builder.getFormatter(field, config);
CsvDateTimeRange anno = field.getAnnotationsByGroup(CsvDateTimeRange.class, groupEmpty).get(0);
try {
factory.create(anno, Optional.empty(), field, formatter, config);
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvInvalidAnnotationException.class)
.hasMessage("'%s' において、アノテーション @CsvDateTimeRange の属性 'max' の値(aaaa)は、java.util.Date (yyyy/MM/dd)として不正です。",
field.getNameWithClass());
}
}
/**
* 属性minの値がmaxよりも大きい場合
*/
@Test
public void testCreate_attrMinMax_wrong() {
FieldAccessor field = getFieldAccessor(ErrorCsv.class, "col_min_max_wrong", comparator);
DateProcessorBuilder builder = (DateProcessorBuilder) builderResolver.resolve(Date.class);
TextFormatter<Date> formatter = builder.getFormatter(field, config);
CsvDateTimeRange anno = field.getAnnotationsByGroup(CsvDateTimeRange.class, groupEmpty).get(0);
try {
factory.create(anno, Optional.empty(), field, formatter, config);
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvInvalidAnnotationException.class)
.hasMessage("'%s' において、アノテーション @CsvDateTimeRange の属性 'min' の値(2000/01/05)は、属性 'max' の値(2000/01/01)より以前の値で設定してください。",
field.getNameWithClass());
}
}
/**
* エラーメッセージのテスト - 標準
*/
@Test
public void testErrorMessage_default() {
BeanMapping<TestCsv> beanMapping = beanMappingFactory.create(TestCsv.class, groupEmpty);
ColumnMapping columnMapping = beanMapping.getColumnMapping("col_default").get();
CellProcessor processor = columnMapping.getCellProcessorForReading();
printCellProcessorChain(processor, name.getMethodName());
assertThat(processor).hasCellProcessor(DateTimeRange.class);
String input = "1999/12/31";
try {
processor.execute(input, testCsvContext(columnMapping, input));
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvValidationException.class);
List<String> messages = exceptionConverter.convertAndFormat((SuperCsvValidationException)e, beanMapping);
assertThat(messages).hasSize(1)
.contains("[2行, 1列] : 項目「カラム1」の値(1999/12/31)は、2000/01/01~2000/01/05 の期間内でなければなりません。");
}
}
/**
* エラーメッセージのテスト - 属性inclusive=falseの場合
*/
@Test
public void testErrorMessage_attrInclusive_false() {
BeanMapping<TestCsv> beanMapping = beanMappingFactory.create(TestCsv.class, groupEmpty);
ColumnMapping columnMapping = beanMapping.getColumnMapping("col_inclusive_false").get();
CellProcessor processor = columnMapping.getCellProcessorForReading();
printCellProcessorChain(processor, name.getMethodName());
assertThat(processor).hasCellProcessor(DateTimeRange.class);
String input = "1999/12/31";
try {
processor.execute(input, testCsvContext(columnMapping, input));
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvValidationException.class);
List<String> messages = exceptionConverter.convertAndFormat((SuperCsvValidationException)e, beanMapping);
assertThat(messages).hasSize(1)
.contains("[2行, 2列] : 項目「col_inclusive_false」の値(1999/12/31)は、2000/01/01~2000/01/05 の期間内でなければなりません。");
}
}
/**
* エラーメッセージのテスト - アノテーションの属性「message」の指定
*/
@Test
public void testErrorMessage_message() {
BeanMapping<TestCsv> beanMapping = beanMappingFactory.create(TestCsv.class, groupEmpty);
ColumnMapping columnMapping = beanMapping.getColumnMapping("col_message").get();
CellProcessor processor = columnMapping.getCellProcessorForReading();
printCellProcessorChain(processor, name.getMethodName());
assertThat(processor).hasCellProcessor(DateTimeRange.class);
String input = "1999/12/31";
try {
processor.execute(input, testCsvContext(columnMapping, input));
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvValidationException.class);
List<String> messages = exceptionConverter.convertAndFormat((SuperCsvValidationException)e, beanMapping);
assertThat(messages).hasSize(1)
.contains("テストメッセージ");
}
}
/**
* エラーメッセージのテスト - アノテーションの属性「message」が空文字の場合
*/
@Test
public void testErrorMessage_empty() {
BeanMapping<TestCsv> beanMapping = beanMappingFactory.create(TestCsv.class, groupEmpty);
ColumnMapping columnMapping = beanMapping.getColumnMapping("col_message_empty").get();
CellProcessor processor = columnMapping.getCellProcessorForReading();
printCellProcessorChain(processor, name.getMethodName());
assertThat(processor).hasCellProcessor(DateTimeRange.class);
String input = "1999/12/31";
try {
processor.execute(input, testCsvContext(columnMapping, input));
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvValidationException.class);
List<String> messages = exceptionConverter.convertAndFormat((SuperCsvValidationException)e, beanMapping);
assertThat(messages).hasSize(1)
.contains("[2行, 11列] : 項目「col_message_empty」の値(1999/12/31)は、2000/01/01~2000/01/05 の期間内でなければなりません。");
}
}
/**
* エラーメッセージのテスト - メッセージ変数の確認
*/
@Test
public void testErrorMessage_variables() {
BeanMapping<TestCsv> beanMapping = beanMappingFactory.create(TestCsv.class, groupEmpty);
ColumnMapping columnMapping = beanMapping.getColumnMapping("col_message_variables").get();
CellProcessor processor = columnMapping.getCellProcessorForReading();
printCellProcessorChain(processor, name.getMethodName());
assertThat(processor).hasCellProcessor(DateTimeRange.class);
String input = "1999/12/31";
try {
processor.execute(input, testCsvContext(columnMapping, input));
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvValidationException.class);
List<String> messages = exceptionConverter.convertAndFormat((SuperCsvValidationException)e, beanMapping);
assertThat(messages).hasSize(1)
.contains("lineNumber=1, rowNumber=2, columnNumber=12, label=col_message_variables, validatedValue=1999/12/31, min=2000/01/01, max=2000/01/05, inclusive=true");
}
}
}