diff --git a/backend/src/main/java/com/angel/autonow/driver/DriverController.java b/backend/src/main/java/com/angel/autonow/driver/DriverController.java index 50b5e76..decc7a9 100644 --- a/backend/src/main/java/com/angel/autonow/driver/DriverController.java +++ b/backend/src/main/java/com/angel/autonow/driver/DriverController.java @@ -5,9 +5,11 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; +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.RestController; @@ -46,4 +48,20 @@ public DriverResponseDTO getDriverByLicenseNumber(@PathVariable String licenseNu public List getAllDrivers() { return driverService.getAllDrivers(); } + + @PutMapping("/{id}") + @PreAuthorize("hasRole('ADMIN')") + public ResponseEntity updateDriver(@PathVariable Long id, @Valid @RequestBody DriverRequestDTO request) { + return driverService.updateDriver(id, request) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.badRequest().build()); + } + + @DeleteMapping("/{id}") + @PreAuthorize("hasRole('ADMIN')") + public ResponseEntity deleteDriver(@PathVariable Long id) { + return driverService.deleteDriver(id) + ? ResponseEntity.noContent().build() + : ResponseEntity.badRequest().build(); + } } diff --git a/backend/src/main/java/com/angel/autonow/driver/DriverMapper.java b/backend/src/main/java/com/angel/autonow/driver/DriverMapper.java index 0c92aaf..574dee3 100644 --- a/backend/src/main/java/com/angel/autonow/driver/DriverMapper.java +++ b/backend/src/main/java/com/angel/autonow/driver/DriverMapper.java @@ -3,6 +3,7 @@ import com.angel.autonow.vehicle.VehicleEntity; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; import org.mapstruct.Named; import java.util.Collections; @@ -19,6 +20,10 @@ public interface DriverMapper { @Mapping(target = "vehicles", ignore = true) DriverEntity toEntity(DriverRequestDTO request); + @Mapping(target = "id", ignore = true) + @Mapping(target = "vehicles", ignore = true) + void updateEntity(DriverRequestDTO request, @MappingTarget DriverEntity entity); + @Named("vehiclesToIds") default Set vehiclesToIds(Set vehicles) { if (vehicles == null) { diff --git a/backend/src/main/java/com/angel/autonow/driver/DriverRequestDTO.java b/backend/src/main/java/com/angel/autonow/driver/DriverRequestDTO.java index f290b98..9dad0ee 100644 --- a/backend/src/main/java/com/angel/autonow/driver/DriverRequestDTO.java +++ b/backend/src/main/java/com/angel/autonow/driver/DriverRequestDTO.java @@ -4,7 +4,9 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; +import lombok.Builder; +@Builder public record DriverRequestDTO( @NotBlank(message = "First name is required") diff --git a/backend/src/main/java/com/angel/autonow/driver/DriverService.java b/backend/src/main/java/com/angel/autonow/driver/DriverService.java index 76c0243..bc6b1a4 100644 --- a/backend/src/main/java/com/angel/autonow/driver/DriverService.java +++ b/backend/src/main/java/com/angel/autonow/driver/DriverService.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Optional; @@ -32,4 +33,22 @@ public List getAllDrivers() { .map(driverMapper::toDTO) .toList(); } + + @Transactional + public Optional updateDriver(Long id, DriverRequestDTO request) { + return driverRepository.findById(id).map(driver -> { + driverMapper.updateEntity(request, driver); + return driverMapper.toDTO(driverRepository.save(driver)); + }); + } + + public boolean deleteDriver(Long id) { + if (!driverRepository.existsById(id)) { + return false; + } + + driverRepository.deleteById(id); + + return true; + } } diff --git a/backend/src/main/java/com/angel/autonow/order/OrderController.java b/backend/src/main/java/com/angel/autonow/order/OrderController.java index ebd7c40..e36bb2d 100644 --- a/backend/src/main/java/com/angel/autonow/order/OrderController.java +++ b/backend/src/main/java/com/angel/autonow/order/OrderController.java @@ -5,9 +5,11 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; +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.RestController; @@ -46,4 +48,20 @@ public List getOrdersByUserId(@PathVariable Long userId) { public List getAllOrders() { return orderService.getAllOrders(); } + + @PutMapping("/{id}") + @PreAuthorize("hasAnyRole('ADMIN', 'CUSTOMER')") + public ResponseEntity updateOrder(@PathVariable Long id, @Valid @RequestBody OrderRequestDTO request) { + return orderService.updateOrder(id, request) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.badRequest().build()); + } + + @DeleteMapping("/{id}") + @PreAuthorize("hasRole('ADMIN')") + public ResponseEntity deleteOrder(@PathVariable Long id) { + return orderService.deleteOrder(id) + ? ResponseEntity.noContent().build() + : ResponseEntity.badRequest().build(); + } } diff --git a/backend/src/main/java/com/angel/autonow/order/OrderMapper.java b/backend/src/main/java/com/angel/autonow/order/OrderMapper.java index 916604f..15c3d75 100644 --- a/backend/src/main/java/com/angel/autonow/order/OrderMapper.java +++ b/backend/src/main/java/com/angel/autonow/order/OrderMapper.java @@ -2,6 +2,7 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; @Mapper(componentModel = "spring") public interface OrderMapper { @@ -21,4 +22,15 @@ public interface OrderMapper { @Mapping(target = "createdAt", ignore = true) @Mapping(target = "updatedAt", ignore = true) OrderEntity toEntity(OrderRequestDTO request); + + @Mapping(target = "id", ignore = true) + @Mapping(target = "user", ignore = true) + @Mapping(target = "driver", ignore = true) + @Mapping(target = "vehicle", ignore = true) + @Mapping(target = "status", ignore = true) + @Mapping(target = "finalPrice", ignore = true) + @Mapping(target = "cancellationReason", ignore = true) + @Mapping(target = "createdAt", ignore = true) + @Mapping(target = "updatedAt", ignore = true) + void updateEntity(OrderRequestDTO request, @MappingTarget OrderEntity entity); } diff --git a/backend/src/main/java/com/angel/autonow/order/OrderRequestDTO.java b/backend/src/main/java/com/angel/autonow/order/OrderRequestDTO.java index 5f6d4ad..a399724 100644 --- a/backend/src/main/java/com/angel/autonow/order/OrderRequestDTO.java +++ b/backend/src/main/java/com/angel/autonow/order/OrderRequestDTO.java @@ -4,7 +4,9 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; +import lombok.Builder; +@Builder public record OrderRequestDTO( @NotNull(message = "User ID is required") diff --git a/backend/src/main/java/com/angel/autonow/order/OrderService.java b/backend/src/main/java/com/angel/autonow/order/OrderService.java index 437a6bf..853d806 100644 --- a/backend/src/main/java/com/angel/autonow/order/OrderService.java +++ b/backend/src/main/java/com/angel/autonow/order/OrderService.java @@ -1,8 +1,10 @@ package com.angel.autonow.order; +import com.angel.autonow.driver.DriverEntity; import com.angel.autonow.driver.DriverRepository; import com.angel.autonow.user.UserEntity; import com.angel.autonow.user.UserRepository; +import com.angel.autonow.vehicle.VehicleEntity; import com.angel.autonow.vehicle.VehicleRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -72,4 +74,58 @@ public List getAllOrders() { .map(orderMapper::toDTO) .toList(); } + + @Transactional + public Optional updateOrder(Long id, OrderRequestDTO request) { + Optional existing = orderRepository.findById(id); + + if (existing.isEmpty()) { + return Optional.empty(); + } + + Optional user = userRepository.findById(request.userId()); + if (user.isEmpty()) { + return Optional.empty(); + } + + DriverEntity driver = null; + if (request.driverId() != null) { + var driverOpt = driverRepository.findById(request.driverId()); + + if (driverOpt.isEmpty()) { + return Optional.empty(); + } + + driver = driverOpt.get(); + } + + VehicleEntity vehicle = null; + if (request.vehicleId() != null) { + var vehicleOpt = vehicleRepository.findById(request.vehicleId()); + + if (vehicleOpt.isEmpty()) { + return Optional.empty(); + } + + vehicle = vehicleOpt.get(); + } + + OrderEntity order = existing.get(); + orderMapper.updateEntity(request, order); + order.setUser(user.get()); + order.setDriver(driver); + order.setVehicle(vehicle); + + return Optional.of(orderMapper.toDTO(orderRepository.save(order))); + } + + public boolean deleteOrder(Long id) { + if (!orderRepository.existsById(id)) { + return false; + } + + orderRepository.deleteById(id); + + return true; + } } diff --git a/backend/src/main/java/com/angel/autonow/payment/PaymentController.java b/backend/src/main/java/com/angel/autonow/payment/PaymentController.java index 1eaddea..838047e 100644 --- a/backend/src/main/java/com/angel/autonow/payment/PaymentController.java +++ b/backend/src/main/java/com/angel/autonow/payment/PaymentController.java @@ -5,9 +5,11 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; +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.RestController; @@ -46,4 +48,20 @@ public PaymentResponseDTO getPaymentByOrderId(@PathVariable Long orderId) { public List getAllPayments() { return paymentService.getAllPayments(); } + + @PutMapping("/{id}") + @PreAuthorize("hasAnyRole('ADMIN', 'CUSTOMER')") + public ResponseEntity updatePayment(@PathVariable Long id, @Valid @RequestBody PaymentRequestDTO request) { + return paymentService.updatePayment(id, request) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.badRequest().build()); + } + + @DeleteMapping("/{id}") + @PreAuthorize("hasRole('ADMIN')") + public ResponseEntity deletePayment(@PathVariable Long id) { + return paymentService.deletePayment(id) + ? ResponseEntity.noContent().build() + : ResponseEntity.badRequest().build(); + } } diff --git a/backend/src/main/java/com/angel/autonow/payment/PaymentMapper.java b/backend/src/main/java/com/angel/autonow/payment/PaymentMapper.java index 76bdf3d..d2caee0 100644 --- a/backend/src/main/java/com/angel/autonow/payment/PaymentMapper.java +++ b/backend/src/main/java/com/angel/autonow/payment/PaymentMapper.java @@ -2,6 +2,7 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; @Mapper(componentModel = "spring") public interface PaymentMapper { @@ -15,4 +16,11 @@ public interface PaymentMapper { @Mapping(target = "createdAt", ignore = true) @Mapping(target = "updatedAt", ignore = true) PaymentEntity toEntity(PaymentRequestDTO request); + + @Mapping(target = "id", ignore = true) + @Mapping(target = "order", ignore = true) + @Mapping(target = "status", ignore = true) + @Mapping(target = "createdAt", ignore = true) + @Mapping(target = "updatedAt", ignore = true) + void updateEntity(PaymentRequestDTO request, @MappingTarget PaymentEntity entity); } diff --git a/backend/src/main/java/com/angel/autonow/payment/PaymentRequestDTO.java b/backend/src/main/java/com/angel/autonow/payment/PaymentRequestDTO.java index 6fb14ab..c71acca 100644 --- a/backend/src/main/java/com/angel/autonow/payment/PaymentRequestDTO.java +++ b/backend/src/main/java/com/angel/autonow/payment/PaymentRequestDTO.java @@ -2,7 +2,9 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; +import lombok.Builder; +@Builder public record PaymentRequestDTO( @NotNull(message = "Order ID is required") diff --git a/backend/src/main/java/com/angel/autonow/payment/PaymentService.java b/backend/src/main/java/com/angel/autonow/payment/PaymentService.java index ebcd2ee..aabe9b4 100644 --- a/backend/src/main/java/com/angel/autonow/payment/PaymentService.java +++ b/backend/src/main/java/com/angel/autonow/payment/PaymentService.java @@ -51,4 +51,45 @@ public List getAllPayments() { .map(paymentMapper::toDTO) .toList(); } + + @Transactional + public Optional updatePayment(Long id, PaymentRequestDTO request) { + Optional existing = paymentRepository.findById(id); + + if (existing.isEmpty()) { + return Optional.empty(); + } + + PaymentEntity payment = existing.get(); + + if (!payment.getOrder().getId().equals(request.orderId())) { + boolean orderAlreadyPaid = paymentRepository.findByOrderId(request.orderId()) + .filter(p -> !p.getId().equals(payment.getId())) + .isPresent(); + + if (orderAlreadyPaid) { + return Optional.empty(); + } + + Optional order = orderRepository.findById(request.orderId()); + if (order.isEmpty()) { + return Optional.empty(); + } + payment.setOrder(order.get()); + } + + paymentMapper.updateEntity(request, payment); + + return Optional.of(paymentMapper.toDTO(paymentRepository.save(payment))); + } + + public boolean deletePayment(Long id) { + if (!paymentRepository.existsById(id)) { + return false; + } + + paymentRepository.deleteById(id); + + return true; + } } diff --git a/backend/src/main/java/com/angel/autonow/rating/RatingController.java b/backend/src/main/java/com/angel/autonow/rating/RatingController.java index a9f31ef..f6271ae 100644 --- a/backend/src/main/java/com/angel/autonow/rating/RatingController.java +++ b/backend/src/main/java/com/angel/autonow/rating/RatingController.java @@ -5,9 +5,11 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; +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.RestController; @@ -46,4 +48,20 @@ public RatingResponseDTO getRatingByOrderId(@PathVariable Long orderId) { public List getAllRatings() { return ratingService.getAllRatings(); } + + @PutMapping("/{id}") + @PreAuthorize("hasAnyRole('ADMIN', 'CUSTOMER')") + public ResponseEntity updateRating(@PathVariable Long id, @Valid @RequestBody RatingRequestDTO request) { + return ratingService.updateRating(id, request) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.badRequest().build()); + } + + @DeleteMapping("/{id}") + @PreAuthorize("hasAnyRole('ADMIN', 'CUSTOMER')") + public ResponseEntity deleteRating(@PathVariable Long id) { + return ratingService.deleteRating(id) + ? ResponseEntity.noContent().build() + : ResponseEntity.badRequest().build(); + } } diff --git a/backend/src/main/java/com/angel/autonow/rating/RatingMapper.java b/backend/src/main/java/com/angel/autonow/rating/RatingMapper.java index a8e34df..008fc70 100644 --- a/backend/src/main/java/com/angel/autonow/rating/RatingMapper.java +++ b/backend/src/main/java/com/angel/autonow/rating/RatingMapper.java @@ -2,6 +2,7 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; @Mapper(componentModel = "spring") public interface RatingMapper { @@ -13,4 +14,9 @@ public interface RatingMapper { @Mapping(target = "order", ignore = true) @Mapping(target = "createdAt", ignore = true) RatingEntity toEntity(RatingRequestDTO request); + + @Mapping(target = "id", ignore = true) + @Mapping(target = "order", ignore = true) + @Mapping(target = "createdAt", ignore = true) + void updateEntity(RatingRequestDTO request, @MappingTarget RatingEntity entity); } diff --git a/backend/src/main/java/com/angel/autonow/rating/RatingRequestDTO.java b/backend/src/main/java/com/angel/autonow/rating/RatingRequestDTO.java index 3872d20..e74d937 100644 --- a/backend/src/main/java/com/angel/autonow/rating/RatingRequestDTO.java +++ b/backend/src/main/java/com/angel/autonow/rating/RatingRequestDTO.java @@ -4,7 +4,9 @@ import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import lombok.Builder; +@Builder public record RatingRequestDTO( @NotNull(message = "Order ID is required") diff --git a/backend/src/main/java/com/angel/autonow/rating/RatingService.java b/backend/src/main/java/com/angel/autonow/rating/RatingService.java index 3e0838f..1144712 100644 --- a/backend/src/main/java/com/angel/autonow/rating/RatingService.java +++ b/backend/src/main/java/com/angel/autonow/rating/RatingService.java @@ -45,4 +45,38 @@ public List getAllRatings() { .map(ratingMapper::toDTO) .toList(); } + + @Transactional + public Optional updateRating(Long id, RatingRequestDTO request) { + Optional existing = ratingRepository.findById(id); + + if (existing.isEmpty()) { + return Optional.empty(); + } + + RatingEntity rating = existing.get(); + + if (!rating.getOrder().getId().equals(request.orderId())) { + Optional order = orderRepository.findById(request.orderId()); + + if (order.isEmpty()) { + return Optional.empty(); + } + + rating.setOrder(order.get()); + } + + ratingMapper.updateEntity(request, rating); + return Optional.of(ratingMapper.toDTO(ratingRepository.save(rating))); + } + + public boolean deleteRating(Long id) { + if (!ratingRepository.existsById(id)) { + return false; + } + + ratingRepository.deleteById(id); + + return true; + } } diff --git a/backend/src/main/java/com/angel/autonow/vehicle/VehicleController.java b/backend/src/main/java/com/angel/autonow/vehicle/VehicleController.java index 8dd4986..2b3f882 100644 --- a/backend/src/main/java/com/angel/autonow/vehicle/VehicleController.java +++ b/backend/src/main/java/com/angel/autonow/vehicle/VehicleController.java @@ -3,10 +3,13 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; +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.ResponseStatus; @@ -39,4 +42,20 @@ public VehicleResponseDTO getVehicleById(@PathVariable Long id) { public List getAllVehicles() { return vehicleService.getAllVehicles(); } + + @PutMapping("/{id}") + @PreAuthorize("hasRole('ADMIN')") + public ResponseEntity updateVehicle(@PathVariable Long id, @Valid @RequestBody VehicleRequestDTO request) { + return vehicleService.updateVehicle(id, request) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.badRequest().build()); + } + + @DeleteMapping("/{id}") + @PreAuthorize("hasRole('ADMIN')") + public ResponseEntity deleteVehicle(@PathVariable Long id) { + return vehicleService.deleteVehicle(id) + ? ResponseEntity.noContent().build() + : ResponseEntity.badRequest().build(); + } } diff --git a/backend/src/main/java/com/angel/autonow/vehicle/VehicleMapper.java b/backend/src/main/java/com/angel/autonow/vehicle/VehicleMapper.java index f20a3f6..55ab423 100644 --- a/backend/src/main/java/com/angel/autonow/vehicle/VehicleMapper.java +++ b/backend/src/main/java/com/angel/autonow/vehicle/VehicleMapper.java @@ -2,6 +2,7 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; @Mapper(componentModel = "spring") public interface VehicleMapper { @@ -10,4 +11,7 @@ public interface VehicleMapper { @Mapping(target = "id", ignore = true) VehicleEntity toEntity(VehicleRequestDTO request); + + @Mapping(target = "id", ignore = true) + void updateEntity(VehicleRequestDTO request, @MappingTarget VehicleEntity entity); } diff --git a/backend/src/main/java/com/angel/autonow/vehicle/VehicleRequestDTO.java b/backend/src/main/java/com/angel/autonow/vehicle/VehicleRequestDTO.java index 65b32bc..86977df 100644 --- a/backend/src/main/java/com/angel/autonow/vehicle/VehicleRequestDTO.java +++ b/backend/src/main/java/com/angel/autonow/vehicle/VehicleRequestDTO.java @@ -3,7 +3,9 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; +import lombok.Builder; +@Builder public record VehicleRequestDTO( @NotBlank(message = "Brand is required") diff --git a/backend/src/main/java/com/angel/autonow/vehicle/VehicleService.java b/backend/src/main/java/com/angel/autonow/vehicle/VehicleService.java index f19215e..f9f11fe 100644 --- a/backend/src/main/java/com/angel/autonow/vehicle/VehicleService.java +++ b/backend/src/main/java/com/angel/autonow/vehicle/VehicleService.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Optional; @@ -29,4 +30,23 @@ public List getAllVehicles() { .map(vehicleMapper::toDTO) .toList(); } + + @Transactional + public Optional updateVehicle(Long id, VehicleRequestDTO request) { + return vehicleRepository.findById(id) + .map(vehicle -> { + vehicleMapper.updateEntity(request, vehicle); + return vehicleMapper.toDTO(vehicleRepository.save(vehicle)); + }); + } + + public boolean deleteVehicle(Long id) { + if (!vehicleRepository.existsById(id)) { + return false; + } + + vehicleRepository.deleteById(id); + + return true; + } } diff --git a/backend/src/test/java/com/angel/autonow/driver/DriverControllerIT.java b/backend/src/test/java/com/angel/autonow/driver/DriverControllerIT.java index e0f7fcb..3fef908 100644 --- a/backend/src/test/java/com/angel/autonow/driver/DriverControllerIT.java +++ b/backend/src/test/java/com/angel/autonow/driver/DriverControllerIT.java @@ -1,6 +1,7 @@ package com.angel.autonow.driver; import com.angel.autonow.data.TestData; +import com.angel.autonow.expertise.ExpertiseType; import tools.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -14,6 +15,8 @@ import static com.angel.autonow.data.TestData.NON_EXISTENT_ID; 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.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @Transactional @@ -61,7 +64,7 @@ void createDriver_asCustomer_returnsForbidden() throws Exception { @Test void createDriver_invalidInput_returnsBadRequest() throws Exception { - var invalidRequest = new DriverRequestDTO(null, null, null, null, null, false, null); + var invalidRequest = DriverRequestDTO.builder().build(); mockMvc.perform(post("/api/drivers") .with(TestData.adminJwt()) @@ -143,4 +146,98 @@ void getAllDrivers_withoutAuth_returnsUnauthorized() throws Exception { mockMvc.perform(get("/api/drivers")) .andExpect(status().isUnauthorized()); } + + @Test + void updateDriver_asAdmin() throws Exception { + var driver = TestData.createDriverEntity(); + driverRepository.save(driver); + + var updateRequest = DriverRequestDTO.builder() + .firstName("Jane") + .lastName("Smith") + .phoneNumber("+9876543210") + .licenseNumber("DL-UPD-001") + .expertiseType(ExpertiseType.C) + .available(false) + .build(); + + mockMvc.perform(put("/api/drivers/{id}", driver.getId()) + .with(TestData.adminJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.firstName").value("Jane")) + .andExpect(jsonPath("$.lastName").value("Smith")) + .andExpect(jsonPath("$.licenseNumber").value("DL-UPD-001")) + .andExpect(jsonPath("$.expertiseType").value("C")); + } + + @Test + void updateDriver_notFound_returnsBadRequest() throws Exception { + var updateRequest = TestData.createDriverRequest(); + + mockMvc.perform(put("/api/drivers/{id}", NON_EXISTENT_ID) + .with(TestData.adminJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isBadRequest()); + } + + @Test + void updateDriver_invalidInput_returnsBadRequest() throws Exception { + var driver = TestData.createDriverEntity(); + driverRepository.save(driver); + + var invalidRequest = DriverRequestDTO.builder().build(); + + mockMvc.perform(put("/api/drivers/{id}", driver.getId()) + .with(TestData.adminJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidRequest))) + .andExpect(status().isBadRequest()); + } + + @Test + void updateDriver_asCustomer_returnsForbidden() throws Exception { + var updateRequest = TestData.createDriverRequest(); + + mockMvc.perform(put("/api/drivers/{id}", 1L) + .with(TestData.customerJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isForbidden()); + } + + @Test + void updateDriver_withoutAuth_returnsUnauthorized() throws Exception { + var updateRequest = TestData.createDriverRequest(); + + mockMvc.perform(put("/api/drivers/{id}", 1L) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isUnauthorized()); + } + + @Test + void deleteDriver_asAdmin() throws Exception { + var driver = TestData.createDriverEntity(); + driverRepository.save(driver); + + mockMvc.perform(delete("/api/drivers/{id}", driver.getId()).with(TestData.adminJwt())).andExpect(status().isNoContent()); + } + + @Test + void deleteDriver_notFound_returnsBadRequest() throws Exception { + mockMvc.perform(delete("/api/drivers/{id}", NON_EXISTENT_ID).with(TestData.adminJwt())).andExpect(status().isBadRequest()); + } + + @Test + void deleteDriver_asCustomer_returnsForbidden() throws Exception { + mockMvc.perform(delete("/api/drivers/{id}", 1L).with(TestData.customerJwt())).andExpect(status().isForbidden()); + } + + @Test + void deleteDriver_withoutAuth_returnsUnauthorized() throws Exception { + mockMvc.perform(delete("/api/drivers/{id}", 1L)).andExpect(status().isUnauthorized()); + } } diff --git a/backend/src/test/java/com/angel/autonow/driver/DriverServiceTest.java b/backend/src/test/java/com/angel/autonow/driver/DriverServiceTest.java index cfcc385..3ef36fd 100644 --- a/backend/src/test/java/com/angel/autonow/driver/DriverServiceTest.java +++ b/backend/src/test/java/com/angel/autonow/driver/DriverServiceTest.java @@ -124,4 +124,54 @@ void getAllDrivers_emptyList() { var result = driverService.getAllDrivers(); assertTrue(result.isEmpty()); } + + @Test + void updateDriver_returnUpdatedResponse() { + DriverRequestDTO request = TestData.createDriverRequest(); + DriverEntity existing = DriverEntity.builder().id(1L).firstName("Michael").build(); + DriverEntity saved = DriverEntity.builder().id(1L).firstName("Michael").build(); + DriverResponseDTO response = TestData.createDriverResponse(1L); + + when(driverRepository.findById(1L)).thenReturn(Optional.of(existing)); + when(driverRepository.save(existing)).thenReturn(saved); + when(driverMapper.toDTO(saved)).thenReturn(response); + + var result = driverService.updateDriver(1L, request); + + assertTrue(result.isPresent()); + assertEquals(1L, result.get().id()); + verify(driverMapper).updateEntity(request, existing); + verify(driverRepository).save(existing); + } + + @Test + void updateDriver_notFound_returnsEmpty() { + DriverRequestDTO request = TestData.createDriverRequest(); + + when(driverRepository.findById(NON_EXISTENT_ID)).thenReturn(Optional.empty()); + + var result = driverService.updateDriver(NON_EXISTENT_ID, request); + + assertTrue(result.isEmpty()); + } + + @Test + void deleteDriver_returnTrue() { + when(driverRepository.existsById(1L)).thenReturn(true); + + var result = driverService.deleteDriver(1L); + + assertTrue(result); + verify(driverRepository).deleteById(1L); + } + + @Test + void deleteDriver_notFound_returnFalse() { + when(driverRepository.existsById(NON_EXISTENT_ID)).thenReturn(false); + + var result = driverService.deleteDriver(NON_EXISTENT_ID); + + assertFalse(result); + verify(driverRepository, never()).deleteById(any()); + } } diff --git a/backend/src/test/java/com/angel/autonow/order/OrderControllerIT.java b/backend/src/test/java/com/angel/autonow/order/OrderControllerIT.java index 909cb31..213784b 100644 --- a/backend/src/test/java/com/angel/autonow/order/OrderControllerIT.java +++ b/backend/src/test/java/com/angel/autonow/order/OrderControllerIT.java @@ -3,6 +3,7 @@ import com.angel.autonow.data.TestData; import com.angel.autonow.user.UserEntity; import com.angel.autonow.user.UserRepository; +import com.angel.autonow.vehicle.VehicleType; import tools.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -17,6 +18,8 @@ import static com.angel.autonow.data.TestData.NON_EXISTENT_ID; 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.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @Transactional @@ -75,7 +78,7 @@ void createOrder_userNotFound_returnsBadRequest() throws Exception { @Test void createOrder_invalidInput_returnsBadRequest() throws Exception { - var invalidRequest = new OrderRequestDTO(null, null, null, null, null, null, null, null, null, null, null, null, null, null); + var invalidRequest = OrderRequestDTO.builder().build(); mockMvc.perform(post("/api/orders") .with(TestData.customerJwt()) @@ -169,4 +172,105 @@ void getAllOrders_withoutAuth_returnsUnauthorized() throws Exception { mockMvc.perform(get("/api/orders")) .andExpect(status().isUnauthorized()); } + + @Test + void updateOrder_asCustomer() throws Exception { + var order = TestData.createOrderEntity(user); + orderRepository.save(order); + + var updateRequest = OrderRequestDTO.builder() + .userId(user.getId()) + .vehicleType(VehicleType.SEMI) + .pickupAddress("789 Elm St") + .pickupLatitude(42.70) + .pickupLongitude(23.33) + .dropoffAddress("101 Pine Rd") + .dropoffLatitude(42.72) + .dropoffLongitude(23.34) + .estimatedPrice(25.00) + .distanceKm(10.5) + .estimatedDurationMinutes(20) + .specialRequirements("Fragile cargo") + .build(); + + mockMvc.perform(put("/api/orders/{id}", order.getId()) + .with(TestData.customerJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.vehicleType").value("SEMI")) + .andExpect(jsonPath("$.pickupAddress").value("789 Elm St")) + .andExpect(jsonPath("$.specialRequirements").value("Fragile cargo")); + } + + @Test + void updateOrder_notFound_returnsBadRequest() throws Exception { + var updateRequest = TestData.createOrderRequest(user.getId()); + + mockMvc.perform(put("/api/orders/{id}", NON_EXISTENT_ID) + .with(TestData.customerJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isBadRequest()); + } + + @Test + void updateOrder_invalidInput_returnsBadRequest() throws Exception { + var order = TestData.createOrderEntity(user); + orderRepository.save(order); + + var invalidRequest = OrderRequestDTO.builder().build(); + + mockMvc.perform(put("/api/orders/{id}", order.getId()) + .with(TestData.customerJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidRequest))) + .andExpect(status().isBadRequest()); + } + + @Test + void updateOrder_asDriver_returnsForbidden() throws Exception { + var updateRequest = TestData.createOrderRequest(user.getId()); + + mockMvc.perform(put("/api/orders/{id}", 1L) + .with(TestData.driverJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isForbidden()); + } + + @Test + void updateOrder_withoutAuth_returnsUnauthorized() throws Exception { + var updateRequest = TestData.createOrderRequest(user.getId()); + + mockMvc.perform(put("/api/orders/{id}", 1L) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isUnauthorized()); + } + + @Test + void deleteOrder_asAdmin() throws Exception { + var order = TestData.createOrderEntity(user); + orderRepository.save(order); + + mockMvc.perform(delete("/api/orders/{id}", order.getId()) + .with(TestData.adminJwt())) + .andExpect(status().isNoContent()); + } + + @Test + void deleteOrder_notFound_returnsBadRequest() throws Exception { + mockMvc.perform(delete("/api/orders/{id}", NON_EXISTENT_ID).with(TestData.adminJwt())).andExpect(status().isBadRequest()); + } + + @Test + void deleteOrder_asCustomer_returnsForbidden() throws Exception { + mockMvc.perform(delete("/api/orders/{id}", 1L).with(TestData.customerJwt())).andExpect(status().isForbidden()); + } + + @Test + void deleteOrder_withoutAuth_returnsUnauthorized() throws Exception { + mockMvc.perform(delete("/api/orders/{id}", 1L)).andExpect(status().isUnauthorized()); + } } diff --git a/backend/src/test/java/com/angel/autonow/order/OrderServiceTest.java b/backend/src/test/java/com/angel/autonow/order/OrderServiceTest.java index bd2d8c5..6792949 100644 --- a/backend/src/test/java/com/angel/autonow/order/OrderServiceTest.java +++ b/backend/src/test/java/com/angel/autonow/order/OrderServiceTest.java @@ -81,10 +81,12 @@ void createOrder_userNotFound_returnsEmpty() { @Test void createOrder_withDriverAndVehicle() { - OrderRequestDTO request = new OrderRequestDTO(1L, 2L, 3L, VehicleType.TAXI, - TestData.DEFAULT_PICKUP_ADDRESS, TestData.DEFAULT_PICKUP_LAT, TestData.DEFAULT_PICKUP_LNG, - TestData.DEFAULT_DROPOFF_ADDRESS, TestData.DEFAULT_DROPOFF_LAT, TestData.DEFAULT_DROPOFF_LNG, - 15.50, 5.2, 15, null); + OrderRequestDTO request = OrderRequestDTO.builder() + .userId(1L).driverId(2L).vehicleId(3L).vehicleType(VehicleType.TAXI) + .pickupAddress(TestData.DEFAULT_PICKUP_ADDRESS).pickupLatitude(TestData.DEFAULT_PICKUP_LAT).pickupLongitude(TestData.DEFAULT_PICKUP_LNG) + .dropoffAddress(TestData.DEFAULT_DROPOFF_ADDRESS).dropoffLatitude(TestData.DEFAULT_DROPOFF_LAT).dropoffLongitude(TestData.DEFAULT_DROPOFF_LNG) + .estimatedPrice(15.50).distanceKm(5.2).estimatedDurationMinutes(15) + .build(); UserEntity user = UserEntity.builder().id(1L).build(); DriverEntity driver = DriverEntity.builder().id(2L).build(); VehicleEntity vehicle = VehicleEntity.builder().id(3L).build(); @@ -113,10 +115,12 @@ void createOrder_withDriverAndVehicle() { @Test void createOrder_driverNotFound_returnsEmpty() { - OrderRequestDTO request = new OrderRequestDTO(1L, NON_EXISTENT_ID, null, VehicleType.TAXI, - TestData.DEFAULT_PICKUP_ADDRESS, TestData.DEFAULT_PICKUP_LAT, TestData.DEFAULT_PICKUP_LNG, - TestData.DEFAULT_DROPOFF_ADDRESS, TestData.DEFAULT_DROPOFF_LAT, TestData.DEFAULT_DROPOFF_LNG, - 15.50, 5.2, 15, null); + OrderRequestDTO request = OrderRequestDTO.builder() + .userId(1L).driverId(NON_EXISTENT_ID).vehicleType(VehicleType.TAXI) + .pickupAddress(TestData.DEFAULT_PICKUP_ADDRESS).pickupLatitude(TestData.DEFAULT_PICKUP_LAT).pickupLongitude(TestData.DEFAULT_PICKUP_LNG) + .dropoffAddress(TestData.DEFAULT_DROPOFF_ADDRESS).dropoffLatitude(TestData.DEFAULT_DROPOFF_LAT).dropoffLongitude(TestData.DEFAULT_DROPOFF_LNG) + .estimatedPrice(15.50).distanceKm(5.2).estimatedDurationMinutes(15) + .build(); UserEntity user = UserEntity.builder().id(1L).build(); when(userRepository.findById(1L)).thenReturn(Optional.of(user)); @@ -131,10 +135,12 @@ void createOrder_driverNotFound_returnsEmpty() { @Test void createOrder_vehicleNotFound_returnsEmpty() { - OrderRequestDTO request = new OrderRequestDTO(1L, null, NON_EXISTENT_ID, VehicleType.TAXI, - TestData.DEFAULT_PICKUP_ADDRESS, TestData.DEFAULT_PICKUP_LAT, TestData.DEFAULT_PICKUP_LNG, - TestData.DEFAULT_DROPOFF_ADDRESS, TestData.DEFAULT_DROPOFF_LAT, TestData.DEFAULT_DROPOFF_LNG, - 15.50, 5.2, 15, null); + OrderRequestDTO request = OrderRequestDTO.builder() + .userId(1L).vehicleId(NON_EXISTENT_ID).vehicleType(VehicleType.TAXI) + .pickupAddress(TestData.DEFAULT_PICKUP_ADDRESS).pickupLatitude(TestData.DEFAULT_PICKUP_LAT).pickupLongitude(TestData.DEFAULT_PICKUP_LNG) + .dropoffAddress(TestData.DEFAULT_DROPOFF_ADDRESS).dropoffLatitude(TestData.DEFAULT_DROPOFF_LAT).dropoffLongitude(TestData.DEFAULT_DROPOFF_LNG) + .estimatedPrice(15.50).distanceKm(5.2).estimatedDurationMinutes(15) + .build(); UserEntity user = UserEntity.builder().id(1L).build(); when(userRepository.findById(1L)).thenReturn(Optional.of(user)); @@ -210,4 +216,110 @@ void getAllOrders_emptyList() { assertTrue(result.isEmpty()); } + + @Test + void updateOrder_returnUpdatedResponse() { + OrderRequestDTO request = TestData.createOrderRequest(1L); + UserEntity user = UserEntity.builder().id(1L).build(); + OrderEntity existing = OrderEntity.builder().id(1L).user(user).vehicleType(VehicleType.TAXI).status(OrderStatus.CREATED).createdAt(NOW).build(); + OrderEntity saved = OrderEntity.builder().id(1L).user(user).vehicleType(VehicleType.TAXI).status(OrderStatus.CREATED).createdAt(NOW).build(); + OrderResponseDTO response = TestData.createOrderResponse(1L, 1L, OrderStatus.CREATED, NOW); + + when(orderRepository.findById(1L)).thenReturn(Optional.of(existing)); + when(userRepository.findById(1L)).thenReturn(Optional.of(user)); + when(orderRepository.save(existing)).thenReturn(saved); + when(orderMapper.toDTO(saved)).thenReturn(response); + + var result = orderService.updateOrder(1L, request); + + assertTrue(result.isPresent()); + assertEquals(1L, result.get().id()); + verify(orderMapper).updateEntity(request, existing); + verify(orderRepository).save(existing); + } + + @Test + void updateOrder_notFound_returnsEmpty() { + OrderRequestDTO request = TestData.createOrderRequest(1L); + + when(orderRepository.findById(NON_EXISTENT_ID)).thenReturn(Optional.empty()); + + var result = orderService.updateOrder(NON_EXISTENT_ID, request); + + assertTrue(result.isEmpty()); + verify(orderRepository, never()).save(any()); + } + + @Test + void updateOrder_userNotFound_returnsEmpty() { + OrderRequestDTO request = TestData.createOrderRequest(NON_EXISTENT_ID); + UserEntity user = UserEntity.builder().id(1L).build(); + OrderEntity existing = OrderEntity.builder().id(1L).user(user).build(); + + when(orderRepository.findById(1L)).thenReturn(Optional.of(existing)); + when(userRepository.findById(NON_EXISTENT_ID)).thenReturn(Optional.empty()); + + var result = orderService.updateOrder(1L, request); + + assertTrue(result.isEmpty()); + } + + @Test + void updateOrder_driverNotFound_returnsEmpty() { + OrderRequestDTO request = OrderRequestDTO.builder() + .userId(1L).driverId(NON_EXISTENT_ID).vehicleType(VehicleType.TAXI) + .pickupAddress(TestData.DEFAULT_PICKUP_ADDRESS).pickupLatitude(TestData.DEFAULT_PICKUP_LAT).pickupLongitude(TestData.DEFAULT_PICKUP_LNG) + .dropoffAddress(TestData.DEFAULT_DROPOFF_ADDRESS).dropoffLatitude(TestData.DEFAULT_DROPOFF_LAT).dropoffLongitude(TestData.DEFAULT_DROPOFF_LNG) + .estimatedPrice(15.50).distanceKm(5.2).estimatedDurationMinutes(15) + .build(); + UserEntity user = UserEntity.builder().id(1L).build(); + OrderEntity existing = OrderEntity.builder().id(1L).user(user).build(); + + when(orderRepository.findById(1L)).thenReturn(Optional.of(existing)); + when(userRepository.findById(1L)).thenReturn(Optional.of(user)); + when(driverRepository.findById(NON_EXISTENT_ID)).thenReturn(Optional.empty()); + + var result = orderService.updateOrder(1L, request); + + assertTrue(result.isEmpty()); + } + + @Test + void updateOrder_vehicleNotFound_returnsEmpty() { + OrderRequestDTO request = OrderRequestDTO.builder() + .userId(1L).vehicleId(NON_EXISTENT_ID).vehicleType(VehicleType.TAXI) + .pickupAddress(TestData.DEFAULT_PICKUP_ADDRESS).pickupLatitude(TestData.DEFAULT_PICKUP_LAT).pickupLongitude(TestData.DEFAULT_PICKUP_LNG) + .dropoffAddress(TestData.DEFAULT_DROPOFF_ADDRESS).dropoffLatitude(TestData.DEFAULT_DROPOFF_LAT).dropoffLongitude(TestData.DEFAULT_DROPOFF_LNG) + .estimatedPrice(15.50).distanceKm(5.2).estimatedDurationMinutes(15) + .build(); + UserEntity user = UserEntity.builder().id(1L).build(); + OrderEntity existing = OrderEntity.builder().id(1L).user(user).build(); + + when(orderRepository.findById(1L)).thenReturn(Optional.of(existing)); + when(userRepository.findById(1L)).thenReturn(Optional.of(user)); + when(vehicleRepository.findById(NON_EXISTENT_ID)).thenReturn(Optional.empty()); + + var result = orderService.updateOrder(1L, request); + + assertTrue(result.isEmpty()); + } + + @Test + void deleteOrder_returnTrue() { + when(orderRepository.existsById(1L)).thenReturn(true); + + var result = orderService.deleteOrder(1L); + + assertTrue(result); + verify(orderRepository).deleteById(1L); + } + + @Test + void deleteOrder_notFound_returnFalse() { + when(orderRepository.existsById(NON_EXISTENT_ID)).thenReturn(false); + + var result = orderService.deleteOrder(NON_EXISTENT_ID); + + assertFalse(result); + } } diff --git a/backend/src/test/java/com/angel/autonow/payment/PaymentControllerIT.java b/backend/src/test/java/com/angel/autonow/payment/PaymentControllerIT.java index 1edc906..ce0cbbd 100644 --- a/backend/src/test/java/com/angel/autonow/payment/PaymentControllerIT.java +++ b/backend/src/test/java/com/angel/autonow/payment/PaymentControllerIT.java @@ -19,6 +19,8 @@ import static com.angel.autonow.data.TestData.NON_EXISTENT_ID; 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.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @Transactional @@ -84,7 +86,7 @@ void createPayment_orderNotFound_returnsBadRequest() throws Exception { @Test void createPayment_invalidInput_returnsBadRequest() throws Exception { - var invalidRequest = new PaymentRequestDTO(null, null, null, null, null); + var invalidRequest = PaymentRequestDTO.builder().build(); mockMvc.perform(post("/api/payments") .with(TestData.customerJwt()) @@ -169,4 +171,102 @@ void getAllPayments_withoutAuth_returnsUnauthorized() throws Exception { mockMvc.perform(get("/api/payments")) .andExpect(status().isUnauthorized()); } + + @Test + void updatePayment_asCustomer() throws Exception { + PaymentEntity payment = TestData.createPaymentEntity(order, 16.00, PaymentMethod.CREDIT_CARD, PaymentStatus.PENDING); + paymentRepository.save(payment); + + var updateRequest = PaymentRequestDTO.builder() + .orderId(order.getId()) + .amount(25.00) + .paymentMethod(PaymentMethod.DEBIT_CARD) + .transactionId("TXN-UPD-001") + .currency("EUR") + .build(); + + mockMvc.perform(put("/api/payments/{id}", payment.getId()) + .with(TestData.customerJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.amount").value(25.00)) + .andExpect(jsonPath("$.paymentMethod").value("DEBIT_CARD")); + } + + @Test + void updatePayment_notFound_returnsBadRequest() throws Exception { + var updateRequest = TestData.createPaymentRequest(order.getId()); + + mockMvc.perform(put("/api/payments/{id}", NON_EXISTENT_ID) + .with(TestData.customerJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isBadRequest()); + } + + @Test + void updatePayment_invalidInput_returnsBadRequest() throws Exception { + PaymentEntity payment = TestData.createPaymentEntity(order, 16.00, PaymentMethod.CREDIT_CARD, PaymentStatus.PENDING); + paymentRepository.save(payment); + + var invalidRequest = PaymentRequestDTO.builder().build(); + + mockMvc.perform(put("/api/payments/{id}", payment.getId()) + .with(TestData.customerJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidRequest))) + .andExpect(status().isBadRequest()); + } + + @Test + void updatePayment_asDriver_returnsForbidden() throws Exception { + var updateRequest = TestData.createPaymentRequest(order.getId()); + + mockMvc.perform(put("/api/payments/{id}", 1L) + .with(TestData.driverJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isForbidden()); + } + + @Test + void updatePayment_withoutAuth_returnsUnauthorized() throws Exception { + var updateRequest = TestData.createPaymentRequest(order.getId()); + + mockMvc.perform(put("/api/payments/{id}", 1L) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isUnauthorized()); + } + + @Test + void deletePayment_asAdmin() throws Exception { + PaymentEntity payment = TestData.createPaymentEntity(order, 16.00, PaymentMethod.CASH, PaymentStatus.PENDING); + paymentRepository.save(payment); + + mockMvc.perform(delete("/api/payments/{id}", payment.getId()) + .with(TestData.adminJwt())) + .andExpect(status().isNoContent()); + } + + @Test + void deletePayment_notFound_returnsBadRequest() throws Exception { + mockMvc.perform(delete("/api/payments/{id}", NON_EXISTENT_ID) + .with(TestData.adminJwt())) + .andExpect(status().isBadRequest()); + } + + @Test + void deletePayment_asCustomer_returnsForbidden() throws Exception { + mockMvc.perform(delete("/api/payments/{id}", 1L) + .with(TestData.customerJwt())) + .andExpect(status().isForbidden()); + } + + @Test + void deletePayment_withoutAuth_returnsUnauthorized() throws Exception { + mockMvc.perform(delete("/api/payments/{id}", 1L)) + .andExpect(status().isUnauthorized()); + } } diff --git a/backend/src/test/java/com/angel/autonow/payment/PaymentServiceTest.java b/backend/src/test/java/com/angel/autonow/payment/PaymentServiceTest.java index f2930e6..b354657 100644 --- a/backend/src/test/java/com/angel/autonow/payment/PaymentServiceTest.java +++ b/backend/src/test/java/com/angel/autonow/payment/PaymentServiceTest.java @@ -61,7 +61,12 @@ void createPayment_returnPaymentResponse() { @Test void createPayment_orderNotFound_returnsEmpty() { - PaymentRequestDTO request = new PaymentRequestDTO(NON_EXISTENT_ID, 16.00, PaymentMethod.CASH, null, "EUR"); + PaymentRequestDTO request = PaymentRequestDTO.builder() + .orderId(NON_EXISTENT_ID) + .amount(16.00) + .paymentMethod(PaymentMethod.CASH) + .currency("EUR") + .build(); when(orderRepository.findById(NON_EXISTENT_ID)).thenReturn(Optional.empty()); @@ -160,4 +165,68 @@ void getAllPayments_emptyList() { assertTrue(result.isEmpty()); } + + @Test + void updatePayment_returnUpdatedResponse() { + PaymentRequestDTO request = TestData.createPaymentRequest(1L); + OrderEntity order = OrderEntity.builder().id(1L).build(); + PaymentEntity existing = PaymentEntity.builder().id(1L).order(order).amount(16.00).createdAt(NOW).build(); + PaymentEntity saved = PaymentEntity.builder().id(1L).order(order).amount(16.00).createdAt(NOW).build(); + PaymentResponseDTO response = TestData.createPaymentResponse(1L, 1L, 16.00, PaymentMethod.CREDIT_CARD, PaymentStatus.PENDING, NOW); + + when(paymentRepository.findById(1L)).thenReturn(Optional.of(existing)); + when(paymentRepository.save(existing)).thenReturn(saved); + when(paymentMapper.toDTO(saved)).thenReturn(response); + + var result = paymentService.updatePayment(1L, request); + + assertTrue(result.isPresent()); + assertEquals(1L, result.get().id()); + verify(paymentMapper).updateEntity(request, existing); + verify(paymentRepository).save(existing); + } + + @Test + void updatePayment_notFound_returnsEmpty() { + PaymentRequestDTO request = TestData.createPaymentRequest(1L); + + when(paymentRepository.findById(NON_EXISTENT_ID)).thenReturn(Optional.empty()); + + var result = paymentService.updatePayment(NON_EXISTENT_ID, request); + + assertTrue(result.isEmpty()); + } + + @Test + void updatePayment_orderNotFound_returnsEmpty() { + PaymentRequestDTO request = TestData.createPaymentRequest(2L); + OrderEntity order = OrderEntity.builder().id(1L).build(); + PaymentEntity existing = PaymentEntity.builder().id(1L).order(order).amount(16.00).createdAt(NOW).build(); + + when(paymentRepository.findById(1L)).thenReturn(Optional.of(existing)); + when(orderRepository.findById(2L)).thenReturn(Optional.empty()); + + var result = paymentService.updatePayment(1L, request); + + assertTrue(result.isEmpty()); + } + + @Test + void deletePayment_returnTrue() { + when(paymentRepository.existsById(1L)).thenReturn(true); + + var result = paymentService.deletePayment(1L); + + assertTrue(result); + verify(paymentRepository).deleteById(1L); + } + + @Test + void deletePayment_notFound_returnFalse() { + when(paymentRepository.existsById(NON_EXISTENT_ID)).thenReturn(false); + + var result = paymentService.deletePayment(NON_EXISTENT_ID); + + assertFalse(result); + } } diff --git a/backend/src/test/java/com/angel/autonow/rating/RatingControllerIT.java b/backend/src/test/java/com/angel/autonow/rating/RatingControllerIT.java index 5a6f328..f77b85d 100644 --- a/backend/src/test/java/com/angel/autonow/rating/RatingControllerIT.java +++ b/backend/src/test/java/com/angel/autonow/rating/RatingControllerIT.java @@ -19,6 +19,8 @@ import static com.angel.autonow.data.TestData.NON_EXISTENT_ID; 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.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @Transactional @@ -105,7 +107,7 @@ void createRating_asGuest_returnsForbidden() throws Exception { @Test void createRating_invalidInput_returnsBadRequest() throws Exception { - var invalidRequest = new RatingRequestDTO(null, null, null); + var invalidRequest = RatingRequestDTO.builder().build(); mockMvc.perform(post("/api/ratings") .with(TestData.customerJwt()) @@ -222,4 +224,110 @@ void getAllRatings_withoutAuth_returnsUnauthorized() throws Exception { mockMvc.perform(get("/api/ratings")) .andExpect(status().isUnauthorized()); } + + @Test + void updateRating_asCustomer() throws Exception { + var rating = TestData.createRatingEntity(order, 3, "OK"); + ratingRepository.save(rating); + + var updateRequest = TestData.createRatingRequest(order.getId(), 5, "Actually great!"); + + mockMvc.perform(put("/api/ratings/{id}", rating.getId()) + .with(TestData.customerJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.rating").value(5)) + .andExpect(jsonPath("$.comment").value("Actually great!")); + } + + @Test + void updateRating_asAdmin() throws Exception { + var rating = TestData.createRatingEntity(order, 3, "OK"); + ratingRepository.save(rating); + + var updateRequest = TestData.createRatingRequest(order.getId(), 4, "Updated by admin"); + + mockMvc.perform(put("/api/ratings/{id}", rating.getId()) + .with(TestData.adminJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.rating").value(4)); + } + + @Test + void updateRating_notFound_returnsBadRequest() throws Exception { + var updateRequest = TestData.createRatingRequest(order.getId()); + + mockMvc.perform(put("/api/ratings/{id}", NON_EXISTENT_ID) + .with(TestData.customerJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isBadRequest()); + } + + @Test + void updateRating_invalidInput_returnsBadRequest() throws Exception { + var rating = TestData.createRatingEntity(order, 3, "OK"); + ratingRepository.save(rating); + + var invalidRequest = RatingRequestDTO.builder().build(); + + mockMvc.perform(put("/api/ratings/{id}", rating.getId()) + .with(TestData.customerJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidRequest))) + .andExpect(status().isBadRequest()); + } + + @Test + void updateRating_asDriver_returnsForbidden() throws Exception { + var updateRequest = TestData.createRatingRequest(order.getId()); + + mockMvc.perform(put("/api/ratings/{id}", 1L) + .with(TestData.driverJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isForbidden()); + } + + @Test + void updateRating_withoutAuth_returnsUnauthorized() throws Exception { + var updateRequest = TestData.createRatingRequest(order.getId()); + + mockMvc.perform(put("/api/ratings/{id}", 1L) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isUnauthorized()); + } + + @Test + void deleteRating_asCustomer() throws Exception { + var rating = TestData.createRatingEntity(order, 5, "Great"); + ratingRepository.save(rating); + mockMvc.perform(delete("/api/ratings/{id}", rating.getId()).with(TestData.customerJwt())).andExpect(status().isNoContent()); + } + + @Test + void deleteRating_asAdmin() throws Exception { + var rating = TestData.createRatingEntity(order, 5, "Great"); + ratingRepository.save(rating); + mockMvc.perform(delete("/api/ratings/{id}", rating.getId()).with(TestData.adminJwt())).andExpect(status().isNoContent()); + } + + @Test + void deleteRating_notFound_returnsBadRequest() throws Exception { + mockMvc.perform(delete("/api/ratings/{id}", NON_EXISTENT_ID).with(TestData.customerJwt())).andExpect(status().isBadRequest()); + } + + @Test + void deleteRating_asDriver_returnsForbidden() throws Exception { + mockMvc.perform(delete("/api/ratings/{id}", 1L).with(TestData.driverJwt())).andExpect(status().isForbidden()); + } + + @Test + void deleteRating_withoutAuth_returnsUnauthorized() throws Exception { + mockMvc.perform(delete("/api/ratings/{id}", 1L)).andExpect(status().isUnauthorized()); + } } diff --git a/backend/src/test/java/com/angel/autonow/rating/RatingServiceTest.java b/backend/src/test/java/com/angel/autonow/rating/RatingServiceTest.java index 7c353ac..ee5dd11 100644 --- a/backend/src/test/java/com/angel/autonow/rating/RatingServiceTest.java +++ b/backend/src/test/java/com/angel/autonow/rating/RatingServiceTest.java @@ -142,4 +142,72 @@ void getAllRatings_emptyList() { assertTrue(result.isEmpty()); } + + @Test + void updateRating_returnUpdatedResponse() { + RatingRequestDTO request = TestData.createRatingRequest(1L, 4, "Updated comment"); + OrderEntity order = OrderEntity.builder().id(1L).build(); + RatingEntity existing = RatingEntity.builder().id(1L).order(order).rating(3).comment("OK").createdAt(NOW).build(); + RatingEntity saved = RatingEntity.builder().id(1L).order(order).rating(4).comment("Updated comment").createdAt(NOW).build(); + RatingResponseDTO response = TestData.createRatingResponse(1L, 1L, 4, "Updated comment", NOW); + + when(ratingRepository.findById(1L)).thenReturn(Optional.of(existing)); + when(ratingRepository.save(existing)).thenReturn(saved); + when(ratingMapper.toDTO(saved)).thenReturn(response); + + var result = ratingService.updateRating(1L, request); + + assertTrue(result.isPresent()); + assertEquals(4, result.get().rating()); + assertEquals("Updated comment", result.get().comment()); + verify(ratingMapper).updateEntity(request, existing); + verify(ratingRepository).save(existing); + } + + @Test + void updateRating_notFound_returnsEmpty() { + RatingRequestDTO request = TestData.createRatingRequest(1L); + + when(ratingRepository.findById(NON_EXISTENT_ID)).thenReturn(Optional.empty()); + + var result = ratingService.updateRating(NON_EXISTENT_ID, request); + + assertTrue(result.isEmpty()); + verify(ratingRepository, never()).save(any()); + } + + @Test + void updateRating_orderNotFound_returnsEmpty() { + RatingRequestDTO request = TestData.createRatingRequest(2L); + OrderEntity order = OrderEntity.builder().id(1L).build(); + RatingEntity existing = RatingEntity.builder().id(1L).order(order).rating(3).createdAt(NOW).build(); + + when(ratingRepository.findById(1L)).thenReturn(Optional.of(existing)); + when(orderRepository.findById(2L)).thenReturn(Optional.empty()); + + var result = ratingService.updateRating(1L, request); + + assertTrue(result.isEmpty()); + verify(ratingRepository, never()).save(any()); + } + + @Test + void deleteRating_returnTrue() { + when(ratingRepository.existsById(1L)).thenReturn(true); + + var result = ratingService.deleteRating(1L); + + assertTrue(result); + verify(ratingRepository).deleteById(1L); + } + + @Test + void deleteRating_notFound_returnFalse() { + when(ratingRepository.existsById(NON_EXISTENT_ID)).thenReturn(false); + + var result = ratingService.deleteRating(NON_EXISTENT_ID); + + assertFalse(result); + verify(ratingRepository, never()).deleteById(any()); + } } diff --git a/backend/src/test/java/com/angel/autonow/vehicle/VehicleControllerIT.java b/backend/src/test/java/com/angel/autonow/vehicle/VehicleControllerIT.java index b4529b1..0960020 100644 --- a/backend/src/test/java/com/angel/autonow/vehicle/VehicleControllerIT.java +++ b/backend/src/test/java/com/angel/autonow/vehicle/VehicleControllerIT.java @@ -14,6 +14,8 @@ import static com.angel.autonow.data.TestData.NON_EXISTENT_ID; 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.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @Transactional @@ -60,7 +62,7 @@ void createVehicle_asCustomer_returnsForbidden() throws Exception { @Test void createVehicle_invalidInput_returnsBadRequest() throws Exception { - var invalidRequest = new VehicleRequestDTO(null, null, null, false, null, null, null); + var invalidRequest = VehicleRequestDTO.builder().build(); mockMvc.perform(post("/api/vehicles") .with(TestData.adminJwt()) @@ -124,4 +126,96 @@ void getAllVehicles_withoutAuth_returnsUnauthorized() throws Exception { mockMvc.perform(get("/api/vehicles")) .andExpect(status().isUnauthorized()); } + + @Test + void updateVehicle_asAdmin() throws Exception { + var vehicle = TestData.createVehicleEntity(); + vehicleRepository.save(vehicle); + + var updateRequest = VehicleRequestDTO.builder() + .brand("Honda") + .model("Civic") + .airConditioning(false) + .numberOfSeats(4) + .trunkCapacity(380.0) + .vehicleType(VehicleType.TAXI) + .build(); + + mockMvc.perform(put("/api/vehicles/{id}", vehicle.getId()) + .with(TestData.adminJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.brand").value("Honda")) + .andExpect(jsonPath("$.model").value("Civic")) + .andExpect(jsonPath("$.numberOfSeats").value(4)); + } + + @Test + void updateVehicle_notFound_returnsBadRequest() throws Exception { + var updateRequest = TestData.createVehicleRequest(); + + mockMvc.perform(put("/api/vehicles/{id}", NON_EXISTENT_ID) + .with(TestData.adminJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isBadRequest()); + } + + @Test + void updateVehicle_invalidInput_returnsBadRequest() throws Exception { + var vehicle = TestData.createVehicleEntity(); + vehicleRepository.save(vehicle); + + var invalidRequest = VehicleRequestDTO.builder().build(); + + mockMvc.perform(put("/api/vehicles/{id}", vehicle.getId()) + .with(TestData.adminJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidRequest))) + .andExpect(status().isBadRequest()); + } + + @Test + void updateVehicle_asCustomer_returnsForbidden() throws Exception { + var updateRequest = TestData.createVehicleRequest(); + + mockMvc.perform(put("/api/vehicles/{id}", 1L) + .with(TestData.customerJwt()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isForbidden()); + } + + @Test + void updateVehicle_withoutAuth_returnsUnauthorized() throws Exception { + var updateRequest = TestData.createVehicleRequest(); + + mockMvc.perform(put("/api/vehicles/{id}", 1L) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequest))) + .andExpect(status().isUnauthorized()); + } + + @Test + void deleteVehicle_asAdmin() throws Exception { + var vehicle = TestData.createVehicleEntity(); + vehicleRepository.save(vehicle); + mockMvc.perform(delete("/api/vehicles/{id}", vehicle.getId()).with(TestData.adminJwt())).andExpect(status().isNoContent()); + } + + @Test + void deleteVehicle_notFound_returnsBadRequest() throws Exception { + mockMvc.perform(delete("/api/vehicles/{id}", NON_EXISTENT_ID).with(TestData.adminJwt())).andExpect(status().isBadRequest()); + } + + @Test + void deleteVehicle_asCustomer_returnsForbidden() throws Exception { + mockMvc.perform(delete("/api/vehicles/{id}", 1L).with(TestData.customerJwt())).andExpect(status().isForbidden()); + } + + @Test + void deleteVehicle_withoutAuth_returnsUnauthorized() throws Exception { + mockMvc.perform(delete("/api/vehicles/{id}", 1L)).andExpect(status().isUnauthorized()); + } } diff --git a/backend/src/test/java/com/angel/autonow/vehicle/VehicleServiceTest.java b/backend/src/test/java/com/angel/autonow/vehicle/VehicleServiceTest.java index 52c2eb0..91fc845 100644 --- a/backend/src/test/java/com/angel/autonow/vehicle/VehicleServiceTest.java +++ b/backend/src/test/java/com/angel/autonow/vehicle/VehicleServiceTest.java @@ -94,4 +94,55 @@ void getAllVehicles_emptyList() { assertTrue(result.isEmpty()); } + + @Test + void updateVehicle_returnUpdatedResponse() { + VehicleRequestDTO request = TestData.createVehicleRequest(); + VehicleEntity existing = VehicleEntity.builder().id(1L).brand("Toyota").build(); + VehicleEntity saved = VehicleEntity.builder().id(1L).brand("Toyota").build(); + VehicleResponseDTO response = TestData.createVehicleResponse(1L); + + when(vehicleRepository.findById(1L)).thenReturn(Optional.of(existing)); + when(vehicleRepository.save(existing)).thenReturn(saved); + when(vehicleMapper.toDTO(saved)).thenReturn(response); + + var result = vehicleService.updateVehicle(1L, request); + + assertTrue(result.isPresent()); + assertEquals(1L, result.get().id()); + verify(vehicleMapper).updateEntity(request, existing); + verify(vehicleRepository).save(existing); + } + + @Test + void updateVehicle_notFound_returnsEmpty() { + VehicleRequestDTO request = TestData.createVehicleRequest(); + + when(vehicleRepository.findById(NON_EXISTENT_ID)).thenReturn(Optional.empty()); + + var result = vehicleService.updateVehicle(NON_EXISTENT_ID, request); + + assertTrue(result.isEmpty()); + verify(vehicleRepository, never()).save(any()); + } + + @Test + void deleteVehicle_returnTrue() { + when(vehicleRepository.existsById(1L)).thenReturn(true); + + var result = vehicleService.deleteVehicle(1L); + + assertTrue(result); + verify(vehicleRepository).deleteById(1L); + } + + @Test + void deleteVehicle_notFound_returnFalse() { + when(vehicleRepository.existsById(NON_EXISTENT_ID)).thenReturn(false); + + var result = vehicleService.deleteVehicle(NON_EXISTENT_ID); + + assertFalse(result); + verify(vehicleRepository, never()).deleteById(any()); + } }