From b664ab1e4494c607500c69486c456d3d27d3a6fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Nicol=C3=A1s=20Rouco?= Date: Thu, 17 Feb 2022 11:31:45 -0300 Subject: [PATCH 1/3] Points 1 to 8 of the readme --- .../tenniscourts/guests/GuestController.java | 60 +++++++++++++++++ .../com/tenniscourts/guests/GuestDTO.java | 27 ++++++++ .../com/tenniscourts/guests/GuestMapper.java | 15 +++++ .../tenniscourts/guests/GuestRepository.java | 13 ++++ .../com/tenniscourts/guests/GuestService.java | 66 +++++++++++++++++++ .../reservations/ReservationController.java | 29 ++++++-- .../reservations/ReservationDTO.java | 3 + .../reservations/ReservationMapper.java | 3 + .../reservations/ReservationRepository.java | 2 + .../reservations/ReservationService.java | 36 ++++++++-- .../schedules/ScheduleController.java | 25 +++++-- .../schedules/ScheduleRepository.java | 4 ++ .../schedules/ScheduleService.java | 29 ++++++-- .../tenniscourts/TennisCourtController.java | 20 ++++-- .../reservations/ReservationServiceTest.java | 13 +++- 15 files changed, 314 insertions(+), 31 deletions(-) create mode 100644 src/main/java/com/tenniscourts/guests/GuestController.java create mode 100644 src/main/java/com/tenniscourts/guests/GuestDTO.java create mode 100644 src/main/java/com/tenniscourts/guests/GuestMapper.java create mode 100644 src/main/java/com/tenniscourts/guests/GuestRepository.java create mode 100644 src/main/java/com/tenniscourts/guests/GuestService.java diff --git a/src/main/java/com/tenniscourts/guests/GuestController.java b/src/main/java/com/tenniscourts/guests/GuestController.java new file mode 100644 index 00000000..3ecaa6df --- /dev/null +++ b/src/main/java/com/tenniscourts/guests/GuestController.java @@ -0,0 +1,60 @@ +package com.tenniscourts.guests; + +import java.util.List; + +import com.tenniscourts.config.BaseRestController; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +@RestController +@RequestMapping("/guest") +public class GuestController extends BaseRestController { + + private final GuestService guestService; + + @GetMapping("/all") + public ResponseEntity> findAll() { + return ResponseEntity.ok(guestService.findAll()); + } + + @GetMapping("/{id}") + public ResponseEntity findById(@PathVariable("id") Long id) { + return ResponseEntity.ok(guestService.findById(id)); + } + + @GetMapping() + public ResponseEntity> findByName(@RequestParam("name") String name) { + return ResponseEntity.ok(guestService.findByName(name)); + } + + @PostMapping("/add") + public ResponseEntity addGuest(@RequestBody GuestDTO guestDTO) { + return ResponseEntity.created(locationByEntity(guestService.addGuest(guestDTO).getId())).build(); + } + + @PutMapping("/update") + public ResponseEntity updateGuest(@RequestBody GuestDTO guestDTO) { + return ResponseEntity.created(locationByEntity(guestService.updateGuest(guestDTO).getId())).build(); + } + + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable("id") Long id) { + return (guestService.delete(id)) ? ResponseEntity.ok().build() : ResponseEntity.status(HttpStatus.CONFLICT).build(); + } + + +} diff --git a/src/main/java/com/tenniscourts/guests/GuestDTO.java b/src/main/java/com/tenniscourts/guests/GuestDTO.java new file mode 100644 index 00000000..da5348ef --- /dev/null +++ b/src/main/java/com/tenniscourts/guests/GuestDTO.java @@ -0,0 +1,27 @@ +package com.tenniscourts.guests; + +import com.tenniscourts.schedules.ScheduleDTO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Builder +@Data +public class GuestDTO { + + private Long id; + + @NotNull + private String name; + +} diff --git a/src/main/java/com/tenniscourts/guests/GuestMapper.java b/src/main/java/com/tenniscourts/guests/GuestMapper.java new file mode 100644 index 00000000..5bb5772a --- /dev/null +++ b/src/main/java/com/tenniscourts/guests/GuestMapper.java @@ -0,0 +1,15 @@ +package com.tenniscourts.guests; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface GuestMapper { + + Guest map(GuestDTO source); + + @InheritInverseConfiguration + GuestDTO map(Guest source); + + +} diff --git a/src/main/java/com/tenniscourts/guests/GuestRepository.java b/src/main/java/com/tenniscourts/guests/GuestRepository.java new file mode 100644 index 00000000..8b8866de --- /dev/null +++ b/src/main/java/com/tenniscourts/guests/GuestRepository.java @@ -0,0 +1,13 @@ +package com.tenniscourts.guests; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface GuestRepository extends JpaRepository { + + List findByNameContainingIgnoreCase(String name); + + } diff --git a/src/main/java/com/tenniscourts/guests/GuestService.java b/src/main/java/com/tenniscourts/guests/GuestService.java new file mode 100644 index 00000000..08d3d78a --- /dev/null +++ b/src/main/java/com/tenniscourts/guests/GuestService.java @@ -0,0 +1,66 @@ +package com.tenniscourts.guests; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +import com.tenniscourts.exceptions.BusinessException; +import com.tenniscourts.exceptions.EntityNotFoundException; + +import org.springframework.stereotype.Service; + +import lombok.AllArgsConstructor; + +@Service +@AllArgsConstructor +public class GuestService { + + private final GuestRepository guestRepository; + + private final GuestMapper guestMapper; + + public List findAll() { + return guestRepository.findAll().stream().map(guest -> { return guestMapper.map(guest); }).collect(Collectors.toList()); + } + + public GuestDTO findById(Long id) { + return guestRepository.findById(id).map(guestMapper::map).orElseThrow(() -> { + throw new EntityNotFoundException("Guest " + id + " not found."); + }); + } + + public List findByName(String name) { + return guestRepository.findByNameContainingIgnoreCase(name).stream().map(guest -> { return guestMapper.map(guest); }).collect(Collectors.toList()); + } + + public GuestDTO addGuest(GuestDTO guest) { + if (guest.getId() > 0) { + throw new BusinessException("Guest id should be zero."); + } + return guestMapper.map(guestRepository.saveAndFlush(guestMapper.map(guest))); + } + + public GuestDTO updateGuest(GuestDTO guest) { + GuestDTO guestUpdated = null; + if (guest.getId() == 0) { + throw new BusinessException("Guest " + guest.getId() + " is zero."); + } + else { + // if not found findById throws an exception + GuestDTO guestToUpdate = findById(guest.getId()); + guestUpdated = guestMapper.map(guestRepository.saveAndFlush(guestMapper.map(guestToUpdate))); + } + return guestUpdated; + } + + public boolean delete(Long id) { + try { + guestRepository.delete(guestRepository.findById(id).get()); + return true; + } + catch (Exception exception) { + return false; + } + } +} diff --git a/src/main/java/com/tenniscourts/reservations/ReservationController.java b/src/main/java/com/tenniscourts/reservations/ReservationController.java index 2b1297b2..d619514c 100644 --- a/src/main/java/com/tenniscourts/reservations/ReservationController.java +++ b/src/main/java/com/tenniscourts/reservations/ReservationController.java @@ -1,27 +1,48 @@ package com.tenniscourts.reservations; +import java.math.BigDecimal; + import com.tenniscourts.config.BaseRestController; import lombok.AllArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; @AllArgsConstructor +@RestController +@RequestMapping("/reservation") public class ReservationController extends BaseRestController { private final ReservationService reservationService; - public ResponseEntity bookReservation(CreateReservationRequestDTO createReservationRequestDTO) { + @PostMapping("/book") + public ResponseEntity bookReservation(@RequestBody CreateReservationRequestDTO createReservationRequestDTO) { return ResponseEntity.created(locationByEntity(reservationService.bookReservation(createReservationRequestDTO).getId())).build(); } - public ResponseEntity findReservation(Long reservationId) { + @GetMapping("/{reservationId}") + public ResponseEntity findReservation(@PathVariable("reservationId") Long reservationId) { return ResponseEntity.ok(reservationService.findReservation(reservationId)); } - public ResponseEntity cancelReservation(Long reservationId) { + @PutMapping("/cancel/{reservationId}") + public ResponseEntity cancelReservation(@PathVariable("reservationId") Long reservationId) { return ResponseEntity.ok(reservationService.cancelReservation(reservationId)); } - public ResponseEntity rescheduleReservation(Long reservationId, Long scheduleId) { + @PutMapping("/reschedule/{reservationId}") + public ResponseEntity rescheduleReservation(@PathVariable("reservationId") Long reservationId, @RequestParam("scheduleId") Long scheduleId) { return ResponseEntity.ok(reservationService.rescheduleReservation(reservationId, scheduleId)); } + + @PutMapping("/chargeDeposit/{reservationId}") + public ResponseEntity chargeReservationDeposit(@PathVariable("reservationId") Long reservationId, @RequestParam(name = "deposit", required = false, defaultValue = "10") BigDecimal deposit) { + return ResponseEntity.ok(reservationService.chargeDeposit(reservationId, deposit)); + } } diff --git a/src/main/java/com/tenniscourts/reservations/ReservationDTO.java b/src/main/java/com/tenniscourts/reservations/ReservationDTO.java index 077bb59a..39a75219 100644 --- a/src/main/java/com/tenniscourts/reservations/ReservationDTO.java +++ b/src/main/java/com/tenniscourts/reservations/ReservationDTO.java @@ -1,5 +1,6 @@ package com.tenniscourts.reservations; +import com.tenniscourts.guests.GuestDTO; import com.tenniscourts.schedules.ScheduleDTO; import lombok.AllArgsConstructor; import lombok.Builder; @@ -23,6 +24,8 @@ public class ReservationDTO { private ScheduleDTO schedule; + private GuestDTO guest; + private String reservationStatus; private ReservationDTO previousReservation; diff --git a/src/main/java/com/tenniscourts/reservations/ReservationMapper.java b/src/main/java/com/tenniscourts/reservations/ReservationMapper.java index b648ccf4..ad83bab1 100644 --- a/src/main/java/com/tenniscourts/reservations/ReservationMapper.java +++ b/src/main/java/com/tenniscourts/reservations/ReservationMapper.java @@ -3,10 +3,13 @@ import org.mapstruct.InheritInverseConfiguration; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; @Mapper(componentModel = "spring") public interface ReservationMapper { + // ReservationMapper INSTANCE = Mappers.getMapper(ReservationMapper.class); + Reservation map(ReservationDTO source); @InheritInverseConfiguration diff --git a/src/main/java/com/tenniscourts/reservations/ReservationRepository.java b/src/main/java/com/tenniscourts/reservations/ReservationRepository.java index d601850f..00210b5f 100644 --- a/src/main/java/com/tenniscourts/reservations/ReservationRepository.java +++ b/src/main/java/com/tenniscourts/reservations/ReservationRepository.java @@ -1,10 +1,12 @@ package com.tenniscourts.reservations; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; import java.time.LocalDateTime; import java.util.List; +@Repository public interface ReservationRepository extends JpaRepository { List findBySchedule_Id(Long scheduleId); diff --git a/src/main/java/com/tenniscourts/reservations/ReservationService.java b/src/main/java/com/tenniscourts/reservations/ReservationService.java index 14aa4436..2483a16c 100644 --- a/src/main/java/com/tenniscourts/reservations/ReservationService.java +++ b/src/main/java/com/tenniscourts/reservations/ReservationService.java @@ -1,23 +1,39 @@ package com.tenniscourts.reservations; import com.tenniscourts.exceptions.EntityNotFoundException; +import com.tenniscourts.guests.GuestDTO; +import com.tenniscourts.guests.GuestService; +import com.tenniscourts.schedules.ScheduleDTO; +import com.tenniscourts.schedules.ScheduleService; + import lombok.AllArgsConstructor; + +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; +import java.util.Optional; @Service @AllArgsConstructor public class ReservationService { private final ReservationRepository reservationRepository; - + private final ReservationMapper reservationMapper; + + private final GuestService guestService; + + private final ScheduleService scheduleService; public ReservationDTO bookReservation(CreateReservationRequestDTO createReservationRequestDTO) { - throw new UnsupportedOperationException(); + ReservationDTO reservationDTO = new ReservationDTO(); + reservationDTO.setGuest(guestService.findById(createReservationRequestDTO.getGuestId())); + reservationDTO.setSchedule(scheduleService.findSchedule(createReservationRequestDTO.getScheduleId())); + reservationDTO.setValue(BigDecimal.valueOf(0)); + return reservationMapper.map(reservationRepository.save(reservationMapper.map(reservationDTO))); } public ReservationDTO findReservation(Long reservationId) { @@ -71,15 +87,15 @@ public BigDecimal getRefundValue(Reservation reservation) { return BigDecimal.ZERO; } - /*TODO: This method actually not fully working, find a way to fix the issue when it's throwing the error: - "Cannot reschedule to the same slot.*/ public ReservationDTO rescheduleReservation(Long previousReservationId, Long scheduleId) { - Reservation previousReservation = cancel(previousReservationId); - - if (scheduleId.equals(previousReservation.getSchedule().getId())) { + ReservationDTO previousReservationDTO = findReservation(previousReservationId); + + if (scheduleId.equals(previousReservationDTO.getSchedule().getId())) { throw new IllegalArgumentException("Cannot reschedule to the same slot."); } + Reservation previousReservation = cancel(previousReservationId); + previousReservation.setReservationStatus(ReservationStatus.RESCHEDULED); reservationRepository.save(previousReservation); @@ -90,4 +106,10 @@ public ReservationDTO rescheduleReservation(Long previousReservationId, Long sch newReservation.setPreviousReservation(reservationMapper.map(previousReservation)); return newReservation; } + + public ReservationDTO chargeDeposit(Long reservationId, BigDecimal deposit) { + ReservationDTO reservationDTO = findReservation(reservationId); + reservationDTO.setValue(deposit); + return reservationMapper.map(reservationRepository.save(reservationMapper.map(reservationDTO))); + } } diff --git a/src/main/java/com/tenniscourts/schedules/ScheduleController.java b/src/main/java/com/tenniscourts/schedules/ScheduleController.java index 7613e89d..4428db4a 100644 --- a/src/main/java/com/tenniscourts/schedules/ScheduleController.java +++ b/src/main/java/com/tenniscourts/schedules/ScheduleController.java @@ -2,7 +2,16 @@ import com.tenniscourts.config.BaseRestController; import lombok.AllArgsConstructor; + +import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import java.time.LocalDate; import java.time.LocalDateTime; @@ -10,23 +19,25 @@ import java.util.List; @AllArgsConstructor +@RestController +@RequestMapping("/schedule") public class ScheduleController extends BaseRestController { private final ScheduleService scheduleService; - //TODO: implement rest and swagger - public ResponseEntity addScheduleTennisCourt(CreateScheduleRequestDTO createScheduleRequestDTO) { + @PostMapping("/add") + public ResponseEntity addScheduleTennisCourt(@RequestBody CreateScheduleRequestDTO createScheduleRequestDTO) { return ResponseEntity.created(locationByEntity(scheduleService.addSchedule(createScheduleRequestDTO.getTennisCourtId(), createScheduleRequestDTO).getId())).build(); } - //TODO: implement rest and swagger - public ResponseEntity> findSchedulesByDates(LocalDate startDate, - LocalDate endDate) { + @GetMapping() + public ResponseEntity> findSchedulesByDates(@RequestParam("startDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate, + @RequestParam("endDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) { return ResponseEntity.ok(scheduleService.findSchedulesByDates(LocalDateTime.of(startDate, LocalTime.of(0, 0)), LocalDateTime.of(endDate, LocalTime.of(23, 59)))); } - //TODO: implement rest and swagger - public ResponseEntity findByScheduleId(Long scheduleId) { + @GetMapping("/{id}") + public ResponseEntity findByScheduleId(@PathVariable("id") Long scheduleId) { return ResponseEntity.ok(scheduleService.findSchedule(scheduleId)); } } diff --git a/src/main/java/com/tenniscourts/schedules/ScheduleRepository.java b/src/main/java/com/tenniscourts/schedules/ScheduleRepository.java index 12a44c6d..78db4185 100644 --- a/src/main/java/com/tenniscourts/schedules/ScheduleRepository.java +++ b/src/main/java/com/tenniscourts/schedules/ScheduleRepository.java @@ -2,9 +2,13 @@ import org.springframework.data.jpa.repository.JpaRepository; +import java.time.LocalDateTime; import java.util.List; public interface ScheduleRepository extends JpaRepository { List findByTennisCourt_IdOrderByStartDateTime(Long id); + + // List findAllByStartDateTimeLessThanEqualAndEndDateTimeGreaterThanEqual(LocalDateTime startDateTime, LocalDateTime endDateTime); + List findAllByStartDateTimeGreaterThanEqualAndEndDateTimeLessThanEqual(LocalDateTime startDateTime, LocalDateTime endDateTime); } \ No newline at end of file diff --git a/src/main/java/com/tenniscourts/schedules/ScheduleService.java b/src/main/java/com/tenniscourts/schedules/ScheduleService.java index 5d94ee1a..da788c54 100644 --- a/src/main/java/com/tenniscourts/schedules/ScheduleService.java +++ b/src/main/java/com/tenniscourts/schedules/ScheduleService.java @@ -1,10 +1,16 @@ package com.tenniscourts.schedules; import lombok.AllArgsConstructor; + import org.springframework.stereotype.Service; import java.time.LocalDateTime; import java.util.List; +import java.util.stream.Collectors; + +import com.tenniscourts.exceptions.EntityNotFoundException; +import com.tenniscourts.tenniscourts.TennisCourtMapper; +import com.tenniscourts.tenniscourts.TennisCourtRepository; @Service @AllArgsConstructor @@ -14,19 +20,30 @@ public class ScheduleService { private final ScheduleMapper scheduleMapper; + private final TennisCourtRepository tennisCourtRepository; + + private final TennisCourtMapper tennisCourtMapper; + public ScheduleDTO addSchedule(Long tennisCourtId, CreateScheduleRequestDTO createScheduleRequestDTO) { - //TODO: implement addSchedule - return null; + ScheduleDTO newSchedule = new ScheduleDTO(); + newSchedule.setTennisCourtId(tennisCourtId); + newSchedule.setStartDateTime(createScheduleRequestDTO.getStartDateTime()); + newSchedule.setEndDateTime(createScheduleRequestDTO.getStartDateTime().plusHours(1L)); + newSchedule.setTennisCourt(tennisCourtRepository.findById(tennisCourtId).map(tennisCourtMapper::map).orElseThrow(() -> { + throw new EntityNotFoundException("Tennis Court not found."); + })); + + return scheduleMapper.map(scheduleRepository.saveAndFlush(scheduleMapper.map(newSchedule))); } public List findSchedulesByDates(LocalDateTime startDate, LocalDateTime endDate) { - //TODO: implement - return null; + return scheduleRepository.findAllByStartDateTimeGreaterThanEqualAndEndDateTimeLessThanEqual(startDate, endDate).stream().map(schedule -> { return scheduleMapper.map(schedule); }).collect(Collectors.toList()); } public ScheduleDTO findSchedule(Long scheduleId) { - //TODO: implement - return null; + return scheduleRepository.findById(scheduleId).map(scheduleMapper::map).orElseThrow(() -> { + throw new EntityNotFoundException("Schedule not found."); + }); } public List findSchedulesByTennisCourtId(Long tennisCourtId) { diff --git a/src/main/java/com/tenniscourts/tenniscourts/TennisCourtController.java b/src/main/java/com/tenniscourts/tenniscourts/TennisCourtController.java index a014bdc6..9f80b33e 100644 --- a/src/main/java/com/tenniscourts/tenniscourts/TennisCourtController.java +++ b/src/main/java/com/tenniscourts/tenniscourts/TennisCourtController.java @@ -3,24 +3,32 @@ import com.tenniscourts.config.BaseRestController; import lombok.AllArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; @AllArgsConstructor +@RestController +@RequestMapping("/tennisCourt") public class TennisCourtController extends BaseRestController { private final TennisCourtService tennisCourtService; - //TODO: implement rest and swagger - public ResponseEntity addTennisCourt(TennisCourtDTO tennisCourtDTO) { + @PostMapping("/add") + public ResponseEntity addTennisCourt(@RequestBody TennisCourtDTO tennisCourtDTO) { return ResponseEntity.created(locationByEntity(tennisCourtService.addTennisCourt(tennisCourtDTO).getId())).build(); } - //TODO: implement rest and swagger - public ResponseEntity findTennisCourtById(Long tennisCourtId) { + @GetMapping("/{tennisCourtId}") + public ResponseEntity findTennisCourtById(@PathVariable("tennisCourtId") Long tennisCourtId) { return ResponseEntity.ok(tennisCourtService.findTennisCourtById(tennisCourtId)); } - //TODO: implement rest and swagger - public ResponseEntity findTennisCourtWithSchedulesById(Long tennisCourtId) { + @GetMapping("/withSchedules/{tennisCourtId}") + public ResponseEntity findTennisCourtWithSchedulesById(@PathVariable("tennisCourtId") Long tennisCourtId) { return ResponseEntity.ok(tennisCourtService.findTennisCourtWithSchedulesById(tennisCourtId)); } } diff --git a/src/test/java/com/tenniscourts/reservations/ReservationServiceTest.java b/src/test/java/com/tenniscourts/reservations/ReservationServiceTest.java index ffb1dade..1dd68868 100644 --- a/src/test/java/com/tenniscourts/reservations/ReservationServiceTest.java +++ b/src/test/java/com/tenniscourts/reservations/ReservationServiceTest.java @@ -13,7 +13,6 @@ import java.math.BigDecimal; import java.time.LocalDateTime; - @FixMethodOrder(MethodSorters.NAME_ASCENDING) @SpringBootTest @RunWith(MockitoJUnitRunner.class) @@ -33,4 +32,16 @@ public void getRefundValueFullRefund() { Assert.assertEquals(reservationService.getRefundValue(Reservation.builder().schedule(schedule).value(new BigDecimal(10L)).build()), new BigDecimal(10)); } + + @Test + public void getRefundValueNotRefund() { + Schedule schedule = new Schedule(); + + LocalDateTime startDateTime = LocalDateTime.now().plusHours(2); + + schedule.setStartDateTime(startDateTime); + + Assert.assertEquals(reservationService.getRefundValue(Reservation.builder().schedule(schedule).value(new BigDecimal(10L)).build()), BigDecimal.ZERO); + } + } \ No newline at end of file From 965420682a69669ca39b8e6b666e235c7088838a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Nicol=C3=A1s=20Rouco?= Date: Fri, 18 Feb 2022 18:20:35 -0300 Subject: [PATCH 2/3] Swagger annotations --- .../tenniscourts/guests/GuestController.java | 9 +++++ .../com/tenniscourts/guests/GuestDTO.java | 11 ++++--- .../CreateReservationRequestDTO.java | 6 ++++ .../reservations/ReservationController.java | 33 ++++++++++++------- .../reservations/ReservationDTO.java | 13 ++++++++ .../schedules/CreateScheduleRequestDTO.java | 6 ++++ .../schedules/ScheduleController.java | 22 +++++++++---- .../tenniscourts/schedules/ScheduleDTO.java | 10 ++++++ .../schedules/ScheduleRepository.java | 1 - .../tenniscourts/TennisCourtController.java | 19 +++++++---- .../tenniscourts/TennisCourtDTO.java | 7 ++++ 11 files changed, 108 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/tenniscourts/guests/GuestController.java b/src/main/java/com/tenniscourts/guests/GuestController.java index 3ecaa6df..f1d02d9d 100644 --- a/src/main/java/com/tenniscourts/guests/GuestController.java +++ b/src/main/java/com/tenniscourts/guests/GuestController.java @@ -16,42 +16,51 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import lombok.AllArgsConstructor; @AllArgsConstructor @RestController @RequestMapping("/guest") +@Api(description = "Set of endpoints for Creating, Retrieving, Updating and Deleting of Guests.") public class GuestController extends BaseRestController { private final GuestService guestService; @GetMapping("/all") + @ApiOperation("Returns list of all guests in the system.") public ResponseEntity> findAll() { return ResponseEntity.ok(guestService.findAll()); } @GetMapping("/{id}") + @ApiOperation("Returns a specific guest by their identifier. 404 if does not exist.") public ResponseEntity findById(@PathVariable("id") Long id) { return ResponseEntity.ok(guestService.findById(id)); } @GetMapping() + @ApiOperation("Returns a list of guests searched by name. It searches with a part of the name or with the full name and ignore case.") public ResponseEntity> findByName(@RequestParam("name") String name) { return ResponseEntity.ok(guestService.findByName(name)); } @PostMapping("/add") + @ApiOperation("Creates a new guest.") public ResponseEntity addGuest(@RequestBody GuestDTO guestDTO) { return ResponseEntity.created(locationByEntity(guestService.addGuest(guestDTO).getId())).build(); } @PutMapping("/update") + @ApiOperation("Updates an existing guest. 404 if does not exist.") public ResponseEntity updateGuest(@RequestBody GuestDTO guestDTO) { return ResponseEntity.created(locationByEntity(guestService.updateGuest(guestDTO).getId())).build(); } @DeleteMapping("/{id}") + @ApiOperation("Deletes a guest from the system. 404 if the guest's identifier is not found.") public ResponseEntity delete(@PathVariable("id") Long id) { return (guestService.delete(id)) ? ResponseEntity.ok().build() : ResponseEntity.status(HttpStatus.CONFLICT).build(); } diff --git a/src/main/java/com/tenniscourts/guests/GuestDTO.java b/src/main/java/com/tenniscourts/guests/GuestDTO.java index da5348ef..583735f9 100644 --- a/src/main/java/com/tenniscourts/guests/GuestDTO.java +++ b/src/main/java/com/tenniscourts/guests/GuestDTO.java @@ -1,6 +1,9 @@ package com.tenniscourts.guests; -import com.tenniscourts.schedules.ScheduleDTO; +import javax.validation.constraints.NotNull; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -8,20 +11,20 @@ import lombok.NoArgsConstructor; import lombok.Setter; -import javax.validation.constraints.NotNull; -import java.math.BigDecimal; - @AllArgsConstructor @NoArgsConstructor @Getter @Setter @Builder @Data +@ApiModel(description = "Class representing a guest tracked by the application.") public class GuestDTO { + @ApiModelProperty(notes = "Unique identifier of the guest. No two guests can have the same id.", example = "1", required = true, position = 0) private Long id; @NotNull + @ApiModelProperty(notes = "Name of the guest.", example = "Roger Federer", required = true, position = 1) private String name; } diff --git a/src/main/java/com/tenniscourts/reservations/CreateReservationRequestDTO.java b/src/main/java/com/tenniscourts/reservations/CreateReservationRequestDTO.java index d50e1d32..01b6b936 100644 --- a/src/main/java/com/tenniscourts/reservations/CreateReservationRequestDTO.java +++ b/src/main/java/com/tenniscourts/reservations/CreateReservationRequestDTO.java @@ -9,18 +9,24 @@ import javax.validation.constraints.NotNull; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + @AllArgsConstructor @NoArgsConstructor @Getter @Setter @Builder @Data +@ApiModel(description = "Class representing a request object to create a new reservation.") public class CreateReservationRequestDTO { @NotNull + @ApiModelProperty(notes = "Guest identifier required to create a reservation.", example = "1", required = true, position = 0) private Long guestId; @NotNull + @ApiModelProperty(notes = "Schedule identifier required to create a reservation.", example = "1", required = true, position = 1) private Long scheduleId; } diff --git a/src/main/java/com/tenniscourts/reservations/ReservationController.java b/src/main/java/com/tenniscourts/reservations/ReservationController.java index d619514c..3b246b82 100644 --- a/src/main/java/com/tenniscourts/reservations/ReservationController.java +++ b/src/main/java/com/tenniscourts/reservations/ReservationController.java @@ -14,35 +14,44 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + @AllArgsConstructor @RestController @RequestMapping("/reservation") +@Api(description = "Set of endpoints for Creating, Retrieving, Cancelling, Rescheduling and changing the deposit value of Reservations.") public class ReservationController extends BaseRestController { private final ReservationService reservationService; @PostMapping("/book") + @ApiOperation("Creates a new reservation.") public ResponseEntity bookReservation(@RequestBody CreateReservationRequestDTO createReservationRequestDTO) { return ResponseEntity.created(locationByEntity(reservationService.bookReservation(createReservationRequestDTO).getId())).build(); } - @GetMapping("/{reservationId}") - public ResponseEntity findReservation(@PathVariable("reservationId") Long reservationId) { - return ResponseEntity.ok(reservationService.findReservation(reservationId)); + @GetMapping("/{id}") + @ApiOperation("Returns a specific reservation by their identifier. 404 if does not exist.") + public ResponseEntity findReservation(@PathVariable("id") Long id) { + return ResponseEntity.ok(reservationService.findReservation(id)); } - @PutMapping("/cancel/{reservationId}") - public ResponseEntity cancelReservation(@PathVariable("reservationId") Long reservationId) { - return ResponseEntity.ok(reservationService.cancelReservation(reservationId)); + @PutMapping("/cancel/{id}") + @ApiOperation("Cancel a specific reservation by their identifier. 404 if does not exist.") + public ResponseEntity cancelReservation(@PathVariable("id") Long id) { + return ResponseEntity.ok(reservationService.cancelReservation(id)); } - @PutMapping("/reschedule/{reservationId}") - public ResponseEntity rescheduleReservation(@PathVariable("reservationId") Long reservationId, @RequestParam("scheduleId") Long scheduleId) { - return ResponseEntity.ok(reservationService.rescheduleReservation(reservationId, scheduleId)); + @PutMapping("/reschedule/{id}") + @ApiOperation("Reschedule a specific reservation by their identifier. 404 if does not exist.") + public ResponseEntity rescheduleReservation(@PathVariable("id") Long id, @RequestParam("scheduleId") Long scheduleId) { + return ResponseEntity.ok(reservationService.rescheduleReservation(id, scheduleId)); } - @PutMapping("/chargeDeposit/{reservationId}") - public ResponseEntity chargeReservationDeposit(@PathVariable("reservationId") Long reservationId, @RequestParam(name = "deposit", required = false, defaultValue = "10") BigDecimal deposit) { - return ResponseEntity.ok(reservationService.chargeDeposit(reservationId, deposit)); + @PutMapping("/chargeDeposit/{id}") + @ApiOperation("Charge a deposit to a specific reservation by their identifier. 404 if does not exist. It accepts as optional parameter the deposit value. $10 is the default if this value is not sent.") + public ResponseEntity chargeReservationDeposit(@PathVariable("id") Long id, @RequestParam(name = "deposit", required = false, defaultValue = "10") BigDecimal deposit) { + return ResponseEntity.ok(reservationService.chargeDeposit(id, deposit)); } } diff --git a/src/main/java/com/tenniscourts/reservations/ReservationDTO.java b/src/main/java/com/tenniscourts/reservations/ReservationDTO.java index 39a75219..d237e868 100644 --- a/src/main/java/com/tenniscourts/reservations/ReservationDTO.java +++ b/src/main/java/com/tenniscourts/reservations/ReservationDTO.java @@ -2,6 +2,9 @@ import com.tenniscourts.guests.GuestDTO; import com.tenniscourts.schedules.ScheduleDTO; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -18,25 +21,35 @@ @Setter @Builder @Data +@ApiModel(description = "Class representing a reservation tracked by the application.") public class ReservationDTO { + @ApiModelProperty(notes = "Unique identifier of the reservation. No two reservations can have the same id.", example = "1", required = true, position = 0) private Long id; + @ApiModelProperty(notes = "Schedule.", required = false, position = 1) private ScheduleDTO schedule; + @ApiModelProperty(notes = "Guest.", required = false, position = 2) private GuestDTO guest; + @ApiModelProperty(notes = "Reservation status. It can be READY_TO_PLAY, CANCELLED or RESCHEDULED.", required = false, position = 3) private String reservationStatus; + @ApiModelProperty(notes = "Reservation.", required = false, position = 4) private ReservationDTO previousReservation; + @ApiModelProperty(notes = "Refund reservation deposit value.", required = false, position = 5) private BigDecimal refundValue; + @ApiModelProperty(notes = "Reservation deposit value.", required = false, position = 6) private BigDecimal value; @NotNull + @ApiModelProperty(notes = "Schedule identifier.", required = true, position = 7) private Long scheduledId; @NotNull + @ApiModelProperty(notes = "Guest identifier.", required = true, position = 8) private Long guestId; } diff --git a/src/main/java/com/tenniscourts/schedules/CreateScheduleRequestDTO.java b/src/main/java/com/tenniscourts/schedules/CreateScheduleRequestDTO.java index 99e2b69a..29f2a60d 100644 --- a/src/main/java/com/tenniscourts/schedules/CreateScheduleRequestDTO.java +++ b/src/main/java/com/tenniscourts/schedules/CreateScheduleRequestDTO.java @@ -1,6 +1,9 @@ package com.tenniscourts.schedules; import com.fasterxml.jackson.annotation.JsonFormat; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; @@ -9,13 +12,16 @@ @Getter @Setter +@ApiModel(description = "Class representing a request object to create a new schedule.") public class CreateScheduleRequestDTO { @NotNull + @ApiModelProperty(notes = "Tennis court identifier required to create a schedule.", example = "1", required = true, position = 0) private Long tennisCourtId; @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm") @NotNull + @ApiModelProperty(notes = "Start schedule date and time.", example = "2022-02-18T14:00", required = true, position = 1) private LocalDateTime startDateTime; } diff --git a/src/main/java/com/tenniscourts/schedules/ScheduleController.java b/src/main/java/com/tenniscourts/schedules/ScheduleController.java index 4428db4a..6ee91be3 100644 --- a/src/main/java/com/tenniscourts/schedules/ScheduleController.java +++ b/src/main/java/com/tenniscourts/schedules/ScheduleController.java @@ -13,6 +13,9 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -21,23 +24,30 @@ @AllArgsConstructor @RestController @RequestMapping("/schedule") +@Api(description = "Set of endpoints for Creating and Retrieving of Schedules.") public class ScheduleController extends BaseRestController { private final ScheduleService scheduleService; @PostMapping("/add") + @ApiOperation("Creates a new schedule.") public ResponseEntity addScheduleTennisCourt(@RequestBody CreateScheduleRequestDTO createScheduleRequestDTO) { return ResponseEntity.created(locationByEntity(scheduleService.addSchedule(createScheduleRequestDTO.getTennisCourtId(), createScheduleRequestDTO).getId())).build(); } + @GetMapping("/{id}") + @ApiOperation("Returns a specific schedule by their identifier. 404 if does not exist.") + public ResponseEntity findByScheduleId(@PathVariable("id") Long id) { + return ResponseEntity.ok(scheduleService.findSchedule(id)); + } + @GetMapping() - public ResponseEntity> findSchedulesByDates(@RequestParam("startDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate, - @RequestParam("endDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) { + @ApiOperation("Returns a schedule list between a start date and a end date.") + public ResponseEntity> findSchedulesByDates( + @RequestParam("startDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate, + @RequestParam("endDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate + ) { return ResponseEntity.ok(scheduleService.findSchedulesByDates(LocalDateTime.of(startDate, LocalTime.of(0, 0)), LocalDateTime.of(endDate, LocalTime.of(23, 59)))); } - @GetMapping("/{id}") - public ResponseEntity findByScheduleId(@PathVariable("id") Long scheduleId) { - return ResponseEntity.ok(scheduleService.findSchedule(scheduleId)); - } } diff --git a/src/main/java/com/tenniscourts/schedules/ScheduleDTO.java b/src/main/java/com/tenniscourts/schedules/ScheduleDTO.java index aacdbdd1..4a61d19b 100644 --- a/src/main/java/com/tenniscourts/schedules/ScheduleDTO.java +++ b/src/main/java/com/tenniscourts/schedules/ScheduleDTO.java @@ -1,6 +1,10 @@ package com.tenniscourts.schedules; import com.tenniscourts.tenniscourts.TennisCourtDTO; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Getter; import lombok.Setter; @@ -10,20 +14,26 @@ @Getter @Setter +@ApiModel(description = "Class representing a schedule tracked by the application.") public class ScheduleDTO { + @ApiModelProperty(notes = "Unique identifier of the schedule. No two schedules can have the same id.", example = "1", required = true, position = 0) private Long id; + @ApiModelProperty(notes = "Tennis court.", required = false, position = 1) private TennisCourtDTO tennisCourt; @NotNull + @ApiModelProperty(notes = "Tennis court identifier.", example = "1", required = true, position = 2) private Long tennisCourtId; @JsonFormat(pattern="yyyy-MM-dd'T'HH:mm") @NotNull + @ApiModelProperty(notes = "Start date and time.", example = "2022-02-18T14:00", required = true, position = 3) private LocalDateTime startDateTime; @JsonFormat(pattern="yyyy-MM-dd'T'HH:mm") + @ApiModelProperty(notes = "End date and time.", example = "2022-02-18T15:00", required = false, position = 4) private LocalDateTime endDateTime; } diff --git a/src/main/java/com/tenniscourts/schedules/ScheduleRepository.java b/src/main/java/com/tenniscourts/schedules/ScheduleRepository.java index 78db4185..48962962 100644 --- a/src/main/java/com/tenniscourts/schedules/ScheduleRepository.java +++ b/src/main/java/com/tenniscourts/schedules/ScheduleRepository.java @@ -9,6 +9,5 @@ public interface ScheduleRepository extends JpaRepository { List findByTennisCourt_IdOrderByStartDateTime(Long id); - // List findAllByStartDateTimeLessThanEqualAndEndDateTimeGreaterThanEqual(LocalDateTime startDateTime, LocalDateTime endDateTime); List findAllByStartDateTimeGreaterThanEqualAndEndDateTimeLessThanEqual(LocalDateTime startDateTime, LocalDateTime endDateTime); } \ No newline at end of file diff --git a/src/main/java/com/tenniscourts/tenniscourts/TennisCourtController.java b/src/main/java/com/tenniscourts/tenniscourts/TennisCourtController.java index 9f80b33e..5be15e0f 100644 --- a/src/main/java/com/tenniscourts/tenniscourts/TennisCourtController.java +++ b/src/main/java/com/tenniscourts/tenniscourts/TennisCourtController.java @@ -10,25 +10,32 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + @AllArgsConstructor @RestController @RequestMapping("/tennisCourt") +@Api(description = "Set of endpoints for Creating and Retrieving of Tennis Courts.") public class TennisCourtController extends BaseRestController { private final TennisCourtService tennisCourtService; @PostMapping("/add") + @ApiOperation("Creates a new tennis court.") public ResponseEntity addTennisCourt(@RequestBody TennisCourtDTO tennisCourtDTO) { return ResponseEntity.created(locationByEntity(tennisCourtService.addTennisCourt(tennisCourtDTO).getId())).build(); } - @GetMapping("/{tennisCourtId}") - public ResponseEntity findTennisCourtById(@PathVariable("tennisCourtId") Long tennisCourtId) { - return ResponseEntity.ok(tennisCourtService.findTennisCourtById(tennisCourtId)); + @GetMapping("/{id}") + @ApiOperation("Returns a specific tennis court by their identifier. 404 if does not exist.") + public ResponseEntity findTennisCourtById(@PathVariable("id") Long id) { + return ResponseEntity.ok(tennisCourtService.findTennisCourtById(id)); } - @GetMapping("/withSchedules/{tennisCourtId}") - public ResponseEntity findTennisCourtWithSchedulesById(@PathVariable("tennisCourtId") Long tennisCourtId) { - return ResponseEntity.ok(tennisCourtService.findTennisCourtWithSchedulesById(tennisCourtId)); + @GetMapping("/withSchedules/{id}") + @ApiOperation("Returns a specific tennis court with their schedule list by their identifier. 404 if does not exist.") + public ResponseEntity findTennisCourtWithSchedulesById(@PathVariable("id") Long id) { + return ResponseEntity.ok(tennisCourtService.findTennisCourtWithSchedulesById(id)); } } diff --git a/src/main/java/com/tenniscourts/tenniscourts/TennisCourtDTO.java b/src/main/java/com/tenniscourts/tenniscourts/TennisCourtDTO.java index a0bba506..3c3d5b2a 100644 --- a/src/main/java/com/tenniscourts/tenniscourts/TennisCourtDTO.java +++ b/src/main/java/com/tenniscourts/tenniscourts/TennisCourtDTO.java @@ -1,6 +1,9 @@ package com.tenniscourts.tenniscourts; import com.tenniscourts.schedules.ScheduleDTO; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -17,13 +20,17 @@ @Builder @NoArgsConstructor @AllArgsConstructor +@ApiModel(description = "Class representing a tennis court tracked by the application.") public class TennisCourtDTO { + @ApiModelProperty(notes = "Unique identifier of the tennis court. No two tennis courts can have the same id.", example = "1", required = true, position = 0) private Long id; @NotNull + @ApiModelProperty(notes = "Name of the tennis court.", example = "Court Philippe-Chatrier", required = true, position = 1) private String name; + @ApiModelProperty(notes = "List of schedules of the tennis court.", required = false, position = 2) private List tennisCourtSchedules; } From 1f218800a88e9a37e71ce23fb3dadbf14c41fcdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Nicol=C3=A1s=20Rouco?= Date: Tue, 22 Feb 2022 13:48:04 -0300 Subject: [PATCH 3/3] Some fixes and tests --- .../tenniscourts/guests/GuestController.java | 2 +- .../com/tenniscourts/guests/GuestService.java | 6 +- .../reservations/ReservationController.java | 13 ++ .../reservations/ReservationRepository.java | 2 + .../reservations/ReservationService.java | 53 +++-- .../schedules/CreateScheduleRequestDTO.java | 2 + .../tenniscourts/schedules/ScheduleDTO.java | 7 + .../schedules/ScheduleService.java | 33 +-- .../tenniscourts/TennisCourt.java | 2 + .../tenniscourts/TennisCourtController.java | 15 ++ .../tenniscourts/TennisCourtDTO.java | 2 + .../tenniscourts/TennisCourtService.java | 25 ++- .../com/tenniscourts/MockDataProvider.java | 99 +++++++++ .../tenniscourts/guests/GuestMapperImpl.java | 21 ++ .../tenniscourts/guests/GuestServiceTest.java | 52 +++++ .../reservations/ReservationMapperImpl.java | 45 +++++ .../reservations/ReservationServiceTest.java | 191 +++++++++++++++++- .../schedules/ScheduleMapperImpl.java | 38 ++++ .../schedules/ScheduleServiceTest.java | 79 ++++++++ .../tenniscourts/TennisCourtMapperImpl.java | 16 ++ .../tenniscourts/TennisCourtServiceTest.java | 133 ++++++++++++ 21 files changed, 793 insertions(+), 43 deletions(-) create mode 100644 src/test/java/com/tenniscourts/MockDataProvider.java create mode 100644 src/test/java/com/tenniscourts/guests/GuestMapperImpl.java create mode 100644 src/test/java/com/tenniscourts/guests/GuestServiceTest.java create mode 100644 src/test/java/com/tenniscourts/reservations/ReservationMapperImpl.java create mode 100644 src/test/java/com/tenniscourts/schedules/ScheduleMapperImpl.java create mode 100644 src/test/java/com/tenniscourts/schedules/ScheduleServiceTest.java create mode 100644 src/test/java/com/tenniscourts/tenniscourts/TennisCourtMapperImpl.java create mode 100644 src/test/java/com/tenniscourts/tenniscourts/TennisCourtServiceTest.java diff --git a/src/main/java/com/tenniscourts/guests/GuestController.java b/src/main/java/com/tenniscourts/guests/GuestController.java index f1d02d9d..4e2c123c 100644 --- a/src/main/java/com/tenniscourts/guests/GuestController.java +++ b/src/main/java/com/tenniscourts/guests/GuestController.java @@ -41,7 +41,7 @@ public ResponseEntity findById(@PathVariable("id") Long id) { } @GetMapping() - @ApiOperation("Returns a list of guests searched by name. It searches with a part of the name or with the full name and ignore case.") + @ApiOperation("Returns a list of guests search by name, entering the full name or part of the name ignoring the case.") public ResponseEntity> findByName(@RequestParam("name") String name) { return ResponseEntity.ok(guestService.findByName(name)); } diff --git a/src/main/java/com/tenniscourts/guests/GuestService.java b/src/main/java/com/tenniscourts/guests/GuestService.java index 08d3d78a..70e03a5f 100644 --- a/src/main/java/com/tenniscourts/guests/GuestService.java +++ b/src/main/java/com/tenniscourts/guests/GuestService.java @@ -1,8 +1,6 @@ package com.tenniscourts.guests; import java.util.List; -import java.util.Optional; -import java.util.stream.Collector; import java.util.stream.Collectors; import com.tenniscourts.exceptions.BusinessException; @@ -16,9 +14,9 @@ @AllArgsConstructor public class GuestService { - private final GuestRepository guestRepository; + private GuestRepository guestRepository; - private final GuestMapper guestMapper; + private GuestMapper guestMapper; public List findAll() { return guestRepository.findAll().stream().map(guest -> { return guestMapper.map(guest); }).collect(Collectors.toList()); diff --git a/src/main/java/com/tenniscourts/reservations/ReservationController.java b/src/main/java/com/tenniscourts/reservations/ReservationController.java index 3b246b82..0271c14e 100644 --- a/src/main/java/com/tenniscourts/reservations/ReservationController.java +++ b/src/main/java/com/tenniscourts/reservations/ReservationController.java @@ -1,6 +1,7 @@ package com.tenniscourts.reservations; import java.math.BigDecimal; +import java.util.List; import com.tenniscourts.config.BaseRestController; import lombok.AllArgsConstructor; @@ -54,4 +55,16 @@ public ResponseEntity rescheduleReservation(@PathVariable("id") public ResponseEntity chargeReservationDeposit(@PathVariable("id") Long id, @RequestParam(name = "deposit", required = false, defaultValue = "10") BigDecimal deposit) { return ResponseEntity.ok(reservationService.chargeDeposit(id, deposit)); } + + // @PutMapping("/keepDeposit/{id}") + // @ApiOperation("Keep a deposit from a specific reservation by their identifier. 404 if does not exist.") + // public ResponseEntity keepReservationDeposit(@PathVariable("id") Long id, @RequestParam(name = "deposit", required = false, defaultValue = "10") BigDecimal deposit) { + // return ResponseEntity.ok(reservationService.keepDeposit(id)); + // } + + @GetMapping("/history") + @ApiOperation("Returns a list of past reservations.") + public ResponseEntity> history() { + return ResponseEntity.ok(reservationService.getPastReservations()); + } } diff --git a/src/main/java/com/tenniscourts/reservations/ReservationRepository.java b/src/main/java/com/tenniscourts/reservations/ReservationRepository.java index 00210b5f..992499b7 100644 --- a/src/main/java/com/tenniscourts/reservations/ReservationRepository.java +++ b/src/main/java/com/tenniscourts/reservations/ReservationRepository.java @@ -13,5 +13,7 @@ public interface ReservationRepository extends JpaRepository List findByReservationStatusAndSchedule_StartDateTimeGreaterThanEqualAndSchedule_EndDateTimeLessThanEqual(ReservationStatus reservationStatus, LocalDateTime startDateTime, LocalDateTime endDateTime); + List findBySchedule_StartDateTimeLessThanEqual(LocalDateTime startDateTime); + // List findByStartDateTimeGreaterThanEqualAndEndDateTimeLessThanEqualAndTennisCourt(LocalDateTime startDateTime, LocalDateTime endDateTime, TennisCourt tennisCourt); } diff --git a/src/main/java/com/tenniscourts/reservations/ReservationService.java b/src/main/java/com/tenniscourts/reservations/ReservationService.java index 2483a16c..f6c614f3 100644 --- a/src/main/java/com/tenniscourts/reservations/ReservationService.java +++ b/src/main/java/com/tenniscourts/reservations/ReservationService.java @@ -1,32 +1,30 @@ package com.tenniscourts.reservations; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.stream.Collectors; + import com.tenniscourts.exceptions.EntityNotFoundException; -import com.tenniscourts.guests.GuestDTO; import com.tenniscourts.guests.GuestService; -import com.tenniscourts.schedules.ScheduleDTO; import com.tenniscourts.schedules.ScheduleService; -import lombok.AllArgsConstructor; - -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.Optional; +import lombok.AllArgsConstructor; @Service @AllArgsConstructor public class ReservationService { - private final ReservationRepository reservationRepository; + private ReservationRepository reservationRepository; - private final ReservationMapper reservationMapper; + private ReservationMapperImpl reservationMapper; - private final GuestService guestService; + private GuestService guestService; - private final ScheduleService scheduleService; + private ScheduleService scheduleService; public ReservationDTO bookReservation(CreateReservationRequestDTO createReservationRequestDTO) { ReservationDTO reservationDTO = new ReservationDTO(); @@ -38,7 +36,7 @@ public ReservationDTO bookReservation(CreateReservationRequestDTO createReservat public ReservationDTO findReservation(Long reservationId) { return reservationRepository.findById(reservationId).map(reservationMapper::map).orElseThrow(() -> { - throw new EntityNotFoundException("Reservation not found."); + throw new EntityNotFoundException("Reservation " + reservationId + " not found."); }); } @@ -59,7 +57,7 @@ private Reservation cancel(Long reservationId) { }); } - private Reservation updateReservation(Reservation reservation, BigDecimal refundValue, ReservationStatus status) { + private Reservation updateReservation(Reservation reservation, BigDecimal refundValue,ReservationStatus status) { reservation.setReservationStatus(status); reservation.setValue(reservation.getValue().subtract(refundValue)); reservation.setRefundValue(refundValue); @@ -80,7 +78,20 @@ private void validateCancellation(Reservation reservation) { public BigDecimal getRefundValue(Reservation reservation) { long hours = ChronoUnit.HOURS.between(LocalDateTime.now(), reservation.getSchedule().getStartDateTime()); - if (hours >= 24) { + if (hours < 2) { + // keep 75% and refund 25% + return BigDecimal.valueOf(reservation.getValue().doubleValue() * BigDecimal.valueOf(0.25).doubleValue()); + } + else if (hours >= 2 && hours < 12) { + // keep 50% and refund 50% + return BigDecimal.valueOf(reservation.getValue().doubleValue() * BigDecimal.valueOf(0.50).doubleValue()); + } + else if (hours >= 12 && hours < 24) { + // keep 25% and refund 75% + return BigDecimal.valueOf(reservation.getValue().doubleValue() * BigDecimal.valueOf(0.75).doubleValue()); + } + else if (hours >= 24) { + // refund 100% return reservation.getValue(); } @@ -112,4 +123,14 @@ public ReservationDTO chargeDeposit(Long reservationId, BigDecimal deposit) { reservationDTO.setValue(deposit); return reservationMapper.map(reservationRepository.save(reservationMapper.map(reservationDTO))); } + + // public ReservationDTO keepDeposit(Long reservationId) { + // ReservationDTO reservationDTO = findReservation(reservationId); + // return reservationMapper.map(reservationRepository.save(reservationMapper.map(reservationDTO))); + // } + + public List getPastReservations() { + List historicReservations = reservationRepository.findBySchedule_StartDateTimeLessThanEqual(LocalDateTime.now()); + return historicReservations.stream().map(historicReservation -> reservationMapper.map(historicReservation)).collect(Collectors.toList()); + } } diff --git a/src/main/java/com/tenniscourts/schedules/CreateScheduleRequestDTO.java b/src/main/java/com/tenniscourts/schedules/CreateScheduleRequestDTO.java index 29f2a60d..14dcb61c 100644 --- a/src/main/java/com/tenniscourts/schedules/CreateScheduleRequestDTO.java +++ b/src/main/java/com/tenniscourts/schedules/CreateScheduleRequestDTO.java @@ -4,6 +4,7 @@ import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import lombok.Builder; import lombok.Getter; import lombok.Setter; @@ -12,6 +13,7 @@ @Getter @Setter +@Builder @ApiModel(description = "Class representing a request object to create a new schedule.") public class CreateScheduleRequestDTO { diff --git a/src/main/java/com/tenniscourts/schedules/ScheduleDTO.java b/src/main/java/com/tenniscourts/schedules/ScheduleDTO.java index 4a61d19b..c6953982 100644 --- a/src/main/java/com/tenniscourts/schedules/ScheduleDTO.java +++ b/src/main/java/com/tenniscourts/schedules/ScheduleDTO.java @@ -6,7 +6,11 @@ import io.swagger.annotations.ApiModelProperty; import com.fasterxml.jackson.annotation.JsonFormat; + +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import javax.validation.constraints.NotNull; @@ -14,6 +18,9 @@ @Getter @Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor @ApiModel(description = "Class representing a schedule tracked by the application.") public class ScheduleDTO { diff --git a/src/main/java/com/tenniscourts/schedules/ScheduleService.java b/src/main/java/com/tenniscourts/schedules/ScheduleService.java index da788c54..67dbcbf8 100644 --- a/src/main/java/com/tenniscourts/schedules/ScheduleService.java +++ b/src/main/java/com/tenniscourts/schedules/ScheduleService.java @@ -1,9 +1,5 @@ package com.tenniscourts.schedules; -import lombok.AllArgsConstructor; - -import org.springframework.stereotype.Service; - import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; @@ -12,26 +8,31 @@ import com.tenniscourts.tenniscourts.TennisCourtMapper; import com.tenniscourts.tenniscourts.TennisCourtRepository; +import org.springframework.stereotype.Service; + +import lombok.AllArgsConstructor; + @Service @AllArgsConstructor public class ScheduleService { - private final ScheduleRepository scheduleRepository; + private ScheduleRepository scheduleRepository; - private final ScheduleMapper scheduleMapper; + private ScheduleMapper scheduleMapper; - private final TennisCourtRepository tennisCourtRepository; + private TennisCourtRepository tennisCourtRepository; - private final TennisCourtMapper tennisCourtMapper; + private TennisCourtMapper tennisCourtMapper; public ScheduleDTO addSchedule(Long tennisCourtId, CreateScheduleRequestDTO createScheduleRequestDTO) { - ScheduleDTO newSchedule = new ScheduleDTO(); - newSchedule.setTennisCourtId(tennisCourtId); - newSchedule.setStartDateTime(createScheduleRequestDTO.getStartDateTime()); - newSchedule.setEndDateTime(createScheduleRequestDTO.getStartDateTime().plusHours(1L)); - newSchedule.setTennisCourt(tennisCourtRepository.findById(tennisCourtId).map(tennisCourtMapper::map).orElseThrow(() -> { - throw new EntityNotFoundException("Tennis Court not found."); - })); + ScheduleDTO newSchedule = ScheduleDTO.builder() + .tennisCourtId(tennisCourtId) + .startDateTime(createScheduleRequestDTO.getStartDateTime()) + .endDateTime(createScheduleRequestDTO.getStartDateTime().plusHours(1L)) + .tennisCourt(tennisCourtRepository.findById(tennisCourtId).map(tennisCourtMapper::map).orElseThrow(() -> { + throw new EntityNotFoundException("Tennis Court not found."); + })) + .build(); return scheduleMapper.map(scheduleRepository.saveAndFlush(scheduleMapper.map(newSchedule))); } @@ -42,7 +43,7 @@ public List findSchedulesByDates(LocalDateTime startDate, LocalDate public ScheduleDTO findSchedule(Long scheduleId) { return scheduleRepository.findById(scheduleId).map(scheduleMapper::map).orElseThrow(() -> { - throw new EntityNotFoundException("Schedule not found."); + throw new EntityNotFoundException("Schedule " + scheduleId + " not found."); }); } diff --git a/src/main/java/com/tenniscourts/tenniscourts/TennisCourt.java b/src/main/java/com/tenniscourts/tenniscourts/TennisCourt.java index eb479e63..3f52b4da 100644 --- a/src/main/java/com/tenniscourts/tenniscourts/TennisCourt.java +++ b/src/main/java/com/tenniscourts/tenniscourts/TennisCourt.java @@ -2,6 +2,7 @@ import com.tenniscourts.config.persistence.BaseEntity; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; @@ -19,6 +20,7 @@ @Setter @NoArgsConstructor @AllArgsConstructor +@Builder @EqualsAndHashCode(callSuper = true) @ToString public class TennisCourt extends BaseEntity { diff --git a/src/main/java/com/tenniscourts/tenniscourts/TennisCourtController.java b/src/main/java/com/tenniscourts/tenniscourts/TennisCourtController.java index 5be15e0f..79328892 100644 --- a/src/main/java/com/tenniscourts/tenniscourts/TennisCourtController.java +++ b/src/main/java/com/tenniscourts/tenniscourts/TennisCourtController.java @@ -1,13 +1,18 @@ package com.tenniscourts.tenniscourts; +import java.time.LocalDateTime; + import com.tenniscourts.config.BaseRestController; import lombok.AllArgsConstructor; + +import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import io.swagger.annotations.Api; @@ -27,6 +32,16 @@ public ResponseEntity addTennisCourt(@RequestBody TennisCourtDTO tennisCou return ResponseEntity.created(locationByEntity(tennisCourtService.addTennisCourt(tennisCourtDTO).getId())).build(); } + @PostMapping("/tennisCourt/createScheduleSlots/{id}") + @ApiOperation("Creates schedule slots from start date time to end date time and returns a tennis court with a list of this slots.") + public ResponseEntity addScheduleSlotsToTennisCourt( + @PathVariable("id") Long id, + @RequestParam("startDateTime") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startDateTime, + @RequestParam("endDateTime") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDateTime + ) { + return ResponseEntity.ok(tennisCourtService.addScheduleSlotsToTennisCourt(id, startDateTime, endDateTime)); + } + @GetMapping("/{id}") @ApiOperation("Returns a specific tennis court by their identifier. 404 if does not exist.") public ResponseEntity findTennisCourtById(@PathVariable("id") Long id) { diff --git a/src/main/java/com/tenniscourts/tenniscourts/TennisCourtDTO.java b/src/main/java/com/tenniscourts/tenniscourts/TennisCourtDTO.java index 3c3d5b2a..54c47cd5 100644 --- a/src/main/java/com/tenniscourts/tenniscourts/TennisCourtDTO.java +++ b/src/main/java/com/tenniscourts/tenniscourts/TennisCourtDTO.java @@ -9,6 +9,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import lombok.Singular; import lombok.ToString; import javax.validation.constraints.NotNull; @@ -31,6 +32,7 @@ public class TennisCourtDTO { private String name; @ApiModelProperty(notes = "List of schedules of the tennis court.", required = false, position = 2) + @Singular private List tennisCourtSchedules; } diff --git a/src/main/java/com/tenniscourts/tenniscourts/TennisCourtService.java b/src/main/java/com/tenniscourts/tenniscourts/TennisCourtService.java index e5bba080..b0f8ef09 100644 --- a/src/main/java/com/tenniscourts/tenniscourts/TennisCourtService.java +++ b/src/main/java/com/tenniscourts/tenniscourts/TennisCourtService.java @@ -1,6 +1,13 @@ package com.tenniscourts.tenniscourts; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; + import com.tenniscourts.exceptions.EntityNotFoundException; +import com.tenniscourts.schedules.CreateScheduleRequestDTO; +import com.tenniscourts.schedules.ScheduleDTO; import com.tenniscourts.schedules.ScheduleService; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; @@ -19,9 +26,25 @@ public TennisCourtDTO addTennisCourt(TennisCourtDTO tennisCourt) { return tennisCourtMapper.map(tennisCourtRepository.saveAndFlush(tennisCourtMapper.map(tennisCourt))); } + public TennisCourtDTO addScheduleSlotsToTennisCourt(Long id, LocalDateTime startDate, LocalDateTime endDate) { + TennisCourtDTO tennisCourt = findTennisCourtById(id); + long hours = ChronoUnit.HOURS.between(startDate, endDate); + + tennisCourt.setTennisCourtSchedules(new ArrayList<>()); + + for (int hoursIndex = 0; hoursIndex <= hours; hoursIndex++) { + LocalDateTime date = startDate; + CreateScheduleRequestDTO createDTO = CreateScheduleRequestDTO.builder().tennisCourtId(id).startDateTime(date.plusHours(hoursIndex)).build(); + ScheduleDTO schedule = scheduleService.addSchedule(id, createDTO); + tennisCourt.getTennisCourtSchedules().add(schedule); + } + + return tennisCourt; + } + public TennisCourtDTO findTennisCourtById(Long id) { return tennisCourtRepository.findById(id).map(tennisCourtMapper::map).orElseThrow(() -> { - throw new EntityNotFoundException("Tennis Court not found."); + throw new EntityNotFoundException("Tennis Court " + id + " not found."); }); } diff --git a/src/test/java/com/tenniscourts/MockDataProvider.java b/src/test/java/com/tenniscourts/MockDataProvider.java new file mode 100644 index 00000000..2a2b326f --- /dev/null +++ b/src/test/java/com/tenniscourts/MockDataProvider.java @@ -0,0 +1,99 @@ +package com.tenniscourts; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import com.tenniscourts.guests.GuestDTO; +import com.tenniscourts.reservations.ReservationDTO; +import com.tenniscourts.reservations.ReservationStatus; +import com.tenniscourts.schedules.CreateScheduleRequestDTO; +import com.tenniscourts.schedules.ScheduleDTO; +import com.tenniscourts.tenniscourts.TennisCourtDTO; + +public class MockDataProvider { + + public static GuestDTO buildGuest1() { + return GuestDTO.builder() + .id(1L) + .name("Roger Federer") + .build(); + } + + public static GuestDTO buildGuest2() { + return GuestDTO.builder() + .id(1L) + .name("Rafael Nadal") + .build(); + } + + public static TennisCourtDTO buildTennisCourt1() { + return TennisCourtDTO.builder() + .id(1L) + .name("Philippe-Chatrier") + // .tennisCourtSchedule(buildSchedule1()) + .build(); + } + + public static TennisCourtDTO buildTennisCourt2() { + return TennisCourtDTO.builder() + .id(2L) + .name("Rod Laver Arena") + // .tennisCourtSchedule(buildSchedule2()) + // .tennisCourtSchedule(buildSchedule3()) + .build(); + } + + public static ScheduleDTO buildSchedule1() { + return ScheduleDTO.builder() + .id(1L) + .tennisCourt(buildTennisCourt1()) + .tennisCourtId(1L) + .startDateTime(LocalDateTime.now().plusDays(1)) + .endDateTime(LocalDateTime.now().plusDays(1).plusHours(1)) + .build(); + } + + public static ScheduleDTO buildSchedule2() { + return ScheduleDTO.builder() + .id(2L) + .tennisCourt(buildTennisCourt2()) + .tennisCourtId(2L) + .startDateTime(LocalDateTime.now().plusDays(2)) + .endDateTime(LocalDateTime.now().plusDays(2).plusHours(1)) + .build(); + } + + public static ScheduleDTO buildSchedule3() { + return ScheduleDTO.builder() + .id(3L) + .tennisCourt(buildTennisCourt2()) + .tennisCourtId(3L) + .startDateTime(LocalDateTime.now().plusDays(2).plusHours(1)) + .endDateTime(LocalDateTime.now().plusDays(2).plusHours(2)) + .build(); + } + + public static List buildScheduleList() { + List schedules = new ArrayList(); + schedules.add(buildSchedule1()); + schedules.add(buildSchedule2()); + schedules.add(buildSchedule3()); + return schedules; + } + + public static ReservationDTO buildReservation1() { + return ReservationDTO.builder() + .id(1L) + .schedule(buildSchedule1()) + .scheduledId(1L) + .guest(buildGuest1()) + .guestId(1l) + .reservationStatus(ReservationStatus.READY_TO_PLAY.name()) + .build(); + } + + public static CreateScheduleRequestDTO buildCreateScheduleRequest(Long tennisCourtId, LocalDateTime startDateTime) { + return CreateScheduleRequestDTO.builder().tennisCourtId(tennisCourtId).startDateTime(startDateTime).build(); + } +} diff --git a/src/test/java/com/tenniscourts/guests/GuestMapperImpl.java b/src/test/java/com/tenniscourts/guests/GuestMapperImpl.java new file mode 100644 index 00000000..3cce8880 --- /dev/null +++ b/src/test/java/com/tenniscourts/guests/GuestMapperImpl.java @@ -0,0 +1,21 @@ +package com.tenniscourts.guests; + +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public class GuestMapperImpl implements GuestMapper { + + public Guest map(GuestDTO source) { + Guest target = null; + if (source != null) { + target = Guest.builder().name(source.getName()).build(); + target.setId(source.getId()); + } + return target; + } + + public GuestDTO map(Guest source) { + return (source != null) ? GuestDTO.builder().id(source.getId()).name(source.getName()).build() : null; + } + +} diff --git a/src/test/java/com/tenniscourts/guests/GuestServiceTest.java b/src/test/java/com/tenniscourts/guests/GuestServiceTest.java new file mode 100644 index 00000000..1c223bdb --- /dev/null +++ b/src/test/java/com/tenniscourts/guests/GuestServiceTest.java @@ -0,0 +1,52 @@ +package com.tenniscourts.guests; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; + +import java.util.Optional; + +import com.tenniscourts.exceptions.EntityNotFoundException; + +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.util.ReflectionTestUtils; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@SpringBootTest +@RunWith(MockitoJUnitRunner.class) +@ContextConfiguration(classes = GuestService.class) +public class GuestServiceTest { + + @InjectMocks + GuestService guestService; + + @Mock + GuestRepository guestRepository; + + @Before + public void setUp() throws Exception { + ReflectionTestUtils.setField(guestService, "guestMapper", new GuestMapperImpl()); + } + + @Test + public void testFindByIdNotFoundException() { + when(guestRepository.findById(anyLong())).thenReturn(Optional.empty()); + + Exception exception = assertThrows(EntityNotFoundException.class, () -> { + guestService.findById(1L); + }); + + assertTrue(exception.getMessage().contains("Guest 1 not found.")); + } + +} diff --git a/src/test/java/com/tenniscourts/reservations/ReservationMapperImpl.java b/src/test/java/com/tenniscourts/reservations/ReservationMapperImpl.java new file mode 100644 index 00000000..e270b0c4 --- /dev/null +++ b/src/test/java/com/tenniscourts/reservations/ReservationMapperImpl.java @@ -0,0 +1,45 @@ +package com.tenniscourts.reservations; + +import com.tenniscourts.guests.Guest; +import com.tenniscourts.guests.GuestMapperImpl; +import com.tenniscourts.schedules.Schedule; +import com.tenniscourts.schedules.ScheduleMapperImpl; + +public class ReservationMapperImpl implements ReservationMapper { + + public Reservation map(ReservationDTO source) { + Reservation target = Reservation.builder() + .guest(new GuestMapperImpl().map(source.getGuest())) + .schedule(new ScheduleMapperImpl().map(source.getSchedule())) + .value(source.getValue()) + .reservationStatus((source.getReservationStatus() != null) ? ReservationStatus.valueOf(source.getReservationStatus()) : ReservationStatus.READY_TO_PLAY) + .refundValue(source.getRefundValue()) + .build(); + target.setId(source.getId()); + return target; + }; + + public ReservationDTO map(Reservation source) { + return ReservationDTO.builder() + .id(source.getId()) + .guest(new GuestMapperImpl().map(source.getGuest())) + .schedule(new ScheduleMapperImpl().map(source.getSchedule())) + .guestId((source.getGuest() != null) ? source.getGuest().getId() : 0) + .scheduledId((source.getSchedule() != null) ? source.getSchedule().getId() : 0) + .value(source.getValue()) + .reservationStatus(source.getReservationStatus().name()) + .refundValue(source.getRefundValue()) + .build(); + } + + public Reservation map(CreateReservationRequestDTO source) { + Guest guest = new Guest(); + guest.setId(source.getGuestId()); + Schedule schedule = new Schedule(); + schedule.setId(source.getScheduleId()); + return Reservation.builder() + .guest(guest) + .schedule(schedule) + .build(); + } +} diff --git a/src/test/java/com/tenniscourts/reservations/ReservationServiceTest.java b/src/test/java/com/tenniscourts/reservations/ReservationServiceTest.java index 1dd68868..ba959689 100644 --- a/src/test/java/com/tenniscourts/reservations/ReservationServiceTest.java +++ b/src/test/java/com/tenniscourts/reservations/ReservationServiceTest.java @@ -1,18 +1,37 @@ package com.tenniscourts.reservations; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Optional; + +import com.tenniscourts.MockDataProvider; +import com.tenniscourts.exceptions.EntityNotFoundException; +import com.tenniscourts.guests.Guest; +import com.tenniscourts.guests.GuestService; import com.tenniscourts.schedules.Schedule; +import com.tenniscourts.schedules.ScheduleService; + import org.junit.Assert; +import org.junit.Before; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.util.ReflectionTestUtils; -import java.math.BigDecimal; -import java.time.LocalDateTime; @FixMethodOrder(MethodSorters.NAME_ASCENDING) @SpringBootTest @RunWith(MockitoJUnitRunner.class) @@ -22,6 +41,20 @@ public class ReservationServiceTest { @InjectMocks ReservationService reservationService; + @Mock + ReservationRepository reservationRepository; + + @Mock + GuestService guestService; + + @Mock + ScheduleService scheduleService; + + @Before + public void setUp() throws Exception { + ReflectionTestUtils.setField(reservationService, "reservationMapper", new ReservationMapperImpl()); + } + @Test public void getRefundValueFullRefund() { Schedule schedule = new Schedule(); @@ -32,16 +65,164 @@ public void getRefundValueFullRefund() { Assert.assertEquals(reservationService.getRefundValue(Reservation.builder().schedule(schedule).value(new BigDecimal(10L)).build()), new BigDecimal(10)); } + + @Test + public void getRefundValue25PercentRefund() { + Schedule schedule = new Schedule(); + + LocalDateTime startDateTime = LocalDateTime.now().plusHours(1); + + schedule.setStartDateTime(startDateTime); + + Assert.assertEquals(reservationService.getRefundValue(Reservation.builder().schedule(schedule).value(new BigDecimal(10L)).build()), BigDecimal.valueOf(2.5)); + } @Test - public void getRefundValueNotRefund() { + public void getRefundValue50PercentRefund() { Schedule schedule = new Schedule(); - LocalDateTime startDateTime = LocalDateTime.now().plusHours(2); + LocalDateTime startDateTime = LocalDateTime.now().plusHours(8); schedule.setStartDateTime(startDateTime); - Assert.assertEquals(reservationService.getRefundValue(Reservation.builder().schedule(schedule).value(new BigDecimal(10L)).build()), BigDecimal.ZERO); + Assert.assertEquals(reservationService.getRefundValue(Reservation.builder().schedule(schedule).value(new BigDecimal(10L)).build()), BigDecimal.valueOf(5.0)); + } + + @Test + public void getRefundValue75PercentRefund() { + Schedule schedule = new Schedule(); + + LocalDateTime startDateTime = LocalDateTime.now().plusHours(15); + + schedule.setStartDateTime(startDateTime); + + Assert.assertEquals(reservationService.getRefundValue(Reservation.builder().schedule(schedule).value(new BigDecimal(10L)).build()), BigDecimal.valueOf(7.5)); + } + + @Test + public void testBookReservation() { + ReservationDTO reservationMock = ReservationDTO.builder() + .guest(MockDataProvider.buildGuest1()) + .schedule(MockDataProvider.buildSchedule1()) + .value(BigDecimal.valueOf(0)) + .reservationStatus(ReservationStatus.READY_TO_PLAY.name()) + .build(); + + when(guestService.findById(anyLong())).thenReturn(reservationMock.getGuest()); + when(scheduleService.findSchedule(anyLong())).thenReturn(reservationMock.getSchedule()); + when(reservationRepository.save(Mockito.any(Reservation.class))).thenAnswer(i -> i.getArguments()[0]); + + ReservationDTO bookedReservation = reservationService.bookReservation(CreateReservationRequestDTO.builder().guestId(1L).scheduleId(1l).build()); + assertEquals(reservationMock.getReservationStatus(), bookedReservation.getReservationStatus()); + assertEquals(reservationMock.getValue(), bookedReservation.getValue()); + assertEquals(reservationMock.getGuest().getId(), bookedReservation.getGuest().getId()); + assertEquals(reservationMock.getGuest().getName(), bookedReservation.getGuest().getName()); + assertEquals(reservationMock.getSchedule().getId(), bookedReservation.getSchedule().getId()); + assertEquals(reservationMock.getSchedule().getStartDateTime(), bookedReservation.getSchedule().getStartDateTime()); + assertEquals(reservationMock.getSchedule().getEndDateTime(), bookedReservation.getSchedule().getEndDateTime()); } + @Test + public void testFindReservationNotFoundException() { + when(reservationRepository.findById(anyLong())).thenReturn(Optional.empty()); + + Exception exception = assertThrows(EntityNotFoundException.class, () -> { + reservationService.findReservation(1L); + }); + + assertTrue(exception.getMessage().contains("Reservation 1 not found.")); + } + + @Test + public void testCancelReservationIllegalArgumentException() { + Schedule schedule = Schedule.builder().startDateTime(LocalDateTime.now().minusHours(5)).build(); + Reservation reservation = Reservation.builder().reservationStatus(ReservationStatus.RESCHEDULED).schedule(schedule).build(); + + when(reservationRepository.findById(anyLong())).thenReturn(Optional.of(reservation)); + + Exception exception = assertThrows(IllegalArgumentException.class, () -> { + reservationService.cancelReservation(1L); + }); + + assertTrue(exception.getMessage().contains("Cannot cancel/reschedule because it's not in ready to play status.")); + + reservation.setReservationStatus(ReservationStatus.READY_TO_PLAY); + + exception = assertThrows(IllegalArgumentException.class, () -> { + reservationService.cancelReservation(1L); + }); + + assertTrue(exception.getMessage().contains("Can cancel/reschedule only future dates.")); + } + + @Test + public void testCancelReservation() { + Schedule schedule = Schedule.builder().startDateTime(LocalDateTime.now().plusDays(2)).build(); + schedule.setId(1L); + Reservation reservation = Reservation.builder() + .reservationStatus(ReservationStatus.READY_TO_PLAY) + .value(BigDecimal.valueOf(10)) + .schedule(schedule) + .build(); + + when(reservationRepository.findById(anyLong())).thenReturn(Optional.of(reservation)); + when(reservationRepository.save(any(Reservation.class))).thenAnswer(i -> i.getArguments()[0]); + + ReservationDTO cancelledReservation = reservationService.cancelReservation(1L); + + assertEquals(ReservationStatus.CANCELLED.name(), cancelledReservation.getReservationStatus()); + assertEquals(BigDecimal.valueOf(0), cancelledReservation.getValue()); + assertEquals(BigDecimal.valueOf(10), cancelledReservation.getRefundValue()); + } + + @Test + public void testCancelReservationWith75PercentRefund() { + Schedule schedule = Schedule.builder().startDateTime(LocalDateTime.now().plusHours(1)).build(); + schedule.setId(1L); + Reservation reservation = Reservation.builder() + .reservationStatus(ReservationStatus.READY_TO_PLAY) + .value(BigDecimal.valueOf(10)) + .schedule(schedule) + .build(); + + when(reservationRepository.findById(anyLong())).thenReturn(Optional.of(reservation)); + when(reservationRepository.save(any(Reservation.class))).thenAnswer(i -> i.getArguments()[0]); + + ReservationDTO cancelledReservation = reservationService.cancelReservation(1L); + + assertEquals(ReservationStatus.CANCELLED.name(), cancelledReservation.getReservationStatus()); + assertEquals(BigDecimal.valueOf(7.5), cancelledReservation.getValue()); + assertEquals(BigDecimal.valueOf(2.5), cancelledReservation.getRefundValue()); + } + + @Test + public void testRescheduleReservation() { + Schedule schedule = Schedule.builder().startDateTime(LocalDateTime.now().plusHours(5)).build(); + schedule.setId(1L); + Reservation reservation = Reservation.builder().value(BigDecimal.valueOf(10)).reservationStatus(ReservationStatus.READY_TO_PLAY).schedule(schedule).build(); + Guest guest = Guest.builder().name("Roger Federer").build(); + guest.setId(1L); + reservation.setGuest(guest); + + when(reservationRepository.findById(anyLong())).thenReturn(Optional.of(reservation)); + when(reservationRepository.save(any(Reservation.class))).thenAnswer(i -> i.getArguments()[0]); + + ReservationDTO rescheduledReservation = reservationService.rescheduleReservation(1L, 2L); + + assertEquals(ReservationStatus.RESCHEDULED.name(), rescheduledReservation.getPreviousReservation().getReservationStatus()); + assertEquals(ReservationStatus.READY_TO_PLAY.name(), rescheduledReservation.getReservationStatus()); + } + + @Test + public void testChargeDeposit() { + + Reservation reservation = Reservation.builder().value(BigDecimal.valueOf(10)).reservationStatus(ReservationStatus.READY_TO_PLAY).build(); + + when(reservationRepository.findById(anyLong())).thenReturn(Optional.of(reservation)); + when(reservationRepository.save(any(Reservation.class))).thenAnswer(i -> i.getArguments()[0]); + + ReservationDTO reservatioWithDeposit = reservationService.chargeDeposit(1L, BigDecimal.valueOf(20)); + + assertEquals(BigDecimal.valueOf(20), reservatioWithDeposit.getValue()); + } } \ No newline at end of file diff --git a/src/test/java/com/tenniscourts/schedules/ScheduleMapperImpl.java b/src/test/java/com/tenniscourts/schedules/ScheduleMapperImpl.java new file mode 100644 index 00000000..3cf5a8c5 --- /dev/null +++ b/src/test/java/com/tenniscourts/schedules/ScheduleMapperImpl.java @@ -0,0 +1,38 @@ +package com.tenniscourts.schedules; + +import java.util.List; +import java.util.stream.Collectors; + +import com.tenniscourts.tenniscourts.TennisCourtMapperImpl; + +public class ScheduleMapperImpl implements ScheduleMapper { + + public Schedule map(ScheduleDTO source) { + Schedule target = null; + if (source != null) { + target = Schedule.builder() + .tennisCourt(new TennisCourtMapperImpl().map(source.getTennisCourt())) + .startDateTime(source.getStartDateTime()) + .endDateTime(source.getEndDateTime()) + .build(); + target.setId(source.getId()); + } + return target; + } + + public ScheduleDTO map(Schedule source) { + return (source != null) ? + ScheduleDTO.builder() + .id(source.getId()) + .tennisCourt(new TennisCourtMapperImpl().map(source.getTennisCourt())) + .tennisCourtId((source.getTennisCourt() != null) ? source.getTennisCourt().getId() : 0) + .startDateTime(source.getStartDateTime()) + .endDateTime(source.getEndDateTime()) + .build() : + null; + } + + public List map(List source) { + return source.stream().map(schedule -> map(schedule)).collect(Collectors.toList()); + } +} diff --git a/src/test/java/com/tenniscourts/schedules/ScheduleServiceTest.java b/src/test/java/com/tenniscourts/schedules/ScheduleServiceTest.java new file mode 100644 index 00000000..5f89c9ae --- /dev/null +++ b/src/test/java/com/tenniscourts/schedules/ScheduleServiceTest.java @@ -0,0 +1,79 @@ +package com.tenniscourts.schedules; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; + +import java.time.LocalDateTime; +import java.util.Optional; + +import com.tenniscourts.exceptions.EntityNotFoundException; +import com.tenniscourts.tenniscourts.TennisCourt; +import com.tenniscourts.tenniscourts.TennisCourtMapperImpl; +import com.tenniscourts.tenniscourts.TennisCourtRepository; + +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.util.ReflectionTestUtils; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@SpringBootTest +@RunWith(MockitoJUnitRunner.class) +@ContextConfiguration(classes = ScheduleService.class) +public class ScheduleServiceTest { + + @InjectMocks + ScheduleService scheduleService; + + @Mock + ScheduleRepository scheduleRepository; + + @Mock + TennisCourtRepository tennisCourtRepository; + + @Before + public void setUp() throws Exception { + ReflectionTestUtils.setField(scheduleService, "scheduleMapper", new ScheduleMapperImpl()); + ReflectionTestUtils.setField(scheduleService, "tennisCourtMapper", new TennisCourtMapperImpl()); + } + + @Test + public void testAddSchedule() { + TennisCourt tennisCourt = TennisCourt.builder().name("Philippe-Chatrier").build(); + tennisCourt.setId(1L); + when(tennisCourtRepository.findById(anyLong())).thenReturn(Optional.of(tennisCourt)); + when(scheduleRepository.saveAndFlush(Mockito.any(Schedule.class))).thenAnswer(i -> i.getArguments()[0]); + + LocalDateTime startDateTime = LocalDateTime.now().plusHours(5); + CreateScheduleRequestDTO createDTO = CreateScheduleRequestDTO.builder().tennisCourtId(1L).startDateTime(startDateTime).build(); + ScheduleDTO schedule = scheduleService.addSchedule(1L, createDTO); + assertTrue(schedule != null); + assertTrue(schedule.getTennisCourt() != null); + assertEquals(tennisCourt.getId(), schedule.getTennisCourtId()); + assertEquals(startDateTime, schedule.getStartDateTime()); + assertEquals(startDateTime.plusHours(1), schedule.getEndDateTime()); + } + + @Test + public void testFindScheduleNotFoundException() { + when(scheduleRepository.findById(anyLong())).thenReturn(Optional.empty()); + + Exception exception = assertThrows(EntityNotFoundException.class, () -> { + scheduleService.findSchedule(1L); + }); + + assertTrue(exception.getMessage().contains("Schedule 1 not found.")); + } + +} diff --git a/src/test/java/com/tenniscourts/tenniscourts/TennisCourtMapperImpl.java b/src/test/java/com/tenniscourts/tenniscourts/TennisCourtMapperImpl.java new file mode 100644 index 00000000..2ce5a971 --- /dev/null +++ b/src/test/java/com/tenniscourts/tenniscourts/TennisCourtMapperImpl.java @@ -0,0 +1,16 @@ +package com.tenniscourts.tenniscourts; + +public class TennisCourtMapperImpl implements TennisCourtMapper { + public TennisCourt map(TennisCourtDTO source) { + TennisCourt target = null; + if (source != null) { + target = TennisCourt.builder().name(source.getName()).build(); + target.setId(source.getId()); + } + return target; + } + + public TennisCourtDTO map(TennisCourt source) { + return (source != null) ? TennisCourtDTO.builder().id(source.getId()).name(source.getName()).build() : null; + } +} diff --git a/src/test/java/com/tenniscourts/tenniscourts/TennisCourtServiceTest.java b/src/test/java/com/tenniscourts/tenniscourts/TennisCourtServiceTest.java new file mode 100644 index 00000000..72a8c0b0 --- /dev/null +++ b/src/test/java/com/tenniscourts/tenniscourts/TennisCourtServiceTest.java @@ -0,0 +1,133 @@ +package com.tenniscourts.tenniscourts; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +import com.tenniscourts.MockDataProvider; +import com.tenniscourts.exceptions.EntityNotFoundException; +import com.tenniscourts.schedules.CreateScheduleRequestDTO; +import com.tenniscourts.schedules.ScheduleDTO; +import com.tenniscourts.schedules.ScheduleService; + +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.util.ReflectionTestUtils; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@SpringBootTest +@RunWith(MockitoJUnitRunner.class) +@ContextConfiguration(classes = TennisCourtService.class) +public class TennisCourtServiceTest { + + @InjectMocks + TennisCourtService tennisCourtService; + + @Mock + ScheduleService scheduleService; + + @Mock + TennisCourtRepository tennisCourtRepository; + + @Before + public void setUp() throws Exception { + ReflectionTestUtils.setField(tennisCourtService, "tennisCourtMapper", new TennisCourtMapperImpl()); + } + + @Test + public void testAddTennisCourt() { + TennisCourtDTO tennisCourtMock = MockDataProvider.buildTennisCourt1(); + + when(tennisCourtRepository.saveAndFlush(any(TennisCourt.class))).thenAnswer(i -> i.getArguments()[0]); + + TennisCourtDTO tennisCourtDTO = tennisCourtService.addTennisCourt(tennisCourtMock); + + assertEquals(tennisCourtMock.getId(), tennisCourtDTO.getId()); + assertEquals(tennisCourtMock.getName(), tennisCourtDTO.getName()); + } + + @Test + public void testAddScheduleSlotsToTennisCourt() { + TennisCourt tennisCourtMock = TennisCourt.builder().name("Philippe-Chatrier").build(); + tennisCourtMock.setId(1L); + + LocalDateTime startDateTime = LocalDateTime.now().plusDays(1); + ScheduleDTO scheduleDTO1 = MockDataProvider.buildSchedule1(); + ScheduleDTO scheduleDTO2 = MockDataProvider.buildSchedule1(); + ScheduleDTO scheduleDTO3 = MockDataProvider.buildSchedule1(); + + when(tennisCourtRepository.findById(anyLong())).thenReturn(Optional.of(tennisCourtMock)); + when(scheduleService.addSchedule(anyLong(), any(CreateScheduleRequestDTO.class))).thenReturn(scheduleDTO1, scheduleDTO2, scheduleDTO3); + + TennisCourtDTO tennisCourtResponse = tennisCourtService.addScheduleSlotsToTennisCourt(1L, startDateTime, startDateTime.plusHours(2)); + assertEquals(3, tennisCourtResponse.getTennisCourtSchedules().size()); + } + + @Test + public void testFindTennisCourtByIdNotFoundException() { + when(tennisCourtRepository.findById(anyLong())).thenReturn(Optional.empty()); + + Exception exception = assertThrows(EntityNotFoundException.class, () -> { + tennisCourtService.findTennisCourtById(1L); + }); + + assertTrue(exception.getMessage().contains("Tennis Court 1 not found.")); + } + + @Test + public void testFindTennisCourtById() { + TennisCourt tennisCourtMock = TennisCourt.builder().name("Philippe-Chatrier").build(); + tennisCourtMock.setId(1L); + when(tennisCourtRepository.findById(anyLong())).thenReturn(Optional.of(tennisCourtMock)); + + TennisCourtDTO tennisCourtDTO = tennisCourtService.findTennisCourtById(1L); + + assertEquals(tennisCourtMock.getId(), tennisCourtDTO.getId()); + assertEquals(tennisCourtMock.getName(), tennisCourtDTO.getName()); + } + + @Test + public void testFindTennisCourtWithSchedulesById() { + TennisCourt tennisCourtMock = TennisCourt.builder().name("Philippe-Chatrier").build(); + tennisCourtMock.setId(1L); + List schedulesMock = MockDataProvider.buildScheduleList(); + ScheduleDTO scheduleMock0 = schedulesMock.get(0); + ScheduleDTO scheduleMock1 = schedulesMock.get(1); + ScheduleDTO scheduleMock2 = schedulesMock.get(2); + + when(tennisCourtRepository.findById(anyLong())).thenReturn(Optional.of(tennisCourtMock)); + when(scheduleService.findSchedulesByTennisCourtId(anyLong())).thenReturn(schedulesMock); + + TennisCourtDTO tennisCourtDTO = tennisCourtService.findTennisCourtWithSchedulesById(1L); + + ScheduleDTO tennisCourtDTO0 = tennisCourtDTO.getTennisCourtSchedules().get(0); + ScheduleDTO tennisCourtDTO1 = tennisCourtDTO.getTennisCourtSchedules().get(1); + ScheduleDTO tennisCourtDTO2 = tennisCourtDTO.getTennisCourtSchedules().get(2); + + assertEquals(tennisCourtMock.getId(), tennisCourtDTO.getId()); + assertEquals(tennisCourtMock.getName(), tennisCourtDTO.getName()); + assertEquals(schedulesMock.size(), tennisCourtDTO.getTennisCourtSchedules().size()); + assertEquals(scheduleMock0.getStartDateTime(), tennisCourtDTO0.getStartDateTime()); + assertEquals(scheduleMock0.getEndDateTime(), tennisCourtDTO0.getEndDateTime()); + assertEquals(scheduleMock1.getStartDateTime(), tennisCourtDTO1.getStartDateTime()); + assertEquals(scheduleMock1.getEndDateTime(), tennisCourtDTO1.getEndDateTime()); + assertEquals(scheduleMock2.getStartDateTime(), tennisCourtDTO2.getStartDateTime()); + assertEquals(scheduleMock2.getEndDateTime(), tennisCourtDTO2.getEndDateTime()); + } + +}