diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a531fc..58dc29b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,10 +44,11 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x ./gradlew - - name: Clean Gradle Cache (Optional but good) + - name: Clean Gradle Cache run: ./gradlew clean --refresh-dependencies - - name: Test And Build with Gradle (Debug Mode) + + - name: Test And Build with Gradle env: JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }} - run: ./gradlew build --stacktrace --info -Dspring.profiles.active=ci + run: ./gradlew build -Dspring.profiles.active=ci diff --git a/build.gradle b/build.gradle index 8a0998b..7bfe89d 100644 --- a/build.gradle +++ b/build.gradle @@ -58,6 +58,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.redisson:redisson:3.23.5' + testImplementation 'org.mockito:mockito-inline:5.2.0' } tasks.named('test') { diff --git a/src/main/java/com/example/eightyage/domain/product/service/ProductImageService.java b/src/main/java/com/example/eightyage/domain/product/service/ProductImageService.java index d5ed35e..0a3599c 100644 --- a/src/main/java/com/example/eightyage/domain/product/service/ProductImageService.java +++ b/src/main/java/com/example/eightyage/domain/product/service/ProductImageService.java @@ -3,7 +3,6 @@ import com.example.eightyage.domain.product.entity.Product; import com.example.eightyage.domain.product.entity.ProductImage; import com.example.eightyage.domain.product.repository.ProductImageRepository; -import com.example.eightyage.domain.product.repository.ProductRepository; import com.example.eightyage.global.exception.NotFoundException; import com.example.eightyage.global.exception.ProductImageUploadException; import lombok.RequiredArgsConstructor; @@ -14,9 +13,8 @@ import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.PutObjectRequest; -import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; + import java.io.IOException; -import java.time.LocalDateTime; import java.util.UUID; @Service @@ -27,11 +25,8 @@ public class ProductImageService { private final ProductImageRepository productImageRepository; private final ProductService productService; - @Value("${aws.s3.bucket}") - private String bucket; - - @Value("${aws.region}") - private String region; + private static final String BUCKET_NAME = "my-gom-bucket"; + private static final String REGION = "ap-northeast-2"; // 제품 이미지 업로드 @Transactional @@ -42,7 +37,7 @@ public String uploadImage(Long productId, MultipartFile file) { // S3에 업로드 s3Client.putObject( PutObjectRequest.builder() - .bucket(bucket) + .bucket(BUCKET_NAME) .key(fileName) .contentType(file.getContentType()) .build(), @@ -50,7 +45,7 @@ public String uploadImage(Long productId, MultipartFile file) { ); // S3 이미지 URL 생성 - String imageUrl = String.format("https://%s.s3.%s.amazonaws.com/%s", bucket, region, fileName); + String imageUrl = String.format("https://%s.s3.%s.amazonaws.com/%s", BUCKET_NAME, REGION, fileName); // DB 저장 Product product = productService.findProductByIdOrElseThrow(productId); diff --git a/src/main/java/com/example/eightyage/global/config/S3Config.java b/src/main/java/com/example/eightyage/global/config/S3Config.java index b62e367..5d48b01 100644 --- a/src/main/java/com/example/eightyage/global/config/S3Config.java +++ b/src/main/java/com/example/eightyage/global/config/S3Config.java @@ -11,19 +11,19 @@ @Configuration public class S3Config { - @Value("${aws.region}") - private String region; + private static final String REGION = "ap-northeast-2"; - @Value("${aws.credentials.access-key}") + @Value("${aws.access-key}") private String accessKey; - @Value("${aws.credentials.secret-key}") + @Value("${aws.secret-key}") private String secretKey; + @Bean public S3Client s3Client() { return S3Client.builder() - .region(Region.of(region)) + .region(Region.of(REGION)) .credentialsProvider(StaticCredentialsProvider.create( AwsBasicCredentials.create(accessKey, secretKey) )) diff --git a/src/main/resources/application-ci.yml b/src/main/resources/application-ci.yml index 1110e9e..17459d6 100644 --- a/src/main/resources/application-ci.yml +++ b/src/main/resources/application-ci.yml @@ -3,27 +3,26 @@ server: spring: datasource: - url: jdbc:mysql://mysql:3306/team8_test + url: jdbc:mysql://localhost:3306/team8_test username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: - ddl-auto: create + ddl-auto: update properties: hibernate: dialect: org.hibernate.dialect.MySQLDialect show_sql: true format_sql: true + + cloud: + aws: + credentials: + access-key: ${AWS_ACCESS_KEY} + secret-key: ${AWS_SECRET_KEY} + jwt: secret: - key: ${JWT_SECRET_KEY} - -aws: - credentials: - access-key: ${AWS_ACCESS_KEY} - secret-key: ${AWS_SECRET_KEY} - region: ap-northeast-2 - s3: - bucket: my-gom-bucket \ No newline at end of file + key: ${JWT_SECRET_KEY} \ No newline at end of file diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 610611b..f89a31b 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -1,18 +1,20 @@ spring: + config: + import: optional:file:.env[.properties] + cloud: aws: credentials: access-key: ${AWS_ACCESS_KEY} secret-key: ${AWS_SECRET_KEY} - region: - static: ap-northeast-2 - s3: - bucket: my-gom-bucket -aws: - credentials: - access-key: ${AWS_ACCESS_KEY} - secret-key: ${AWS_SECRET_KEY} - region: ap-northeast-2 - s3: - bucket: my-gom-bucket \ No newline at end of file + datasource: + url: ${DB_URL} + username: ${DB_USER} + password: ${DB_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver + + data: + redis: + host: localhost + port: 6379 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b7d1ee6..768de84 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -9,19 +9,9 @@ server: session: timeout: 1800 -spring: - config: - import: optional:file:.env[.properties] - application: name: eightyage - datasource: - url: ${DB_URL} - username: ${DB_USER} - password: ${DB_PASSWORD} - driver-class-name: com.mysql.cj.jdbc.Driver - jpa: hibernate: ddl-auto: update @@ -32,29 +22,7 @@ spring: use_sql_comments: true dialect: org.hibernate.dialect.MySQLDialect -# cloud: -# aws: -# credentials: -# access-key: ${S3_ACCESS_KEY} -# secret-key: ${S3_SECRET_KEY} -# region: -# static: ap-northeast-2 -# s3: -# bucket: ${S3_BUCKET} -# -#aws: -# credentials: -# access-key: ${S3_ACCESS_KEY} -# secret-key: ${S3_SECRET_KEY} -# region: ap-northeast-2 -# s3: -# bucket: ${S3_BUCKET} - - data: - redis: - host: ${REDIS_HOST} - port: 6379 - jwt: secret: key: ${JWT_SECRET_KEY} + diff --git a/src/test/java/com/example/eightyage/domain/product/service/ProductImageServiceTest.java b/src/test/java/com/example/eightyage/domain/product/service/ProductImageServiceTest.java index bdbc2b1..b3898b8 100644 --- a/src/test/java/com/example/eightyage/domain/product/service/ProductImageServiceTest.java +++ b/src/test/java/com/example/eightyage/domain/product/service/ProductImageServiceTest.java @@ -1,5 +1,6 @@ package com.example.eightyage.domain.product.service; +import com.example.eightyage.domain.product.category.Category; import com.example.eightyage.domain.product.entity.Product; import com.example.eightyage.domain.product.entity.ProductImage; import com.example.eightyage.domain.product.repository.ProductImageRepository; @@ -13,17 +14,16 @@ import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectResponse; import java.util.Optional; import java.util.function.Consumer; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class ProductImageServiceTest { @@ -45,43 +45,53 @@ class ProductImageServiceTest { private MockMultipartFile mockFile; + private Product mockProduct; + @BeforeEach void setUp(){ mockFile = new MockMultipartFile( "file", - "tesst.jpg", + "test.jpg", "image/jpeg", "test image content".getBytes() ); - ReflectionTestUtils.setField(productImageService, "bucket", "test-bucket"); - ReflectionTestUtils.setField(productImageService, "region", "us-west-2"); + mockProduct = Product.builder() + .name("Test Product") + .category(Category.SKINCARE) + .content("This is test product.") + .price(10000) + .build(); + + ReflectionTestUtils.setField(mockProduct, "id", 1L); } @Test - void 이미지_업로드_성공(){ + void 이미지_업로드_성공() { // given Long productId = 1L; - String bucket = "test-bucket"; - String region = "us-west-2"; - String expectedImageUrl = String.format("https://%s.s3.%s.amazonaws.com/", bucket, region); + given(productService.findProductByIdOrElseThrow(productId)).willReturn(mockProduct); + + ProductImage mockProductImage = mock(ProductImage.class); + given(productImageRepository.save(any(ProductImage.class))).willReturn(mockProductImage); - given(productImageRepository.save(any())).willReturn(productImage); + when(s3Client.putObject(any(PutObjectRequest.class), any(RequestBody.class))) + .thenReturn(PutObjectResponse.builder().build()); // when String imageUrl = productImageService.uploadImage(productId, mockFile); // then - assertTrue(imageUrl.startsWith(expectedImageUrl)); + assertNotNull(imageUrl); + assertTrue(imageUrl.startsWith("https://my-gom-bucket.s3.ap-northeast-2.amazonaws.com/")); } + @Test void 이미지_삭제_성공(){ // given Long imageId = 1L; - String imageUrl = "imageUrl-example"; - - given(productImageRepository.findById(any())).willReturn(Optional.of(productImage)); + given(productImageRepository.findById(imageId)).willReturn(Optional.of(productImage)); // when productImageService.deleteImage(imageId);