From c22fbe3e96092763355dcbba5efdd973c159ef81 Mon Sep 17 00:00:00 2001 From: Tran Anh Khoa Date: Wed, 29 Apr 2026 10:35:21 +0700 Subject: [PATCH 1/2] test (cart): add unit tests for AbstractCircuitBreakFallbackHandler and ProductService, and create ConstantsTest for error codes --- ...stractCircuitBreakFallbackHandlerTest.java | 29 +++++++++ .../yas/cart/service/ProductServiceTest.java | 61 ++++++++++++++++++- .../com/yas/cart/utils/ConstantsTest.java | 24 ++++++++ 3 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 cart/src/test/java/com/yas/cart/service/AbstractCircuitBreakFallbackHandlerTest.java create mode 100644 cart/src/test/java/com/yas/cart/utils/ConstantsTest.java diff --git a/cart/src/test/java/com/yas/cart/service/AbstractCircuitBreakFallbackHandlerTest.java b/cart/src/test/java/com/yas/cart/service/AbstractCircuitBreakFallbackHandlerTest.java new file mode 100644 index 0000000000..1fc0d9e217 --- /dev/null +++ b/cart/src/test/java/com/yas/cart/service/AbstractCircuitBreakFallbackHandlerTest.java @@ -0,0 +1,29 @@ +package com.yas.cart.service; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class AbstractCircuitBreakFallbackHandlerTest { + + private final TestFallbackHandler handler = new TestFallbackHandler(); + + @Test + void handleBodilessFallback_shouldRethrowThrowable() { + RuntimeException throwable = new RuntimeException("fallback failed"); + + assertThatThrownBy(() -> handler.handleBodilessFallback(throwable)) + .isSameAs(throwable); + } + + @Test + void handleTypedFallback_shouldRethrowThrowable() { + RuntimeException throwable = new RuntimeException("typed fallback failed"); + + assertThatThrownBy(() -> handler.handleTypedFallback(throwable)) + .isSameAs(throwable); + } + + private static final class TestFallbackHandler extends AbstractCircuitBreakFallbackHandler { + } +} diff --git a/cart/src/test/java/com/yas/cart/service/ProductServiceTest.java b/cart/src/test/java/com/yas/cart/service/ProductServiceTest.java index c00a06e638..20a8eedaf5 100644 --- a/cart/src/test/java/com/yas/cart/service/ProductServiceTest.java +++ b/cart/src/test/java/com/yas/cart/service/ProductServiceTest.java @@ -2,11 +2,14 @@ import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; -import com.yas.commonlibrary.config.ServiceUrlConfig; import com.yas.cart.viewmodel.ProductThumbnailVm; +import com.yas.commonlibrary.config.ServiceUrlConfig; import java.net.URI; +import java.util.Collections; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -64,6 +67,60 @@ void getProducts_NormalCase_ReturnProductThumbnailVms() { assertThat(result.get(2).id()).isEqualTo(3); } + @Test + void getProductById_whenProductsExist_shouldReturnFirstProduct() { + ProductService productServiceSpy = Mockito.spy(productService); + ProductThumbnailVm product = getProductThumbnailVms().getFirst(); + + doReturn(List.of(product)).when(productServiceSpy).getProducts(List.of(product.id())); + + ProductThumbnailVm result = productServiceSpy.getProductById(product.id()); + + assertThat(result).isEqualTo(product); + } + + @Test + void getProductById_whenProductsAreEmpty_shouldReturnNull() { + ProductService productServiceSpy = Mockito.spy(productService); + + doReturn(Collections.emptyList()).when(productServiceSpy).getProducts(List.of(1L)); + + ProductThumbnailVm result = productServiceSpy.getProductById(1L); + + assertThat(result).isNull(); + } + + @Test + void existsById_whenProductExists_shouldReturnTrue() { + ProductService productServiceSpy = Mockito.spy(productService); + ProductThumbnailVm product = getProductThumbnailVms().getFirst(); + + doReturn(List.of(product)).when(productServiceSpy).getProducts(List.of(product.id())); + + boolean result = productServiceSpy.existsById(product.id()); + + assertThat(result).isTrue(); + } + + @Test + void existsById_whenProductDoesNotExist_shouldReturnFalse() { + ProductService productServiceSpy = Mockito.spy(productService); + + doReturn(Collections.emptyList()).when(productServiceSpy).getProducts(List.of(1L)); + + boolean result = productServiceSpy.existsById(1L); + + assertThat(result).isFalse(); + } + + @Test + void handleProductThumbnailFallback_shouldRethrowThrowable() { + RuntimeException throwable = new RuntimeException("product service unavailable"); + + assertThatThrownBy(() -> productService.handleProductThumbnailFallback(throwable)) + .isSameAs(throwable); + } + private List getProductThumbnailVms() { ProductThumbnailVm product1 = new ProductThumbnailVm( @@ -87,4 +144,4 @@ private List getProductThumbnailVms() { return List.of(product1, product2, product3); } -} \ No newline at end of file +} diff --git a/cart/src/test/java/com/yas/cart/utils/ConstantsTest.java b/cart/src/test/java/com/yas/cart/utils/ConstantsTest.java new file mode 100644 index 0000000000..1732456b36 --- /dev/null +++ b/cart/src/test/java/com/yas/cart/utils/ConstantsTest.java @@ -0,0 +1,24 @@ +package com.yas.cart.utils; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class ConstantsTest { + + @Test + void constructors_shouldBeInstantiable() { + assertThat(new Constants()).isNotNull(); + assertThat(new Constants.ErrorCode()).isNotNull(); + } + + @Test + void errorCodes_shouldMatchExpectedValues() { + assertThat(Constants.ErrorCode.NOT_FOUND_PRODUCT).isEqualTo("NOT_FOUND_PRODUCT"); + assertThat(Constants.ErrorCode.NOT_EXISTING_ITEM_IN_CART).isEqualTo("NOT_EXISTING_ITEM_IN_CART"); + assertThat(Constants.ErrorCode.NOT_EXISTING_PRODUCT_IN_CART).isEqualTo("NOT_EXISTING_PRODUCT_IN_CART"); + assertThat(Constants.ErrorCode.NON_EXISTING_CART_ITEM).isEqualTo("NON_EXISTING_CART_ITEM"); + assertThat(Constants.ErrorCode.ADD_CART_ITEM_FAILED).isEqualTo("ADD_CART_ITEM_FAILED"); + assertThat(Constants.ErrorCode.DUPLICATED_CART_ITEMS_TO_DELETE).isEqualTo("DUPLICATED_CART_ITEMS_TO_DELETE"); + } +} From 3bf22963ecac8ad88b003d143fa5c3d0ff86ee54 Mon Sep 17 00:00:00 2001 From: Tran Anh Khoa Date: Wed, 29 Apr 2026 10:58:31 +0700 Subject: [PATCH 2/2] ci (cart): update CI configuration and SonarCloud properties for cart service --- .github/workflows/cart-ci.yaml | 89 +++++++++++++++------------------- cart/pom.xml | 4 +- 2 files changed, 40 insertions(+), 53 deletions(-) diff --git a/.github/workflows/cart-ci.yaml b/.github/workflows/cart-ci.yaml index 59f0045fa6..0aa12eebbe 100644 --- a/.github/workflows/cart-ci.yaml +++ b/.github/workflows/cart-ci.yaml @@ -2,89 +2,76 @@ name: cart service ci on: push: - branches: [ "main" ] + branches: ['main'] paths: - - "cart/**" - - ".github/workflows/actions/action.yaml" - - ".github/workflows/cart-ci.yaml" - - "pom.xml" + - 'cart/**' + - '.github/workflows/actions/action.yaml' + - '.github/workflows/cart-ci.yaml' + - 'pom.xml' pull_request: - branches: [ "main" ] + branches: ['main'] paths: - - "cart/**" - - ".github/workflows/actions/action.yaml" - - ".github/workflows/cart-ci.yaml" - - "pom.xml" + - 'cart/**' + - '.github/workflows/actions/action.yaml' + - '.github/workflows/cart-ci.yaml' + - 'pom.xml' workflow_dispatch: jobs: - Build: + Test: runs-on: ubuntu-latest env: FROM_ORIGINAL_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.ref == 'refs/heads/main' }} steps: - uses: actions/checkout@v4 with: - fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - uses: ./.github/workflows/actions - - name: Run Maven Build Command + + - name: Run tests and generate coverage report run: mvn clean install -pl cart -am + - name: Run Maven Checkstyle if: ${{ env.FROM_ORIGINAL_REPOSITORY == 'true' }} run: mvn checkstyle:checkstyle -pl cart -am -Dcheckstyle.output.file=cart-checkstyle-result.xml + - name: Upload Checkstyle Result if: ${{ env.FROM_ORIGINAL_REPOSITORY == 'true' }} uses: jwgmeligmeyling/checkstyle-github-action@master with: path: '**/cart-checkstyle-result.xml' - - name: Test Results + + - name: Upload test results uses: dorny/test-reporter@v1 if: ${{ env.FROM_ORIGINAL_REPOSITORY == 'true' && (success() || failure()) }} with: name: Cart-Service-Unit-Test-Results - path: "cart/**/*-reports/TEST*.xml" + path: 'cart/**/*-reports/TEST*.xml' reporter: java-junit - - name: OWASP Dependency Check - if: ${{ env.FROM_ORIGINAL_REPOSITORY == 'true' }} - uses: dependency-check/Dependency-Check_Action@main - env: - JAVA_HOME: /opt/jdk - with: - project: 'yas' - path: '.' - format: 'HTML' - - name: Upload OWASP Dependency Check results - if: ${{ env.FROM_ORIGINAL_REPOSITORY == 'true' }} - uses: actions/upload-artifact@master - with: - name: OWASP Dependency Check Report - path: ${{github.workspace}}/reports - - name: Analyze with sonar cloud - if: ${{ env.FROM_ORIGINAL_REPOSITORY == 'true' }} - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: mvn org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -pl cart -am + - name: Add coverage report to PR uses: madrapps/jacoco-report@v1.6.1 if: ${{ env.FROM_ORIGINAL_REPOSITORY == 'true' }} with: paths: ${{github.workspace}}/cart/target/site/jacoco/jacoco.xml token: ${{secrets.GITHUB_TOKEN}} - min-coverage-overall: 80 - min-coverage-changed-files: 60 + min-coverage-overall: 70 + min-coverage-changed-files: 70 title: 'Cart Coverage Report' update-comment: true - - name: Log in to the Container registry - if: ${{ github.ref == 'refs/heads/main' }} - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push Docker images - if: ${{ github.ref == 'refs/heads/main' }} - uses: docker/build-push-action@v6 - with: - context: ./cart - push: true - tags: ghcr.io/nashtech-garage/yas-cart:latest \ No newline at end of file + + - name: Analyze with sonar cloud + if: ${{ env.FROM_ORIGINAL_REPOSITORY == 'true' }} + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: mvn org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -f cart + + Build: + runs-on: ubuntu-latest + needs: Test + steps: + - uses: actions/checkout@v4 + - uses: ./.github/workflows/actions + - name: Build artifact + run: mvn clean install -pl cart -am -DskipTests \ No newline at end of file diff --git a/cart/pom.xml b/cart/pom.xml index d9da458518..a06cbcad5e 100644 --- a/cart/pom.xml +++ b/cart/pom.xml @@ -14,9 +14,9 @@ YAS Cart service - nashtech-garage + longtoz https://sonarcloud.io - nashtech-garage_yas-cart + longtoz_yas-delivery