pibizza commented on code in PR #3788:
URL: 
https://github.com/apache/incubator-kie-kogito-runtimes/pull/3788#discussion_r1865447581


##########
jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/CalendarBeanTest.java:
##########
@@ -0,0 +1,295 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.jbpm.process.core.timer;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.END_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAYS;
+import static 
org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.START_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.TIMEZONE;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.WEEKEND_DAYS;
+import static 
org.jbpm.process.core.timer.CalendarBean.DEFAULT_HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_TIMEZONE;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKENDS;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKEND_DAYS;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CalendarBeanTest {
+
+    // Static validation methods
+    @ParameterizedTest
+    @MethodSource("getMissingPropertiesCalendar")
+    void requiredPropertyValidation(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.requiredPropertyValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getWronglyFormatPropertiesCalendar")
+    void propertyFormatValidation(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.propertyFormatValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getBusinessInvalidPropertiesCalendar")
+    void businessValidation(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.businessValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getPartialPropertiesCalendar")
+    void missingDataPopulation(Map<String, Object> propertyMap, Map<String, 
Object> defaultValuesMap) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        defaultValuesMap.keySet().forEach(key -> 
assertThat(calendarConfiguration.containsKey(key)).isFalse());
+        CalendarBean.missingDataPopulation(calendarConfiguration);
+        defaultValuesMap.forEach((key, value) -> {
+            assertThat(calendarConfiguration.containsKey(key)).isTrue();
+            
assertThat(calendarConfiguration.getProperty(key)).isEqualTo(value);
+        });
+    }
+
+    @Test
+    void getPropertyAsInt() {
+        Properties calendarConfiguration = new Properties();
+        String propertyName = "propertyName";
+        int originalValue = 1;
+        String value = "" + originalValue;
+        calendarConfiguration.put(propertyName, value);
+        int retrieved = CalendarBean.getPropertyAsInt(propertyName, 
calendarConfiguration);
+        assertThat(retrieved).isEqualTo(originalValue);
+        value = "WRONG";
+        calendarConfiguration.put(propertyName, value);
+        NumberFormatException thrown = 
assertThrows(NumberFormatException.class, () -> 
CalendarBean.getPropertyAsInt(propertyName, calendarConfiguration));
+        String expectedMessage = "For input string: \"WRONG\"";
+        assertThat(thrown.getMessage()).isEqualTo(expectedMessage);

Review Comment:
   you can use assertj assertions to inspect directly the message of an 
exception. see assertThat(thrown).hasMessage(expectedMessage);



##########
jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/CalendarBeanTest.java:
##########
@@ -0,0 +1,295 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.jbpm.process.core.timer;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.END_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAYS;
+import static 
org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.START_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.TIMEZONE;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.WEEKEND_DAYS;
+import static 
org.jbpm.process.core.timer.CalendarBean.DEFAULT_HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_TIMEZONE;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKENDS;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKEND_DAYS;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CalendarBeanTest {
+
+    // Static validation methods
+    @ParameterizedTest
+    @MethodSource("getMissingPropertiesCalendar")
+    void requiredPropertyValidation(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.requiredPropertyValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getWronglyFormatPropertiesCalendar")
+    void propertyFormatValidation(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.propertyFormatValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getBusinessInvalidPropertiesCalendar")
+    void businessValidation(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.businessValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getPartialPropertiesCalendar")
+    void missingDataPopulation(Map<String, Object> propertyMap, Map<String, 
Object> defaultValuesMap) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        defaultValuesMap.keySet().forEach(key -> 
assertThat(calendarConfiguration.containsKey(key)).isFalse());
+        CalendarBean.missingDataPopulation(calendarConfiguration);
+        defaultValuesMap.forEach((key, value) -> {
+            assertThat(calendarConfiguration.containsKey(key)).isTrue();
+            
assertThat(calendarConfiguration.getProperty(key)).isEqualTo(value);
+        });
+    }
+
+    @Test
+    void getPropertyAsInt() {
+        Properties calendarConfiguration = new Properties();
+        String propertyName = "propertyName";
+        int originalValue = 1;
+        String value = "" + originalValue;
+        calendarConfiguration.put(propertyName, value);
+        int retrieved = CalendarBean.getPropertyAsInt(propertyName, 
calendarConfiguration);
+        assertThat(retrieved).isEqualTo(originalValue);
+        value = "WRONG";
+        calendarConfiguration.put(propertyName, value);
+        NumberFormatException thrown = 
assertThrows(NumberFormatException.class, () -> 
CalendarBean.getPropertyAsInt(propertyName, calendarConfiguration));
+        String expectedMessage = "For input string: \"WRONG\"";
+        assertThat(thrown.getMessage()).isEqualTo(expectedMessage);
+    }
+
+    @Test
+    void validateRequiredProperty() {
+        Properties calendarConfiguration = new Properties();
+        String propertyName = "propertyName";
+        String value = "propertyValue";
+        calendarConfiguration.put(propertyName, value);
+        StringBuilder errorMessage = new StringBuilder();
+        CalendarBean.validateRequiredProperty(propertyName, errorMessage, 
calendarConfiguration);
+        assertThat(errorMessage).isEmpty();
+        CalendarBean.validateRequiredProperty("missingProperty", errorMessage, 
calendarConfiguration);
+        String[] retrievedErrors = errorMessage.toString().split("\n");
+        assertThat(retrievedErrors).hasSize(1);
+        assertThat(retrievedErrors).contains("Property missingProperty is 
required");

Review Comment:
   you can chain assertions. Also this should be "containsExactly"



##########
jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/BusinessCalendarImplTest.java:
##########
@@ -18,419 +18,323 @@
  */
 package org.jbpm.process.core.timer;
 
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
 import java.util.Calendar;
+import java.util.Collections;
 import java.util.Date;
+import java.util.List;
 import java.util.Properties;
-import java.util.concurrent.TimeUnit;
+import java.util.function.BiFunction;
+import java.util.stream.IntStream;
 
 import org.jbpm.test.util.AbstractBaseTest;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
-import org.kie.kogito.timer.SessionPseudoClock;
 import org.slf4j.LoggerFactory;
 
+import static java.time.temporal.ChronoUnit.DAYS;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.END_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAYS;
+import static 
org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.START_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.WEEKEND_DAYS;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-public class BusinessCalendarImplTest extends AbstractBaseTest {
+class BusinessCalendarImplTest extends AbstractBaseTest {
 
     public void addLogger() {
         logger = LoggerFactory.getLogger(this.getClass());
     }
 
     @Test
-    public void testCalculateHours() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-04 16:45";
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-04 13:45").getTime());
-
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("3h");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void instantiate() {
+        BusinessCalendarImpl retrieved = 
BusinessCalendarImpl.builder().build();
+        assertThat(retrieved).isNotNull();
+        retrieved = BusinessCalendarImpl.builder()
+                .withCalendarBean(CalendarBeanFactory.createCalendarBean())
+                .build();
+        assertThat(retrieved).isNotNull();
+
+        Properties calendarConfiguration = new Properties();
+        int startHour = 10;
+        int endHour = 16;
+        calendarConfiguration.put(START_HOUR, String.valueOf(startHour));
+        calendarConfiguration.put(END_HOUR, String.valueOf(endHour));
+        retrieved = BusinessCalendarImpl.builder()
+                .withCalendarBean(new CalendarBean(calendarConfiguration))
+                .build();
+        assertThat(retrieved).isNotNull();
     }
 
     @Test
-    public void testCalculateHoursCustomWorkingHours() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOURS_PER_DAY, "6");
-        String expectedDate = "2012-05-04 15:45";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-03 13:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("8h");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateInsideDailyWorkingHourWithDelay() {
+        int daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 3, 
daysToSkip, null, null);
     }
 
     @Test
-    public void testCalculateHoursPassingOverWeekend() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-07 12:45";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-04 13:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("7h");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateInsideDailyWorkingHourWithoutDelay() {
+        int daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 0, 
daysToSkip, null, null);
     }
 
+    @Disabled("TO FIX")
     @Test
-    public void testCalculateHoursPassingOverCustomDefinedWeekend() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.WEEKEND_DAYS, Calendar.FRIDAY 
+ "," + Calendar.SATURDAY);
-        String expectedDate = "2012-05-06 12:45";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-03 13:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("7h");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateInsideNightlyWorkingHour() {
+        int daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(4, -4, 0, 3, 
daysToSkip, null, null);
     }
 
     @Test
-    public void testCalculateMinutesPassingOverWeekend() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-07 09:15";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-04 16:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("30m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateBeforeWorkingHourWithDelay() {
+        int daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(2, 4, -1, 1, 
daysToSkip, null, null);
     }
 
     @Test
-    public void testCalculateMinutesPassingOverHoliday() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, 
"2012-05-12:2012-05-19");
-        String expectedDate = "2012-05-21 09:15";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-11 16:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("30m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateBeforeWorkingHourWithoutDelay() {
+        int daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-1, 4, -2, 1, 
daysToSkip, null, null);
     }
 
     @Test
-    public void testCalculateDays() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-14 09:00";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDate("2012-05-04").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("6d");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateAfterWorkingHour() {
+        int daysToSkip = 1;
+        commonCalculateBusinessTimeAsDateAssertAtStartHour(-1, 2, 3, 3, 
daysToSkip, null, null);
     }
 
     @Test
-    public void testCalculateDaysStartingInWeekend() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-09 09:00";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDate("2012-05-05").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("2d");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateWhenTodayAndTomorrowAreHolidays() {
+        String holidayDateFormat = "yyyy-MM-dd";
+        DateTimeFormatter sdf = DateTimeFormatter.ofPattern(holidayDateFormat);
+        LocalDate today = LocalDate.now();
+        LocalDate tomorrow = today.plusDays(1);
+        String holidays = sdf.format(today) + "," + sdf.format(tomorrow);
+        int daysToSkip = 2;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 3, 
daysToSkip, holidayDateFormat, holidays);
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 5, 3, 
daysToSkip, holidayDateFormat, holidays);
     }
 
     @Test
-    public void testCalculateDaysCustomWorkingDays() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.DAYS_PER_WEEK, "4");
-        config.setProperty(BusinessCalendarImpl.WEEKEND_DAYS, Calendar.FRIDAY 
+ "," + Calendar.SATURDAY + "," + Calendar.SUNDAY);
-        String expectedDate = "2012-05-15 14:30";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-03 14:30").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("6d");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateWhenNextDayIsHoliday() {
+        String holidayDateFormat = "yyyy-MM-dd";
+        DateTimeFormatter sdf = DateTimeFormatter.ofPattern(holidayDateFormat);
+        LocalDate tomorrow = LocalDate.now().plusDays(1);
+        String holidays = sdf.format(tomorrow);
+        int daysToSkip = 2;
+
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 4, 
daysToSkip, holidayDateFormat, holidays);
+        daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 3, 
daysToSkip, holidayDateFormat, holidays);
     }
 
     @Test
-    public void testCalculateDaysMiddleDay() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-11 12:27";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-03 12:27").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("6d");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void rollCalendarToDailyWorkingHour() {
+        int startHour = 14;
+        int endHour = 16;
+        Calendar toRoll = Calendar.getInstance();
+        int currentHour = 8;
+        toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+        int dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+        BusinessCalendarImpl.rollCalendarToDailyWorkingHour(toRoll, startHour, 
endHour);
+        assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+        assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear);
+
+        toRoll = Calendar.getInstance();
+        currentHour = 19;
+        toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+        dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+        BusinessCalendarImpl.rollCalendarToDailyWorkingHour(toRoll, startHour, 
endHour);
+        assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+        assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear + 1);
     }
 
+    @Disabled("TO FIX 
https://github.com/apache/incubator-kie-issues/issues/1651";)
     @Test
-    public void testCalculateDaysHoursMinutes() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-14 14:20";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDate("2012-05-04").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("6d4h80m");
+    void rollCalendarToNightlyWorkingHour() {
+        int startHour = 20;
+        int endHour = 4;
+        Calendar toRoll = Calendar.getInstance();
+        int currentHour = 21;
+        toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+        int dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+        BusinessCalendarImpl.rollCalendarToNightlyWorkingHour(toRoll, 
startHour, endHour);
+        assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+        assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear);
+
+        toRoll = Calendar.getInstance();
+        currentHour = 3;
+        toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+        dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+        BusinessCalendarImpl.rollCalendarToNightlyWorkingHour(toRoll, 
startHour, endHour);
+        assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+        assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear + 1);
 
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
     }
 
+    @Disabled("TO FIX 
https://github.com/apache/incubator-kie-issues/issues/1651";)
     @Test
-    public void testCalculateTimeDaysHoursMinutesHolidays() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, 
"2012-05-10:2012-05-19");
-        String expectedDate = "2012-05-21 14:20";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDate("2012-05-04").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("6d4h80m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void rollCalendarAfterHolidays() {
+        Instant now = Instant.now();
+        int holidayLeft = 4;
+        Instant startHolidayInstant = now.minus(2, DAYS);
+        Instant endHolidayInstant = now.plus(holidayLeft, DAYS);
+        Date startHoliday = Date.from(startHolidayInstant);
+        Date endHoliday = Date.from(endHolidayInstant);
+        List<BusinessCalendarImpl.TimePeriod> holidays = 
Collections.singletonList(new BusinessCalendarImpl.TimePeriod(startHoliday, 
endHoliday));
+        List<Integer> weekendDays = Collections.emptyList();
+        Calendar calendar = Calendar.getInstance();
+        int currentDayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
+        BusinessCalendarImpl.rollCalendarAfterHolidays(calendar, holidays, 
weekendDays, false);
+        int expected = currentDayOfYear + holidayLeft + 1;
+        assertThat(calendar.get(Calendar.DAY_OF_YEAR)).isEqualTo(expected);
     }
 
     @Test
-    public void testCalculateTimeDaysHoursMinutesSingleDayHolidays() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, "2012-05-07");
-        String expectedDate = "2012-05-08 13:20";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDate("2012-05-04").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("1d4h20m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking() {
+        List<Integer> workingDays = IntStream.range(Calendar.MONDAY, 
Calendar.SATURDAY).boxed().toList();
+        List<Integer> weekendDays = Arrays.asList(Calendar.SATURDAY, 
Calendar.SUNDAY);
+        boolean resetTime = false;
+        workingDays.forEach(workingDay -> {
+            Calendar calendar = getCalendarAtExpectedWeekDay(workingDay);
+            
BusinessCalendarImpl.rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking(calendar,
 weekendDays, resetTime);
+            
assertThat(calendar.get(Calendar.DAY_OF_WEEK)).isEqualTo(workingDay);
+        });
+        weekendDays.forEach(weekendDay -> {
+            Calendar calendar = getCalendarAtExpectedWeekDay(weekendDay);
+            
BusinessCalendarImpl.rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking(calendar,
 weekendDays, resetTime);
+            
assertThat(calendar.get(Calendar.DAY_OF_WEEK)).isEqualTo(Calendar.MONDAY);
+        });
     }
 
     @Test
-    public void 
testCalculateTimeDaysHoursMinutesSingleDayHolidaysInMiddleOfWeek() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, "2012-05-09");
-        String expectedDate = "2012-05-10 15:30";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-08 11:10").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("1d4h20m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void isWorkingDay() {
+        List<Integer> workingDays = IntStream.range(Calendar.MONDAY, 
Calendar.SATURDAY).boxed().toList();
+        List<Integer> weekendDays = Arrays.asList(Calendar.SATURDAY, 
Calendar.SUNDAY);
+        workingDays.forEach(workingDay -> 
assertThat(BusinessCalendarImpl.isWorkingDay(weekendDays, 
workingDay)).isTrue());

Review Comment:
   Consider using directly assertj assertions related to collections and 
streams. This is a general comment.
   for example the previous assertions would become.
   ```suggestion
           assertThat(workingDays).allMatch(workingDay -> 
BusinessCalendarImpl.isWorkingDay(weekendDays, workingDay));
   ```
   This better expresses the intention of the test.



##########
jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/CalendarBeanTest.java:
##########
@@ -0,0 +1,295 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.jbpm.process.core.timer;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.END_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAYS;
+import static 
org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.START_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.TIMEZONE;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.WEEKEND_DAYS;
+import static 
org.jbpm.process.core.timer.CalendarBean.DEFAULT_HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_TIMEZONE;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKENDS;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKEND_DAYS;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CalendarBeanTest {
+
+    // Static validation methods
+    @ParameterizedTest
+    @MethodSource("getMissingPropertiesCalendar")
+    void requiredPropertyValidation(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.requiredPropertyValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getWronglyFormatPropertiesCalendar")
+    void propertyFormatValidation(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.propertyFormatValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getBusinessInvalidPropertiesCalendar")
+    void businessValidation(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.businessValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getPartialPropertiesCalendar")
+    void missingDataPopulation(Map<String, Object> propertyMap, Map<String, 
Object> defaultValuesMap) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        defaultValuesMap.keySet().forEach(key -> 
assertThat(calendarConfiguration.containsKey(key)).isFalse());
+        CalendarBean.missingDataPopulation(calendarConfiguration);
+        defaultValuesMap.forEach((key, value) -> {
+            assertThat(calendarConfiguration.containsKey(key)).isTrue();
+            
assertThat(calendarConfiguration.getProperty(key)).isEqualTo(value);
+        });
+    }
+
+    @Test
+    void getPropertyAsInt() {
+        Properties calendarConfiguration = new Properties();
+        String propertyName = "propertyName";
+        int originalValue = 1;
+        String value = "" + originalValue;
+        calendarConfiguration.put(propertyName, value);
+        int retrieved = CalendarBean.getPropertyAsInt(propertyName, 
calendarConfiguration);
+        assertThat(retrieved).isEqualTo(originalValue);
+        value = "WRONG";

Review Comment:
   Do not reuse variables in tests. they tend to make the test more confused.



##########
jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/CalendarBeanFactoryTest.java:
##########
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.jbpm.process.core.timer;
+
+import java.util.Calendar;
+import java.util.List;
+import java.util.TimeZone;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class CalendarBeanFactoryTest {
+
+    @Test
+    void testCreateCalendarBean() {
+        // This test relies on src/test/resources/calendar.properties.
+        // Checked values comes from calendar.properties
+        CalendarBean calendarBean = CalendarBeanFactory.createCalendarBean();
+        assertNotNull(calendarBean);

Review Comment:
   Do not use assert from Junit5. Use assertions from assertj. this is the 
standard in Kie.



##########
jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/CalendarBeanTest.java:
##########
@@ -0,0 +1,295 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.jbpm.process.core.timer;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.END_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAYS;
+import static 
org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.START_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.TIMEZONE;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.WEEKEND_DAYS;
+import static 
org.jbpm.process.core.timer.CalendarBean.DEFAULT_HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_TIMEZONE;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKENDS;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKEND_DAYS;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CalendarBeanTest {
+
+    // Static validation methods
+    @ParameterizedTest
+    @MethodSource("getMissingPropertiesCalendar")
+    void requiredPropertyValidation(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.requiredPropertyValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getWronglyFormatPropertiesCalendar")
+    void propertyFormatValidation(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.propertyFormatValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getBusinessInvalidPropertiesCalendar")
+    void businessValidation(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.businessValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getPartialPropertiesCalendar")
+    void missingDataPopulation(Map<String, Object> propertyMap, Map<String, 
Object> defaultValuesMap) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        defaultValuesMap.keySet().forEach(key -> 
assertThat(calendarConfiguration.containsKey(key)).isFalse());
+        CalendarBean.missingDataPopulation(calendarConfiguration);
+        defaultValuesMap.forEach((key, value) -> {
+            assertThat(calendarConfiguration.containsKey(key)).isTrue();
+            
assertThat(calendarConfiguration.getProperty(key)).isEqualTo(value);
+        });
+    }
+
+    @Test
+    void getPropertyAsInt() {
+        Properties calendarConfiguration = new Properties();
+        String propertyName = "propertyName";
+        int originalValue = 1;
+        String value = "" + originalValue;
+        calendarConfiguration.put(propertyName, value);
+        int retrieved = CalendarBean.getPropertyAsInt(propertyName, 
calendarConfiguration);
+        assertThat(retrieved).isEqualTo(originalValue);
+        value = "WRONG";
+        calendarConfiguration.put(propertyName, value);
+        NumberFormatException thrown = 
assertThrows(NumberFormatException.class, () -> 
CalendarBean.getPropertyAsInt(propertyName, calendarConfiguration));
+        String expectedMessage = "For input string: \"WRONG\"";
+        assertThat(thrown.getMessage()).isEqualTo(expectedMessage);
+    }
+
+    @Test
+    void validateRequiredProperty() {
+        Properties calendarConfiguration = new Properties();
+        String propertyName = "propertyName";
+        String value = "propertyValue";
+        calendarConfiguration.put(propertyName, value);
+        StringBuilder errorMessage = new StringBuilder();
+        CalendarBean.validateRequiredProperty(propertyName, errorMessage, 
calendarConfiguration);
+        assertThat(errorMessage).isEmpty();
+        CalendarBean.validateRequiredProperty("missingProperty", errorMessage, 
calendarConfiguration);
+        String[] retrievedErrors = errorMessage.toString().split("\n");
+        assertThat(retrievedErrors).hasSize(1);
+        assertThat(retrievedErrors).contains("Property missingProperty is 
required");
+    }
+
+    @Test
+    void getFormattedDate() throws ParseException {
+        Properties calendarConfiguration = new Properties();
+        String dateFormat = "dd-MM-yyyy";
+        String date = "27-11-2024";
+        calendarConfiguration.put(HOLIDAY_DATE_FORMAT, dateFormat);
+        Date retrieved = CalendarBean.getFormattedDate(date, 
calendarConfiguration);
+        Date expected = 
CalendarBean.getSimpleDateFormat(dateFormat).parse(date);
+        assertThat(retrieved).isEqualTo(expected);
+
+    }
+
+    @Test
+    void getSimpleDateFormat() {
+        SimpleDateFormat retrieved = 
CalendarBean.getSimpleDateFormat(DEFAULT_HOLIDAY_DATE_FORMAT);
+        assertThat(retrieved).isNotNull();
+        retrieved = CalendarBean.getSimpleDateFormat("dd-MM-yyyy");
+        assertThat(retrieved).isNotNull();
+        String wrong = "WRONG";
+        IllegalArgumentException thrown = 
assertThrows(IllegalArgumentException.class, () -> 
CalendarBean.getSimpleDateFormat(wrong));

Review Comment:
   do not use assertions from JUnit5. Use assertions from assertj. This is the 
standard in Kie.



##########
jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/CalendarBeanTest.java:
##########
@@ -0,0 +1,295 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.jbpm.process.core.timer;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.END_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAYS;
+import static 
org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.START_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.TIMEZONE;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.WEEKEND_DAYS;
+import static 
org.jbpm.process.core.timer.CalendarBean.DEFAULT_HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_TIMEZONE;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKENDS;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKEND_DAYS;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CalendarBeanTest {
+
+    // Static validation methods
+    @ParameterizedTest
+    @MethodSource("getMissingPropertiesCalendar")
+    void requiredPropertyValidation(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.requiredPropertyValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getWronglyFormatPropertiesCalendar")
+    void propertyFormatValidation(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.propertyFormatValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getBusinessInvalidPropertiesCalendar")
+    void businessValidation(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.businessValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getPartialPropertiesCalendar")
+    void missingDataPopulation(Map<String, Object> propertyMap, Map<String, 
Object> defaultValuesMap) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        defaultValuesMap.keySet().forEach(key -> 
assertThat(calendarConfiguration.containsKey(key)).isFalse());
+        CalendarBean.missingDataPopulation(calendarConfiguration);
+        defaultValuesMap.forEach((key, value) -> {
+            assertThat(calendarConfiguration.containsKey(key)).isTrue();
+            
assertThat(calendarConfiguration.getProperty(key)).isEqualTo(value);
+        });
+    }
+
+    @Test
+    void getPropertyAsInt() {
+        Properties calendarConfiguration = new Properties();
+        String propertyName = "propertyName";
+        int originalValue = 1;
+        String value = "" + originalValue;
+        calendarConfiguration.put(propertyName, value);
+        int retrieved = CalendarBean.getPropertyAsInt(propertyName, 
calendarConfiguration);
+        assertThat(retrieved).isEqualTo(originalValue);
+        value = "WRONG";
+        calendarConfiguration.put(propertyName, value);
+        NumberFormatException thrown = 
assertThrows(NumberFormatException.class, () -> 
CalendarBean.getPropertyAsInt(propertyName, calendarConfiguration));
+        String expectedMessage = "For input string: \"WRONG\"";
+        assertThat(thrown.getMessage()).isEqualTo(expectedMessage);
+    }
+
+    @Test
+    void validateRequiredProperty() {
+        Properties calendarConfiguration = new Properties();
+        String propertyName = "propertyName";
+        String value = "propertyValue";
+        calendarConfiguration.put(propertyName, value);
+        StringBuilder errorMessage = new StringBuilder();
+        CalendarBean.validateRequiredProperty(propertyName, errorMessage, 
calendarConfiguration);
+        assertThat(errorMessage).isEmpty();
+        CalendarBean.validateRequiredProperty("missingProperty", errorMessage, 
calendarConfiguration);
+        String[] retrievedErrors = errorMessage.toString().split("\n");
+        assertThat(retrievedErrors).hasSize(1);
+        assertThat(retrievedErrors).contains("Property missingProperty is 
required");
+    }
+
+    @Test
+    void getFormattedDate() throws ParseException {
+        Properties calendarConfiguration = new Properties();
+        String dateFormat = "dd-MM-yyyy";
+        String date = "27-11-2024";
+        calendarConfiguration.put(HOLIDAY_DATE_FORMAT, dateFormat);
+        Date retrieved = CalendarBean.getFormattedDate(date, 
calendarConfiguration);
+        Date expected = 
CalendarBean.getSimpleDateFormat(dateFormat).parse(date);
+        assertThat(retrieved).isEqualTo(expected);
+
+    }
+
+    @Test
+    void getSimpleDateFormat() {
+        SimpleDateFormat retrieved = 
CalendarBean.getSimpleDateFormat(DEFAULT_HOLIDAY_DATE_FORMAT);
+        assertThat(retrieved).isNotNull();
+        retrieved = CalendarBean.getSimpleDateFormat("dd-MM-yyyy");
+        assertThat(retrieved).isNotNull();
+        String wrong = "WRONG";
+        IllegalArgumentException thrown = 
assertThrows(IllegalArgumentException.class, () -> 
CalendarBean.getSimpleDateFormat(wrong));
+        String expectedMessage = "Illegal pattern character 'R'";
+        assertThat(thrown.getMessage()).isEqualTo(expectedMessage);
+    }
+
+    // Instance methods
+    @ParameterizedTest
+    @MethodSource("getMissingPropertiesCalendar")
+    void requiredPropertyMissing(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonIllegalArgumentAssertion(() -> new 
CalendarBean(calendarConfiguration), errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getWronglyFormatPropertiesCalendar")
+    void propertyWrongFormat(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonIllegalArgumentAssertion(() -> new 
CalendarBean(calendarConfiguration), errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getBusinessInvalidPropertiesCalendar")
+    void businessInvalid(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonIllegalArgumentAssertion(() -> new 
CalendarBean(calendarConfiguration), errorMessages);
+    }
+
+    @Test
+    void instantiationFull() throws ParseException {
+        Properties calendarConfiguration = new Properties();
+        int startHour = 10;
+        int endHour = 16;
+        List<Integer> weekendDays = Arrays.asList(3, 4);
+        String dateFormat = "dd-MM-yyyy";
+        String timezone = "ACT";
+        String holidays = "27-11-2024";
+        calendarConfiguration.put(START_HOUR, String.valueOf(startHour));
+        calendarConfiguration.put(END_HOUR, String.valueOf(endHour));
+        calendarConfiguration.put(WEEKEND_DAYS, 
weekendDays.stream().map(String::valueOf).collect(Collectors.joining(",")));
+        calendarConfiguration.put(HOLIDAY_DATE_FORMAT, dateFormat);
+        calendarConfiguration.put(TIMEZONE, timezone);
+        calendarConfiguration.put(HOLIDAYS, holidays);
+        CalendarBean retrieved = new CalendarBean(calendarConfiguration);
+
+        Date from = CalendarBean.getFormattedDate(holidays, 
calendarConfiguration);
+        Date to = CalendarBean.getFormattedDate("28-11-2024", 
calendarConfiguration);
+        assertThat(retrieved.getHolidays()).isEqualTo(List.of(new 
BusinessCalendarImpl.TimePeriod(from, to)));
+        assertThat(retrieved.getWeekendDays()).isEqualTo(weekendDays);
+        assertThat(retrieved.getDaysPerWeek()).isEqualTo(7 - 
weekendDays.size());
+        assertThat(retrieved.getTimezone()).isEqualTo(timezone);
+        assertThat(retrieved.getStartHour()).isEqualTo(startHour);
+        assertThat(retrieved.getEndHour()).isEqualTo(endHour);
+        assertThat(retrieved.getHoursInDay()).isEqualTo(endHour - startHour);
+    }
+
+    @Test
+    void instantiationPartial() {
+        Properties calendarConfiguration = new Properties();
+        int startHour = 10;
+        int endHour = 16;
+        calendarConfiguration.put(START_HOUR, String.valueOf(startHour));
+        calendarConfiguration.put(END_HOUR, String.valueOf(endHour));
+        CalendarBean retrieved = new CalendarBean(calendarConfiguration);
+        assertThat(retrieved.getHolidays()).isEqualTo(Collections.emptyList());
+        assertThat(retrieved.getWeekendDays()).isEqualTo(DEFAULT_WEEKEND_DAYS);
+        assertThat(retrieved.getDaysPerWeek()).isEqualTo(5);
+        assertThat(retrieved.getTimezone()).isEqualTo(DEFAULT_TIMEZONE);
+        assertThat(retrieved.getStartHour()).isEqualTo(startHour);
+        assertThat(retrieved.getEndHour()).isEqualTo(endHour);
+        assertThat(retrieved.getHoursInDay()).isEqualTo(endHour - startHour);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getMissingPropertiesCalendar")
+    void missingProperties(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonIllegalArgumentAssertion(() -> new 
CalendarBean(calendarConfiguration), errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getInvalidPropertiesCalendar")
+    public void invalidProperties(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonIllegalArgumentAssertion(() -> new 
CalendarBean(calendarConfiguration), errorMessages);
+    }
+
+    // Let's avoid duplication
+    private void commonStaticMethodValidation(Consumer<StringBuilder> 
executedMethod,
+            List<String> errorMessages) {
+        StringBuilder errors = new StringBuilder();
+        assertThat(errors).isEmpty();
+        executedMethod.accept(errors);
+        assertThat(errors).isNotEmpty();
+        String[] retrievedErrors = errors.toString().split("\n");
+        assertThat(retrievedErrors).hasSize(errorMessages.size());
+        assertThat(retrievedErrors).contains(retrievedErrors);
+    }
+
+    private void commonIllegalArgumentAssertion(Executable executedMethod, 
List<String> errorMessages) {
+        IllegalArgumentException exception = 
assertThrows(IllegalArgumentException.class, executedMethod);

Review Comment:
   do not use assertions from JUnit5. Use assertions from assertj. This is the 
standard in Kie.



##########
jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/CalendarBeanTest.java:
##########
@@ -0,0 +1,295 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.jbpm.process.core.timer;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.END_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAYS;
+import static 
org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.START_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.TIMEZONE;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.WEEKEND_DAYS;
+import static 
org.jbpm.process.core.timer.CalendarBean.DEFAULT_HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_TIMEZONE;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKENDS;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKEND_DAYS;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CalendarBeanTest {
+
+    // Static validation methods
+    @ParameterizedTest
+    @MethodSource("getMissingPropertiesCalendar")
+    void requiredPropertyValidation(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.requiredPropertyValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getWronglyFormatPropertiesCalendar")
+    void propertyFormatValidation(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.propertyFormatValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getBusinessInvalidPropertiesCalendar")
+    void businessValidation(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.businessValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getPartialPropertiesCalendar")
+    void missingDataPopulation(Map<String, Object> propertyMap, Map<String, 
Object> defaultValuesMap) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        defaultValuesMap.keySet().forEach(key -> 
assertThat(calendarConfiguration.containsKey(key)).isFalse());
+        CalendarBean.missingDataPopulation(calendarConfiguration);
+        defaultValuesMap.forEach((key, value) -> {
+            assertThat(calendarConfiguration.containsKey(key)).isTrue();
+            
assertThat(calendarConfiguration.getProperty(key)).isEqualTo(value);
+        });
+    }
+
+    @Test
+    void getPropertyAsInt() {
+        Properties calendarConfiguration = new Properties();
+        String propertyName = "propertyName";
+        int originalValue = 1;
+        String value = "" + originalValue;
+        calendarConfiguration.put(propertyName, value);
+        int retrieved = CalendarBean.getPropertyAsInt(propertyName, 
calendarConfiguration);
+        assertThat(retrieved).isEqualTo(originalValue);
+        value = "WRONG";
+        calendarConfiguration.put(propertyName, value);
+        NumberFormatException thrown = 
assertThrows(NumberFormatException.class, () -> 
CalendarBean.getPropertyAsInt(propertyName, calendarConfiguration));
+        String expectedMessage = "For input string: \"WRONG\"";
+        assertThat(thrown.getMessage()).isEqualTo(expectedMessage);
+    }
+
+    @Test
+    void validateRequiredProperty() {
+        Properties calendarConfiguration = new Properties();
+        String propertyName = "propertyName";
+        String value = "propertyValue";
+        calendarConfiguration.put(propertyName, value);
+        StringBuilder errorMessage = new StringBuilder();
+        CalendarBean.validateRequiredProperty(propertyName, errorMessage, 
calendarConfiguration);
+        assertThat(errorMessage).isEmpty();
+        CalendarBean.validateRequiredProperty("missingProperty", errorMessage, 
calendarConfiguration);
+        String[] retrievedErrors = errorMessage.toString().split("\n");
+        assertThat(retrievedErrors).hasSize(1);
+        assertThat(retrievedErrors).contains("Property missingProperty is 
required");
+    }
+
+    @Test
+    void getFormattedDate() throws ParseException {
+        Properties calendarConfiguration = new Properties();
+        String dateFormat = "dd-MM-yyyy";
+        String date = "27-11-2024";
+        calendarConfiguration.put(HOLIDAY_DATE_FORMAT, dateFormat);
+        Date retrieved = CalendarBean.getFormattedDate(date, 
calendarConfiguration);
+        Date expected = 
CalendarBean.getSimpleDateFormat(dateFormat).parse(date);
+        assertThat(retrieved).isEqualTo(expected);
+
+    }
+
+    @Test
+    void getSimpleDateFormat() {
+        SimpleDateFormat retrieved = 
CalendarBean.getSimpleDateFormat(DEFAULT_HOLIDAY_DATE_FORMAT);
+        assertThat(retrieved).isNotNull();
+        retrieved = CalendarBean.getSimpleDateFormat("dd-MM-yyyy");
+        assertThat(retrieved).isNotNull();
+        String wrong = "WRONG";
+        IllegalArgumentException thrown = 
assertThrows(IllegalArgumentException.class, () -> 
CalendarBean.getSimpleDateFormat(wrong));
+        String expectedMessage = "Illegal pattern character 'R'";
+        assertThat(thrown.getMessage()).isEqualTo(expectedMessage);
+    }
+
+    // Instance methods
+    @ParameterizedTest
+    @MethodSource("getMissingPropertiesCalendar")
+    void requiredPropertyMissing(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonIllegalArgumentAssertion(() -> new 
CalendarBean(calendarConfiguration), errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getWronglyFormatPropertiesCalendar")
+    void propertyWrongFormat(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonIllegalArgumentAssertion(() -> new 
CalendarBean(calendarConfiguration), errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getBusinessInvalidPropertiesCalendar")
+    void businessInvalid(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonIllegalArgumentAssertion(() -> new 
CalendarBean(calendarConfiguration), errorMessages);
+    }
+
+    @Test
+    void instantiationFull() throws ParseException {
+        Properties calendarConfiguration = new Properties();
+        int startHour = 10;
+        int endHour = 16;
+        List<Integer> weekendDays = Arrays.asList(3, 4);
+        String dateFormat = "dd-MM-yyyy";
+        String timezone = "ACT";
+        String holidays = "27-11-2024";
+        calendarConfiguration.put(START_HOUR, String.valueOf(startHour));
+        calendarConfiguration.put(END_HOUR, String.valueOf(endHour));
+        calendarConfiguration.put(WEEKEND_DAYS, 
weekendDays.stream().map(String::valueOf).collect(Collectors.joining(",")));
+        calendarConfiguration.put(HOLIDAY_DATE_FORMAT, dateFormat);
+        calendarConfiguration.put(TIMEZONE, timezone);
+        calendarConfiguration.put(HOLIDAYS, holidays);
+        CalendarBean retrieved = new CalendarBean(calendarConfiguration);
+
+        Date from = CalendarBean.getFormattedDate(holidays, 
calendarConfiguration);
+        Date to = CalendarBean.getFormattedDate("28-11-2024", 
calendarConfiguration);
+        assertThat(retrieved.getHolidays()).isEqualTo(List.of(new 
BusinessCalendarImpl.TimePeriod(from, to)));
+        assertThat(retrieved.getWeekendDays()).isEqualTo(weekendDays);
+        assertThat(retrieved.getDaysPerWeek()).isEqualTo(7 - 
weekendDays.size());
+        assertThat(retrieved.getTimezone()).isEqualTo(timezone);
+        assertThat(retrieved.getStartHour()).isEqualTo(startHour);
+        assertThat(retrieved.getEndHour()).isEqualTo(endHour);
+        assertThat(retrieved.getHoursInDay()).isEqualTo(endHour - startHour);
+    }
+
+    @Test
+    void instantiationPartial() {
+        Properties calendarConfiguration = new Properties();
+        int startHour = 10;
+        int endHour = 16;
+        calendarConfiguration.put(START_HOUR, String.valueOf(startHour));
+        calendarConfiguration.put(END_HOUR, String.valueOf(endHour));
+        CalendarBean retrieved = new CalendarBean(calendarConfiguration);
+        assertThat(retrieved.getHolidays()).isEqualTo(Collections.emptyList());
+        assertThat(retrieved.getWeekendDays()).isEqualTo(DEFAULT_WEEKEND_DAYS);
+        assertThat(retrieved.getDaysPerWeek()).isEqualTo(5);
+        assertThat(retrieved.getTimezone()).isEqualTo(DEFAULT_TIMEZONE);
+        assertThat(retrieved.getStartHour()).isEqualTo(startHour);
+        assertThat(retrieved.getEndHour()).isEqualTo(endHour);
+        assertThat(retrieved.getHoursInDay()).isEqualTo(endHour - startHour);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getMissingPropertiesCalendar")
+    void missingProperties(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonIllegalArgumentAssertion(() -> new 
CalendarBean(calendarConfiguration), errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getInvalidPropertiesCalendar")
+    public void invalidProperties(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonIllegalArgumentAssertion(() -> new 
CalendarBean(calendarConfiguration), errorMessages);
+    }
+
+    // Let's avoid duplication
+    private void commonStaticMethodValidation(Consumer<StringBuilder> 
executedMethod,
+            List<String> errorMessages) {
+        StringBuilder errors = new StringBuilder();
+        assertThat(errors).isEmpty();
+        executedMethod.accept(errors);
+        assertThat(errors).isNotEmpty();
+        String[] retrievedErrors = errors.toString().split("\n");
+        assertThat(retrievedErrors).hasSize(errorMessages.size());
+        assertThat(retrievedErrors).contains(retrievedErrors);
+    }
+
+    private void commonIllegalArgumentAssertion(Executable executedMethod, 
List<String> errorMessages) {
+        IllegalArgumentException exception = 
assertThrows(IllegalArgumentException.class, executedMethod);
+        errorMessages.forEach(msg -> 
assertTrue(exception.getMessage().contains(msg)));

Review Comment:
   do not use assertions from JUnit5. Use assertions from assertj. This is the 
standard in Kie.



##########
jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/BusinessCalendarImplTest.java:
##########
@@ -18,419 +18,323 @@
  */
 package org.jbpm.process.core.timer;
 
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
 import java.util.Calendar;
+import java.util.Collections;
 import java.util.Date;
+import java.util.List;
 import java.util.Properties;
-import java.util.concurrent.TimeUnit;
+import java.util.function.BiFunction;
+import java.util.stream.IntStream;
 
 import org.jbpm.test.util.AbstractBaseTest;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
-import org.kie.kogito.timer.SessionPseudoClock;
 import org.slf4j.LoggerFactory;
 
+import static java.time.temporal.ChronoUnit.DAYS;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.END_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAYS;
+import static 
org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.START_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.WEEKEND_DAYS;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-public class BusinessCalendarImplTest extends AbstractBaseTest {
+class BusinessCalendarImplTest extends AbstractBaseTest {
 
     public void addLogger() {
         logger = LoggerFactory.getLogger(this.getClass());
     }
 
     @Test
-    public void testCalculateHours() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-04 16:45";
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-04 13:45").getTime());
-
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("3h");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void instantiate() {
+        BusinessCalendarImpl retrieved = 
BusinessCalendarImpl.builder().build();
+        assertThat(retrieved).isNotNull();
+        retrieved = BusinessCalendarImpl.builder()
+                .withCalendarBean(CalendarBeanFactory.createCalendarBean())
+                .build();
+        assertThat(retrieved).isNotNull();
+
+        Properties calendarConfiguration = new Properties();
+        int startHour = 10;
+        int endHour = 16;
+        calendarConfiguration.put(START_HOUR, String.valueOf(startHour));
+        calendarConfiguration.put(END_HOUR, String.valueOf(endHour));
+        retrieved = BusinessCalendarImpl.builder()
+                .withCalendarBean(new CalendarBean(calendarConfiguration))
+                .build();
+        assertThat(retrieved).isNotNull();
     }
 
     @Test
-    public void testCalculateHoursCustomWorkingHours() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOURS_PER_DAY, "6");
-        String expectedDate = "2012-05-04 15:45";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-03 13:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("8h");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateInsideDailyWorkingHourWithDelay() {
+        int daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 3, 
daysToSkip, null, null);
     }
 
     @Test
-    public void testCalculateHoursPassingOverWeekend() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-07 12:45";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-04 13:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("7h");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateInsideDailyWorkingHourWithoutDelay() {
+        int daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 0, 
daysToSkip, null, null);
     }
 
+    @Disabled("TO FIX")
     @Test
-    public void testCalculateHoursPassingOverCustomDefinedWeekend() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.WEEKEND_DAYS, Calendar.FRIDAY 
+ "," + Calendar.SATURDAY);
-        String expectedDate = "2012-05-06 12:45";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-03 13:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("7h");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateInsideNightlyWorkingHour() {
+        int daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(4, -4, 0, 3, 
daysToSkip, null, null);
     }
 
     @Test
-    public void testCalculateMinutesPassingOverWeekend() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-07 09:15";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-04 16:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("30m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateBeforeWorkingHourWithDelay() {
+        int daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(2, 4, -1, 1, 
daysToSkip, null, null);
     }
 
     @Test
-    public void testCalculateMinutesPassingOverHoliday() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, 
"2012-05-12:2012-05-19");
-        String expectedDate = "2012-05-21 09:15";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-11 16:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("30m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateBeforeWorkingHourWithoutDelay() {
+        int daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-1, 4, -2, 1, 
daysToSkip, null, null);
     }
 
     @Test
-    public void testCalculateDays() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-14 09:00";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDate("2012-05-04").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("6d");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateAfterWorkingHour() {
+        int daysToSkip = 1;
+        commonCalculateBusinessTimeAsDateAssertAtStartHour(-1, 2, 3, 3, 
daysToSkip, null, null);
     }
 
     @Test
-    public void testCalculateDaysStartingInWeekend() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-09 09:00";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDate("2012-05-05").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("2d");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateWhenTodayAndTomorrowAreHolidays() {
+        String holidayDateFormat = "yyyy-MM-dd";
+        DateTimeFormatter sdf = DateTimeFormatter.ofPattern(holidayDateFormat);
+        LocalDate today = LocalDate.now();
+        LocalDate tomorrow = today.plusDays(1);
+        String holidays = sdf.format(today) + "," + sdf.format(tomorrow);
+        int daysToSkip = 2;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 3, 
daysToSkip, holidayDateFormat, holidays);
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 5, 3, 
daysToSkip, holidayDateFormat, holidays);
     }
 
     @Test
-    public void testCalculateDaysCustomWorkingDays() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.DAYS_PER_WEEK, "4");
-        config.setProperty(BusinessCalendarImpl.WEEKEND_DAYS, Calendar.FRIDAY 
+ "," + Calendar.SATURDAY + "," + Calendar.SUNDAY);
-        String expectedDate = "2012-05-15 14:30";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-03 14:30").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("6d");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateWhenNextDayIsHoliday() {
+        String holidayDateFormat = "yyyy-MM-dd";
+        DateTimeFormatter sdf = DateTimeFormatter.ofPattern(holidayDateFormat);
+        LocalDate tomorrow = LocalDate.now().plusDays(1);
+        String holidays = sdf.format(tomorrow);
+        int daysToSkip = 2;
+
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 4, 
daysToSkip, holidayDateFormat, holidays);
+        daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 3, 
daysToSkip, holidayDateFormat, holidays);
     }
 
     @Test
-    public void testCalculateDaysMiddleDay() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-11 12:27";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-03 12:27").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("6d");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void rollCalendarToDailyWorkingHour() {
+        int startHour = 14;
+        int endHour = 16;
+        Calendar toRoll = Calendar.getInstance();
+        int currentHour = 8;
+        toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+        int dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+        BusinessCalendarImpl.rollCalendarToDailyWorkingHour(toRoll, startHour, 
endHour);
+        assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+        assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear);
+
+        toRoll = Calendar.getInstance();
+        currentHour = 19;
+        toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+        dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+        BusinessCalendarImpl.rollCalendarToDailyWorkingHour(toRoll, startHour, 
endHour);
+        assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+        assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear + 1);
     }
 
+    @Disabled("TO FIX 
https://github.com/apache/incubator-kie-issues/issues/1651";)
     @Test
-    public void testCalculateDaysHoursMinutes() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-14 14:20";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDate("2012-05-04").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("6d4h80m");
+    void rollCalendarToNightlyWorkingHour() {
+        int startHour = 20;
+        int endHour = 4;
+        Calendar toRoll = Calendar.getInstance();
+        int currentHour = 21;
+        toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+        int dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+        BusinessCalendarImpl.rollCalendarToNightlyWorkingHour(toRoll, 
startHour, endHour);
+        assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+        assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear);
+
+        toRoll = Calendar.getInstance();
+        currentHour = 3;
+        toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+        dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+        BusinessCalendarImpl.rollCalendarToNightlyWorkingHour(toRoll, 
startHour, endHour);
+        assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+        assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear + 1);
 
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
     }
 
+    @Disabled("TO FIX 
https://github.com/apache/incubator-kie-issues/issues/1651";)
     @Test
-    public void testCalculateTimeDaysHoursMinutesHolidays() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, 
"2012-05-10:2012-05-19");
-        String expectedDate = "2012-05-21 14:20";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDate("2012-05-04").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("6d4h80m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void rollCalendarAfterHolidays() {
+        Instant now = Instant.now();
+        int holidayLeft = 4;
+        Instant startHolidayInstant = now.minus(2, DAYS);
+        Instant endHolidayInstant = now.plus(holidayLeft, DAYS);
+        Date startHoliday = Date.from(startHolidayInstant);
+        Date endHoliday = Date.from(endHolidayInstant);
+        List<BusinessCalendarImpl.TimePeriod> holidays = 
Collections.singletonList(new BusinessCalendarImpl.TimePeriod(startHoliday, 
endHoliday));
+        List<Integer> weekendDays = Collections.emptyList();
+        Calendar calendar = Calendar.getInstance();
+        int currentDayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
+        BusinessCalendarImpl.rollCalendarAfterHolidays(calendar, holidays, 
weekendDays, false);
+        int expected = currentDayOfYear + holidayLeft + 1;
+        assertThat(calendar.get(Calendar.DAY_OF_YEAR)).isEqualTo(expected);
     }
 
     @Test
-    public void testCalculateTimeDaysHoursMinutesSingleDayHolidays() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, "2012-05-07");
-        String expectedDate = "2012-05-08 13:20";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDate("2012-05-04").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("1d4h20m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking() {
+        List<Integer> workingDays = IntStream.range(Calendar.MONDAY, 
Calendar.SATURDAY).boxed().toList();
+        List<Integer> weekendDays = Arrays.asList(Calendar.SATURDAY, 
Calendar.SUNDAY);
+        boolean resetTime = false;
+        workingDays.forEach(workingDay -> {
+            Calendar calendar = getCalendarAtExpectedWeekDay(workingDay);
+            
BusinessCalendarImpl.rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking(calendar,
 weekendDays, resetTime);
+            
assertThat(calendar.get(Calendar.DAY_OF_WEEK)).isEqualTo(workingDay);
+        });
+        weekendDays.forEach(weekendDay -> {
+            Calendar calendar = getCalendarAtExpectedWeekDay(weekendDay);
+            
BusinessCalendarImpl.rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking(calendar,
 weekendDays, resetTime);
+            
assertThat(calendar.get(Calendar.DAY_OF_WEEK)).isEqualTo(Calendar.MONDAY);
+        });
     }
 
     @Test
-    public void 
testCalculateTimeDaysHoursMinutesSingleDayHolidaysInMiddleOfWeek() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, "2012-05-09");
-        String expectedDate = "2012-05-10 15:30";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-08 11:10").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("1d4h20m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void isWorkingDay() {
+        List<Integer> workingDays = IntStream.range(Calendar.MONDAY, 
Calendar.SATURDAY).boxed().toList();
+        List<Integer> weekendDays = Arrays.asList(Calendar.SATURDAY, 
Calendar.SUNDAY);
+        workingDays.forEach(workingDay -> 
assertThat(BusinessCalendarImpl.isWorkingDay(weekendDays, 
workingDay)).isTrue());
+        weekendDays.forEach(workingDay -> 
assertThat(BusinessCalendarImpl.isWorkingDay(weekendDays, 
workingDay)).isFalse());
     }
 
-    @Test
-    public void testCalculateDaysPassingOverHolidayAtYearEnd() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, 
"2012-12-31:2013-01-01");
-        String expectedDate = "2013-01-04 09:15";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-12-28 16:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("2d30m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    private void commonCalculateBusinessTimeAsDateAssertBetweenHours(int 
startHourGap, int endHourGap, int testingCalendarHourGap, int 
executionHourDelay, int daysToSkip, String holidayDateFormat,
+            String holidays) {
+        BiFunction<Instant, Instant, Boolean> startBooleanCondition = 
(resultInstant, expectedStartTime) -> {
+            logger.debug("Check if {} is after or equal to {} ", 
resultInstant, expectedStartTime);
+            return !resultInstant.isBefore(expectedStartTime);
+        };
+        commonCalculateBusinessTimeAsDate(startHourGap,
+                endHourGap,
+                testingCalendarHourGap,
+                executionHourDelay,
+                daysToSkip,
+                holidayDateFormat,
+                holidays,
+                startBooleanCondition);
     }
 
-    @Test
-    public void testCalculateDaysPassingOverHolidayAtYearEndWithWildcards() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, "*-12-31:*-01-01");
-        String expectedDate = "2013-01-02 09:15";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-12-28 16:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("2d30m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    private void commonCalculateBusinessTimeAsDateAssertAtStartHour(int 
startHourGap, int endHourGap, int testingCalendarHourGap, int 
executionHourDelay, int daysToSkip, String holidayDateFormat,
+            String holidays) {
+        BiFunction<Instant, Instant, Boolean> startBooleanCondition = 
(resultInstant, expectedStartTime) -> {
+            logger.debug("Check if {} is equal to {} ", resultInstant, 
expectedStartTime);
+            return resultInstant.getEpochSecond() == 
expectedStartTime.getEpochSecond();
+        };
+        commonCalculateBusinessTimeAsDate(startHourGap,
+                endHourGap,
+                testingCalendarHourGap,
+                executionHourDelay,
+                daysToSkip,
+                holidayDateFormat,
+                holidays,
+                startBooleanCondition);
     }
 
-    @Test
-    public void testCalculateISOHours() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-04 16:45";
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-04 13:45").getTime());
-
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("PT3H");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
-    }
-
-    @Test
-    public void testCalculateISODaysAndHours() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, "2012-05-09");
-        String expectedDate = "2012-05-10 15:30";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-08 11:10").getTime());
-
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("P1DT4H20M");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
-    }
-
-    @Test
-    public void testSingleHolidayWithinGivenTime() {
-        final Properties props = new Properties();
-        props.put(BusinessCalendarImpl.HOLIDAYS, "2015-01-13");
-        String expectedDate = "2015-01-15 11:38";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTimeAndMillis("2015-01-08 
11:38:30.198").getTime());
-
-        BusinessCalendarImpl businessCalendarImpl = new 
BusinessCalendarImpl(props, clock);
-
-        Date result = businessCalendarImpl.calculateBusinessTimeAsDate("4d");
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
-    }
-
-    @Test
-    public void testCalculateMillisecondsAsDefault() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-04 16:45:10.000";
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTimeAndMillis("2012-05-04 
16:45:00.000").getTime());
-
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("10000");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm:ss.SSS", 
result)).isEqualTo(expectedDate);
-    }
-
-    @Test
-    public void testCalculateMinutesPassingAfterHour() {
-        Properties config = new Properties();
-        String currentDate = "2018-05-02 19:51:33";
-        String expectedDate = "2018-05-03 09:01:00";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime(currentDate).getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("1m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm:ss", 
result)).isEqualTo(expectedDate);
-    }
-
-    @Test
-    public void testBusinessCalendarWithoutProvidedConfiguration() {
-        assertDoesNotThrow(() -> new BusinessCalendarImpl());
-    }
-
-    @Test
-    public void testCalculateMinutesPassingHoliday() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.DAYS_PER_WEEK, "5");
-        config.setProperty(BusinessCalendarImpl.HOURS_PER_DAY, "8");
-        config.setProperty(BusinessCalendarImpl.START_HOUR, "9");
-        config.setProperty(BusinessCalendarImpl.END_HOUR, "18");
-        config.setProperty(BusinessCalendarImpl.WEEKEND_DAYS, "1,7"); // 
sun,sat
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, 
"2018-04-30,2018-05-03:2018-05-05");
-        config.setProperty(BusinessCalendarImpl.HOLIDAY_DATE_FORMAT, 
"yyyy-MM-dd");
-        String currentDate = "2018-05-03 13:51:33";
-        String duration = "10m";
-        String expectedDate = "2018-05-07 09:10:00";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime(currentDate).getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate(duration);
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm:ss", 
result)).isEqualTo(expectedDate);
-    }
+    private void commonCalculateBusinessTimeAsDate(int startHourGap,
+            int endHourGap, int testingCalendarHourGap,
+            int executionHourDelay, int daysToSkip, String holidayDateFormat, 
String holidays,
+            BiFunction<Instant, Instant, Boolean> startBooleanCondition) {
+        logger.debug("startHourGap {}", startHourGap);
+        logger.debug("endHourGap {}", endHourGap);
+        logger.debug("testingCalendarHourGap {}", testingCalendarHourGap);
+        logger.debug("executionHourDelay {}", executionHourDelay);
+        logger.debug("numberOfHolidays {}", daysToSkip);
+        logger.debug("holidayDateFormat {}", holidayDateFormat);
+        logger.debug("holidays {}", holidays);
+
+        // lets pretend 12.00 is the current time
+        Calendar testingCalendar = Calendar.getInstance();
+        testingCalendar.set(Calendar.HOUR_OF_DAY, 12);
+        testingCalendar.set(Calendar.MINUTE, 0);
+        testingCalendar.set(Calendar.SECOND, 0);
+        logger.debug("testingCalendar {}", testingCalendar.getTime());
+        Calendar startCalendar = (Calendar) testingCalendar.clone();
+        startCalendar.add(Calendar.HOUR_OF_DAY, startHourGap);
+        logger.debug("startCalendar {}", startCalendar.getTime());
+        Calendar endCalendar = (Calendar) testingCalendar.clone();
+        endCalendar.add(Calendar.HOUR_OF_DAY, endHourGap);
+        logger.debug("endCalendar {}", endCalendar.getTime());
+
+        int startHour = startCalendar.get(Calendar.HOUR_OF_DAY);
+        int endHour = endCalendar.get(Calendar.HOUR_OF_DAY);
+
+        int hoursInDay = startHour < endHour ? endHour - startHour : 24 - 
(startHour - endHour);
+        int daysToAdd = daysToSkip;
+        logger.debug("daysToAdd (= numberOfHolidays) {}", daysToAdd);
+        if (executionHourDelay >= hoursInDay) {
+            daysToAdd += executionHourDelay / hoursInDay;
+            logger.debug("daysToAdd += (hourDelay / hoursInDay) {}", 
daysToAdd);
+        }
+        if (daysToAdd > 0) {
+            startCalendar.add(Calendar.DAY_OF_YEAR, daysToAdd);
+            endCalendar.add(Calendar.DAY_OF_YEAR, daysToAdd);
+            logger.debug("startCalendar (startCalendar + days to add) {}", 
startCalendar.getTime());
+            logger.debug("endCalendar (endCalendar + days to add) {}", 
endCalendar.getTime());
+        }
 
-    @Test
-    public void testCalculateMinutesPassingWeekend() {
         Properties config = new Properties();
-        String currentDate = "2018-05-06 13:51:33";
-        String duration = "10m";
-        String expectedDate = "2018-05-07 09:10:00";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime(currentDate).getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate(duration);
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm:ss", 
result)).isEqualTo(expectedDate);
-    }
-
-    private Date parseToDate(String dateString) {
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
-
-        Date testTime;
-        try {
-            testTime = sdf.parse(dateString);
-
-            return testTime;
-        } catch (ParseException e) {
-            return null;
+        config.setProperty(START_HOUR, String.valueOf(startHour));
+        config.setProperty(END_HOUR, String.valueOf(endHour));
+        config.setProperty(WEEKEND_DAYS, "0");
+        if (holidayDateFormat != null) {
+            config.setProperty(HOLIDAY_DATE_FORMAT, holidayDateFormat);
         }
-    }
-
-    private Date parseToDateWithTime(String dateString) {
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
-
-        Date testTime;
-        try {
-            testTime = sdf.parse(dateString);
-
-            return testTime;
-        } catch (ParseException e) {
-            return null;
+        if (holidays != null) {
+            config.setProperty(HOLIDAYS, holidays);
         }
-    }
 
-    private Date parseToDateWithTimeAndMillis(String dateString) {
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
-
-        Date testTime;
-        try {
-            testTime = sdf.parse(dateString);
-
-            return testTime;
-        } catch (ParseException e) {
-            return null;
-        }
-    }
+        testingCalendar.add(Calendar.HOUR_OF_DAY, testingCalendarHourGap);
+        logger.debug("testingCalendar after testingCalendarHourGap {}", 
testingCalendar.getTime());
+        BusinessCalendarImpl businessCal = 
BusinessCalendarImpl.builder().withCalendarBean(new CalendarBean(config))
+                .withTestingCalendar(testingCalendar)
+                .build();
+        Date retrieved = 
businessCal.calculateBusinessTimeAsDate(String.format("%sh", 
executionHourDelay));
+        logger.debug("retrieved {}", retrieved);
 
-    private String formatDate(String pattern, Date date) {
-        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
+        Date expectedStart = startCalendar.getTime();
+        Date expectedEnd = endCalendar.getTime();
 
-        String testTime = sdf.format(date);
+        Instant retrievedInstant = retrieved.toInstant();
+        Instant expectedStartTime = expectedStart.toInstant();
+        Instant expectedEndTime = expectedEnd.toInstant();
 
-        return testTime;
+        logger.debug("retrievedInstant {}", retrievedInstant);
+        logger.debug("expectedStartTime {}", expectedStartTime);
+        logger.debug("expectedEndTime {}", expectedEndTime);
 
+        assertTrue(startBooleanCondition.apply(retrievedInstant, 
expectedStartTime));
+        logger.debug("Check if {} is not after {} ", retrievedInstant, 
expectedEndTime);
+        assertFalse(retrievedInstant.isAfter(expectedEndTime));

Review Comment:
   do not use assertions from JUnit5. Use assertions from assertj. This is the 
standard in Kie.



##########
jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/BusinessCalendarImplTest.java:
##########
@@ -18,419 +18,323 @@
  */
 package org.jbpm.process.core.timer;
 
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
 import java.util.Calendar;
+import java.util.Collections;
 import java.util.Date;
+import java.util.List;
 import java.util.Properties;
-import java.util.concurrent.TimeUnit;
+import java.util.function.BiFunction;
+import java.util.stream.IntStream;
 
 import org.jbpm.test.util.AbstractBaseTest;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
-import org.kie.kogito.timer.SessionPseudoClock;
 import org.slf4j.LoggerFactory;
 
+import static java.time.temporal.ChronoUnit.DAYS;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.END_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAYS;
+import static 
org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.START_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.WEEKEND_DAYS;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-public class BusinessCalendarImplTest extends AbstractBaseTest {
+class BusinessCalendarImplTest extends AbstractBaseTest {
 
     public void addLogger() {
         logger = LoggerFactory.getLogger(this.getClass());
     }
 
     @Test
-    public void testCalculateHours() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-04 16:45";
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-04 13:45").getTime());
-
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("3h");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void instantiate() {
+        BusinessCalendarImpl retrieved = 
BusinessCalendarImpl.builder().build();
+        assertThat(retrieved).isNotNull();
+        retrieved = BusinessCalendarImpl.builder()
+                .withCalendarBean(CalendarBeanFactory.createCalendarBean())
+                .build();
+        assertThat(retrieved).isNotNull();
+
+        Properties calendarConfiguration = new Properties();
+        int startHour = 10;
+        int endHour = 16;
+        calendarConfiguration.put(START_HOUR, String.valueOf(startHour));
+        calendarConfiguration.put(END_HOUR, String.valueOf(endHour));
+        retrieved = BusinessCalendarImpl.builder()
+                .withCalendarBean(new CalendarBean(calendarConfiguration))
+                .build();
+        assertThat(retrieved).isNotNull();
     }
 
     @Test
-    public void testCalculateHoursCustomWorkingHours() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOURS_PER_DAY, "6");
-        String expectedDate = "2012-05-04 15:45";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-03 13:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("8h");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateInsideDailyWorkingHourWithDelay() {
+        int daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 3, 
daysToSkip, null, null);
     }
 
     @Test
-    public void testCalculateHoursPassingOverWeekend() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-07 12:45";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-04 13:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("7h");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateInsideDailyWorkingHourWithoutDelay() {
+        int daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 0, 
daysToSkip, null, null);
     }
 
+    @Disabled("TO FIX")
     @Test
-    public void testCalculateHoursPassingOverCustomDefinedWeekend() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.WEEKEND_DAYS, Calendar.FRIDAY 
+ "," + Calendar.SATURDAY);
-        String expectedDate = "2012-05-06 12:45";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-03 13:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("7h");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateInsideNightlyWorkingHour() {
+        int daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(4, -4, 0, 3, 
daysToSkip, null, null);
     }
 
     @Test
-    public void testCalculateMinutesPassingOverWeekend() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-07 09:15";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-04 16:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("30m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateBeforeWorkingHourWithDelay() {
+        int daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(2, 4, -1, 1, 
daysToSkip, null, null);
     }
 
     @Test
-    public void testCalculateMinutesPassingOverHoliday() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, 
"2012-05-12:2012-05-19");
-        String expectedDate = "2012-05-21 09:15";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-11 16:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("30m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateBeforeWorkingHourWithoutDelay() {
+        int daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-1, 4, -2, 1, 
daysToSkip, null, null);
     }
 
     @Test
-    public void testCalculateDays() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-14 09:00";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDate("2012-05-04").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("6d");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateAfterWorkingHour() {
+        int daysToSkip = 1;
+        commonCalculateBusinessTimeAsDateAssertAtStartHour(-1, 2, 3, 3, 
daysToSkip, null, null);
     }
 
     @Test
-    public void testCalculateDaysStartingInWeekend() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-09 09:00";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDate("2012-05-05").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("2d");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateWhenTodayAndTomorrowAreHolidays() {
+        String holidayDateFormat = "yyyy-MM-dd";
+        DateTimeFormatter sdf = DateTimeFormatter.ofPattern(holidayDateFormat);
+        LocalDate today = LocalDate.now();
+        LocalDate tomorrow = today.plusDays(1);
+        String holidays = sdf.format(today) + "," + sdf.format(tomorrow);
+        int daysToSkip = 2;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 3, 
daysToSkip, holidayDateFormat, holidays);
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 5, 3, 
daysToSkip, holidayDateFormat, holidays);
     }
 
     @Test
-    public void testCalculateDaysCustomWorkingDays() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.DAYS_PER_WEEK, "4");
-        config.setProperty(BusinessCalendarImpl.WEEKEND_DAYS, Calendar.FRIDAY 
+ "," + Calendar.SATURDAY + "," + Calendar.SUNDAY);
-        String expectedDate = "2012-05-15 14:30";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-03 14:30").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("6d");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void calculateBusinessTimeAsDateWhenNextDayIsHoliday() {
+        String holidayDateFormat = "yyyy-MM-dd";
+        DateTimeFormatter sdf = DateTimeFormatter.ofPattern(holidayDateFormat);
+        LocalDate tomorrow = LocalDate.now().plusDays(1);
+        String holidays = sdf.format(tomorrow);
+        int daysToSkip = 2;
+
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 4, 
daysToSkip, holidayDateFormat, holidays);
+        daysToSkip = 0;
+        commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 3, 
daysToSkip, holidayDateFormat, holidays);
     }
 
     @Test
-    public void testCalculateDaysMiddleDay() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-11 12:27";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-03 12:27").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("6d");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void rollCalendarToDailyWorkingHour() {
+        int startHour = 14;
+        int endHour = 16;
+        Calendar toRoll = Calendar.getInstance();
+        int currentHour = 8;
+        toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+        int dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+        BusinessCalendarImpl.rollCalendarToDailyWorkingHour(toRoll, startHour, 
endHour);
+        assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+        assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear);
+
+        toRoll = Calendar.getInstance();
+        currentHour = 19;
+        toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+        dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+        BusinessCalendarImpl.rollCalendarToDailyWorkingHour(toRoll, startHour, 
endHour);
+        assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+        assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear + 1);
     }
 
+    @Disabled("TO FIX 
https://github.com/apache/incubator-kie-issues/issues/1651";)
     @Test
-    public void testCalculateDaysHoursMinutes() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-14 14:20";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDate("2012-05-04").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("6d4h80m");
+    void rollCalendarToNightlyWorkingHour() {
+        int startHour = 20;
+        int endHour = 4;
+        Calendar toRoll = Calendar.getInstance();
+        int currentHour = 21;
+        toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+        int dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+        BusinessCalendarImpl.rollCalendarToNightlyWorkingHour(toRoll, 
startHour, endHour);
+        assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+        assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear);
+
+        toRoll = Calendar.getInstance();
+        currentHour = 3;
+        toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+        dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+        BusinessCalendarImpl.rollCalendarToNightlyWorkingHour(toRoll, 
startHour, endHour);
+        assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+        assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear + 1);
 
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
     }
 
+    @Disabled("TO FIX 
https://github.com/apache/incubator-kie-issues/issues/1651";)
     @Test
-    public void testCalculateTimeDaysHoursMinutesHolidays() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, 
"2012-05-10:2012-05-19");
-        String expectedDate = "2012-05-21 14:20";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDate("2012-05-04").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("6d4h80m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void rollCalendarAfterHolidays() {
+        Instant now = Instant.now();
+        int holidayLeft = 4;
+        Instant startHolidayInstant = now.minus(2, DAYS);
+        Instant endHolidayInstant = now.plus(holidayLeft, DAYS);
+        Date startHoliday = Date.from(startHolidayInstant);
+        Date endHoliday = Date.from(endHolidayInstant);
+        List<BusinessCalendarImpl.TimePeriod> holidays = 
Collections.singletonList(new BusinessCalendarImpl.TimePeriod(startHoliday, 
endHoliday));
+        List<Integer> weekendDays = Collections.emptyList();
+        Calendar calendar = Calendar.getInstance();
+        int currentDayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
+        BusinessCalendarImpl.rollCalendarAfterHolidays(calendar, holidays, 
weekendDays, false);
+        int expected = currentDayOfYear + holidayLeft + 1;
+        assertThat(calendar.get(Calendar.DAY_OF_YEAR)).isEqualTo(expected);
     }
 
     @Test
-    public void testCalculateTimeDaysHoursMinutesSingleDayHolidays() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, "2012-05-07");
-        String expectedDate = "2012-05-08 13:20";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDate("2012-05-04").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("1d4h20m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking() {
+        List<Integer> workingDays = IntStream.range(Calendar.MONDAY, 
Calendar.SATURDAY).boxed().toList();
+        List<Integer> weekendDays = Arrays.asList(Calendar.SATURDAY, 
Calendar.SUNDAY);
+        boolean resetTime = false;
+        workingDays.forEach(workingDay -> {
+            Calendar calendar = getCalendarAtExpectedWeekDay(workingDay);
+            
BusinessCalendarImpl.rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking(calendar,
 weekendDays, resetTime);
+            
assertThat(calendar.get(Calendar.DAY_OF_WEEK)).isEqualTo(workingDay);
+        });
+        weekendDays.forEach(weekendDay -> {
+            Calendar calendar = getCalendarAtExpectedWeekDay(weekendDay);
+            
BusinessCalendarImpl.rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking(calendar,
 weekendDays, resetTime);
+            
assertThat(calendar.get(Calendar.DAY_OF_WEEK)).isEqualTo(Calendar.MONDAY);
+        });
     }
 
     @Test
-    public void 
testCalculateTimeDaysHoursMinutesSingleDayHolidaysInMiddleOfWeek() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, "2012-05-09");
-        String expectedDate = "2012-05-10 15:30";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-08 11:10").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("1d4h20m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    void isWorkingDay() {
+        List<Integer> workingDays = IntStream.range(Calendar.MONDAY, 
Calendar.SATURDAY).boxed().toList();
+        List<Integer> weekendDays = Arrays.asList(Calendar.SATURDAY, 
Calendar.SUNDAY);
+        workingDays.forEach(workingDay -> 
assertThat(BusinessCalendarImpl.isWorkingDay(weekendDays, 
workingDay)).isTrue());
+        weekendDays.forEach(workingDay -> 
assertThat(BusinessCalendarImpl.isWorkingDay(weekendDays, 
workingDay)).isFalse());
     }
 
-    @Test
-    public void testCalculateDaysPassingOverHolidayAtYearEnd() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, 
"2012-12-31:2013-01-01");
-        String expectedDate = "2013-01-04 09:15";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-12-28 16:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("2d30m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    private void commonCalculateBusinessTimeAsDateAssertBetweenHours(int 
startHourGap, int endHourGap, int testingCalendarHourGap, int 
executionHourDelay, int daysToSkip, String holidayDateFormat,
+            String holidays) {
+        BiFunction<Instant, Instant, Boolean> startBooleanCondition = 
(resultInstant, expectedStartTime) -> {
+            logger.debug("Check if {} is after or equal to {} ", 
resultInstant, expectedStartTime);
+            return !resultInstant.isBefore(expectedStartTime);
+        };
+        commonCalculateBusinessTimeAsDate(startHourGap,
+                endHourGap,
+                testingCalendarHourGap,
+                executionHourDelay,
+                daysToSkip,
+                holidayDateFormat,
+                holidays,
+                startBooleanCondition);
     }
 
-    @Test
-    public void testCalculateDaysPassingOverHolidayAtYearEndWithWildcards() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, "*-12-31:*-01-01");
-        String expectedDate = "2013-01-02 09:15";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-12-28 16:45").getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("2d30m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
+    private void commonCalculateBusinessTimeAsDateAssertAtStartHour(int 
startHourGap, int endHourGap, int testingCalendarHourGap, int 
executionHourDelay, int daysToSkip, String holidayDateFormat,
+            String holidays) {
+        BiFunction<Instant, Instant, Boolean> startBooleanCondition = 
(resultInstant, expectedStartTime) -> {
+            logger.debug("Check if {} is equal to {} ", resultInstant, 
expectedStartTime);
+            return resultInstant.getEpochSecond() == 
expectedStartTime.getEpochSecond();
+        };
+        commonCalculateBusinessTimeAsDate(startHourGap,
+                endHourGap,
+                testingCalendarHourGap,
+                executionHourDelay,
+                daysToSkip,
+                holidayDateFormat,
+                holidays,
+                startBooleanCondition);
     }
 
-    @Test
-    public void testCalculateISOHours() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-04 16:45";
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-04 13:45").getTime());
-
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("PT3H");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
-    }
-
-    @Test
-    public void testCalculateISODaysAndHours() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, "2012-05-09");
-        String expectedDate = "2012-05-10 15:30";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime("2012-05-08 11:10").getTime());
-
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("P1DT4H20M");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
-    }
-
-    @Test
-    public void testSingleHolidayWithinGivenTime() {
-        final Properties props = new Properties();
-        props.put(BusinessCalendarImpl.HOLIDAYS, "2015-01-13");
-        String expectedDate = "2015-01-15 11:38";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTimeAndMillis("2015-01-08 
11:38:30.198").getTime());
-
-        BusinessCalendarImpl businessCalendarImpl = new 
BusinessCalendarImpl(props, clock);
-
-        Date result = businessCalendarImpl.calculateBusinessTimeAsDate("4d");
-        assertThat(formatDate("yyyy-MM-dd HH:mm", 
result)).isEqualTo(expectedDate);
-    }
-
-    @Test
-    public void testCalculateMillisecondsAsDefault() {
-        Properties config = new Properties();
-        String expectedDate = "2012-05-04 16:45:10.000";
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTimeAndMillis("2012-05-04 
16:45:00.000").getTime());
-
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("10000");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm:ss.SSS", 
result)).isEqualTo(expectedDate);
-    }
-
-    @Test
-    public void testCalculateMinutesPassingAfterHour() {
-        Properties config = new Properties();
-        String currentDate = "2018-05-02 19:51:33";
-        String expectedDate = "2018-05-03 09:01:00";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime(currentDate).getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate("1m");
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm:ss", 
result)).isEqualTo(expectedDate);
-    }
-
-    @Test
-    public void testBusinessCalendarWithoutProvidedConfiguration() {
-        assertDoesNotThrow(() -> new BusinessCalendarImpl());
-    }
-
-    @Test
-    public void testCalculateMinutesPassingHoliday() {
-        Properties config = new Properties();
-        config.setProperty(BusinessCalendarImpl.DAYS_PER_WEEK, "5");
-        config.setProperty(BusinessCalendarImpl.HOURS_PER_DAY, "8");
-        config.setProperty(BusinessCalendarImpl.START_HOUR, "9");
-        config.setProperty(BusinessCalendarImpl.END_HOUR, "18");
-        config.setProperty(BusinessCalendarImpl.WEEKEND_DAYS, "1,7"); // 
sun,sat
-        config.setProperty(BusinessCalendarImpl.HOLIDAYS, 
"2018-04-30,2018-05-03:2018-05-05");
-        config.setProperty(BusinessCalendarImpl.HOLIDAY_DATE_FORMAT, 
"yyyy-MM-dd");
-        String currentDate = "2018-05-03 13:51:33";
-        String duration = "10m";
-        String expectedDate = "2018-05-07 09:10:00";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime(currentDate).getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate(duration);
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm:ss", 
result)).isEqualTo(expectedDate);
-    }
+    private void commonCalculateBusinessTimeAsDate(int startHourGap,
+            int endHourGap, int testingCalendarHourGap,
+            int executionHourDelay, int daysToSkip, String holidayDateFormat, 
String holidays,
+            BiFunction<Instant, Instant, Boolean> startBooleanCondition) {
+        logger.debug("startHourGap {}", startHourGap);
+        logger.debug("endHourGap {}", endHourGap);
+        logger.debug("testingCalendarHourGap {}", testingCalendarHourGap);
+        logger.debug("executionHourDelay {}", executionHourDelay);
+        logger.debug("numberOfHolidays {}", daysToSkip);
+        logger.debug("holidayDateFormat {}", holidayDateFormat);
+        logger.debug("holidays {}", holidays);
+
+        // lets pretend 12.00 is the current time
+        Calendar testingCalendar = Calendar.getInstance();
+        testingCalendar.set(Calendar.HOUR_OF_DAY, 12);
+        testingCalendar.set(Calendar.MINUTE, 0);
+        testingCalendar.set(Calendar.SECOND, 0);
+        logger.debug("testingCalendar {}", testingCalendar.getTime());
+        Calendar startCalendar = (Calendar) testingCalendar.clone();
+        startCalendar.add(Calendar.HOUR_OF_DAY, startHourGap);
+        logger.debug("startCalendar {}", startCalendar.getTime());
+        Calendar endCalendar = (Calendar) testingCalendar.clone();
+        endCalendar.add(Calendar.HOUR_OF_DAY, endHourGap);
+        logger.debug("endCalendar {}", endCalendar.getTime());
+
+        int startHour = startCalendar.get(Calendar.HOUR_OF_DAY);
+        int endHour = endCalendar.get(Calendar.HOUR_OF_DAY);
+
+        int hoursInDay = startHour < endHour ? endHour - startHour : 24 - 
(startHour - endHour);
+        int daysToAdd = daysToSkip;
+        logger.debug("daysToAdd (= numberOfHolidays) {}", daysToAdd);
+        if (executionHourDelay >= hoursInDay) {
+            daysToAdd += executionHourDelay / hoursInDay;
+            logger.debug("daysToAdd += (hourDelay / hoursInDay) {}", 
daysToAdd);
+        }
+        if (daysToAdd > 0) {
+            startCalendar.add(Calendar.DAY_OF_YEAR, daysToAdd);
+            endCalendar.add(Calendar.DAY_OF_YEAR, daysToAdd);
+            logger.debug("startCalendar (startCalendar + days to add) {}", 
startCalendar.getTime());
+            logger.debug("endCalendar (endCalendar + days to add) {}", 
endCalendar.getTime());
+        }
 
-    @Test
-    public void testCalculateMinutesPassingWeekend() {
         Properties config = new Properties();
-        String currentDate = "2018-05-06 13:51:33";
-        String duration = "10m";
-        String expectedDate = "2018-05-07 09:10:00";
-
-        SessionPseudoClock clock = new 
StaticPseudoClock(parseToDateWithTime(currentDate).getTime());
-        BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config, 
clock);
-
-        Date result = businessCal.calculateBusinessTimeAsDate(duration);
-
-        assertThat(formatDate("yyyy-MM-dd HH:mm:ss", 
result)).isEqualTo(expectedDate);
-    }
-
-    private Date parseToDate(String dateString) {
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
-
-        Date testTime;
-        try {
-            testTime = sdf.parse(dateString);
-
-            return testTime;
-        } catch (ParseException e) {
-            return null;
+        config.setProperty(START_HOUR, String.valueOf(startHour));
+        config.setProperty(END_HOUR, String.valueOf(endHour));
+        config.setProperty(WEEKEND_DAYS, "0");
+        if (holidayDateFormat != null) {
+            config.setProperty(HOLIDAY_DATE_FORMAT, holidayDateFormat);
         }
-    }
-
-    private Date parseToDateWithTime(String dateString) {
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
-
-        Date testTime;
-        try {
-            testTime = sdf.parse(dateString);
-
-            return testTime;
-        } catch (ParseException e) {
-            return null;
+        if (holidays != null) {
+            config.setProperty(HOLIDAYS, holidays);
         }
-    }
 
-    private Date parseToDateWithTimeAndMillis(String dateString) {
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
-
-        Date testTime;
-        try {
-            testTime = sdf.parse(dateString);
-
-            return testTime;
-        } catch (ParseException e) {
-            return null;
-        }
-    }
+        testingCalendar.add(Calendar.HOUR_OF_DAY, testingCalendarHourGap);
+        logger.debug("testingCalendar after testingCalendarHourGap {}", 
testingCalendar.getTime());
+        BusinessCalendarImpl businessCal = 
BusinessCalendarImpl.builder().withCalendarBean(new CalendarBean(config))
+                .withTestingCalendar(testingCalendar)
+                .build();
+        Date retrieved = 
businessCal.calculateBusinessTimeAsDate(String.format("%sh", 
executionHourDelay));
+        logger.debug("retrieved {}", retrieved);
 
-    private String formatDate(String pattern, Date date) {
-        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
+        Date expectedStart = startCalendar.getTime();
+        Date expectedEnd = endCalendar.getTime();
 
-        String testTime = sdf.format(date);
+        Instant retrievedInstant = retrieved.toInstant();
+        Instant expectedStartTime = expectedStart.toInstant();
+        Instant expectedEndTime = expectedEnd.toInstant();
 
-        return testTime;
+        logger.debug("retrievedInstant {}", retrievedInstant);
+        logger.debug("expectedStartTime {}", expectedStartTime);
+        logger.debug("expectedEndTime {}", expectedEndTime);
 
+        assertTrue(startBooleanCondition.apply(retrievedInstant, 
expectedStartTime));

Review Comment:
   do not use assertions from JUnit5. Use assertions from assertj. This is the 
standard in Kie.



##########
jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/CalendarBeanTest.java:
##########
@@ -0,0 +1,295 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.jbpm.process.core.timer;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.END_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAYS;
+import static 
org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.START_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.TIMEZONE;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.WEEKEND_DAYS;
+import static 
org.jbpm.process.core.timer.CalendarBean.DEFAULT_HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_TIMEZONE;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKENDS;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKEND_DAYS;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CalendarBeanTest {
+
+    // Static validation methods
+    @ParameterizedTest
+    @MethodSource("getMissingPropertiesCalendar")
+    void requiredPropertyValidation(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.requiredPropertyValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getWronglyFormatPropertiesCalendar")
+    void propertyFormatValidation(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.propertyFormatValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getBusinessInvalidPropertiesCalendar")
+    void businessValidation(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonStaticMethodValidation(errorMessage -> 
CalendarBean.businessValidation(errorMessage, calendarConfiguration), 
errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getPartialPropertiesCalendar")
+    void missingDataPopulation(Map<String, Object> propertyMap, Map<String, 
Object> defaultValuesMap) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        defaultValuesMap.keySet().forEach(key -> 
assertThat(calendarConfiguration.containsKey(key)).isFalse());
+        CalendarBean.missingDataPopulation(calendarConfiguration);
+        defaultValuesMap.forEach((key, value) -> {
+            assertThat(calendarConfiguration.containsKey(key)).isTrue();
+            
assertThat(calendarConfiguration.getProperty(key)).isEqualTo(value);
+        });
+    }
+
+    @Test
+    void getPropertyAsInt() {
+        Properties calendarConfiguration = new Properties();
+        String propertyName = "propertyName";
+        int originalValue = 1;
+        String value = "" + originalValue;
+        calendarConfiguration.put(propertyName, value);
+        int retrieved = CalendarBean.getPropertyAsInt(propertyName, 
calendarConfiguration);
+        assertThat(retrieved).isEqualTo(originalValue);
+        value = "WRONG";
+        calendarConfiguration.put(propertyName, value);
+        NumberFormatException thrown = 
assertThrows(NumberFormatException.class, () -> 
CalendarBean.getPropertyAsInt(propertyName, calendarConfiguration));
+        String expectedMessage = "For input string: \"WRONG\"";
+        assertThat(thrown.getMessage()).isEqualTo(expectedMessage);
+    }
+
+    @Test
+    void validateRequiredProperty() {
+        Properties calendarConfiguration = new Properties();
+        String propertyName = "propertyName";
+        String value = "propertyValue";
+        calendarConfiguration.put(propertyName, value);
+        StringBuilder errorMessage = new StringBuilder();
+        CalendarBean.validateRequiredProperty(propertyName, errorMessage, 
calendarConfiguration);
+        assertThat(errorMessage).isEmpty();
+        CalendarBean.validateRequiredProperty("missingProperty", errorMessage, 
calendarConfiguration);
+        String[] retrievedErrors = errorMessage.toString().split("\n");
+        assertThat(retrievedErrors).hasSize(1);
+        assertThat(retrievedErrors).contains("Property missingProperty is 
required");
+    }
+
+    @Test
+    void getFormattedDate() throws ParseException {
+        Properties calendarConfiguration = new Properties();
+        String dateFormat = "dd-MM-yyyy";
+        String date = "27-11-2024";
+        calendarConfiguration.put(HOLIDAY_DATE_FORMAT, dateFormat);
+        Date retrieved = CalendarBean.getFormattedDate(date, 
calendarConfiguration);
+        Date expected = 
CalendarBean.getSimpleDateFormat(dateFormat).parse(date);
+        assertThat(retrieved).isEqualTo(expected);
+
+    }
+
+    @Test
+    void getSimpleDateFormat() {
+        SimpleDateFormat retrieved = 
CalendarBean.getSimpleDateFormat(DEFAULT_HOLIDAY_DATE_FORMAT);
+        assertThat(retrieved).isNotNull();
+        retrieved = CalendarBean.getSimpleDateFormat("dd-MM-yyyy");
+        assertThat(retrieved).isNotNull();
+        String wrong = "WRONG";
+        IllegalArgumentException thrown = 
assertThrows(IllegalArgumentException.class, () -> 
CalendarBean.getSimpleDateFormat(wrong));
+        String expectedMessage = "Illegal pattern character 'R'";
+        assertThat(thrown.getMessage()).isEqualTo(expectedMessage);
+    }
+
+    // Instance methods
+    @ParameterizedTest
+    @MethodSource("getMissingPropertiesCalendar")
+    void requiredPropertyMissing(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonIllegalArgumentAssertion(() -> new 
CalendarBean(calendarConfiguration), errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getWronglyFormatPropertiesCalendar")
+    void propertyWrongFormat(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonIllegalArgumentAssertion(() -> new 
CalendarBean(calendarConfiguration), errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getBusinessInvalidPropertiesCalendar")
+    void businessInvalid(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonIllegalArgumentAssertion(() -> new 
CalendarBean(calendarConfiguration), errorMessages);
+    }
+
+    @Test
+    void instantiationFull() throws ParseException {
+        Properties calendarConfiguration = new Properties();
+        int startHour = 10;
+        int endHour = 16;
+        List<Integer> weekendDays = Arrays.asList(3, 4);
+        String dateFormat = "dd-MM-yyyy";
+        String timezone = "ACT";
+        String holidays = "27-11-2024";
+        calendarConfiguration.put(START_HOUR, String.valueOf(startHour));
+        calendarConfiguration.put(END_HOUR, String.valueOf(endHour));
+        calendarConfiguration.put(WEEKEND_DAYS, 
weekendDays.stream().map(String::valueOf).collect(Collectors.joining(",")));
+        calendarConfiguration.put(HOLIDAY_DATE_FORMAT, dateFormat);
+        calendarConfiguration.put(TIMEZONE, timezone);
+        calendarConfiguration.put(HOLIDAYS, holidays);
+        CalendarBean retrieved = new CalendarBean(calendarConfiguration);
+
+        Date from = CalendarBean.getFormattedDate(holidays, 
calendarConfiguration);
+        Date to = CalendarBean.getFormattedDate("28-11-2024", 
calendarConfiguration);
+        assertThat(retrieved.getHolidays()).isEqualTo(List.of(new 
BusinessCalendarImpl.TimePeriod(from, to)));
+        assertThat(retrieved.getWeekendDays()).isEqualTo(weekendDays);
+        assertThat(retrieved.getDaysPerWeek()).isEqualTo(7 - 
weekendDays.size());
+        assertThat(retrieved.getTimezone()).isEqualTo(timezone);
+        assertThat(retrieved.getStartHour()).isEqualTo(startHour);
+        assertThat(retrieved.getEndHour()).isEqualTo(endHour);
+        assertThat(retrieved.getHoursInDay()).isEqualTo(endHour - startHour);
+    }
+
+    @Test
+    void instantiationPartial() {
+        Properties calendarConfiguration = new Properties();
+        int startHour = 10;
+        int endHour = 16;
+        calendarConfiguration.put(START_HOUR, String.valueOf(startHour));
+        calendarConfiguration.put(END_HOUR, String.valueOf(endHour));
+        CalendarBean retrieved = new CalendarBean(calendarConfiguration);
+        assertThat(retrieved.getHolidays()).isEqualTo(Collections.emptyList());
+        assertThat(retrieved.getWeekendDays()).isEqualTo(DEFAULT_WEEKEND_DAYS);
+        assertThat(retrieved.getDaysPerWeek()).isEqualTo(5);
+        assertThat(retrieved.getTimezone()).isEqualTo(DEFAULT_TIMEZONE);
+        assertThat(retrieved.getStartHour()).isEqualTo(startHour);
+        assertThat(retrieved.getEndHour()).isEqualTo(endHour);
+        assertThat(retrieved.getHoursInDay()).isEqualTo(endHour - startHour);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getMissingPropertiesCalendar")
+    void missingProperties(Map<String, Object> propertyMap, List<String> 
errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonIllegalArgumentAssertion(() -> new 
CalendarBean(calendarConfiguration), errorMessages);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getInvalidPropertiesCalendar")
+    public void invalidProperties(Map<String, Object> propertyMap, 
List<String> errorMessages) {
+        Properties calendarConfiguration = new Properties();
+        calendarConfiguration.putAll(propertyMap);
+        commonIllegalArgumentAssertion(() -> new 
CalendarBean(calendarConfiguration), errorMessages);
+    }
+
+    // Let's avoid duplication
+    private void commonStaticMethodValidation(Consumer<StringBuilder> 
executedMethod,
+            List<String> errorMessages) {
+        StringBuilder errors = new StringBuilder();
+        assertThat(errors).isEmpty();
+        executedMethod.accept(errors);
+        assertThat(errors).isNotEmpty();
+        String[] retrievedErrors = errors.toString().split("\n");
+        assertThat(retrievedErrors).hasSize(errorMessages.size());
+        assertThat(retrievedErrors).contains(retrievedErrors);
+    }
+
+    private void commonIllegalArgumentAssertion(Executable executedMethod, 
List<String> errorMessages) {

Review Comment:
   Hiding assertions inside general methods tend to make the test less clear. 
Ideally assertions should be in the main body of the test itself.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to