From 2077d74ba22ba2cf93853935d340bf5e391b0b14 Mon Sep 17 00:00:00 2001 From: FranInfanti Date: Tue, 2 Jun 2026 14:57:29 -0300 Subject: [PATCH 1/5] feat: replaces the Mercado Pago API with a simulation, so that stress tests can be performed --- Dockerfile.test | 30 +++++++++ docker-compose.test.yml | 44 +++++++++++++ pom.xml | 12 ++++ .../orders/config/MercadoPagoConfig.java | 2 + .../bazaar/orders/config/RabbitMQConfig.java | 2 + .../orders/service/MercadoPagoService.java | 1 + .../orders/config/MercadoPagoConfigTests.java | 65 +++++++++++++++++++ 7 files changed, 156 insertions(+) create mode 100644 Dockerfile.test create mode 100644 docker-compose.test.yml create mode 100644 src/test/java/com/bazaar/orders/config/MercadoPagoConfigTests.java diff --git a/Dockerfile.test b/Dockerfile.test new file mode 100644 index 0000000..e1c8f4d --- /dev/null +++ b/Dockerfile.test @@ -0,0 +1,30 @@ +FROM maven:3.9.6-eclipse-temurin-21 AS build + +WORKDIR /app + +COPY pom.xml . +COPY src ./src + +RUN mvn clean test-compile package -DskipTests + +RUN mvn dependency:copy-dependencies -DoutputDirectory=target/dependency + +FROM eclipse-temurin:21-jdk-alpine + +RUN apk add --no-cache curl + +RUN mkdir -p /otel && \ + curl -L -o /otel/opentelemetry-javaagent.jar \ + https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar + +WORKDIR /app + +COPY --from=build /app/target/classes ./classes +COPY --from=build /app/target/test-classes ./test-classes +COPY --from=build /app/target/dependency ./dependency + +EXPOSE 8080 + +ENV JAVA_TOOL_OPTIONS="-javaagent:/otel/opentelemetry-javaagent.jar" + +ENTRYPOINT ["java", "-cp", "classes:test-classes:dependency/*", "com.bazaar.orders.OrdersApplication", "--spring.profiles.active=stress"] \ No newline at end of file diff --git a/docker-compose.test.yml b/docker-compose.test.yml new file mode 100644 index 0000000..4b4ddf9 --- /dev/null +++ b/docker-compose.test.yml @@ -0,0 +1,44 @@ +services: + postgres-orders: + image: postgres:15 + container_name: postgres-orders + env_file: .env + environment: + POSTGRES_DB: ${DATABASE_NAME} + POSTGRES_USER: ${DATABASE_USER} + POSTGRES_PASSWORD: ${DATABASE_PASSWORD} + ports: + - "${EXPOSE_PORT}:${DATABASE_PORT}" + volumes: + - postgres_orders_data_test:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -p ${DATABASE_PORT} -U ${DATABASE_USER} -d ${DATABASE_NAME}"] + interval: 5s + timeout: 5s + retries: 5 + + orders-service: + build: + context: . + dockerfile: Dockerfile.test + image: bazaar-orders-test + container_name: orders-service-test + depends_on: + postgres-orders: + condition: service_healthy + env_file: .env + ports: + - "${PORT}:${PORT}" + healthcheck: + test: [ "CMD-SHELL", "curl -f http://localhost:${PORT}/actuator/health || exit 1" ] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + postgres_orders_data_test: + +networks: + default: + external: true + name: bazaar-network diff --git a/pom.xml b/pom.xml index 3d961b1..ea29ecb 100644 --- a/pom.xml +++ b/pom.xml @@ -146,6 +146,18 @@ test + + org.mockito + mockito-core + test + + + + org.mockito + mockito-junit-jupiter + test + + diff --git a/src/main/java/com/bazaar/orders/config/MercadoPagoConfig.java b/src/main/java/com/bazaar/orders/config/MercadoPagoConfig.java index e59c3c9..b7165d0 100644 --- a/src/main/java/com/bazaar/orders/config/MercadoPagoConfig.java +++ b/src/main/java/com/bazaar/orders/config/MercadoPagoConfig.java @@ -6,8 +6,10 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; @Configuration +@Profile("!stress") public class MercadoPagoConfig { @Value("${mercadopago.access.token}") diff --git a/src/main/java/com/bazaar/orders/config/RabbitMQConfig.java b/src/main/java/com/bazaar/orders/config/RabbitMQConfig.java index f76f1e6..c2a82c4 100644 --- a/src/main/java/com/bazaar/orders/config/RabbitMQConfig.java +++ b/src/main/java/com/bazaar/orders/config/RabbitMQConfig.java @@ -6,11 +6,13 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; /** * RabbitMQ Configuration constants and Bean definitions for the Order processing system. */ @Configuration +@Profile("!stress") public class RabbitMQConfig { /** diff --git a/src/main/java/com/bazaar/orders/service/MercadoPagoService.java b/src/main/java/com/bazaar/orders/service/MercadoPagoService.java index caa101d..eb93bb4 100644 --- a/src/main/java/com/bazaar/orders/service/MercadoPagoService.java +++ b/src/main/java/com/bazaar/orders/service/MercadoPagoService.java @@ -12,6 +12,7 @@ import com.bazaar.orders.model.Order; import com.bazaar.orders.model.OrderItem; import com.bazaar.orders.model.OrderPayment; + import com.mercadopago.client.payment.PaymentClient; import com.mercadopago.client.preference.PreferenceBackUrlsRequest; import com.mercadopago.client.preference.PreferenceClient; diff --git a/src/test/java/com/bazaar/orders/config/MercadoPagoConfigTests.java b/src/test/java/com/bazaar/orders/config/MercadoPagoConfigTests.java new file mode 100644 index 0000000..072403e --- /dev/null +++ b/src/test/java/com/bazaar/orders/config/MercadoPagoConfigTests.java @@ -0,0 +1,65 @@ +package com.bazaar.orders.config; + +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +import com.mercadopago.client.payment.PaymentClient; +import com.mercadopago.client.preference.PreferenceClient; +import com.mercadopago.resources.payment.Payment; +import com.mercadopago.resources.preference.Preference; + +import jakarta.annotation.PostConstruct; + +@Configuration +@Profile("stress") +public class MercadoPagoConfigTests { + + @Value("${mercadopago.access.token}") + private String accessToken; + + @PostConstruct + public void configMercadoPago() { + com.mercadopago.MercadoPagoConfig.setAccessToken(accessToken); + } + + @Bean + public PaymentClient paymentClient() throws Exception { + PaymentClient client = Mockito.mock(PaymentClient.class); + + Mockito.when(client.get(Mockito.anyLong())).thenAnswer(invocation -> { + Thread.sleep(250); + + Long paymentId = invocation.getArgument(0); + + Payment payment = Mockito.mock(Payment.class); + + Mockito.when(payment.getExternalReference()).thenReturn("REQ-STRESS-" + paymentId); + + Mockito.when(payment.getStatus()).thenReturn("approved"); + + return payment; + }); + + return client; + } + + @Bean + public PreferenceClient preferenceClient() throws Exception { + PreferenceClient client = Mockito.mock(PreferenceClient.class); + + Mockito.when(client.create(Mockito.any())).thenAnswer(invocation -> { + Thread.sleep(250); + + Preference preference = Mockito.mock(Preference.class); + Mockito.when(preference.getInitPoint()).thenReturn("https://www.mercadopago.com.ar/sandbox/init/fake"); + + return preference; + }); + + return client; + } + +} From 0d4c68afb3f6d0290bcba3c808a9380d27894320 Mon Sep 17 00:00:00 2001 From: FranInfanti Date: Tue, 2 Jun 2026 15:40:58 -0300 Subject: [PATCH 2/5] docs: add instructions in README to run k6 tests --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a0c3184..c278c96 100644 --- a/README.md +++ b/README.md @@ -64,4 +64,22 @@ cp .env.example .env ```bash # The flag -v is optional docker-compose down -v -``` \ No newline at end of file +``` + +--- +## k6 Testing + +In order to run the k6 tests, it is necessary to start the service in stress mode: + +```bash +docker compose -f docker-compose.test.yml up --build +``` + +And to stop the service: + +```bash +docker compose -f docker-compose.test.yml down -v +``` + +> [!IMPORTANT] +> Please do not use production envs for k6 testing. If there is any issue, just ask. \ No newline at end of file From 21c4e9447b1fe4dcbfe94ec026ed87a26a8410bc Mon Sep 17 00:00:00 2001 From: FranInfanti Date: Tue, 2 Jun 2026 15:46:48 -0300 Subject: [PATCH 3/5] style: apply java-format --- .../com/bazaar/orders/config/CacheConfig.java | 18 +-- .../bazaar/orders/config/RabbitMQConfig.java | 2 - .../orders/controller/ReviewsController.java | 13 +- .../dto/reviews/OrderDetailedReviewDTO.java | 20 +-- .../dto/reviews/OrderItemReviewDTO.java | 6 +- .../orders/dto/reviews/OrderReviewDTO.java | 3 +- .../dto/reviews/SellerReputationDTO.java | 6 +- .../dto/user/UserProfileResponseDTO.java | 6 +- .../repository/OrderReviewRepository.java | 17 +-- .../bazaar/orders/service/ReviewsService.java | 44 +++--- .../controller/ReviewsControllerTest.java | 125 ++++++------------ .../orders/testConfig/AbstractTest.java | 40 +++--- 12 files changed, 118 insertions(+), 182 deletions(-) diff --git a/src/main/java/com/bazaar/orders/config/CacheConfig.java b/src/main/java/com/bazaar/orders/config/CacheConfig.java index 2cd9dde..6b34ec4 100644 --- a/src/main/java/com/bazaar/orders/config/CacheConfig.java +++ b/src/main/java/com/bazaar/orders/config/CacheConfig.java @@ -13,17 +13,13 @@ @EnableCaching public class CacheConfig { - @Bean - public CacheManager cacheManager() { - CaffeineCacheManager cacheManager = - new CaffeineCacheManager("sellerReputation"); + @Bean + public CacheManager cacheManager() { + CaffeineCacheManager cacheManager = new CaffeineCacheManager("sellerReputation"); - cacheManager.setCaffeine( - Caffeine.newBuilder() - .expireAfterWrite(Duration.ofMinutes(5)) - .maximumSize(1000) - ); + cacheManager.setCaffeine(Caffeine.newBuilder().expireAfterWrite(Duration.ofMinutes(5)).maximumSize(1000)); + + return cacheManager; + } - return cacheManager; - } } \ No newline at end of file diff --git a/src/main/java/com/bazaar/orders/config/RabbitMQConfig.java b/src/main/java/com/bazaar/orders/config/RabbitMQConfig.java index c2a82c4..f76f1e6 100644 --- a/src/main/java/com/bazaar/orders/config/RabbitMQConfig.java +++ b/src/main/java/com/bazaar/orders/config/RabbitMQConfig.java @@ -6,13 +6,11 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; /** * RabbitMQ Configuration constants and Bean definitions for the Order processing system. */ @Configuration -@Profile("!stress") public class RabbitMQConfig { /** diff --git a/src/main/java/com/bazaar/orders/controller/ReviewsController.java b/src/main/java/com/bazaar/orders/controller/ReviewsController.java index b9b97fd..9693724 100644 --- a/src/main/java/com/bazaar/orders/controller/ReviewsController.java +++ b/src/main/java/com/bazaar/orders/controller/ReviewsController.java @@ -100,18 +100,19 @@ public OrderReviewDTO getOrderReview(@RequestHeader("X-User-Id") Long userId, @P * @param orderId identifier of the reviewed order. * @return matching {@link OrderReviewDTO}. */ - @Operation(summary = "Get order review", description = "Retrieves the review and products review associated with an order.") + @Operation(summary = "Get order review", + description = "Retrieves the review and products review associated with an order.") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Order review retrieved successfully"), @ApiResponse(responseCode = "404", description = "Order review not found"), @ApiResponse(responseCode = "403", description = "Forbidden user") }) @GetMapping("/orders/{orderId}/detailed") - public OrderDetailedReviewDTO getDetailedOrderReview(@RequestHeader("X-User-Id") Long userId, @PathVariable Long orderId) { + public OrderDetailedReviewDTO getDetailedOrderReview(@RequestHeader("X-User-Id") Long userId, + @PathVariable Long orderId) { LOGGER.info("Received request to get full review for order {} from user {}", orderId, userId); return reviewsService.getDetailedOrderReview(orderId, userId); } - /** * Creates reviews for multiple products in an order. * @param orderId identifier of the associated order. @@ -176,7 +177,8 @@ public OrderItemReviewDTO getOrderItemReview(@PathVariable Long productId, @Path * @return list of {@link OrderItemReviewDTO}. */ @Operation(summary = "Get product reviews", description = "Retrieves all reviews associated with a product.") - @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Product reviews retrieved successfully") }) + @ApiResponses( + value = { @ApiResponse(responseCode = "200", description = "Product reviews retrieved successfully") }) @GetMapping("/products/{productId}") public List getProductReviews(@PathVariable Long productId) { LOGGER.info("Received request to get reviews for product {}", productId); @@ -204,7 +206,8 @@ public List getSellerReviews(@PathVariable Long sellerId) { * @return list of {@link OrderItemReviewDTO}. */ @Operation(summary = "Get seller reviews", description = "Retrieves all reviews associated with a seller.") - @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Seller reputation retrieved successfully") }) + @ApiResponses( + value = { @ApiResponse(responseCode = "200", description = "Seller reputation retrieved successfully") }) @GetMapping("/seller/{sellerId}/reputation") public SellerReputationDTO getSellerReputation(@PathVariable Long sellerId) { LOGGER.info("Received request to get reputation for seller {}", sellerId); diff --git a/src/main/java/com/bazaar/orders/dto/reviews/OrderDetailedReviewDTO.java b/src/main/java/com/bazaar/orders/dto/reviews/OrderDetailedReviewDTO.java index 1cdf6de..c220e7d 100644 --- a/src/main/java/com/bazaar/orders/dto/reviews/OrderDetailedReviewDTO.java +++ b/src/main/java/com/bazaar/orders/dto/reviews/OrderDetailedReviewDTO.java @@ -16,14 +16,16 @@ * @param name user name * @param surname user surname */ -public record OrderDetailedReviewDTO(Long id, Long sellerId, Integer rating, String comment, String name, String surname, List itemReviews) { +public record OrderDetailedReviewDTO(Long id, Long sellerId, Integer rating, String comment, String name, + String surname, List itemReviews) { - /** - * Converts an OrderReview entity into its detailed DTO representation. - * @return {@link OrderReview} containing the review data. - */ - public static OrderDetailedReviewDTO fromOrderReview(OrderReview orderReview, List itemReviews, UserProfileResponseDTO userProfile) { - return new OrderDetailedReviewDTO(orderReview.getId(), orderReview.getSellerId(), orderReview.getRating(), - orderReview.getComment(), userProfile.name(), userProfile.surname(), itemReviews); - } + /** + * Converts an OrderReview entity into its detailed DTO representation. + * @return {@link OrderReview} containing the review data. + */ + public static OrderDetailedReviewDTO fromOrderReview(OrderReview orderReview, List itemReviews, + UserProfileResponseDTO userProfile) { + return new OrderDetailedReviewDTO(orderReview.getId(), orderReview.getSellerId(), orderReview.getRating(), + orderReview.getComment(), userProfile.name(), userProfile.surname(), itemReviews); + } } diff --git a/src/main/java/com/bazaar/orders/dto/reviews/OrderItemReviewDTO.java b/src/main/java/com/bazaar/orders/dto/reviews/OrderItemReviewDTO.java index cedc269..e5c123e 100644 --- a/src/main/java/com/bazaar/orders/dto/reviews/OrderItemReviewDTO.java +++ b/src/main/java/com/bazaar/orders/dto/reviews/OrderItemReviewDTO.java @@ -15,13 +15,15 @@ * @param name user name * @param surname user surname */ -public record OrderItemReviewDTO(Long id, Long orderItemId, Long orderId, Long userId, Integer rating, String comment, String name, String surname) { +public record OrderItemReviewDTO(Long id, Long orderItemId, Long orderId, Long userId, Integer rating, String comment, + String name, String surname) { /** * Converts an OrderItemReview entity into its DTO representation. * @return {@link OrderItemReviewDTO} containing the review data. */ - public static OrderItemReviewDTO fromOrderItemReview(OrderItemReview orderItemReview, UserProfileResponseDTO userProfile) { + public static OrderItemReviewDTO fromOrderItemReview(OrderItemReview orderItemReview, + UserProfileResponseDTO userProfile) { return new OrderItemReviewDTO(orderItemReview.getId(), orderItemReview.getOrderItemId(), orderItemReview.getOrderId(), orderItemReview.getUserId(), orderItemReview.getRating(), orderItemReview.getComment(), userProfile.name(), userProfile.surname()); diff --git a/src/main/java/com/bazaar/orders/dto/reviews/OrderReviewDTO.java b/src/main/java/com/bazaar/orders/dto/reviews/OrderReviewDTO.java index 99bac27..577647c 100644 --- a/src/main/java/com/bazaar/orders/dto/reviews/OrderReviewDTO.java +++ b/src/main/java/com/bazaar/orders/dto/reviews/OrderReviewDTO.java @@ -15,7 +15,8 @@ * @param name user name * @param surname user surname */ -public record OrderReviewDTO(Long id, Long sellerId, Integer rating, String comment, String name, String surname, LocalDateTime createdAt) { +public record OrderReviewDTO(Long id, Long sellerId, Integer rating, String comment, String name, String surname, + LocalDateTime createdAt) { /** * Converts an OrderReview entity into its DTO representation. diff --git a/src/main/java/com/bazaar/orders/dto/reviews/SellerReputationDTO.java b/src/main/java/com/bazaar/orders/dto/reviews/SellerReputationDTO.java index 5e9050a..531f831 100644 --- a/src/main/java/com/bazaar/orders/dto/reviews/SellerReputationDTO.java +++ b/src/main/java/com/bazaar/orders/dto/reviews/SellerReputationDTO.java @@ -7,9 +7,5 @@ * @param averageRating the average rating received by the seller. * @param totalReviews the total number of reviews received by the seller. */ -public record SellerReputationDTO( - Long sellerId, - Double averageRating, - Long totalReviews -) { +public record SellerReputationDTO(Long sellerId, Double averageRating, Long totalReviews) { } diff --git a/src/main/java/com/bazaar/orders/dto/user/UserProfileResponseDTO.java b/src/main/java/com/bazaar/orders/dto/user/UserProfileResponseDTO.java index 9d3dae1..8a239f9 100644 --- a/src/main/java/com/bazaar/orders/dto/user/UserProfileResponseDTO.java +++ b/src/main/java/com/bazaar/orders/dto/user/UserProfileResponseDTO.java @@ -12,8 +12,6 @@ * @param description User's current description. * @param createdAt User's created account date. */ -public record UserProfileResponseDTO( - String name, String surname, String imageURL, String description, - LocalDateTime createdAt -) { +public record UserProfileResponseDTO(String name, String surname, String imageURL, String description, + LocalDateTime createdAt) { } diff --git a/src/main/java/com/bazaar/orders/repository/OrderReviewRepository.java b/src/main/java/com/bazaar/orders/repository/OrderReviewRepository.java index d7fb29c..1135b72 100644 --- a/src/main/java/com/bazaar/orders/repository/OrderReviewRepository.java +++ b/src/main/java/com/bazaar/orders/repository/OrderReviewRepository.java @@ -36,13 +36,14 @@ public interface OrderReviewRepository extends JpaRepository * @return an {@link SellerReputationDTO} */ @Query(""" - SELECT new com.bazaar.orders.dto.reviews.SellerReputationDTO( - :sellerId, - COALESCE(AVG(r.rating), 0), - COUNT(r) - ) - FROM OrderReview r - WHERE r.sellerId = :sellerId - """) + SELECT new com.bazaar.orders.dto.reviews.SellerReputationDTO( + :sellerId, + COALESCE(AVG(r.rating), 0), + COUNT(r) + ) + FROM OrderReview r + WHERE r.sellerId = :sellerId + """) SellerReputationDTO getSellerReputation(@Param("sellerId") Long sellerId); + } \ No newline at end of file diff --git a/src/main/java/com/bazaar/orders/service/ReviewsService.java b/src/main/java/com/bazaar/orders/service/ReviewsService.java index 02149e7..3292f40 100644 --- a/src/main/java/com/bazaar/orders/service/ReviewsService.java +++ b/src/main/java/com/bazaar/orders/service/ReviewsService.java @@ -121,7 +121,6 @@ public OrderReviewDTO getOrderReview(Long orderId, Long userId) { return OrderReviewDTO.fromOrderReview(getExistingOrderReview(orderId, userId), getUserProfile(userId)); } - /** * Retrieves the detailed review associated with an order and user. * @param orderId identifier of the reviewed order. @@ -135,14 +134,13 @@ public OrderDetailedReviewDTO getDetailedOrderReview(final Long orderId, final L OrderReview orderReview = getExistingOrderReview(orderId, userId); List orderItems = orderItemReviewRepository.findAllByOrderId(orderId); - List itemReviewDTOs = orderItems.stream() - .map(review -> { - var userProfile = getUserProfile(review.getUserId()); - return OrderItemReviewDTO.fromOrderItemReview(review, userProfile); - }) - .toList(); + List itemReviewDTOs = orderItems.stream().map(review -> { + var userProfile = getUserProfile(review.getUserId()); + return OrderItemReviewDTO.fromOrderItemReview(review, userProfile); + }).toList(); - return OrderDetailedReviewDTO.fromOrderReview(orderReview, itemReviewDTOs, getUserProfile(orderReview.getUserId())); + return OrderDetailedReviewDTO.fromOrderReview(orderReview, itemReviewDTOs, + getUserProfile(orderReview.getUserId())); } @@ -154,7 +152,7 @@ public OrderDetailedReviewDTO getDetailedOrderReview(final Long orderId, final L * @return list of created {@link OrderItemReviewDTO}. */ public List createOrderItemsReview(Long orderId, Long userId, - CreateOrderItemsReviewRequestDTO request) { + CreateOrderItemsReviewRequestDTO request) { checkUserStatus(userId); validateOrder(orderId); @@ -232,13 +230,10 @@ public List getProductReviews(Long productId) { List orderItemIds = orderItems.stream().map(OrderItem::getId).toList(); - return orderItemReviewRepository.findAllByOrderItemIdIn(orderItemIds) - .stream() - .map(review -> { - var userProfile = getUserProfile(review.getUserId()); - return OrderItemReviewDTO.fromOrderItemReview(review, userProfile); - }) - .toList(); + return orderItemReviewRepository.findAllByOrderItemIdIn(orderItemIds).stream().map(review -> { + var userProfile = getUserProfile(review.getUserId()); + return OrderItemReviewDTO.fromOrderItemReview(review, userProfile); + }).toList(); } /** @@ -258,11 +253,7 @@ public List getSellerReviews(final Long sellerId) { * @param sellerId identifier of the reviewed product. * @return list of {@link OrderReviewDTO}. */ - @Cacheable( - value = "sellerReputation", - key = "#sellerId", - sync = true - ) + @Cacheable(value = "sellerReputation", key = "#sellerId", sync = true) public SellerReputationDTO getSellerReputation(final Long sellerId) { logger.info("Calculating reputation for seller with id {}", sellerId); return orderReviewRepository.getSellerReputation(sellerId); @@ -319,13 +310,10 @@ private Long getOrderItemId(Long orderId, Long productId) { * @return list of saved {@link OrderItemReviewDTO}. */ private List saveOrderItemReviews(List reviews) { - return orderItemReviewRepository.saveAll(reviews) - .stream() - .map(review -> { - var userProfile = getUserProfile(review.getUserId()); - return OrderItemReviewDTO.fromOrderItemReview(review, userProfile); - }) - .toList(); + return orderItemReviewRepository.saveAll(reviews).stream().map(review -> { + var userProfile = getUserProfile(review.getUserId()); + return OrderItemReviewDTO.fromOrderItemReview(review, userProfile); + }).toList(); } /** diff --git a/src/test/java/com/bazaar/orders/controller/ReviewsControllerTest.java b/src/test/java/com/bazaar/orders/controller/ReviewsControllerTest.java index 25c225a..0e8e9b2 100644 --- a/src/test/java/com/bazaar/orders/controller/ReviewsControllerTest.java +++ b/src/test/java/com/bazaar/orders/controller/ReviewsControllerTest.java @@ -95,7 +95,7 @@ private String orderItemReviewJson(Long productId, int rating, String comment) { @Test void testCreateOrderReviewSuccess() { when(userClient.getUserProfile(2L)) - .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); + .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); mockArrivedOrder(9L); @@ -152,7 +152,7 @@ void testCreateOrderReviewInvalidRating() { @Test void testGetOrderReviewSuccess() { when(userClient.getUserProfile(1L)) - .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); + .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); mockArrivedOrder(2L); @@ -191,16 +191,14 @@ void testGetOrderReviewForbidden() { @Test void testGetDetailedOrderReviewSuccess() { when(userClient.getUserProfile(1L)) - .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); + .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); mockArrivedOrder(15L); mockOrderItem(15L, 2L, 4L); - postOrderReview(15L, 1L, - sellerReviewJson(5L, 5, "Excellent seller")); + postOrderReview(15L, 1L, sellerReviewJson(5L, 5, "Excellent seller")); - postOrderItemsReview(15L, 1L, - orderItemReviewJson(2L, 4, "Nice product")); + postOrderItemsReview(15L, 1L, orderItemReviewJson(2L, 4, "Nice product")); var response = getDetailedOrderReview(15L, 1L); @@ -208,32 +206,22 @@ void testGetDetailedOrderReviewSuccess() { String body = response.getBody(); - assertEquals(5, - ((Number) JsonPath.read(body, "$.data.rating")).intValue()); + assertEquals(5, ((Number) JsonPath.read(body, "$.data.rating")).intValue()); - assertEquals("Excellent seller", - JsonPath.read(body, "$.data.comment")); + assertEquals("Excellent seller", JsonPath.read(body, "$.data.comment")); - assertEquals(1, - ((Number) JsonPath.read(body, "$.data.itemReviews.length()")).intValue()); + assertEquals(1, ((Number) JsonPath.read(body, "$.data.itemReviews.length()")).intValue()); - assertEquals(4, - ((Number) JsonPath.read(body, "$.data.itemReviews[0].rating")).intValue()); + assertEquals(4, ((Number) JsonPath.read(body, "$.data.itemReviews[0].rating")).intValue()); - assertEquals("Nice product", - JsonPath.read(body, "$.data.itemReviews[0].comment")); + assertEquals("Nice product", JsonPath.read(body, "$.data.itemReviews[0].comment")); } @Test void testGetDetailedOrderReviewForbidden() { when(userClient.getUserStatus(1L)) - .thenReturn( - new ApiResponse<>( - new UserStatusResponseDTO( - 1L, - "MOCKED", - UserStatus.BLOCKED))); + .thenReturn(new ApiResponse<>(new UserStatusResponseDTO(1L, "MOCKED", UserStatus.BLOCKED))); var response = getDetailedOrderReview(1L, 1L); @@ -251,7 +239,7 @@ void testGetDetailedOrderReviewNotFound() { @Test void testUpsertOrderReviewSuccess() { when(userClient.getUserProfile(1L)) - .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); + .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); mockArrivedOrder(1L); @@ -273,7 +261,7 @@ void testUpsertOrderReviewSuccess() { @Test void testUpsertOrderReviewForbidden() { when(userClient.getUserProfile(1L)) - .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); + .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); mockArrivedOrder(1L); @@ -293,7 +281,7 @@ void testUpsertOrderReviewForbidden() { @Test void testCreateOrderItemsReviewSuccess() { when(userClient.getUserProfile(1L)) - .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); + .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); mockOrderItem(1L, 6L, 7L); @@ -340,7 +328,7 @@ void testCreateOrderItemsReviewInvalidOrderItem() { @Test void testGetOrderItemReviewSuccess() { when(userClient.getUserProfile(1L)) - .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); + .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); mockOrderItem(1L, 2L, 40L); @@ -375,7 +363,7 @@ void testGetOrderItemReviewForbidden() { @Test void testGetProductReviewsSuccess() { when(userClient.getUserProfile(1L)) - .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); + .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); var orderItem = mockOrderItem(1L, 2L, 8L); @@ -399,14 +387,11 @@ void testGetProductReviewsSuccess() { @Test void testGetSellerReviewsSuccess() { when(userClient.getUserProfile(1L)) - .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); + .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); mockArrivedOrder(25L); - postOrderReview( - 25L, - 1L, - sellerReviewJson(9L, 5, "Excellent seller")); + postOrderReview(25L, 1L, sellerReviewJson(9L, 5, "Excellent seller")); var response = getSellerReviews(9L, 1L); @@ -414,17 +399,13 @@ void testGetSellerReviewsSuccess() { String body = response.getBody(); - assertEquals(1, - ((Number) JsonPath.read(body, "$.data.length()")).intValue()); + assertEquals(1, ((Number) JsonPath.read(body, "$.data.length()")).intValue()); - assertEquals(5, - ((Number) JsonPath.read(body, "$.data[0].rating")).intValue()); + assertEquals(5, ((Number) JsonPath.read(body, "$.data[0].rating")).intValue()); - assertEquals("Excellent seller", - JsonPath.read(body, "$.data[0].comment")); + assertEquals("Excellent seller", JsonPath.read(body, "$.data[0].comment")); - assertEquals(9L, - ((Number) JsonPath.read(body, "$.data[0].sellerId")).longValue()); + assertEquals(9L, ((Number) JsonPath.read(body, "$.data[0].sellerId")).longValue()); } @Test @@ -436,32 +417,23 @@ void testGetSellerReviewsEmpty() { String body = response.getBody(); - assertEquals(0, - ((Number) JsonPath.read(body, "$.data.length()")).intValue()); + assertEquals(0, ((Number) JsonPath.read(body, "$.data.length()")).intValue()); } @Test void testGetSellerReviewsMultipleUsers() { when(userClient.getUserProfile(1L)) - .thenReturn(new ApiResponse<>( - new UserProfileResponseDTO("juan", "perez", null, null, null))); + .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); when(userClient.getUserProfile(2L)) - .thenReturn(new ApiResponse<>( - new UserProfileResponseDTO("maria", "gomez", null, null, null))); + .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("maria", "gomez", null, null, null))); mockArrivedOrder(100L); mockArrivedOrder(200L); - postOrderReview( - 100L, - 1L, - sellerReviewJson(8L, 5, "Excellent seller")); + postOrderReview(100L, 1L, sellerReviewJson(8L, 5, "Excellent seller")); - postOrderReview( - 200L, - 2L, - sellerReviewJson(8L, 4, "Very good seller")); + postOrderReview(200L, 2L, sellerReviewJson(8L, 4, "Very good seller")); var response = getSellerReviews(8L, 1L); @@ -469,8 +441,7 @@ void testGetSellerReviewsMultipleUsers() { String body = response.getBody(); - assertEquals(2, - ((Number) JsonPath.read(body, "$.data.length()")).intValue()); + assertEquals(2, ((Number) JsonPath.read(body, "$.data.length()")).intValue()); List ratings = JsonPath.read(body, "$.data[*].rating"); assertTrue(ratings.contains(5)); @@ -484,25 +455,17 @@ void testGetSellerReviewsMultipleUsers() { @Test void testGetSellerReputationSuccess() { when(userClient.getUserProfile(1L)) - .thenReturn(new ApiResponse<>( - new UserProfileResponseDTO("juan", "perez", null, null, null))); + .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("juan", "perez", null, null, null))); when(userClient.getUserProfile(2L)) - .thenReturn(new ApiResponse<>( - new UserProfileResponseDTO("maria", "gomez", null, null, null))); + .thenReturn(new ApiResponse<>(new UserProfileResponseDTO("maria", "gomez", null, null, null))); mockArrivedOrder(102L); mockArrivedOrder(202L); - postOrderReview( - 102L, - 1L, - sellerReviewJson(10L, 5, "Excellent seller")); + postOrderReview(102L, 1L, sellerReviewJson(10L, 5, "Excellent seller")); - postOrderReview( - 202L, - 2L, - sellerReviewJson(10L, 4, "Very good seller")); + postOrderReview(202L, 2L, sellerReviewJson(10L, 4, "Very good seller")); getSellerReputation(10L); getSellerReputation(10L); @@ -516,17 +479,11 @@ void testGetSellerReputationSuccess() { String body = response.getBody(); - assertEquals( - 10L, - ((Number) JsonPath.read(body, "$.data.sellerId")).longValue()); + assertEquals(10L, ((Number) JsonPath.read(body, "$.data.sellerId")).longValue()); - assertEquals( - 4.5, - ((Number) JsonPath.read(body, "$.data.averageRating")).doubleValue()); + assertEquals(4.5, ((Number) JsonPath.read(body, "$.data.averageRating")).doubleValue()); - assertEquals( - 2, - ((Number) JsonPath.read(body, "$.data.totalReviews")).intValue()); + assertEquals(2, ((Number) JsonPath.read(body, "$.data.totalReviews")).intValue()); } @Test @@ -538,17 +495,11 @@ void testGetSellerReputationWithoutReviews() { String body = response.getBody(); - assertEquals( - 999L, - ((Number) JsonPath.read(body, "$.data.sellerId")).longValue()); + assertEquals(999L, ((Number) JsonPath.read(body, "$.data.sellerId")).longValue()); - assertEquals( - 0.0, - ((Number) JsonPath.read(body, "$.data.averageRating")).doubleValue()); + assertEquals(0.0, ((Number) JsonPath.read(body, "$.data.averageRating")).doubleValue()); - assertEquals( - 0, - ((Number) JsonPath.read(body, "$.data.totalReviews")).intValue()); + assertEquals(0, ((Number) JsonPath.read(body, "$.data.totalReviews")).intValue()); } } \ No newline at end of file diff --git a/src/test/java/com/bazaar/orders/testConfig/AbstractTest.java b/src/test/java/com/bazaar/orders/testConfig/AbstractTest.java index a13af7f..17bfa01 100644 --- a/src/test/java/com/bazaar/orders/testConfig/AbstractTest.java +++ b/src/test/java/com/bazaar/orders/testConfig/AbstractTest.java @@ -368,15 +368,15 @@ protected ResponseEntity getOrderReview(Long orderId, Long userId) { protected ResponseEntity getDetailedOrderReview(Long orderId, Long userId) { var result = webTestClient.get() - .uri("/reviews/orders/%d/detailed".formatted(orderId)) - .header("X-User-Id", userId.toString()) - .exchange() - .expectBody(String.class) - .returnResult(); + .uri("/reviews/orders/%d/detailed".formatted(orderId)) + .header("X-User-Id", userId.toString()) + .exchange() + .expectBody(String.class) + .returnResult(); return ResponseEntity.status(result.getStatus()) - .headers(result.getResponseHeaders()) - .body(result.getResponseBody()); + .headers(result.getResponseHeaders()) + .body(result.getResponseBody()); } protected ResponseEntity putOrderReview(Long orderId, Long userId, String requestJson) { @@ -442,28 +442,28 @@ protected ResponseEntity getProductReviews(Long productId, Long userId) protected ResponseEntity getSellerReviews(Long sellerId, Long userId) { var result = webTestClient.get() - .uri("/reviews/seller/%d".formatted(sellerId)) - .header("X-User-Id", userId.toString()) - .exchange() - .expectBody(String.class) - .returnResult(); + .uri("/reviews/seller/%d".formatted(sellerId)) + .header("X-User-Id", userId.toString()) + .exchange() + .expectBody(String.class) + .returnResult(); return ResponseEntity.status(result.getStatus()) - .headers(result.getResponseHeaders()) - .body(result.getResponseBody()); + .headers(result.getResponseHeaders()) + .body(result.getResponseBody()); } protected ResponseEntity getSellerReputation(Long sellerId) { var result = webTestClient.get() - .uri("/reviews/seller/%d/reputation".formatted(sellerId)) - .exchange() - .expectBody(String.class) - .returnResult(); + .uri("/reviews/seller/%d/reputation".formatted(sellerId)) + .exchange() + .expectBody(String.class) + .returnResult(); return ResponseEntity.status(result.getStatus()) - .headers(result.getResponseHeaders()) - .body(result.getResponseBody()); + .headers(result.getResponseHeaders()) + .body(result.getResponseBody()); } public void assertPageResponse(String body, Integer page, Integer size, Integer totalElements, Integer totalPages) { From 9ef7c35659d253f1308065f6c50fe765a044c5f6 Mon Sep 17 00:00:00 2001 From: FranInfanti Date: Tue, 2 Jun 2026 16:57:18 -0300 Subject: [PATCH 4/5] fix: fix order payment cleanup service --- .../repository/OrderItemReviewRepository.java | 6 ++++++ .../orders/repository/OrderReviewRepository.java | 6 ++++++ .../repository/OrderStatusHistoryRepository.java | 6 ++++++ .../com/bazaar/orders/service/CleanupService.java | 13 +++++++++++++ 4 files changed, 31 insertions(+) diff --git a/src/main/java/com/bazaar/orders/repository/OrderItemReviewRepository.java b/src/main/java/com/bazaar/orders/repository/OrderItemReviewRepository.java index fd13b43..83a9a65 100644 --- a/src/main/java/com/bazaar/orders/repository/OrderItemReviewRepository.java +++ b/src/main/java/com/bazaar/orders/repository/OrderItemReviewRepository.java @@ -42,4 +42,10 @@ public interface OrderItemReviewRepository extends JpaRepository findAllByOrderId(Long orderId); + /** + * Deletes all order item reviews associated with the given order item IDs. + * @param orderItemIds The list of order item IDs whose reviews should be deleted. + */ + void deleteByOrderItemIdIn(List orderItemIds); + } \ No newline at end of file diff --git a/src/main/java/com/bazaar/orders/repository/OrderReviewRepository.java b/src/main/java/com/bazaar/orders/repository/OrderReviewRepository.java index 1135b72..459326f 100644 --- a/src/main/java/com/bazaar/orders/repository/OrderReviewRepository.java +++ b/src/main/java/com/bazaar/orders/repository/OrderReviewRepository.java @@ -46,4 +46,10 @@ public interface OrderReviewRepository extends JpaRepository """) SellerReputationDTO getSellerReputation(@Param("sellerId") Long sellerId); + /** + * Deletes all order reviews associated with the given order IDs. + * @param orderIds The list of order IDs whose reviews should be deleted. + */ + void deleteByOrderIdIn(List orderIds); + } \ No newline at end of file diff --git a/src/main/java/com/bazaar/orders/repository/OrderStatusHistoryRepository.java b/src/main/java/com/bazaar/orders/repository/OrderStatusHistoryRepository.java index 0ee8486..4fe8bd8 100644 --- a/src/main/java/com/bazaar/orders/repository/OrderStatusHistoryRepository.java +++ b/src/main/java/com/bazaar/orders/repository/OrderStatusHistoryRepository.java @@ -18,4 +18,10 @@ public interface OrderStatusHistoryRepository extends JpaRepository findByOrderIdOrderByChangedAtAsc(Long orderId); + /** + * Deletes all status history entries associated with the given order IDs. + * @param orderIds The list of order IDs whose history entries should be deleted. + */ + void deleteByOrderIdIn(List orderIds); + } diff --git a/src/main/java/com/bazaar/orders/service/CleanupService.java b/src/main/java/com/bazaar/orders/service/CleanupService.java index 3c61052..fd7b4ff 100644 --- a/src/main/java/com/bazaar/orders/service/CleanupService.java +++ b/src/main/java/com/bazaar/orders/service/CleanupService.java @@ -8,8 +8,11 @@ import com.bazaar.orders.model.OrderItem; import com.bazaar.orders.model.OrderPayment; import com.bazaar.orders.repository.OrderItemRepository; +import com.bazaar.orders.repository.OrderItemReviewRepository; import com.bazaar.orders.repository.OrderPaymentRepository; import com.bazaar.orders.repository.OrderRepository; +import com.bazaar.orders.repository.OrderReviewRepository; +import com.bazaar.orders.repository.OrderStatusHistoryRepository; import lombok.RequiredArgsConstructor; @@ -35,6 +38,12 @@ public class CleanupService { private final OrderItemRepository orderItemRepository; + private final OrderItemReviewRepository orderItemReviewRepository; + + private final OrderReviewRepository orderReviewRepository; + + private final OrderStatusHistoryRepository orderStatusHistoryRepository; + /** * Performs an atomic cleanup of a single abandoned payment. *

@@ -60,6 +69,7 @@ public void orderPaymentCleanup(OrderPayment payment) { List orders = orderRepository.findByOrderPaymentId(payment.getId()); List orderIds = orders.stream().map(Order::getId).toList(); List orderItems = orderItemRepository.findByOrderIdIn(orderIds); + List orderItemIds = orderItems.stream().map(OrderItem::getId).toList(); List stockRequests = orderItems.stream() .map(UpdateProductStockRequestDTO::fromOrderItem) @@ -67,6 +77,9 @@ public void orderPaymentCleanup(OrderPayment payment) { rabbitMQProducer.send(new UpdateProductsStockRequestDTO(stockRequests), RabbitMQConfig.ROUTING_KEY_RESTORE); + orderItemReviewRepository.deleteByOrderItemIdIn(orderItemIds); + orderReviewRepository.deleteByOrderIdIn(orderIds); + orderStatusHistoryRepository.deleteByOrderIdIn(orderIds); orderItemRepository.deleteAll(orderItems); orderRepository.deleteAll(orders); orderPaymentRepository.delete(payment); From 7cf34b3ad40bed80bfd0f48746fd9bafe612ab94 Mon Sep 17 00:00:00 2001 From: FranInfanti Date: Tue, 2 Jun 2026 16:57:29 -0300 Subject: [PATCH 5/5] docs: update README --- README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c278c96..814cee5 100644 --- a/README.md +++ b/README.md @@ -67,19 +67,22 @@ docker-compose down -v ``` --- + ## k6 Testing In order to run the k6 tests, it is necessary to start the service in stress mode: -```bash -docker compose -f docker-compose.test.yml up --build -``` - -And to stop the service: - -```bash -docker compose -f docker-compose.test.yml down -v -``` +1. **Configure the enviorment variables** Create a `.env` file based on [.env.example](/auth/.env.example): + * `ENVIORMENT` - Must be set to `stress`. + * `RABBITMQ_ADDRESSES` - Must be set to `amqp://guest:guest@rabbitmq:5672/`. +2. **Running the Service** + ```bash + docker compose -f docker-compose.test.yml up --build + ``` +3. **Stopping the Service** + ```bash + docker compose -f docker-compose.test.yml down -v + ``` > [!IMPORTANT] > Please do not use production envs for k6 testing. If there is any issue, just ask. \ No newline at end of file