package org.synyx.urlaubsverwaltung.web.overtime;
import org.joda.time.DateMidnight;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.util.ReflectionUtils;
import org.springframework.validation.Errors;
import org.synyx.urlaubsverwaltung.core.overtime.Overtime;
import org.synyx.urlaubsverwaltung.core.overtime.OvertimeService;
import org.synyx.urlaubsverwaltung.core.person.Person;
import org.synyx.urlaubsverwaltung.core.settings.Settings;
import org.synyx.urlaubsverwaltung.core.settings.SettingsService;
import org.synyx.urlaubsverwaltung.test.TestDataCreator;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.Optional;
import java.util.function.Consumer;
/**
* @author Aljona Murygina - murygina@synyx.de
*/
public class OvertimeValidatorTest {
private OvertimeValidator validator;
private OvertimeForm overtimeForm;
private Settings settings;
private Errors errors;
private OvertimeService overtimeServiceMock;
private SettingsService settingsServiceMock;
@Before
public void setUp() {
overtimeServiceMock = Mockito.mock(OvertimeService.class);
settingsServiceMock = Mockito.mock(SettingsService.class);
validator = new OvertimeValidator(overtimeServiceMock, settingsServiceMock);
errors = Mockito.mock(Errors.class);
Overtime overtimeRecord = TestDataCreator.createOvertimeRecord();
overtimeForm = new OvertimeForm(overtimeRecord);
settings = new Settings();
settings.getWorkingTimeSettings().setOvertimeActive(true);
Mockito.when(settingsServiceMock.getSettings()).thenReturn(settings);
Mockito.when(overtimeServiceMock.getLeftOvertimeForPerson(Mockito.any(Person.class)))
.thenReturn(BigDecimal.ZERO);
}
// Support method --------------------------------------------------------------------------------------------------
@Test
public void ensureSupportsOvertimeFormClass() {
Assert.assertTrue("Should support overtime form class", validator.supports(OvertimeForm.class));
}
@Test
public void ensureDoesNotSupportOtherClass() {
Assert.assertFalse("Should not support other class than overtime form class", validator.supports(Person.class));
}
// Validate method -------------------------------------------------------------------------------------------------
@Test
public void ensureNoErrorsIfValid() {
validator.validate(overtimeForm, errors);
Mockito.verifyZeroInteractions(errors);
}
// Validate period -------------------------------------------------------------------------------------------------
@Test
public void ensureStartDateIsMandatory() {
overtimeForm.setStartDate(null);
validator.validate(overtimeForm, errors);
Mockito.verify(errors).rejectValue("startDate", "error.entry.mandatory");
}
@Test
public void ensureEndDateIsMandatory() {
overtimeForm.setEndDate(null);
validator.validate(overtimeForm, errors);
Mockito.verify(errors).rejectValue("endDate", "error.entry.mandatory");
}
@Test
public void ensureStartAndEndDateCanBeEquals() {
DateMidnight now = DateMidnight.now();
overtimeForm.setStartDate(now);
overtimeForm.setEndDate(now);
validator.validate(overtimeForm, errors);
Mockito.verifyZeroInteractions(errors);
}
@Test
public void ensureStartDateCanNotBeAfterEndDate() {
overtimeForm.setStartDate(overtimeForm.getEndDate().plusDays(3));
validator.validate(overtimeForm, errors);
Mockito.verify(errors).rejectValue("endDate", "error.entry.invalidPeriod");
}
@Test
public void ensureNoErrorMessageForMandatoryIfStartDateIsNullBecauseOfTypeMismatch() {
Mockito.when(errors.hasFieldErrors("startDate")).thenReturn(true);
overtimeForm.setStartDate(null);
validator.validate(overtimeForm, errors);
Mockito.verify(errors).hasFieldErrors("startDate");
Mockito.verify(errors, Mockito.never()).rejectValue("startDate", "error.entry.mandatory");
}
@Test
public void ensureNoErrorMessageForMandatoryIfEndDateIsNullBecauseOfTypeMismatch() {
Mockito.when(errors.hasFieldErrors("endDate")).thenReturn(true);
overtimeForm.setEndDate(null);
validator.validate(overtimeForm, errors);
Mockito.verify(errors).hasFieldErrors("endDate");
Mockito.verify(errors, Mockito.never()).rejectValue("endDate", "error.entry.mandatory");
}
// Validate number of hours ----------------------------------------------------------------------------------------
@Test
public void ensureNumberOfHoursIsMandatory() {
overtimeForm.setNumberOfHours(null);
validator.validate(overtimeForm, errors);
Mockito.verify(errors).rejectValue("numberOfHours", "error.entry.mandatory");
}
@Test
public void ensureNumberOfHoursCanBeNegative() {
overtimeForm.setNumberOfHours(BigDecimal.ONE.negate());
validator.validate(overtimeForm, errors);
Mockito.verifyZeroInteractions(errors);
}
@Test
public void ensureNumberOfHoursCanBeZero() {
overtimeForm.setNumberOfHours(BigDecimal.ZERO);
validator.validate(overtimeForm, errors);
Mockito.verifyZeroInteractions(errors);
}
@Test
public void ensureNumberOfHoursCanBeADecimalNumber() {
overtimeForm.setNumberOfHours(new BigDecimal("0.5"));
validator.validate(overtimeForm, errors);
Mockito.verifyZeroInteractions(errors);
}
@Test
public void ensureNoErrorMessageForMandatoryIfNumberOfHoursIsNullBecauseOfTypeMismatch() {
Mockito.when(errors.hasFieldErrors("numberOfHours")).thenReturn(true);
overtimeForm.setNumberOfHours(null);
validator.validate(overtimeForm, errors);
Mockito.verify(errors).hasFieldErrors("numberOfHours");
Mockito.verify(errors, Mockito.never()).rejectValue("endDate", "error.entry.mandatory");
}
// Validate using overtime settings --------------------------------------------------------------------------------
@Test
public void ensureCanNotRecordOvertimeIfOvertimeManagementIsDeactivated() {
settings.getWorkingTimeSettings().setOvertimeActive(false);
validator.validate(overtimeForm, errors);
Mockito.verify(errors).reject("overtime.record.error.deactivated");
}
@Test
public void ensureCanNotRecordOvertimeIfMaximumOvertimeIsZero() {
settings.getWorkingTimeSettings().setMaximumOvertime(0);
Mockito.when(overtimeServiceMock.getLeftOvertimeForPerson(Mockito.any(Person.class)))
.thenReturn(BigDecimal.ZERO);
// just not important how many number of hours, can not record overtime!
overtimeForm.setNumberOfHours(BigDecimal.ZERO);
validator.validate(overtimeForm, errors);
Mockito.verify(errors).reject("overtime.record.error.deactivated");
Mockito.verify(settingsServiceMock).getSettings();
Mockito.verifyZeroInteractions(overtimeServiceMock);
}
@Test
public void ensureCanRecordOvertimeIfMaximumOvertimeReachedButNotExceeded() {
settings.getWorkingTimeSettings().setMaximumOvertime(16);
Mockito.when(overtimeServiceMock.getLeftOvertimeForPerson(Mockito.any(Person.class)))
.thenReturn(new BigDecimal("8"));
overtimeForm.setNumberOfHours(new BigDecimal("8"));
validator.validate(overtimeForm, errors);
Mockito.verifyZeroInteractions(errors);
Mockito.verify(settingsServiceMock).getSettings();
Mockito.verify(overtimeServiceMock).getLeftOvertimeForPerson(overtimeForm.getPerson());
}
@Test
public void ensureCanNotRecordOvertimeIfMaximumOvertimeExceeded() {
settings.getWorkingTimeSettings().setMaximumOvertime(16);
Mockito.when(overtimeServiceMock.getLeftOvertimeForPerson(Mockito.any(Person.class)))
.thenReturn(new BigDecimal("8"));
overtimeForm.setNumberOfHours(new BigDecimal("8.5"));
validator.validate(overtimeForm, errors);
Mockito.verify(errors)
.rejectValue("numberOfHours", "overtime.data.numberOfHours.error.maxOvertime",
new Object[] { new BigDecimal("16") }, null);
Mockito.verify(settingsServiceMock).getSettings();
Mockito.verify(overtimeServiceMock).getLeftOvertimeForPerson(overtimeForm.getPerson());
}
@Test
public void ensureCanNotRecordOvertimeIfMinimumOvertimeExceeded() {
settings.getWorkingTimeSettings().setMinimumOvertime(10);
Mockito.when(overtimeServiceMock.getLeftOvertimeForPerson(Mockito.any(Person.class)))
.thenReturn(new BigDecimal("-9"));
overtimeForm.setNumberOfHours(new BigDecimal("-1.5"));
validator.validate(overtimeForm, errors);
Mockito.verify(errors)
.rejectValue("numberOfHours", "overtime.data.numberOfHours.error.minOvertime",
new Object[] { new BigDecimal("10") }, null);
Mockito.verify(settingsServiceMock).getSettings();
Mockito.verify(overtimeServiceMock).getLeftOvertimeForPerson(overtimeForm.getPerson());
}
// Validate changes in existing overtime record --------------------------------------------------------------------
@Test
public void foo() throws IllegalAccessException {
settings.getWorkingTimeSettings().setMaximumOvertime(100);
Mockito.when(overtimeServiceMock.getLeftOvertimeForPerson(Mockito.any(Person.class)))
.thenReturn(new BigDecimal("99.5"));
overtimeForm.setNumberOfHours(new BigDecimal("2"));
// ensure overtime form has ID
Field idField = ReflectionUtils.findField(OvertimeForm.class, "id");
idField.setAccessible(true);
idField.set(overtimeForm, 42);
Overtime originalOvertimeRecord = TestDataCreator.createOvertimeRecord();
originalOvertimeRecord.setHours(new BigDecimal("3"));
Mockito.when(overtimeServiceMock.getOvertimeById(Mockito.anyInt()))
.thenReturn(Optional.of(originalOvertimeRecord));
validator.validate(overtimeForm, errors);
Mockito.verifyZeroInteractions(errors);
Mockito.verify(overtimeServiceMock).getOvertimeById(overtimeForm.getId());
Mockito.verify(settingsServiceMock).getSettings();
Mockito.verify(overtimeServiceMock).getLeftOvertimeForPerson(overtimeForm.getPerson());
}
@Test
public void ensureCanEditOvertimeRecordChangingPositiveHours() throws IllegalAccessException {
settings.getWorkingTimeSettings().setMaximumOvertime(4);
Mockito.when(overtimeServiceMock.getLeftOvertimeForPerson(Mockito.any(Person.class)))
.thenReturn(new BigDecimal("3.5"));
overtimeForm.setNumberOfHours(new BigDecimal("3"));
// ensure overtime form has ID
Field idField = ReflectionUtils.findField(OvertimeForm.class, "id");
idField.setAccessible(true);
idField.set(overtimeForm, 42);
Overtime originalOvertimeRecord = TestDataCreator.createOvertimeRecord();
originalOvertimeRecord.setHours(new BigDecimal("2.5"));
Mockito.when(overtimeServiceMock.getOvertimeById(Mockito.anyInt()))
.thenReturn(Optional.of(originalOvertimeRecord));
validator.validate(overtimeForm, errors);
Mockito.verifyZeroInteractions(errors);
Mockito.verify(overtimeServiceMock).getOvertimeById(overtimeForm.getId());
Mockito.verify(settingsServiceMock).getSettings();
Mockito.verify(overtimeServiceMock).getLeftOvertimeForPerson(overtimeForm.getPerson());
}
@Test
public void ensureCanEditOvertimeRecordChangingNegativeHours() throws IllegalAccessException {
settings.getWorkingTimeSettings().setMinimumOvertime(4);
Mockito.when(overtimeServiceMock.getLeftOvertimeForPerson(Mockito.any(Person.class)))
.thenReturn(new BigDecimal("-3.5"));
overtimeForm.setNumberOfHours(new BigDecimal("-3"));
// ensure overtime form has ID
Field idField = ReflectionUtils.findField(OvertimeForm.class, "id");
idField.setAccessible(true);
idField.set(overtimeForm, 42);
Overtime originalOvertimeRecord = TestDataCreator.createOvertimeRecord();
originalOvertimeRecord.setHours(new BigDecimal("-2.5"));
Mockito.when(overtimeServiceMock.getOvertimeById(Mockito.anyInt()))
.thenReturn(Optional.of(originalOvertimeRecord));
validator.validate(overtimeForm, errors);
Mockito.verifyZeroInteractions(errors);
Mockito.verify(overtimeServiceMock).getOvertimeById(overtimeForm.getId());
Mockito.verify(settingsServiceMock).getSettings();
Mockito.verify(overtimeServiceMock).getLeftOvertimeForPerson(overtimeForm.getPerson());
}
// Validate comment ------------------------------------------------------------------------------------------------
@Test
public void ensureCommentIsNotMandatory() {
Consumer<String> assertMayBeEmpty = (comment) -> {
overtimeForm.setComment(comment);
validator.validate(overtimeForm, errors);
Mockito.verifyZeroInteractions(errors);
};
assertMayBeEmpty.accept(null);
assertMayBeEmpty.accept("");
}
@Test
public void ensureCommentHasMaximumCharacterLength() {
overtimeForm.setComment(
"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore "
+ "et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores e");
validator.validate(overtimeForm, errors);
Mockito.verify(errors).rejectValue("comment", "error.entry.tooManyChars");
}
}