From 86ecf3b317277c10c50c755300e935fdf8e39ac1 Mon Sep 17 00:00:00 2001 From: Matthias Engelien Date: Mon, 16 Sep 2024 07:07:30 +0200 Subject: [PATCH] Implemented story 1 and missing utils --- pom.xml | 8 +- .../etecture/ga/api/GarageApiController.java | 59 +++++------- .../dto/mapper/AppointmentTerminMapper.java | 19 ++-- .../de/etecture/ga/model/Appointment.java | 37 ++++---- .../ga/repository/AppointmentRepository.java | 9 +- .../ga/service/AppointmentService.java | 82 +++++++++------- .../ga/service/GarageImportService.java | 23 +++-- .../de/etecture/ga/util/DateTimeUtil.java | 94 +++++++++++++++++++ src/main/resources/schema.sql | 2 +- ...AppointmentManagementApplicationTests.java | 15 +-- .../ga/api/GarageApiControllerTest.java | 40 ++++++-- src/test/resources/import/test-data.csv | 1 - 12 files changed, 263 insertions(+), 126 deletions(-) create mode 100644 src/main/java/de/etecture/ga/util/DateTimeUtil.java diff --git a/pom.xml b/pom.xml index 19720d7..5d85a1b 100644 --- a/pom.xml +++ b/pom.xml @@ -57,13 +57,17 @@ h2 runtime - + org.projectlombok lombok true - + + org.threeten + threeten-extra + 1.8.0 + org.springframework.boot diff --git a/src/main/java/de/etecture/ga/api/GarageApiController.java b/src/main/java/de/etecture/ga/api/GarageApiController.java index 87880e0..9eee602 100644 --- a/src/main/java/de/etecture/ga/api/GarageApiController.java +++ b/src/main/java/de/etecture/ga/api/GarageApiController.java @@ -3,13 +3,11 @@ */ package de.etecture.ga.api; -import java.text.ParseException; -import java.text.SimpleDateFormat; +import java.time.LocalDateTime; import java.util.Date; import java.util.List; import java.util.Optional; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -21,6 +19,7 @@ import de.etecture.ga.dto.TerminRequest; import de.etecture.ga.dto.mapper.AppointmentTerminMapper; import de.etecture.ga.model.Appointment; import de.etecture.ga.service.AppointmentService; +import de.etecture.ga.util.DateTimeUtil; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; @@ -57,8 +56,8 @@ public class GarageApiController implements WerkstattApi { long garageId = Long.parseLong(werkstattId); Optional serviceId = NumberUtils.isParsable(leistungsId) ? Optional.of(Long.parseLong(leistungsId)) : Optional.empty(); - Optional appointmentsFrom = Optional.ofNullable(parseDate(von)); - Optional appointmentsTill = Optional.ofNullable(parseDate(bis)); + Optional appointmentsFrom = Optional.ofNullable(DateTimeUtil.toDate(von)); + Optional appointmentsTill = Optional.ofNullable(DateTimeUtil.toDate(bis)); log.info("Filter appointments by garage {}, serviceId {}, from {}, till {}", garageId, serviceId, appointmentsFrom, appointmentsTill); @@ -71,16 +70,21 @@ public class GarageApiController implements WerkstattApi { @Override public ResponseEntity> getTerminvorschlaege(String werkstattId, @NotNull @Valid String leistungsId, @Valid String von, @Valid String bis) { + + // input validation Assert.isTrue(NumberUtils.isParsable(werkstattId), "werkstattId ungültig"); + Assert.isTrue(NumberUtils.isParsable(leistungsId), "leistungsId ungültig"); + Assert.notNull(DateTimeUtil.toDate(von), "von ungültig"); + Assert.notNull(DateTimeUtil.toDate(bis), "bis ungültig"); long garageId = Long.parseLong(werkstattId); - Optional serviceId = NumberUtils.isParsable(leistungsId) ? Optional.of(Long.parseLong(leistungsId)) - : Optional.empty(); - Optional appointmentsFrom = Optional.ofNullable(parseDate(von)); - Optional appointmentsTill = Optional.ofNullable(parseDate(bis)); - - - return WerkstattApi.super.getTerminvorschlaege(werkstattId, leistungsId, von, bis); + long serviceId = Long.parseLong(leistungsId); + LocalDateTime appointmentFrom = DateTimeUtil.toLocalDateTime(von); + LocalDateTime appointmentTill = DateTimeUtil.toLocalDateTime(bis); + + return ResponseEntity + .ok(appointmentService.getAppointmentSuggestion(garageId, serviceId, appointmentFrom, appointmentTill) + .stream().map(AppointmentTerminMapper::toTermin).toList()); } @Override @@ -89,34 +93,19 @@ public class GarageApiController implements WerkstattApi { // input validation Assert.isTrue(NumberUtils.isParsable(werkstattId), "werkstattId ungültig"); Assert.isTrue(NumberUtils.isParsable(termin.getLeistungsId()), "leistungsId ungültig"); - Assert.notNull(parseDate(termin.getVon()), "von ungültig"); - Assert.notNull(parseDate(termin.getBis()), "bis ungültig"); - + Assert.notNull(DateTimeUtil.toDate(termin.getVon()), "von ungültig"); + Assert.notNull(DateTimeUtil.toDate(termin.getBis()), "bis ungültig"); + long garageId = Long.parseLong(werkstattId); long serviceId = Long.parseLong(termin.getLeistungsId()); - Date appointmentFrom = parseDate(termin.getVon()); - Date appointmentTill = parseDate(termin.getBis()); - + LocalDateTime appointmentFrom = DateTimeUtil.toLocalDateTime(termin.getVon()); + LocalDateTime appointmentTill = DateTimeUtil.toLocalDateTime(termin.getBis()); + // create appointment if possible - Optional appointment = appointmentService.createAppointment(garageId, serviceId, - appointmentFrom, appointmentTill); + Optional appointment = appointmentService.createAppointment(garageId, serviceId, appointmentFrom, + appointmentTill); return appointment.map(a -> ResponseEntity.ok(AppointmentTerminMapper.toTermin(a))) .orElse(ResponseEntity.status(HttpStatus.CONFLICT).build()); } - - private Date parseDate(String dateTimeString) { - - if (StringUtils.isNotBlank(dateTimeString)) { - try { - SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy HH:mm"); - - return format.parse(dateTimeString); - } catch (ParseException e) { - log.error("Invalid data to parse", e); - } - } - - return null; - } } diff --git a/src/main/java/de/etecture/ga/dto/mapper/AppointmentTerminMapper.java b/src/main/java/de/etecture/ga/dto/mapper/AppointmentTerminMapper.java index f8048eb..7d44e04 100644 --- a/src/main/java/de/etecture/ga/dto/mapper/AppointmentTerminMapper.java +++ b/src/main/java/de/etecture/ga/dto/mapper/AppointmentTerminMapper.java @@ -1,9 +1,10 @@ package de.etecture.ga.dto.mapper; -import java.time.format.DateTimeFormatter; +import java.util.Optional; import de.etecture.ga.dto.Termin; import de.etecture.ga.model.Appointment; +import de.etecture.ga.util.DateTimeUtil; import lombok.extern.slf4j.Slf4j; @Slf4j @@ -13,13 +14,15 @@ public class AppointmentTerminMapper { log.info("Mapping Object {}", appointment); - return new Termin() - .id(Long.toString(appointment.id())) - .leistungsId(Long.toString(appointment.serviceId().getId())) - .leistung(appointment.serviceName()) - .werkstattName("to_be_set") - .von(appointment.appointmentStart().format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"))) - .bis(appointment.appointmentEnd().format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"))); + String id = Optional.ofNullable(appointment.id()).map(i -> Long.toString(i)).orElse(null); + String serviceId = Optional.ofNullable(appointment.serviceId().getId()).map(i -> Long.toString(i)).orElse(null); + String serviceName = Optional.ofNullable(appointment.serviceName()).orElse(null); + String garageName = "to_be_set"; + String from = Optional.ofNullable(appointment.appointmentStart()).map(DateTimeUtil::toString).orElse(null); + String till = Optional.ofNullable(appointment.appointmentEnd()).map(DateTimeUtil::toString).orElse(null); + + return new Termin().id(id).leistungsId(serviceId).leistung(serviceName).werkstattName(garageName).von(from) + .bis(till); } } diff --git a/src/main/java/de/etecture/ga/model/Appointment.java b/src/main/java/de/etecture/ga/model/Appointment.java index be767be..891b647 100644 --- a/src/main/java/de/etecture/ga/model/Appointment.java +++ b/src/main/java/de/etecture/ga/model/Appointment.java @@ -1,15 +1,14 @@ package de.etecture.ga.model; import java.time.Duration; -import java.time.Instant; import java.time.LocalDateTime; -import java.time.ZoneId; import java.util.Date; import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.core.mapping.AggregateReference; import org.springframework.data.relational.core.mapping.Column; +import de.etecture.ga.util.DateTimeUtil; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; @@ -17,46 +16,48 @@ import lombok.experimental.Accessors; @Data @Accessors(fluent = true, chain = true) @EqualsAndHashCode(onlyExplicitlyIncluded = true) -public class Appointment implements Comparable{ +public class Appointment implements Comparable { @Id @EqualsAndHashCode.Include private Long id; - + @Column("GARAGE_ID") private AggregateReference garageId; - + @Column("SERVICE_ID") private AggregateReference serviceId; - + @EqualsAndHashCode.Include private String serviceCode; - + private String serviceName; - + @EqualsAndHashCode.Include private Date appointmentTime; - + @EqualsAndHashCode.Include private Integer slot = 1; - + private Duration duration = Duration.ZERO; - - + public LocalDateTime appointmentStart() { - return Instant.ofEpochMilli(this.appointmentTime().getTime()) - .atZone(ZoneId.systemDefault()).toLocalDateTime(); + if (this.appointmentTime == null) + return null; + return DateTimeUtil.toLocalDateTime(this.appointmentTime); } - + public LocalDateTime appointmentEnd() { + if (this.appointmentTime == null) + return null; return this.appointmentStart().plus(this.duration()); } - @Override public int compareTo(Appointment o) { - if(o == null || o.appointmentTime() == null) return 1; - + if (o == null || o.appointmentTime() == null) + return 1; + return this.appointmentTime().compareTo(o.appointmentTime()); } } diff --git a/src/main/java/de/etecture/ga/repository/AppointmentRepository.java b/src/main/java/de/etecture/ga/repository/AppointmentRepository.java index 84f6b58..2162f87 100644 --- a/src/main/java/de/etecture/ga/repository/AppointmentRepository.java +++ b/src/main/java/de/etecture/ga/repository/AppointmentRepository.java @@ -3,7 +3,6 @@ package de.etecture.ga.repository; import java.util.List; import java.util.Optional; -import org.springframework.data.jdbc.repository.query.Query; import org.springframework.data.repository.CrudRepository; import de.etecture.ga.model.Appointment; @@ -11,12 +10,8 @@ import de.etecture.ga.model.Appointment; public interface AppointmentRepository extends CrudRepository { public Optional findByIdAndGarageId(long id, long garageId); - + public List findByGarageId(long garageId); - - @Query("select a.* from APPOINTMENT a " - + "join GARAGE g on g.ID = a.GARAGE_ID " - + "join MD_SERVICE s on s.ID = a.SERVICE_ID " - + "where s.CODE = :serviceCode and g.ID = :garageId ") + public List findByServiceCodeAndGarageId(String serviceCode, long garageId); } diff --git a/src/main/java/de/etecture/ga/service/AppointmentService.java b/src/main/java/de/etecture/ga/service/AppointmentService.java index 90e7744..f6aa50c 100644 --- a/src/main/java/de/etecture/ga/service/AppointmentService.java +++ b/src/main/java/de/etecture/ga/service/AppointmentService.java @@ -1,9 +1,9 @@ package de.etecture.ga.service; -import java.time.Instant; +import java.time.Duration; import java.time.LocalDateTime; -import java.time.ZoneId; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Optional; @@ -17,10 +17,13 @@ import de.etecture.ga.model.Garage; import de.etecture.ga.model.GarageServices; import de.etecture.ga.model.MDService; import de.etecture.ga.repository.AppointmentRepository; +import de.etecture.ga.util.DateTimeUtil; import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; @Service @AllArgsConstructor +@Slf4j public class AppointmentService { private final AppointmentRepository repository; @@ -29,12 +32,6 @@ public class AppointmentService { private final MDServiceService serviceService; - public Optional getAppointment(long appointmentId) { - - return repository.findById(appointmentId); - - } - public Optional getAppointment(long appointmentId, long garageId) { return repository.findByIdAndGarageId(appointmentId, garageId); @@ -60,7 +57,26 @@ public class AppointmentService { return appointments.toList(); } - public Optional createAppointment(long garageId, long serviceId, Date from, Date till) { + public List getAppointmentSuggestion(Long garageId, Long serviceId, LocalDateTime from, + LocalDateTime till) { + + Optional garage = garageService.getGarage(garageId); + if (garage.isEmpty()) + throw new IllegalArgumentException("GarageId not valid"); + + Optional garageService = garage.get().garageServices().stream() + .filter(gs -> serviceId == gs.serviceId().getId()).findFirst(); + if (garageService.isEmpty()) + throw new IllegalArgumentException("serviceId not valid"); + + return getValidAppointmentTimeList(garage.get(), garageService.get(), from, till).stream() + .map(t -> new Appointment().serviceId(AggregateReference.to(serviceId)) + .appointmentTime(DateTimeUtil.toDate(t))) + .toList(); + } + + public Optional createAppointment(long garageId, long serviceId, LocalDateTime from, + LocalDateTime till) { // validate input Optional garage = garageService.getGarage(garageId); @@ -69,17 +85,17 @@ public class AppointmentService { Optional service = serviceService.getMDService(serviceId) .filter(s -> garage.get().garageServices().stream().anyMatch(gs -> s.id() == gs.serviceId().getId())); - Optional garageService = garage.get().garageServices().stream() + Optional garageServices = garage.get().garageServices().stream() .filter(gs -> serviceId == gs.serviceId().getId()).findFirst(); - if (service.isEmpty() || garageService.isEmpty()) + if (service.isEmpty() || garageServices.isEmpty()) throw new IllegalArgumentException("serviceId not valid"); // check appointment times - LocalDateTime validAppointmentTime = getValidAppointmentTime(from, till, garage); + LocalDateTime validAppointmentTime = getValidAppointmentTime(garage.get(), garageServices.get(), from, till); // create appointment if (validAppointmentTime != null) { - Appointment appointment = createAppointmentObj(garage, service, garageService, validAppointmentTime); + Appointment appointment = createAppointmentObj(garage, service, garageServices, validAppointmentTime); return Optional.of(repository.save(appointment)); @@ -88,24 +104,27 @@ public class AppointmentService { } } - private LocalDateTime getValidAppointmentTime(Date from, Date till, Optional garage) { + private List getValidAppointmentTimeList(Garage garage, GarageServices service, LocalDateTime from, + LocalDateTime till) { - LocalDateTime fromDateTime = Instant.ofEpochMilli(from.getTime()).atZone(ZoneId.systemDefault()) - .toLocalDateTime(); - fromDateTime = roundUpToQuarter(fromDateTime); - LocalDateTime tillDateTime = Instant.ofEpochMilli(till.getTime()).atZone(ZoneId.systemDefault()) - .toLocalDateTime(); + List result = new ArrayList<>(); - LocalDateTime validAppointmentTime = null; - while (!fromDateTime.isAfter(tillDateTime)) { - if (isSlotAvailable(garage.get(), fromDateTime)) { - validAppointmentTime = fromDateTime; - break; + from = roundUpToQuarter(from); + + while (from.isBefore(till)) { + if (isSlotAvailable(garage, from, service.duration())) { + result.add(from); } - fromDateTime = fromDateTime.plusMinutes(15); + from = from.plusMinutes(15); } - return validAppointmentTime; + return result; + } + + private LocalDateTime getValidAppointmentTime(Garage garage, GarageServices service, LocalDateTime from, + LocalDateTime till) { + + return getValidAppointmentTimeList(garage, service, from, till).getFirst(); } private LocalDateTime roundUpToQuarter(LocalDateTime datetime) { @@ -117,20 +136,19 @@ public class AppointmentService { return datetime.plusMinutes(minutesToAdd).truncatedTo(ChronoUnit.MINUTES); } - private boolean isSlotAvailable(Garage garage, LocalDateTime time) { + private boolean isSlotAvailable(Garage garage, LocalDateTime startTime, Duration duration) { - long appointments = garage.appointments().stream() - .filter(a -> (a.appointmentStart().isBefore(time) || a.appointmentStart().isEqual(time)) - && a.appointmentEnd().isAfter(time)) - .count(); + long appointments = garage.appointments().stream().filter(a -> DateTimeUtil.overlaps(a.appointmentStart(), + a.appointmentEnd(), startTime, startTime.plus(duration))).count(); return appointments < garage.maxAppointments(); } private Appointment createAppointmentObj(Optional garage, Optional service, Optional garageService, LocalDateTime validAppointmentTime) { + Appointment appointment = new Appointment().garageId(AggregateReference.to(garage.get().id())) - .appointmentTime(Date.from(validAppointmentTime.atZone(ZoneId.systemDefault()).toInstant())) + .appointmentTime(DateTimeUtil.toDate(validAppointmentTime)) .serviceId(AggregateReference.to(service.get().id())).serviceCode(service.get().code()) .serviceName(service.get().name()).duration(garageService.get().duration()); return appointment; diff --git a/src/main/java/de/etecture/ga/service/GarageImportService.java b/src/main/java/de/etecture/ga/service/GarageImportService.java index 3110117..bc33076 100644 --- a/src/main/java/de/etecture/ga/service/GarageImportService.java +++ b/src/main/java/de/etecture/ga/service/GarageImportService.java @@ -15,7 +15,6 @@ import java.util.stream.Stream; import org.springframework.data.jdbc.core.mapping.AggregateReference; import org.springframework.stereotype.Service; -import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.MappingIterator; import com.fasterxml.jackson.dataformat.csv.CsvMapper; import com.fasterxml.jackson.dataformat.csv.CsvSchema; @@ -26,6 +25,7 @@ import de.etecture.ga.model.MDService; import de.etecture.ga.repository.AppointmentRepository; import de.etecture.ga.repository.GarageRepository; import de.etecture.ga.repository.GarageServiceRepository; +import de.etecture.ga.util.DateTimeUtil; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -89,7 +89,7 @@ public class GarageImportService { try { CsvSchema bootstrapSchema = CsvSchema.emptySchema().withHeader(); CsvMapper mapper = new CsvMapper(); - + MappingIterator readValues = mapper.readerFor(CSVData.class).with(bootstrapSchema) .readValues(file.toFile()); @@ -103,18 +103,27 @@ public class GarageImportService { private void addAppointmentToGarage(CSVData data, Garage garage) { - Optional garageService = garageServiceRepository.findByServiceCodeAndGarage(data.SERVICE, - garage.code()); + Optional garageService = garageServiceRepository + .findByServiceCodeAndGarage(data.appointmentServiceCode(), garage.code()); - garageService.map(service -> getAppointmentForService(service, data.APP_DATE, garage.id())) + garageService.map(service -> getAppointmentForService(service, data.appointmentDate(), garage.id())) .ifPresent(garage::addAppointment); } private Appointment getAppointmentForService(MDService service, Date date, Long garageId) { return new Appointment().appointmentTime(date).serviceCode(service.code()).serviceName(service.name()) - .duration(service.duration()).garageId(AggregateReference.to(garageId)).serviceId(AggregateReference.to(service.id())); + .duration(service.duration()).garageId(AggregateReference.to(garageId)) + .serviceId(AggregateReference.to(service.id())); } - private record CSVData(@JsonFormat(pattern="yyyy-MM-dd'T'HH:mm'Z'") Date APP_DATE, String SERVICE) { + private record CSVData(String APP_DATE, String SERVICE) { + + public Date appointmentDate() { + return DateTimeUtil.toDate(APP_DATE); + } + + public String appointmentServiceCode() { + return SERVICE; + } } } diff --git a/src/main/java/de/etecture/ga/util/DateTimeUtil.java b/src/main/java/de/etecture/ga/util/DateTimeUtil.java new file mode 100644 index 0000000..7c39df4 --- /dev/null +++ b/src/main/java/de/etecture/ga/util/DateTimeUtil.java @@ -0,0 +1,94 @@ +package de.etecture.ga.util; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; +import org.threeten.extra.Interval; + +import lombok.extern.slf4j.Slf4j; + +/** + * Some methods to handle {@link Date} and {@link LocalDateTime} conversion and + * parsing + */ +@Slf4j +public class DateTimeUtil { + + private static final String DEFAULT_DATEFORMAT = "dd.MM.yyyy HH:mm"; + + private static final String IMPORT_DATEFORMAT = "yyyy-MM-dd'T'HH:mm'Z'"; + + private static final Pattern IMPORT_DATEPATTERN = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})Z"); + + private DateTimeUtil() { + } + + public static Date toDate(LocalDateTime ldt) { + if (ldt == null) + return null; + return Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); + } + + public static LocalDateTime toLocalDateTime(Date date) { + if (date == null) + return null; + return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); + } + + public static String toString(Date date) { + + return toString(toLocalDateTime(date)); + } + + public static String toString(LocalDateTime ldt) { + if (ldt == null) + return null; + return ldt.format(DateTimeFormatter.ofPattern(DEFAULT_DATEFORMAT)); + } + + public static Date toDate(String dateTimeString) { + + if (StringUtils.isNotBlank(dateTimeString)) { + try { + DateFormat format = getFormatter(dateTimeString); + return format.parse(dateTimeString); + } catch (ParseException e) { + log.error("Invalid data to parse", e); + } + } + + return null; + } + + public static LocalDateTime toLocalDateTime(String dateTimeString) { + + Date date = toDate(dateTimeString); + if (date != null) + return toLocalDateTime(date); + + return null; + } + + public static boolean overlaps(LocalDateTime startA, LocalDateTime endA, LocalDateTime startB, LocalDateTime endB) { + Interval intervalA = Interval.of(startA.atZone(ZoneId.systemDefault()).toInstant(), endA.atZone(ZoneId.systemDefault()).toInstant()); + Interval IntervalB = Interval.of(startB.atZone(ZoneId.systemDefault()).toInstant(), endB.atZone(ZoneId.systemDefault()).toInstant()); + + return intervalA.overlaps(IntervalB); + } + + private static DateFormat getFormatter(String dateTimeString) { + + if (IMPORT_DATEPATTERN.matcher(dateTimeString).matches()) + return new SimpleDateFormat(IMPORT_DATEFORMAT); + else + return new SimpleDateFormat(DEFAULT_DATEFORMAT); + } + +} diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index c0b111a..022f163 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -37,7 +37,7 @@ CREATE TABLE APPOINTMENT ( SERVICE_ID BIGINT NOT NULL, SERVICE_CODE VARCHAR(5) DEFAULT NULL, SERVICE_NAME VARCHAR(50) DEFAULT NULL, - APPOINTMENT_TIME DATE DEFAULT NULL, + APPOINTMENT_TIME TIMESTAMP DEFAULT NULL, SLOT INT NOT NULL DEFAULT 1, DURATION BIGINT NOT NULL DEFAULT 0, diff --git a/src/test/java/de/etecture/ga/GarageAppointmentManagementApplicationTests.java b/src/test/java/de/etecture/ga/GarageAppointmentManagementApplicationTests.java index 0845f31..760347f 100644 --- a/src/test/java/de/etecture/ga/GarageAppointmentManagementApplicationTests.java +++ b/src/test/java/de/etecture/ga/GarageAppointmentManagementApplicationTests.java @@ -1,6 +1,8 @@ package de.etecture.ga; -import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.URISyntaxException; import java.util.Optional; @@ -26,11 +28,12 @@ class GarageAppointmentManagementApplicationTests { void testImportedGarageData() throws URISyntaxException { Optional testGarage = garageRepository.findByCode("test-data"); - - assertThat(testGarage).isPresent(); - assertThat(testGarage.get().name()).isEqualTo("Test Autohaus"); - assertThat(testGarage.get().appointments()).hasSize(19); - assertThat(testGarage.get().garageServices()).hasSize(3); + + + assertNotNull(testGarage.get(), "Garage should not be null"); + assertEquals("Test Autohaus", testGarage.get().name()); + assertTrue(testGarage.get().appointments().size() == 19, "Test Autohaus should have 19 appointments"); + assertTrue(testGarage.get().garageServices().size() == 3, "Test Autohaus should have 3 services"); } } diff --git a/src/test/java/de/etecture/ga/api/GarageApiControllerTest.java b/src/test/java/de/etecture/ga/api/GarageApiControllerTest.java index a2d2631..4b4a5a9 100644 --- a/src/test/java/de/etecture/ga/api/GarageApiControllerTest.java +++ b/src/test/java/de/etecture/ga/api/GarageApiControllerTest.java @@ -1,7 +1,6 @@ package de.etecture.ga.api; import static org.hamcrest.Matchers.hasSize; -import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -59,8 +58,8 @@ class GarageApiControllerTest { .andExpect(MockMvcResultMatchers.jsonPath("$.id").value("1")) .andExpect(MockMvcResultMatchers.jsonPath("$.leistung").value("Radwechsel")) .andExpect(MockMvcResultMatchers.jsonPath("$.leistungsId").value("3")) - .andExpect(MockMvcResultMatchers.jsonPath("$.von").value("02.01.2019 12:00")) - .andExpect(MockMvcResultMatchers.jsonPath("$.bis").value("02.01.2019 12:30")); + .andExpect(MockMvcResultMatchers.jsonPath("$.von").value("02.01.2019 13:00")) + .andExpect(MockMvcResultMatchers.jsonPath("$.bis").value("02.01.2019 13:30")); } @@ -69,7 +68,7 @@ class GarageApiControllerTest { this.mockMvc .perform( - MockMvcRequestBuilders.get("/werkstatt/{werkstattId}/termin/{terminId}", testGarage.id(), "20")) + MockMvcRequestBuilders.get("/werkstatt/{werkstattId}/termin/{terminId}", testGarage.id(), "-5")) .andDo(MockMvcResultHandlers.print()).andExpect(MockMvcResultMatchers.status().isNotFound()); this.mockMvc @@ -86,7 +85,7 @@ class GarageApiControllerTest { .perform(MockMvcRequestBuilders.get("/werkstatt/{werkstattId}/termine", testGarage.id()).param("thing", "somewhere")) .andDo(MockMvcResultHandlers.print()).andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(MockMvcResultMatchers.jsonPath("$", hasSize(19))); + .andExpect(MockMvcResultMatchers.jsonPath("$", hasSize(18))); } @Test @@ -96,7 +95,7 @@ class GarageApiControllerTest { .perform(MockMvcRequestBuilders.get("/werkstatt/{werkstattId}/termine", testGarage.id()) .param("leistungsId", "3")) .andDo(MockMvcResultHandlers.print()).andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(MockMvcResultMatchers.jsonPath("$", hasSize(7))); + .andExpect(MockMvcResultMatchers.jsonPath("$", hasSize(8))); this.mockMvc .perform(MockMvcRequestBuilders.get("/werkstatt/{werkstattId}/termine", testGarage.id()) @@ -108,13 +107,36 @@ class GarageApiControllerTest { .perform(MockMvcRequestBuilders.get("/werkstatt/{werkstattId}/termine", testGarage.id()) .param("leistungsId", "3").param("von", "05.01.2019 12:00").param("bis", "08.01.2019 12:00")) .andDo(MockMvcResultHandlers.print()).andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(MockMvcResultMatchers.jsonPath("$", hasSize(1))); + .andExpect(MockMvcResultMatchers.jsonPath("$", hasSize(2))); } @Test - void testGetTerminvorschlaege() { - fail("Not yet implemented"); + void testGetTerminvorschlaege() throws Exception { + + this.mockMvc + .perform(MockMvcRequestBuilders.get("/werkstatt/{werkstattId}/terminvorschlag/", testGarage.id()) + .param("leistungsId", "3").param("von", "05.01.2019 12:00").param("bis", "05.01.2019 13:00")) + .andDo(MockMvcResultHandlers.print()).andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$", hasSize(3))); + + this.mockMvc + .perform(MockMvcRequestBuilders.get("/werkstatt/{werkstattId}/terminvorschlag/", testGarage.id()) + .param("leistungsId", "3").param("von", "05.01.2019 12:05").param("bis", "05.01.2019 13:00")) + .andDo(MockMvcResultHandlers.print()).andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$", hasSize(2))); + + this.mockMvc + .perform(MockMvcRequestBuilders.get("/werkstatt/{werkstattId}/terminvorschlag/", testGarage.id()) + .param("leistungsId", "3").param("von", "08.01.2019 09:00").param("bis", "08.01.2019 10:00")) + .andDo(MockMvcResultHandlers.print()).andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$", hasSize(1))); + + this.mockMvc + .perform(MockMvcRequestBuilders.get("/werkstatt/{werkstattId}/terminvorschlag/", testGarage.id()) + .param("leistungsId", "3").param("von", "08.01.2019 09:00").param("bis", "08.01.2019 13:30")) + .andDo(MockMvcResultHandlers.print()).andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$", hasSize(2))); } @Test diff --git a/src/test/resources/import/test-data.csv b/src/test/resources/import/test-data.csv index f3b382e..0b0fa8f 100644 --- a/src/test/resources/import/test-data.csv +++ b/src/test/resources/import/test-data.csv @@ -3,7 +3,6 @@ APP_DATE,SERVICE 2019-01-02T15:00Z,WHE 2019-01-04T08:15Z,MOT 2019-01-04T09:00Z,OIL -2019-01-04T09:00Z,OIL 2019-01-05T10:15Z,MOT 2019-01-05T13:00Z,WHE 2019-01-07T08:15Z,OIL