// Copyright © 2015 HSL <https://www.hsl.fi>
// This program is dual-licensed under the EUPL v1.2 and AGPLv3 licenses.
package fi.hsl.parkandride.core.service;
import com.google.common.collect.ImmutableMap;
import fi.hsl.parkandride.core.back.FacilityHistoryRepository;
import fi.hsl.parkandride.core.back.FacilityRepository;
import fi.hsl.parkandride.core.domain.*;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.List;
import java.util.Map;
import static com.google.common.collect.Lists.newArrayList;
import static fi.hsl.parkandride.core.domain.CapacityType.CAR;
import static fi.hsl.parkandride.core.domain.FacilityStatus.*;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.apache.commons.lang3.RandomUtils.nextInt;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.when;
public class FacilityHistoryServiceTest {
private static final LocalDate START = LocalDate.now().minusMonths(1).dayOfMonth().withMinimumValue();
private static final LocalDate NEXT = START.plusDays(1);
private static final LocalDate END = START.withDayOfMonth(11);
private static final DateTime START_DATETIME = START.toDateTimeAtStartOfDay();
private static final DateTime NEXT_DATETIME = NEXT.toDateTimeAtStartOfDay();
private static final MultilingualString STATUS_DESCRIPTION = new MultilingualString();
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
FacilityHistoryService service;
@Mock
FacilityHistoryRepository repository;
@Mock
FacilityRepository facilityRepository;
static final LocalTime midnight = LocalTime.MIDNIGHT;
static final LocalTime am759 = new LocalTime("07:59");
static final LocalTime am800 = new LocalTime("08:00");
static final LocalTime am801 = new LocalTime("08:01");
static final DateTime first = START.toDateTime(midnight);
static final DateTime second = START.plusDays(1).toDateTime(am759);
static final DateTime third = START.plusDays(2).toDateTime(am800);
static final DateTime fourth = START.plusDays(3).toDateTime(am801);
@Before
public void setup() {
service = new FacilityHistoryService(repository, facilityRepository);
when(repository.getStatusHistory(anyLong(), any(), any())).thenReturn(emptyList());
}
@Test
public void statusHistory_noHistory_hasInOperationForAllDays() {
final Map<LocalDate, FacilityStatus> statusHistoryByDay = service.getStatusHistoryByDay(1l, START, END);
assertThat(statusHistoryByDay.keySet()).hasSize(11);
statusHistoryByDay.keySet().forEach(date -> assertThat(date).isLessThanOrEqualTo(END).isGreaterThanOrEqualTo(START));
statusHistoryByDay.values().forEach(s -> assertThat(s).isEqualTo(IN_OPERATION));
}
@Test
public void statusHistory_changedAtMidnight() {
when(repository.getStatusHistory(1l, START, NEXT)).thenReturn(asList(
new FacilityStatusHistory(1l, START_DATETIME, NEXT_DATETIME, EXCEPTIONAL_SITUATION, STATUS_DESCRIPTION),
new FacilityStatusHistory(1l, NEXT_DATETIME, null, IN_OPERATION, STATUS_DESCRIPTION)
));
final Map<LocalDate, FacilityStatus> statusHistoryByDay = service.getStatusHistoryByDay(1l, START, NEXT);
assertThat(statusHistoryByDay).hasSize(2);
assertThat(statusHistoryByDay)
.containsEntry(START, EXCEPTIONAL_SITUATION)
.containsEntry(NEXT, IN_OPERATION);
}
@Test
public void statusHistory_mostEffectiveStatusSelected() {
when(repository.getStatusHistory(1l, START, fourth.toLocalDate())).thenReturn(asList(
new FacilityStatusHistory(1l, first, second, EXCEPTIONAL_SITUATION, STATUS_DESCRIPTION),
new FacilityStatusHistory(1l, second, third, IN_OPERATION, STATUS_DESCRIPTION),
new FacilityStatusHistory(1l, third, fourth, TEMPORARILY_CLOSED, STATUS_DESCRIPTION),
new FacilityStatusHistory(1l, fourth, null, IN_OPERATION, STATUS_DESCRIPTION)
));
final Map<LocalDate, FacilityStatus> statusHistoryByDay = service.getStatusHistoryByDay(1l, START, fourth.toLocalDate());
// First -> Exceptional situation whole day
// Second -> Exceptional situation end at 7.59 (1.59 in window 6-10)
// In operation for 2.01 of the significant window
// Third -> Temporarily closed begins at 8.00
// Equal periods in operation and closed, select first
// Fourth -> Returns to operation at 8.01, temporarily closed for 2.01 of the window
assertThat(statusHistoryByDay)
.hasSize(4)
.containsEntry(START, EXCEPTIONAL_SITUATION)
.containsEntry(START.plusDays(1), IN_OPERATION)
.containsEntry(START.plusDays(2), IN_OPERATION)
.containsEntry(START.plusDays(3), TEMPORARILY_CLOSED);
}
@Test
public void capacityHistory_hasKeysForAllDays_capacityFromCurrent_ifNoHistory() {
Facility fac = returnFacilityWithDummyCapacities(dummyUnavailable());
final Map<LocalDate, FacilityCapacity> historyByDate = service.getCapacityHistory(1l, START, END);
assertThat(historyByDate.keySet()).hasSize(11);
historyByDate.keySet().forEach(date -> assertThat(date).isLessThanOrEqualTo(END).isGreaterThanOrEqualTo(START));
historyByDate.values().forEach(s -> assertThat(s).isEqualTo(new FacilityCapacity(fac.builtCapacity, fac.unavailableCapacities)));
}
@Test
public void capacityHistory_changedAtMidnight() {
returnFacilityWithDummyCapacities(dummyUnavailable());
final FacilityCapacityHistory first = new FacilityCapacityHistory(1l, START_DATETIME, NEXT_DATETIME, null, null);
final FacilityCapacityHistory second = new FacilityCapacityHistory(1l, NEXT_DATETIME, null, null, dummyUnavailable());
when(repository.getCapacityHistory(1l, START, NEXT)).thenReturn(asList(first, second));
final Map<LocalDate, FacilityCapacity> historyByDate = service.getCapacityHistory(1l, START, NEXT);
assertThat(historyByDate).hasSize(2)
.containsEntry(START, new FacilityCapacity(first))
.containsEntry(NEXT, new FacilityCapacity(second));
}
@Test
public void capacityHistory_mostEffectiveStatusSelected() {
returnFacilityWithDummyCapacities(newArrayList());
final FacilityCapacityHistory first = new FacilityCapacityHistory(1l, FacilityHistoryServiceTest.first, second, null, dummyUnavailable());
final FacilityCapacityHistory second = new FacilityCapacityHistory(1l, FacilityHistoryServiceTest.second, third, null, dummyUnavailable());
final FacilityCapacityHistory third = new FacilityCapacityHistory(1l, FacilityHistoryServiceTest.third, fourth, null, dummyUnavailable());
final FacilityCapacityHistory fourth = new FacilityCapacityHistory(1l, FacilityHistoryServiceTest.fourth, null, null, dummyUnavailable());
when(repository.getCapacityHistory(1l, START, FacilityHistoryServiceTest.fourth.toLocalDate())).thenReturn(asList(first, second, third, fourth));
final Map<LocalDate, FacilityCapacity> statusHistoryByDay = service.getCapacityHistory(1l, START, FacilityHistoryServiceTest.fourth.toLocalDate());
// First -> Exceptional situation whole day
// Second -> Exceptional situation end at 7.59 (1.59 in window 6-10)
// In operation for 2.01 of the significant window
// Third -> Temporarily closed begins at 8.00
// Equal periods in operation and closed, select first
// Fourth -> Returns to operation at 8.01, temporarily closed for 2.01 of the window
assertThat(statusHistoryByDay)
.hasSize(4)
.containsEntry(START, new FacilityCapacity(first))
.containsEntry(START.plusDays(1), new FacilityCapacity(second))
.containsEntry(START.plusDays(2), new FacilityCapacity(second))
.containsEntry(START.plusDays(3), new FacilityCapacity(third));
}
private Facility returnFacilityWithDummyCapacities(List<UnavailableCapacity> capacities) {
final Facility facility = new Facility();
facility.builtCapacity = ImmutableMap.of(CAR, 50);
facility.unavailableCapacities = capacities;
when(facilityRepository.getFacility(1l)).thenReturn(facility);
return facility;
}
private List<UnavailableCapacity> dummyUnavailable() {
return singletonList(new UnavailableCapacity(
randomFrom(CapacityType.class),
randomFrom(Usage.class),
nextInt(1, 10)
));
}
private <E extends Enum<E>, T extends Class<E>> E randomFrom(T clazz) {
final E[] values = clazz.getEnumConstants();
return values[nextInt(0, values.length)];
}
}