Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package com.capgemini.training.appointmentbooking.service.impl;

import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.capgemini.training.appointmentbooking.common.BaseTest;
import com.capgemini.training.appointmentbooking.common.datatype.AppointmentStatus;
import com.capgemini.training.appointmentbooking.common.to.AppointmentCto;
import com.capgemini.training.appointmentbooking.common.to.AppointmentEto;
import com.capgemini.training.appointmentbooking.common.to.ClientEto;
import com.capgemini.training.appointmentbooking.common.to.TreatmentCto;
import com.capgemini.training.appointmentbooking.common.to.TreatmentEto;
import com.capgemini.training.appointmentbooking.dataaccess.repository.criteria.AppointmentCriteria;
import com.capgemini.training.appointmentbooking.logic.FindAppointmentUc;
import com.capgemini.training.appointmentbooking.logic.ManageAppointmentUc;
import com.capgemini.training.appointmentbooking.service.config.ServiceMappingConfiguration;
import com.capgemini.training.appointmentbooking.service.model.AppointmentRequest;
import com.capgemini.training.appointmentbooking.service.model.AppointmentStatusUpdate;
import com.capgemini.training.appointmentbooking.service.model.AppointmentStatusUpdate.StatusEnum;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.Instant;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;

@WebMvcTest(controllers = AppointmentsApiController.class)
@Import(ServiceMappingConfiguration.class)
public class AppointmentsApiControllerTest extends BaseTest {

@Autowired private MockMvc mockMvc;

@MockitoBean private FindAppointmentUc findAppointmentUc;

@MockitoBean private ManageAppointmentUc manageAppointmentUc;

@Autowired private ObjectMapper objectMapper;

@Test
void shouldCreateAppointmentAndReturn201() throws Exception {
Instant now = Instant.now();
AppointmentRequest request = createAppointmentRequest(1L, 2L, now);
AppointmentCto appointmentCto =
createAppointmentCto(1L, now, AppointmentStatus.SCHEDULED, 1L, 2L, "Cleaning");

when(manageAppointmentUc.bookAppointment(any())).thenReturn(appointmentCto);

mockMvc
.perform(
post("/api/v1/appointments")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.status").value(AppointmentStatus.SCHEDULED.name()))
.andExpect(jsonPath("$.dateTime", notNullValue()))
.andExpect(jsonPath("$.clientId").value(1))
.andExpect(jsonPath("$.treatmentId").value(2));
}

@Test
void shouldReturn400WhenCreatingAppointmentWithInvalidPayload() throws Exception {
mockMvc
.perform(
post("/api/v1/appointments")
.contentType(MediaType.APPLICATION_JSON)
.content("{ \"specialistId\" : \"abc\" }"))
.andExpect(status().isBadRequest());
}

@Test
void shouldReturnAppointmentsListWith200() throws Exception {
Instant now = Instant.now();
AppointmentCto appointmentCto =
createAppointmentCto(1L, now, AppointmentStatus.SCHEDULED, 1L, 2L, "Filling");

when(findAppointmentUc.findByCriteria(any())).thenReturn(List.of(appointmentCto));

mockMvc
.perform(get("/api/v1/appointments"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].id").value(1))
.andExpect(jsonPath("$[0].status").value(AppointmentStatus.SCHEDULED.name()))
.andExpect(jsonPath("$[0].clientId").value(1))
.andExpect(jsonPath("$[0].treatmentId").value(2));
}

@Test
void shouldReturnAppointmentsListWith200Filtered() throws Exception {
Instant now = Instant.now();
AppointmentCto appointmentCto =
createAppointmentCto(1L, now, AppointmentStatus.SCHEDULED, 1L, 2L, "Filling");

when(findAppointmentUc.findByCriteria(
AppointmentCriteria.builder().status(AppointmentStatus.SCHEDULED).build()))
.thenReturn(List.of(appointmentCto));
when(findAppointmentUc.findByCriteria(
AppointmentCriteria.builder().status(AppointmentStatus.CANCELLED).build()))
.thenReturn(List.of());

mockMvc
.perform(get("/api/v1/appointments").param("status", AppointmentStatus.SCHEDULED.name()))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].id").value(1))
.andExpect(jsonPath("$[0].status").value(AppointmentStatus.SCHEDULED.name()))
.andExpect(jsonPath("$[0].clientId").value(1))
.andExpect(jsonPath("$[0].treatmentId").value(2));

mockMvc
.perform(get("/api/v1/appointments").param("status", AppointmentStatus.CANCELLED.name()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()", is(0)));
}

@Test
void shouldReturnEmptyListWhenNoAppointmentsExist() throws Exception {
when(findAppointmentUc.findAll()).thenReturn(Collections.emptyList());

mockMvc
.perform(get("/api/v1/appointments"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()", is(0)));
}

@Test
void shouldUpdateAppointmentStatusAndReturn200() throws Exception {
AppointmentStatusUpdate update = new AppointmentStatusUpdate();
update.setStatus(StatusEnum.CANCELLED);

mockMvc
.perform(
patch("/api/v1/appointments/1")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(update)))
.andExpect(status().isOk());
}

@Test
void shouldReturn400WhenUpdatingAppointmentWithInvalidStatus() throws Exception {
String invalidJson = "{ \"status\": \"INVALID\" }";

mockMvc
.perform(
patch("/api/v1/appointments/1")
.contentType(MediaType.APPLICATION_JSON)
.content(invalidJson))
.andExpect(status().isBadRequest());
}

@Test
void shouldReturnAvailabilityStatusWith200() throws Exception {
String date = "2024-12-12T10:15:30Z";
when(findAppointmentUc.hasConflictingAppointment(1L, Instant.parse(date))).thenReturn(false);

mockMvc
.perform(get("/api/v1/availability").param("specialistId", "1").param("dateTime", date))
.andExpect(status().isOk())
.andExpect(jsonPath("$.available", is(true)));
}

@Test
void shouldReturn400WhenAvailabilityQueryIsMissingParams() throws Exception {
mockMvc.perform(get("/api/v1/availability")).andExpect(status().isBadRequest());
}

// ======= HELPER METHODS =======

private AppointmentRequest createAppointmentRequest(
Long clientId, Long treatmentId, Instant dateTime) {
AppointmentRequest request = new AppointmentRequest();
request.setClientId(clientId);
request.setTreatmentId(treatmentId);
request.setDateTime(Date.from(dateTime));
return request;
}

private AppointmentCto createAppointmentCto(
Long appointmentId,
Instant dateTime,
AppointmentStatus status,
Long clientId,
Long treatmentId,
String treatmentName) {
return AppointmentCto.builder()
.appointmentEto(
AppointmentEto.builder().id(appointmentId).dateTime(dateTime).status(status).build())
.clientEto(ClientEto.builder().id(clientId).build())
.treatmentCto(
TreatmentCto.builder()
.treatmentEto(TreatmentEto.builder().id(treatmentId).name(treatmentName).build())
.build())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package com.capgemini.training.appointmentbooking.service.impl;

import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.capgemini.training.appointmentbooking.common.BaseTest;
import com.capgemini.training.appointmentbooking.common.datatype.Specialization;
import com.capgemini.training.appointmentbooking.common.to.SpecialistEto;
import com.capgemini.training.appointmentbooking.common.to.TreatmentCto;
import com.capgemini.training.appointmentbooking.common.to.TreatmentEto;
import com.capgemini.training.appointmentbooking.logic.FindTreatmentUc;
import com.capgemini.training.appointmentbooking.logic.ManageTreatmentUc;
import com.capgemini.training.appointmentbooking.service.config.ServiceMappingConfiguration;
import com.capgemini.training.appointmentbooking.service.model.TreatmentRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;

@WebMvcTest(controllers = TreatmentsApiController.class)
@Import(ServiceMappingConfiguration.class)
public class TreatmentsApiControllerTest extends BaseTest {
@Autowired private MockMvc mockMvc;

@MockitoBean private FindTreatmentUc findTreatmentUc;

@MockitoBean private ManageTreatmentUc manageTreatmentUc;

@Autowired private ObjectMapper objectMapper;

@Test
void shouldCreateTreatmentAndReturn201() throws Exception {
// given
String name = "Test name";
int duration = 30;
Long specialistId = 101L;

TreatmentRequest request = createTreatmentRequest(name, duration, specialistId);
TreatmentCto treatmentCto =
createTreatmentCto(1L, name, duration, specialistId, Specialization.DENTIST);

when(manageTreatmentUc.createTreatment(any())).thenReturn(treatmentCto);

// when / then
mockMvc
.perform(
post("/api/v1/treatments")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.name", is(name)))
.andExpect(jsonPath("$.duration", is(duration)))
.andExpect(jsonPath("$.specialistId").value(specialistId));
}

@Test
void shouldReturn400OnInvalidTreatmentRequest() throws Exception {
// given – invalid JSON (wrong type)
String invalidJson = "{ \"specialistId\" : \"abc\" }";

// when / then
mockMvc
.perform(
post("/api/v1/treatments").contentType(MediaType.APPLICATION_JSON).content(invalidJson))
.andExpect(status().isBadRequest());
}

@Test
void shouldReturnTreatmentsListWith200() throws Exception {
// given
String name = "Treatment A";
int duration = 45;
Long specialistId = 123L;

TreatmentCto treatmentCto =
createTreatmentCto(1L, name, duration, specialistId, Specialization.DENTIST);
when(findTreatmentUc.findAll()).thenReturn(List.of(treatmentCto));

// when / then
mockMvc
.perform(get("/api/v1/treatments"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].name", is(name)))
.andExpect(jsonPath("$[0].duration", is(duration)))
.andExpect(jsonPath("$[0].specialistId").value(specialistId));
}

@Test
void shouldReturnTreatmentDetailsWith200WhenFound() throws Exception {
// given
Long treatmentId = 101L;
String name = "Test treatment";
int duration = 30;
Long specialistId = 101L;

TreatmentCto treatmentCto =
createTreatmentCto(treatmentId, name, duration, specialistId, Specialization.DENTIST);
when(findTreatmentUc.findById(treatmentId)).thenReturn(Optional.of(treatmentCto));

// when / then
mockMvc
.perform(get("/api/v1/treatments/" + treatmentId))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name", is(name)))
.andExpect(jsonPath("$.duration", is(duration)))
.andExpect(jsonPath("$.specialistId").value(specialistId))
.andExpect(jsonPath("$.specialist.name", is(Specialization.DENTIST.name())));
}

@Test
void shouldReturn404WhenTreatmentNotFound() throws Exception {
// given
Long treatmentId = 101L;

when(findTreatmentUc.findById(treatmentId)).thenReturn(Optional.empty());

// when / then
mockMvc.perform(get("/api/v1/treatments/" + treatmentId)).andExpect(status().isNotFound());
}

private TreatmentRequest createTreatmentRequest(String name, int duration, Long specialistId) {
TreatmentRequest request = new TreatmentRequest();
request.setName(Optional.of(name));
request.setDuration(Optional.of(duration));
request.setSpecialistId(Optional.of(specialistId));
return request;
}

private TreatmentCto createTreatmentCto(
Long id, String name, int duration, Long specialistId, Specialization specialization) {
return TreatmentCto.builder()
.specialistEto(
SpecialistEto.builder().id(specialistId).specialization(specialization).build())
.treatmentEto(TreatmentEto.builder().id(id).name(name).durationMinutes(duration).build())
.build();
}
}
Loading