// Copyright © 2016 HSL <https://www.hsl.fi>
// This program is dual-licensed under the EUPL v1.2 and AGPLv3 licenses.
package fi.hsl.parkandride.back;
import com.mysema.commons.lang.CloseableIterator;
import fi.hsl.parkandride.core.back.FacilityRepository;
import fi.hsl.parkandride.core.back.UtilizationRepository;
import fi.hsl.parkandride.core.domain.*;
import org.joda.time.DateTime;
import org.joda.time.Minutes;
import org.junit.Before;
import org.junit.Test;
import org.springframework.transaction.annotation.Transactional;
import javax.inject.Inject;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static fi.hsl.parkandride.core.domain.CapacityType.CAR;
import static fi.hsl.parkandride.core.domain.CapacityType.MOTORCYCLE;
import static fi.hsl.parkandride.core.domain.Usage.COMMERCIAL;
import static fi.hsl.parkandride.core.domain.Usage.HSL_TRAVEL_CARD;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
@Transactional
public class UtilizationDaoTest extends AbstractDaoTest {
@Inject Dummies dummies;
@Inject UtilizationRepository utilizationDao;
@Inject FacilityRepository facilityDao;
private long facilityId;
@Before
public void initialize() {
facilityId = createFacility();
}
// finding the latest utilization
@Test
public void findLatestUtilization_when_nothing_to_find() {
Set<Utilization> results = utilizationDao.findLatestUtilization(facilityId);
assertThat(results).isEmpty();
}
@Test
public void findLatestUtilization_when_empty_list_registered() {
Set<Utilization> results = utilizationDao.findLatestUtilization(facilityId);
utilizationDao.insertUtilizations(emptyList());
assertThat(results).isEmpty();
}
@Test
public void findLatestUtilization_returns_latest_entry() {
Utilization u1 = newUtilization(facilityId, new DateTime(2000, 1, 1, 12, 0), 100, 150);
Utilization u2 = newUtilization(facilityId, new DateTime(2000, 1, 1, 13, 0), 200, 250);
utilizationDao.insertUtilizations(asList(u1, u2));
Set<Utilization> results = utilizationDao.findLatestUtilization(facilityId);
assertThat(results).containsOnly(u2);
}
@Test
public void findLatestUtilization_returns_each_capacity_type_and_usage_combination() {
Utilization u1 = newUtilization(facilityId, new DateTime(2000, 1, 1, 12, 0), 100, 100);
u1.capacityType = CAR;
u1.usage = HSL_TRAVEL_CARD;
Utilization u2 = newUtilization(facilityId, new DateTime(2000, 1, 1, 13, 0), 200, 200);
u2.capacityType = CAR;
u2.usage = COMMERCIAL;
Utilization u3 = newUtilization(facilityId, new DateTime(2000, 1, 1, 14, 0), 300, 300);
u3.capacityType = MOTORCYCLE;
u3.usage = HSL_TRAVEL_CARD;
utilizationDao.insertUtilizations(asList(u1, u2, u3));
Set<Utilization> results = utilizationDao.findLatestUtilization(facilityId);
assertThat(results).containsOnly(u1, u2, u3);
}
@Test
public void findLatestUtilization_hides_capacity_type_and_usage_combinations_which_do_not_have_pricing_information() {
Utilization u1 = newUtilization(facilityId, new DateTime(2000, 1, 1, 12, 0), 100, 100);
u1.capacityType = CAR;
u1.usage = HSL_TRAVEL_CARD;
Utilization u2 = newUtilization(facilityId, new DateTime(2000, 1, 1, 13, 0), 200, 200);
u2.capacityType = CAR;
u2.usage = COMMERCIAL;
Utilization u3 = newUtilization(facilityId, new DateTime(2000, 1, 1, 14, 0), 300, 300);
u3.capacityType = MOTORCYCLE;
u3.usage = HSL_TRAVEL_CARD;
utilizationDao.insertUtilizations(asList(u1, u2, u3));
Facility facility = facilityDao.getFacility(facilityId);
facility.pricing.removeIf(pricing -> pricing.usage.equals(HSL_TRAVEL_CARD));
facilityDao.updateFacility(facilityId, facility);
Set<Utilization> results = utilizationDao.findLatestUtilization(facilityId);
assertThat(results).containsOnly(u2);
}
@Test
public void findLatestUtilization_returns_utilizations_for_all_facilities_if_facility_ID_is_not_defined() {
long facilityId2 = createFacility();
Utilization u1 = newUtilization(facilityId, new DateTime(2000, 1, 1, 12, 0), 100, 100);
u1.capacityType = CAR;
u1.usage = HSL_TRAVEL_CARD;
Utilization u2 = newUtilization(facilityId2, new DateTime(2000, 1, 1, 13, 0), 200, 200);
u2.capacityType = CAR;
u2.usage = HSL_TRAVEL_CARD;
utilizationDao.insertUtilizations(asList(u1, u2));
Set<Utilization> results = utilizationDao.findLatestUtilization();
assertThat(results).containsOnly(u1, u2);
}
// finding utilization at a point in time
@Test
public void findUtilizationAtInstant_when_instant_matches_utilization() {
DateTime time = new DateTime(2000, 1, 1, 12, 0);
Utilization u = newUtilization(facilityId, time, 100, 100);
utilizationDao.insertUtilizations(asList(u));
Optional<Utilization> result = utilizationDao.findUtilizationAtInstant(u.getUtilizationKey(), time);
assertThat(result).isEqualTo(Optional.of(newUtilization(facilityId, time, 100, 100)));
}
@Test
public void findUtilizationAtInstant_when_instant_is_after_utilization() {
DateTime time = new DateTime(2000, 1, 1, 12, 0);
Utilization u = newUtilization(facilityId, time, 100, 100);
utilizationDao.insertUtilizations(asList(u));
Optional<Utilization> result = utilizationDao.findUtilizationAtInstant(u.getUtilizationKey(), time.plusHours(1));
assertThat(result).isEqualTo(Optional.of(newUtilization(facilityId, time.plusHours(1), 100, 100)));
}
@Test
public void findUtilizationAtInstant_when_instant_is_before_utilization() {
DateTime time = new DateTime(2000, 1, 1, 12, 0);
Utilization u = newUtilization(facilityId, time, 100, 100);
utilizationDao.insertUtilizations(asList(u));
Optional<Utilization> result = utilizationDao.findUtilizationAtInstant(u.getUtilizationKey(), time.minusHours(1));
assertThat(result).isEqualTo(Optional.empty());
}
@Test
public void findUtilizationAtInstant_when_multiple_utilizations_then_returns_the_latest_before_instant() {
DateTime time = new DateTime(2000, 1, 1, 12, 0);
Utilization u1 = newUtilization(facilityId, time.plusHours(1), 100, 100);
Utilization u2 = newUtilization(facilityId, time.plusHours(2), 200, 200);
utilizationDao.insertUtilizations(asList(u1, u2));
Optional<Utilization> result = utilizationDao.findUtilizationAtInstant(u1.getUtilizationKey(), time.plusHours(3));
assertThat(result).isEqualTo(Optional.of(newUtilization(facilityId, time.plusHours(3), 200, 200)));
}
// finding utilizations by date range
@Test
public void findUtilizationsBetween_limits_to_start_and_end_time_inclusive_ordered_by_time() {
Utilization u1 = newUtilization(facilityId, new DateTime(2000, 1, 1, 12, 0, 0, 1), 100, 100);
Utilization u2 = newUtilization(facilityId, new DateTime(2000, 1, 1, 12, 0, 0, 2), 200, 200);
Utilization u3 = newUtilization(facilityId, new DateTime(2000, 1, 1, 12, 0, 0, 3), 300, 300);
Utilization u4 = newUtilization(facilityId, new DateTime(2000, 1, 1, 12, 0, 0, 4), 400, 400);
Utilization u5 = newUtilization(facilityId, new DateTime(2000, 1, 1, 12, 0, 0, 5), 500, 500);
UtilizationKey key = u1.getUtilizationKey();
utilizationDao.insertUtilizations(asList(u1, u2, u3, u4, u5));
try (CloseableIterator<Utilization> results = utilizationDao.findUtilizationsBetween(key, u2.timestamp, u4.timestamp)) {
assertThat(results).containsExactly(u2, u3, u4);
}
}
@Test
public void findUtilizationsBetween_is_facility_specific() {
DateTime time = new DateTime(2000, 1, 1, 12, 0);
Utilization u1 = newUtilization(dummies.createFacility(), time, 100, 100);
Utilization u2 = newUtilization(dummies.createFacility(), time, 200, 200);
utilizationDao.insertUtilizations(asList(u1, u2));
try (CloseableIterator<Utilization> results = utilizationDao.findUtilizationsBetween(u1.getUtilizationKey(), time, time)) {
assertThat(results).containsExactly(u1);
}
}
@Test
public void findUtilizationsBetween_is_capacity_type_specific() {
DateTime time = new DateTime(2000, 1, 1, 12, 0);
Utilization u1 = newUtilization(facilityId, time, 100, 100);
u1.capacityType = CAR;
Utilization u2 = newUtilization(facilityId, time, 200, 200);
u2.capacityType = MOTORCYCLE;
utilizationDao.insertUtilizations(asList(u1, u2));
try (CloseableIterator<Utilization> results = utilizationDao.findUtilizationsBetween(u1.getUtilizationKey(), time, time)) {
assertThat(results).containsExactly(u1);
}
}
@Test
public void findUtilizationsBetween_is_usage_specific() {
DateTime time = new DateTime(2000, 1, 1, 12, 0);
Utilization u1 = newUtilization(facilityId, time, 100, 100);
u1.usage = COMMERCIAL;
Utilization u2 = newUtilization(facilityId, time, 200, 200);
u2.usage = HSL_TRAVEL_CARD;
utilizationDao.insertUtilizations(asList(u1, u2));
try (CloseableIterator<Utilization> results = utilizationDao.findUtilizationsBetween(u1.getUtilizationKey(), time, time)) {
assertThat(results).containsExactly(u1);
}
}
// finding utilizations by date range and adjusted to specific resolution
@Test
public void findUtilizationsWithResolution_repeats_the_previous_utilization_at_resolution_intervals() {
DateTime start = new DateTime(2000, 1, 1, 12, 0);
DateTime end = start.plusHours(1);
Minutes resolution = Minutes.minutes(30);
Utilization u1 = newUtilization(facilityId, start.minusHours(1), 100, 100);
utilizationDao.insertUtilizations(asList(u1));
UtilizationKey utilizationKey = u1.getUtilizationKey();
List<Utilization> results = utilizationDao.findUtilizationsWithResolution(utilizationKey, start, end, resolution);
assertThat(results).containsExactly(
newUtilization(facilityId, start, 100, 100),
newUtilization(facilityId, start.plus(resolution), 100, 100),
newUtilization(facilityId, end, 100, 100));
}
@Test
public void findUtilizationsWithResolution_availability_is_updated_when_there_are_new_utilizations() {
DateTime start = new DateTime(2000, 1, 1, 12, 0);
DateTime end = start.plusHours(1);
Minutes resolution = Minutes.minutes(30);
Utilization u1 = newUtilization(facilityId, start, 100, 100);
Utilization u2 = newUtilization(facilityId, start.plus(resolution), 200, 200);
utilizationDao.insertUtilizations(asList(u1, u2));
UtilizationKey utilizationKey = u1.getUtilizationKey();
List<Utilization> results = utilizationDao.findUtilizationsWithResolution(utilizationKey, start, end, resolution);
assertThat(results).containsExactly(
newUtilization(facilityId, start, 100, 100),
newUtilization(facilityId, start.plus(resolution), 200, 200),
newUtilization(facilityId, end, 200, 200));
}
@Test
public void findUtilizationsWithResolution_when_there_are_no_utilizations() {
DateTime start = new DateTime(2000, 1, 1, 12, 0);
DateTime end = start.plusHours(1);
Minutes resolution = Minutes.minutes(30);
Utilization dummy = newUtilization(facilityId, start, 100, 100);
UtilizationKey utilizationKey = dummy.getUtilizationKey();
List<Utilization> results = utilizationDao.findUtilizationsWithResolution(utilizationKey, start, end, resolution);
assertThat(results).isEmpty();
}
@Test
public void findUtilizationsWithResolution_when_the_first_utilization_is_within_the_range() {
DateTime start = new DateTime(2000, 1, 1, 12, 0);
DateTime end = start.plusHours(1);
Minutes resolution = Minutes.minutes(30);
Utilization u1 = newUtilization(facilityId, start.plusMinutes(15), 100, 100);
utilizationDao.insertUtilizations(asList(u1));
UtilizationKey utilizationKey = u1.getUtilizationKey();
List<Utilization> results = utilizationDao.findUtilizationsWithResolution(utilizationKey, start, end, resolution);
assertThat(results).containsExactly(
newUtilization(facilityId, start.plus(resolution), 100, 100),
newUtilization(facilityId, end, 100, 100));
}
// helpers
public long createFacility() {
long facilityId = dummies.createFacility();
// by default support all capacity type and usage combinations in tests
Facility facility = facilityDao.getFacility(facilityId);
facility.pricingMethod = PricingMethod.CUSTOM;
for (CapacityType capacityType : CapacityType.values()) {
facility.builtCapacity.put(capacityType, 1000);
}
for (CapacityType capacityType : CapacityType.values()) {
for (Usage usage : Usage.values()) {
for (DayType dayType : DayType.values()) {
facility.pricing.add(new Pricing(capacityType, usage, 10000, dayType, "00:00", "24:00", ""));
}
}
}
facilityDao.updateFacility(facilityId, facility);
return facilityId;
}
private static Utilization newUtilization(long facilityId, DateTime time, int spacesAvailable, int capacity) {
Utilization u = new Utilization();
u.facilityId = facilityId;
u.capacityType = CAR;
u.usage = HSL_TRAVEL_CARD;
u.timestamp = time;
u.spacesAvailable = spacesAvailable;
u.capacity = capacity;
return u;
}
}