-
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
323 additions
and
17 deletions.
There are no files selected for viewing
30 changes: 30 additions & 0 deletions
30
jollyday-tests/src/test/java/de/focus_shift/jollyday/tests/HolidayCheckerApi.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package de.focus_shift.jollyday.tests; | ||
|
||
import de.focus_shift.jollyday.core.HolidayCalendar; | ||
import de.focus_shift.jollyday.core.HolidayType; | ||
|
||
import java.time.Month; | ||
import java.time.Year; | ||
|
||
public interface HolidayCheckerApi { | ||
|
||
interface Holiday { | ||
Between fixed(final String propertyKey, final Month month, final int day); | ||
Between fixed(final String propertyKey, final Month month, final int day, final HolidayType type); | ||
|
||
Between christian(final String propertyKey); | ||
Between christian(final String propertyKey, final HolidayType type); | ||
} | ||
|
||
interface Between { | ||
Between valid(Year from, Year to); | ||
|
||
Holiday and(); | ||
|
||
void check(); | ||
} | ||
|
||
static HolidayCheckerFluent assertHolidays(final HolidayCalendar calendar) { | ||
return new HolidayCheckerFluent(calendar); | ||
} | ||
} |
273 changes: 273 additions & 0 deletions
273
jollyday-tests/src/test/java/de/focus_shift/jollyday/tests/HolidayCheckerFluent.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,273 @@ | ||
package de.focus_shift.jollyday.tests; | ||
|
||
import de.focus_shift.jollyday.core.Holiday; | ||
import de.focus_shift.jollyday.core.HolidayCalendar; | ||
import de.focus_shift.jollyday.core.HolidayManager; | ||
import de.focus_shift.jollyday.core.HolidayType; | ||
import net.jqwik.api.Arbitraries; | ||
import net.jqwik.time.api.arbitraries.YearArbitrary; | ||
import org.junit.jupiter.api.Assertions; | ||
|
||
import java.time.LocalDate; | ||
import java.time.Month; | ||
import java.time.Year; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.Iterator; | ||
import java.util.List; | ||
import java.util.NoSuchElementException; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
|
||
import static de.focus_shift.jollyday.core.ManagerParameters.create; | ||
import static de.focus_shift.jollyday.tests.HolidayCheckerFluent.Category.BY_DAY; | ||
import static de.focus_shift.jollyday.tests.HolidayCheckerFluent.Category.BY_KEY; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
public class HolidayCheckerFluent implements HolidayCheckerApi.Holiday, HolidayCheckerApi.Between { | ||
|
||
enum Category { | ||
BY_DAY, | ||
BY_KEY | ||
} | ||
|
||
private final HolidayCalendar calendar; | ||
private String propertyKey; | ||
private Month month; | ||
private int day; | ||
private HolidayType type; | ||
private Category category; | ||
private List<YearRange> validRanges = new ArrayList<>(); | ||
|
||
private final List<HolidayCheck> checks = new ArrayList<>(); | ||
|
||
public HolidayCheckerFluent(HolidayCalendar calendar) { | ||
this.calendar = calendar; | ||
} | ||
|
||
|
||
@Override | ||
public HolidayCheckerApi.Between christian(final String propertyKey) { | ||
return christian(propertyKey, HolidayType.PUBLIC_HOLIDAY); | ||
} | ||
|
||
@Override | ||
public HolidayCheckerApi.Between christian(final String propertyKey, final HolidayType type) { | ||
Objects.requireNonNull(propertyKey, "propertyKey is required"); | ||
Objects.requireNonNull(type, "holiday type is required"); | ||
|
||
this.category = BY_KEY; | ||
this.propertyKey = "christian." + propertyKey; | ||
this.type = type; | ||
|
||
return this; | ||
} | ||
|
||
@Override | ||
public HolidayCheckerApi.Between fixed(final String propertyKey, final Month month, final int day) { | ||
return fixed(propertyKey, month, day, HolidayType.PUBLIC_HOLIDAY); | ||
} | ||
|
||
@Override | ||
public HolidayCheckerApi.Between fixed(final String propertyKey, final Month month, final int day, final HolidayType type) { | ||
|
||
Objects.requireNonNull(propertyKey, "propertyKey is required"); | ||
Objects.requireNonNull(month, "month is required"); | ||
if (day >= 32 || day <= 0) { | ||
throw new IllegalArgumentException("day must be between 1 and 31"); | ||
} | ||
Objects.requireNonNull(type, "holiday type is required"); | ||
|
||
this.category = BY_DAY; | ||
this.propertyKey = propertyKey; | ||
this.month = month; | ||
this.day = day; | ||
this.type = type; | ||
|
||
return this; | ||
} | ||
|
||
@Override | ||
public HolidayCheckerApi.Between valid(final Year from, Year to) { | ||
validRanges.add(new YearRange(from, to)); | ||
return this; | ||
} | ||
|
||
@Override | ||
public HolidayCheckerApi.Holiday and() { | ||
checks.add(new HolidayCheck(calendar, propertyKey, month, day, type, validRanges, category)); | ||
|
||
this.propertyKey = null; | ||
this.month = null; | ||
this.day = 0; | ||
this.type = null; | ||
this.category = null; | ||
this.validRanges = new ArrayList<>(); | ||
|
||
return this; | ||
} | ||
|
||
@Override | ||
public void check() { | ||
checks.add(new HolidayCheck(calendar, propertyKey, month, day, type, validRanges, category)); | ||
|
||
this.propertyKey = null; | ||
this.month = null; | ||
this.day = 0; | ||
this.type = null; | ||
this.category = null; | ||
this.validRanges = new ArrayList<>(); | ||
|
||
for (HolidayCheck check : checks) { | ||
switch (check.category) { | ||
case BY_DAY: | ||
checkByDate(check); | ||
break; | ||
case BY_KEY: | ||
checkByKey(check); | ||
break; | ||
default: | ||
throw new IllegalStateException("Unexpected value: " + check.category); | ||
} | ||
} | ||
|
||
this.checks.clear(); | ||
} | ||
|
||
private void checkByDate(HolidayCheck check) { | ||
for (final YearRange validRange : check.getValidRanges()) { | ||
((YearArbitrary) Arbitraries.defaultFor(Year.class)) | ||
.between(validRange.getFrom().getValue(), validRange.getTo().getValue()) | ||
.forEachValue(year -> { | ||
final Set<Holiday> holidays = HolidayManager.getInstance(create(check.calendar)).getHolidays(year); | ||
assertThat(holidays) | ||
.isNotEmpty() | ||
.contains(new Holiday(LocalDate.of(year.getValue(), check.getMonth(), check.getDay()), check.getPropertiesKey(), check.getHolidayType())); | ||
} | ||
); | ||
} | ||
} | ||
|
||
private void checkByKey(HolidayCheck check) { | ||
for (final YearRange validRange : check.getValidRanges()) { | ||
((YearArbitrary) Arbitraries.defaultFor(Year.class)) | ||
.between(validRange.getFrom().getValue(), validRange.getTo().getValue()) | ||
.forEachValue(year -> { | ||
final Set<Holiday> holidays = HolidayManager.getInstance(create(check.calendar)).getHolidays(year); | ||
assertThat(holidays) | ||
.isNotEmpty() | ||
.filteredOn(holiday -> holiday.getPropertiesKey().equals(check.getPropertiesKey())) | ||
.extracting(Holiday::getType) | ||
.contains(check.getHolidayType()); | ||
} | ||
); | ||
} | ||
} | ||
|
||
private static final class HolidayCheck { | ||
|
||
private final HolidayCalendar calendar; | ||
private final List<YearRange> validRanges; | ||
private final Month month; | ||
private final int day; | ||
private final String propertiesKey; | ||
private final HolidayType holidayType; | ||
private final Category category; | ||
|
||
HolidayCheck(HolidayCalendar calendar, | ||
String propertiesKey, Month month, int day, HolidayType holidayType, | ||
List<YearRange> validRanges, Category category | ||
) { | ||
this.calendar = calendar; | ||
this.propertiesKey = propertiesKey; | ||
this.month = month; | ||
this.day = day; | ||
this.holidayType = holidayType; | ||
this.validRanges = validRanges.isEmpty() ? List.of(new YearRange(Year.of(1900), Year.of(2500))) : Collections.unmodifiableList(validRanges); | ||
this.category = category; | ||
} | ||
|
||
public HolidayCalendar getCalendar() { | ||
return calendar; | ||
} | ||
|
||
public List<YearRange> getValidRanges() { | ||
return validRanges; | ||
} | ||
|
||
public Month getMonth() { | ||
return month; | ||
} | ||
|
||
public int getDay() { | ||
return day; | ||
} | ||
|
||
public String getPropertiesKey() { | ||
return propertiesKey; | ||
} | ||
|
||
public HolidayType getHolidayType() { | ||
return holidayType; | ||
} | ||
|
||
public Category getCategory() { | ||
return category; | ||
} | ||
} | ||
|
||
private static class YearRange implements Iterable<Year> { | ||
|
||
private final Year from; | ||
private final Year to; | ||
|
||
YearRange(final Year from, final Year to) { | ||
if (from != null && to != null) { | ||
Assertions.assertFalse(from.isAfter(to), "To must be greater than or equal to the from year."); | ||
} | ||
this.from = from; | ||
this.to = to; | ||
} | ||
|
||
public Year getFrom() { | ||
return from; | ||
} | ||
|
||
public Year getTo() { | ||
return to; | ||
} | ||
|
||
@Override | ||
public Iterator<Year> iterator() { | ||
return new YearRangeIterator(from, to); | ||
} | ||
|
||
private static final class YearRangeIterator implements Iterator<Year> { | ||
|
||
private final Year endYear; | ||
private Year cursor; | ||
|
||
YearRangeIterator(final Year startYear, final Year endYear) { | ||
this.cursor = startYear; | ||
this.endYear = endYear; | ||
} | ||
|
||
@Override | ||
public boolean hasNext() { | ||
return cursor.isBefore(endYear) || cursor.equals(endYear); | ||
} | ||
|
||
@Override | ||
public Year next() { | ||
|
||
if (cursor.isAfter(endYear)) { | ||
throw new NoSuchElementException("next year is after endYear which is not in range anymore."); | ||
} | ||
|
||
final Year current = cursor; | ||
cursor = cursor.plusYears(1); | ||
return current; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters