From a6c6d262902770d14c2f700d42a5b3344d3feeaf Mon Sep 17 00:00:00 2001 From: rossa Date: Tue, 7 Jan 2025 19:28:31 +0000 Subject: [PATCH] added tests for the controller --- pom.xml | 73 ++++++----- .../atu/bookingservice/BookingController.java | 39 +++--- .../ie/atu/bookingservice/BookingDetails.java | 19 +++ .../ie/atu/bookingservice/BookingService.java | 14 ++ .../ie/atu/bookingservice/SecurityConfig.java | 31 +++++ .../bookingservice/BookingControllerTest.java | 123 +++++++----------- 6 files changed, 173 insertions(+), 126 deletions(-) create mode 100644 src/main/java/ie/atu/bookingservice/SecurityConfig.java diff --git a/pom.xml b/pom.xml index 08cbbf2..830b8eb 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,3 @@ - 4.0.0 @@ -6,39 +5,23 @@ org.springframework.boot spring-boot-starter-parent 3.3.5 - + ie.atu BookingService 0.0.1-SNAPSHOT BookingService BookingService - - - - - - - - - - - - - 17 2023.0.3 + org.springframework.boot spring-boot-starter-validation - - org.springframework.cloud - spring-cloud-starter-openfeign - org.springframework.boot spring-boot-starter-data-mongodb @@ -47,18 +30,54 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + - org.projectlombok - lombok - true + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + runtime + + org.springframework.boot spring-boot-starter-test test + + org.springframework.security + spring-security-test + test + + + + + org.projectlombok + lombok + true + @@ -71,22 +90,12 @@ - org.springframework.boot spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - diff --git a/src/main/java/ie/atu/bookingservice/BookingController.java b/src/main/java/ie/atu/bookingservice/BookingController.java index 6fbfd7a..bbcdf02 100644 --- a/src/main/java/ie/atu/bookingservice/BookingController.java +++ b/src/main/java/ie/atu/bookingservice/BookingController.java @@ -1,5 +1,6 @@ package ie.atu.bookingservice; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -23,13 +24,6 @@ public ResponseEntity createBooking(@RequestBody BookingDetails return ResponseEntity.ok(createdBooking); } - // Endpoint to get all available bookings (those that are not yet booked) - @GetMapping("/available") - public ResponseEntity> getAvailableBookings() { - List availableBookings = bookingService.getAvailableBookings(); - return ResponseEntity.ok(availableBookings); - } - // Endpoint to get all bookings @GetMapping public ResponseEntity> getAllBookings() { @@ -37,20 +31,6 @@ public ResponseEntity> getAllBookings() { return ResponseEntity.ok(bookings); } - // Endpoint to get a booking by its ID - @GetMapping("getbooking/{id}") - public ResponseEntity getBookingById(@PathVariable String id) { - Optional bookingDetails = bookingService.getBookingById(id); - return bookingDetails.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build()); - } - - // Endpoint to get bookings by user ID - @GetMapping("/user/{userId}") - public ResponseEntity> getBookingByUserId(@PathVariable String userId) { - List bookings = bookingService.getByBookingUserId(userId); - return ResponseEntity.ok(bookings); - } - // Endpoint for updating a booking @PutMapping("update/{id}") public ResponseEntity updateBooking(@PathVariable String id, @RequestBody BookingDetails bookingDetails) { @@ -61,6 +41,23 @@ public ResponseEntity updateBooking(@PathVariable String id, @Re return ResponseEntity.notFound().build(); } + @PostMapping("/confirmBooking") + public ResponseEntity confirmBooking(@RequestBody BookingDetails bookingDetails) { + String authToken = bookingDetails.getToken(); + + if (authToken == null || authToken.isEmpty()) { + return ResponseEntity.badRequest().body("Auth token is required."); + } + + boolean isConfirmed = bookingService.confirmBooking(bookingDetails.getId()); + + if (isConfirmed) { + return ResponseEntity.ok("Booking confirmed successfully."); + } else { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Booking could not be confirmed."); + } + } + // Endpoint for deleting a booking @DeleteMapping("delete/{id}") public ResponseEntity deleteBooking(@PathVariable String id) { diff --git a/src/main/java/ie/atu/bookingservice/BookingDetails.java b/src/main/java/ie/atu/bookingservice/BookingDetails.java index e49abb8..d987d6e 100644 --- a/src/main/java/ie/atu/bookingservice/BookingDetails.java +++ b/src/main/java/ie/atu/bookingservice/BookingDetails.java @@ -14,6 +14,7 @@ public class BookingDetails { private String serviceType; private String status; private Date bookingDate; + private String token = null; public String getId() { return id; @@ -62,4 +63,22 @@ public Date getBookingDate() { public void setBookingDate(Date bookingDate) { this.bookingDate = bookingDate; } + + public void setBookedBy(String username) { + } + + public void setBooked(boolean b) { + } + + public boolean isBooked() { + return false; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } } diff --git a/src/main/java/ie/atu/bookingservice/BookingService.java b/src/main/java/ie/atu/bookingservice/BookingService.java index 73be71f..585f698 100644 --- a/src/main/java/ie/atu/bookingservice/BookingService.java +++ b/src/main/java/ie/atu/bookingservice/BookingService.java @@ -47,5 +47,19 @@ public List getAllBookings() { public List getAvailableBookings() { return bookingRepository.findByStatus("AVAILABLE"); } + public boolean confirmBooking(String id) { + Optional bookingOptional = bookingRepository.findById(id); + if (bookingOptional.isPresent()) { + BookingDetails booking = bookingOptional.get(); + // Check if the status is "AVAILABLE" + if ("AVAILABLE".equals(booking.getStatus())) { + // If AVAILABLE, update status to "BOOKED" + booking.setStatus("BOOKED"); + bookingRepository.save(booking); // Save the updated booking + return true; + } + } + return false; // Return false if the booking is not available or doesn't exist + } } diff --git a/src/main/java/ie/atu/bookingservice/SecurityConfig.java b/src/main/java/ie/atu/bookingservice/SecurityConfig.java new file mode 100644 index 0000000..30fcf46 --- /dev/null +++ b/src/main/java/ie/atu/bookingservice/SecurityConfig.java @@ -0,0 +1,31 @@ +package ie.atu.bookingservice; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(csrf -> csrf.disable()) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/booking/confirmBooking").permitAll() + .anyRequest().authenticated() // Protect all other endpoints + ) + .httpBasic(httpBasic -> httpBasic.disable()) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // Stateless JWT auth + return http.build(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} \ No newline at end of file diff --git a/src/test/java/ie/atu/bookingservice/BookingControllerTest.java b/src/test/java/ie/atu/bookingservice/BookingControllerTest.java index 84a7280..274ddbc 100644 --- a/src/test/java/ie/atu/bookingservice/BookingControllerTest.java +++ b/src/test/java/ie/atu/bookingservice/BookingControllerTest.java @@ -1,4 +1,5 @@ package ie.atu.bookingservice; + import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -9,17 +10,17 @@ import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; -import java.util.Arrays; import java.util.Date; -import java.util.List; -import java.util.Optional; import static org.mockito.Mockito.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @WebMvcTest(BookingController.class) class BookingControllerTest { + @Autowired private MockMvc mockMvc; @@ -47,7 +48,9 @@ void createBooking_ShouldReturnCreatedBooking() throws Exception { mockMvc.perform(post("/api/booking/createBooking") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(bookingDetails))) + .content(objectMapper.writeValueAsString(bookingDetails)) + .with(csrf()) // Add CSRF token + .with(user("mockUser").roles("USER"))) // Mock user authentication .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value("1")) .andExpect(jsonPath("$.userId").value("User1")); @@ -56,68 +59,63 @@ void createBooking_ShouldReturnCreatedBooking() throws Exception { } @Test - void getAvailableBookings_ShouldReturnAvailableBookings() throws Exception { - List availableBookings = Arrays.asList(bookingDetails); - when(bookingService.getAvailableBookings()).thenReturn(availableBookings); + void confirmBooking_ShouldReturnBookingNotConfirmed() throws Exception { + bookingDetails.setToken("mock-token"); // Mock valid token - mockMvc.perform(get("/api/booking/available") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.size()").value(1)) - .andExpect(jsonPath("$[0].id").value("1")); - - verify(bookingService, times(1)).getAvailableBookings(); - } + when(bookingService.confirmBooking(bookingDetails.getId())).thenReturn(false); - @Test - void getAllBookings_ShouldReturnAllBookings() throws Exception { - List allBookings = Arrays.asList(bookingDetails); - when(bookingService.getAllBookings()).thenReturn(allBookings); - - mockMvc.perform(get("/api/booking") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.size()").value(1)) - .andExpect(jsonPath("$[0].id").value("1")); + mockMvc.perform(post("/api/booking/confirmBooking") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(bookingDetails)) + .with(csrf()) // Add CSRF token + .with(user("mockUser").roles("USER"))) // Mock user authentication + .andExpect(status().isBadRequest()) + .andExpect(content().string("Booking could not be confirmed.")); - verify(bookingService, times(1)).getAllBookings(); + verify(bookingService, times(1)).confirmBooking(bookingDetails.getId()); } @Test - void getBookingById_ShouldReturnBookingWhenFound() throws Exception { - when(bookingService.getBookingById("1")).thenReturn(Optional.of(bookingDetails)); + void confirmBooking_ShouldReturnBookingConfirmed() throws Exception { + bookingDetails.setToken("mock-token"); // Mock valid token - mockMvc.perform(get("/api/booking/getbooking/1") - .contentType(MediaType.APPLICATION_JSON)) + when(bookingService.confirmBooking(bookingDetails.getId())).thenReturn(true); + + mockMvc.perform(post("/api/booking/confirmBooking") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(bookingDetails)) + .with(csrf()) // Add CSRF token + .with(user("mockUser").roles("USER"))) // Mock user authentication .andExpect(status().isOk()) - .andExpect(jsonPath("$.id").value("1")); + .andExpect(content().string("Booking confirmed successfully.")); - verify(bookingService, times(1)).getBookingById("1"); + verify(bookingService, times(1)).confirmBooking(bookingDetails.getId()); } @Test - void getBookingById_ShouldReturnNotFoundWhenNotFound() throws Exception { - when(bookingService.getBookingById("2")).thenReturn(Optional.empty()); - - mockMvc.perform(get("/api/booking/getbooking/2") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isNotFound()); + void confirmBooking_ShouldReturnUnauthorizedIfInvalidToken() throws Exception { + bookingDetails.setToken("invalid-token"); // Mock invalid token - verify(bookingService, times(1)).getBookingById("2"); + mockMvc.perform(post("/api/booking/confirmBooking") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(bookingDetails)) + .with(csrf()) // Add CSRF token + .with(user("mockUser").roles("USER"))) // Mock user authentication + .andExpect(status().isUnauthorized()) + .andExpect(content().string("Invalid or expired auth token.")); } @Test - void getBookingByUserId_ShouldReturnBookingsForUser() throws Exception { - List userBookings = Arrays.asList(bookingDetails); - when(bookingService.getByBookingUserId("User1")).thenReturn(userBookings); + void deleteBooking_ShouldReturnOk() throws Exception { + doNothing().when(bookingService).deleteBooking("1"); - mockMvc.perform(get("/api/booking/user/User1") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.size()").value(1)) - .andExpect(jsonPath("$[0].id").value("1")); + mockMvc.perform(delete("/api/booking/delete/1") + .contentType(MediaType.APPLICATION_JSON) + .with(csrf()) // Add CSRF token + .with(user("mockUser").roles("USER"))) // Mock user authentication + .andExpect(status().isOk()); - verify(bookingService, times(1)).getByBookingUserId("User1"); + verify(bookingService, times(1)).deleteBooking("1"); } @Test @@ -134,34 +132,13 @@ void updateBooking_ShouldReturnUpdatedBooking() throws Exception { mockMvc.perform(put("/api/booking/update/1") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updatedBooking))) + .content(objectMapper.writeValueAsString(updatedBooking)) + .with(csrf()) // Add CSRF token + .with(user("mockUser").roles("USER"))) // Mock user authentication .andExpect(status().isOk()) .andExpect(jsonPath("$.userId").value("User2")) .andExpect(jsonPath("$.status").value("BOOKED")); verify(bookingService, times(1)).updateBooking(eq("1"), Mockito.any(BookingDetails.class)); } - - @Test - void updateBooking_ShouldReturnNotFoundWhenNotFound() throws Exception { - when(bookingService.updateBooking(eq("2"), Mockito.any(BookingDetails.class))).thenReturn(null); - - mockMvc.perform(put("/api/booking/update/2") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(bookingDetails))) - .andExpect(status().isNotFound()); - - verify(bookingService, times(1)).updateBooking(eq("2"), Mockito.any(BookingDetails.class)); - } - - @Test - void deleteBooking_ShouldReturnOk() throws Exception { - doNothing().when(bookingService).deleteBooking("1"); - - mockMvc.perform(delete("/api/booking/delete/1") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()); - - verify(bookingService, times(1)).deleteBooking("1"); - } -} \ No newline at end of file +}