Skip to content

Commit

Permalink
WIP: first fluent checker tests
Browse files Browse the repository at this point in the history
  • Loading branch information
derTobsch committed Dec 13, 2024
1 parent 963c578 commit ff00980
Show file tree
Hide file tree
Showing 6 changed files with 379 additions and 145 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
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 CalendarCheckerApi {

interface Holiday {
Properties hasFixedHoliday(final String propertyKey, final Month month, final int day);
Properties hasFixedHoliday(final String propertyKey, final Month month, final int day, final HolidayType type);

Properties hasChristianHoliday(final String propertyKey);
Properties hasChristianHoliday(final String propertyKey, final HolidayType type);

Properties hasIslamicHoliday(final String propertyKey);
Properties hasIslamicHoliday(final String propertyKey, final HolidayType type);
}

interface Properties extends Subdivision, Between, Check {
}

interface Subdivision extends Check {
Between inSubdivision(final String... subdivisions);
}

interface Between extends Check {
Between between(Year from, Year to);
}

interface Check {
Holiday and();
void check();
}

static CalendarCheckerFluent assertFor(final HolidayCalendar calendar) {
return new CalendarCheckerFluent(calendar);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
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.List;
import java.util.Objects;
import java.util.Set;

import static de.focus_shift.jollyday.core.ManagerParameters.create;
import static de.focus_shift.jollyday.tests.CalendarCheckerFluent.Category.BY_DAY;
import static de.focus_shift.jollyday.tests.CalendarCheckerFluent.Category.BY_KEY;
import static org.assertj.core.api.Assertions.assertThat;

public class CalendarCheckerFluent implements CalendarCheckerApi.Holiday, CalendarCheckerApi.Between, CalendarCheckerApi.Properties {

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 String[] subdivisions = new String[]{};
private List<YearRange> validRanges = new ArrayList<>();

private final List<HolidayCalendarCheck> checks = new ArrayList<>();

public CalendarCheckerFluent(HolidayCalendar calendar) {
this.calendar = calendar;
}


@Override
public CalendarCheckerApi.Properties hasChristianHoliday(final String propertyKey) {
return hasChristianHoliday(propertyKey, HolidayType.PUBLIC_HOLIDAY);
}

@Override
public CalendarCheckerApi.Properties hasChristianHoliday(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 CalendarCheckerApi.Properties hasIslamicHoliday(final String propertyKey) {
return hasIslamicHoliday(propertyKey, HolidayType.PUBLIC_HOLIDAY);
}

@Override
public CalendarCheckerApi.Properties hasIslamicHoliday(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 = "islamic." + propertyKey;
this.type = type;

return this;
}

@Override
public CalendarCheckerApi.Properties hasFixedHoliday(final String propertyKey, final Month month, final int day) {
return hasFixedHoliday(propertyKey, month, day, HolidayType.PUBLIC_HOLIDAY);
}

@Override
public CalendarCheckerApi.Properties hasFixedHoliday(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 CalendarCheckerApi.Between between(final Year from, Year to) {
validRanges.add(new YearRange(from, to));
return this;
}

@Override
public CalendarCheckerApi.Between inSubdivision(String... subdivisions) {
this.subdivisions = subdivisions;
return this;
}

@Override
public CalendarCheckerApi.Holiday and() {
checks.add(new HolidayCalendarCheck(this.calendar, this.propertyKey, this.month, this.day, this.type, this.validRanges, this.subdivisions, this.category));

clearProperties();

return this;
}

private void clearProperties() {
this.propertyKey = null;
this.month = null;
this.day = 0;
this.type = null;
this.category = null;
this.subdivisions = new String[]{};
this.validRanges = new ArrayList<>();
}

@Override
public void check() {
checks.add(new HolidayCalendarCheck(calendar, propertyKey, month, day, type, validRanges, subdivisions, category));

clearProperties();

for (HolidayCalendarCheck 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(HolidayCalendarCheck check) {
final HolidayManager holidayManager = HolidayManager.getInstance(create(check.calendar));

for (final YearRange validRange : check.getValidRanges()) {
createYearArbitrary()
.between(validRange.getFrom().getValue(), validRange.getTo().getValue())
.forEachValue(year -> {
final Set<Holiday> holidays = holidayManager.getHolidays(year, check.getSubdivisions());
assertThat(holidays)
.isNotEmpty()
.contains(new Holiday(LocalDate.of(year.getValue(), check.getMonth(), check.getDay()), check.getPropertiesKey(), check.getHolidayType()));
}
);
}
}

private void checkByKey(HolidayCalendarCheck check) {
final HolidayManager holidayManager = HolidayManager.getInstance(create(check.calendar));

for (final YearRange validRange : check.getValidRanges()) {
createYearArbitrary()
.between(validRange.getFrom().getValue(), validRange.getTo().getValue())
.forEachValue(year -> {
final Set<Holiday> holidays = holidayManager.getHolidays(year, check.getSubdivisions());
assertThat(holidays)
.isNotEmpty()
.filteredOn(holiday -> holiday.getPropertiesKey().equals(check.getPropertiesKey()))
.extracting(Holiday::getType)
.contains(check.getHolidayType());
}
);
}
}

private static YearArbitrary createYearArbitrary() {
return (YearArbitrary) Arbitraries.defaultFor(Year.class);
}

private static final class HolidayCalendarCheck {

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 String[] subdivisions;
private final Category category;

HolidayCalendarCheck(HolidayCalendar calendar,
String propertiesKey, Month month, int day, HolidayType holidayType,
List<YearRange> validRanges, String[] subdivisions, 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.subdivisions = subdivisions;
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 String[] getSubdivisions() {
return subdivisions;
}

public Category getCategory() {
return category;
}
}

private static class YearRange {

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;
}
}
}
Loading

0 comments on commit ff00980

Please sign in to comment.