From 41d29a66469ed083b186cd382e3f5efc1e7a8985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8E=E1=85=AC=E1=84=8C=E1=85=AE=E1=86=AB=E1=84=92?= =?UTF-8?q?=E1=85=A9?= Date: Wed, 22 Nov 2023 11:12:56 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EB=A1=9C=EC=BB=AC=EB=A1=9C=20=EC=A0=80=EC=9E=A5=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pium/service/PetPlantService.java | 37 ++++---- .../official/pium/util/PhotoLocalManager.java | 83 ++++++++++++++++++ .../official/pium/util/PhotoS3Manager.java | 4 - .../src/main/resources/application.properties | 37 -------- .../pium/src/main/resources/application.yml | 85 +++++++++++++++++++ .../acceptance/DictionaryPlantApiTest.java | 4 + .../official/pium/config/ImageCleaner.java | 39 +++++++++ .../pium/config/ImageCleanerExtension.java | 12 +++ .../official/pium/fixture/FileFixture.java | 11 ++- .../pium/service/PetPlantServiceTest.java | 82 +++++++----------- .../pium/support/PetPlantSupport.java | 9 +- .../official/pium/util/PhotoManagerTest.java | 34 ++++++++ .../pium/util/PhotoS3ManagerTest.java | 45 ---------- .../src/test/resources/application.properties | 26 ------ .../pium/src/test/resources/application.yml | 78 +++++++++++++++++ 15 files changed, 400 insertions(+), 186 deletions(-) create mode 100644 backend/pium/src/main/java/com/official/pium/util/PhotoLocalManager.java delete mode 100644 backend/pium/src/main/resources/application.properties create mode 100644 backend/pium/src/main/resources/application.yml create mode 100644 backend/pium/src/test/java/com/official/pium/config/ImageCleaner.java create mode 100644 backend/pium/src/test/java/com/official/pium/config/ImageCleanerExtension.java create mode 100644 backend/pium/src/test/java/com/official/pium/util/PhotoManagerTest.java delete mode 100644 backend/pium/src/test/java/com/official/pium/util/PhotoS3ManagerTest.java delete mode 100644 backend/pium/src/test/resources/application.properties create mode 100644 backend/pium/src/test/resources/application.yml diff --git a/backend/pium/src/main/java/com/official/pium/service/PetPlantService.java b/backend/pium/src/main/java/com/official/pium/service/PetPlantService.java index 950f61c61..791b1136e 100644 --- a/backend/pium/src/main/java/com/official/pium/service/PetPlantService.java +++ b/backend/pium/src/main/java/com/official/pium/service/PetPlantService.java @@ -1,10 +1,6 @@ package com.official.pium.service; -import com.official.pium.domain.DictionaryPlant; -import com.official.pium.domain.History; -import com.official.pium.domain.HistoryType; -import com.official.pium.domain.Member; -import com.official.pium.domain.PetPlant; +import com.official.pium.domain.*; import com.official.pium.event.history.HistoryEvent; import com.official.pium.event.history.HistoryEvents; import com.official.pium.event.history.LastWaterDateEvent; @@ -13,14 +9,7 @@ import com.official.pium.repository.DictionaryPlantRepository; import com.official.pium.repository.HistoryRepository; import com.official.pium.repository.PetPlantRepository; -import com.official.pium.service.dto.DataResponse; -import com.official.pium.service.dto.PetPlantCreateRequest; -import com.official.pium.service.dto.PetPlantResponse; -import com.official.pium.service.dto.PetPlantUpdateRequest; -import com.official.pium.service.dto.SinglePetPlantResponse; -import java.time.LocalDate; -import java.util.List; -import java.util.NoSuchElementException; +import com.official.pium.service.dto.*; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationEventPublisher; @@ -31,13 +20,23 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import java.time.LocalDate; +import java.util.List; +import java.util.NoSuchElementException; + @Service @Transactional(readOnly = true) @RequiredArgsConstructor public class PetPlantService { @Value("${petPlant.image.directory}") - private String workingDirectory; + private String petPlantImageDirectory; + + @Value("${local.image.web}") + private String webPath; + + @Value("${dictPlant.image.directory}") + private String dictionaryPlantImageDirectory; private final PetPlantRepository petPlantRepository; private final DictionaryPlantRepository dictionaryPlantRepository; @@ -66,7 +65,7 @@ private String saveImageIfExists(MultipartFile image, String imageDefaultUrl) { if (image == null || image.isEmpty()) { return imageDefaultUrl; } - return photoManager.upload(image, workingDirectory); + return photoManager.upload(image, petPlantImageDirectory); } private void createHistory(PetPlant petPlant) { @@ -132,8 +131,12 @@ private String updateImage(MultipartFile image, String originalImageUrl) { if (image == null || image.isEmpty()) { return originalImageUrl; } - photoManager.delete(originalImageUrl, workingDirectory); - return photoManager.upload(image, workingDirectory); + + if (!originalImageUrl.startsWith(webPath + dictionaryPlantImageDirectory)) { + photoManager.delete(originalImageUrl, petPlantImageDirectory); + } + + return photoManager.upload(image, petPlantImageDirectory); } private void publishPetPlantHistories(PetPlant petPlant, PetPlantHistory previousPetPlantHistory, diff --git a/backend/pium/src/main/java/com/official/pium/util/PhotoLocalManager.java b/backend/pium/src/main/java/com/official/pium/util/PhotoLocalManager.java new file mode 100644 index 000000000..908a466cb --- /dev/null +++ b/backend/pium/src/main/java/com/official/pium/util/PhotoLocalManager.java @@ -0,0 +1,83 @@ +package com.official.pium.util; + +import com.official.pium.service.PhotoManager; +import java.io.File; +import java.io.IOException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +@Component +public class PhotoLocalManager implements PhotoManager { + + private static final String SLASH = "/"; + + @Value("${local.image.web}") + private String webPath; + + @Value("${local.image.root}") + private String localPath; + + @Override + public String upload(MultipartFile multipartFile, String workingDirectory) { + if (multipartFile == null || multipartFile.isEmpty()) { + throw new IllegalArgumentException("이미지 파일이 존재하지 않습니다. multipartFile: " + null); + } + return uploadPhoto(multipartFile, workingDirectory); + } + + private String uploadPhoto(MultipartFile multipartFile, String workingDirectory) { + try { + String fileName = PhotoNameGenerator.of(multipartFile.getOriginalFilename()); + File uploadDirectory = loadDirectory(getLocalDirectoryPath(workingDirectory)); + File uploadPath = new File(uploadDirectory, fileName); + uploadFileInLocal(multipartFile, uploadPath); + + return webPath + SLASH + uploadDirectory + SLASH + fileName; + } catch (Exception e) { + throw new IllegalStateException("파일 업로드를 실패했습니다."); + } + } + + private String getLocalDirectoryPath(String workingDirectory) { + return localPath + SLASH + workingDirectory; + } + + private File loadDirectory(String directoryLocation) { + File directory = new File(directoryLocation); + if (!directory.exists()) { + directory.mkdirs(); + } + return directory; + } + + private void uploadFileInLocal(MultipartFile multipartFile, File uploadPath) { + try { + multipartFile.transferTo(uploadPath); + } catch (IOException e) { + throw new IllegalStateException("파일 변환이 실패했습니다."); + } + } + + @Override + public void delete(String originalImageUrl, String workingDirectory) { + String deletePath = getFileLocalPath(originalImageUrl); + File file = new File(deletePath); + deleteFile(file); + } + + private String getFileLocalPath(String fullPath) { + int urlIndex = fullPath.lastIndexOf(webPath); + if (urlIndex == -1) { + throw new IllegalArgumentException("잘못된 파일 경로입니다."); + } + int urlNextIndex = urlIndex + webPath.length(); + return localPath + fullPath.substring(urlNextIndex); + } + + private void deleteFile(File file) { + if (file.exists()) { + file.delete(); + } + } +} diff --git a/backend/pium/src/main/java/com/official/pium/util/PhotoS3Manager.java b/backend/pium/src/main/java/com/official/pium/util/PhotoS3Manager.java index e03766714..84468d585 100644 --- a/backend/pium/src/main/java/com/official/pium/util/PhotoS3Manager.java +++ b/backend/pium/src/main/java/com/official/pium/util/PhotoS3Manager.java @@ -7,10 +7,8 @@ import java.io.IOException; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; -@Component @RequiredArgsConstructor public class PhotoS3Manager implements PhotoManager { @@ -51,7 +49,6 @@ private String uploadPhoto(MultipartFile multipartFile, String workingDirectory) file.delete(); return rootPath + SLASH + directory + workingDirectory + SLASH + fileName; } catch (Exception e) { - e.printStackTrace(); throw new IllegalStateException("파일 업로드를 실패했습니다."); } } @@ -72,7 +69,6 @@ private File uploadFileInLocal(MultipartFile multipartFile, File uploadPath) { try { multipartFile.transferTo(uploadPath); } catch (IOException e) { - e.printStackTrace(); throw new IllegalStateException("파일 변환이 실패했습니다."); } return uploadPath; diff --git a/backend/pium/src/main/resources/application.properties b/backend/pium/src/main/resources/application.properties deleted file mode 100644 index 618452658..000000000 --- a/backend/pium/src/main/resources/application.properties +++ /dev/null @@ -1,37 +0,0 @@ -spring.datasource.url=jdbc:h2:mem:test;MODE=MYSQL -spring.datasource.username=sa -spring.jpa.show-sql=true -spring.jpa.properties.hibernate.format_sql=true -spring.jpa.hibernate.ddl-auto=validate -spring.sql.init.mode=always -spring.sql.init.schema-locations=classpath:sql/schema.sql -spring.sql.init.data-locations=classpath:sql/data.sql -auth.kakao.token-request-uri=https://kauth.kakao.com/oauth/token -auth.kakao.member-info-request-uri=https://kapi.kakao.com/v2/user/me -auth.kakao.redirect-uri=http://localhost:8282/authorization -auth.kakao.unlink-uri=https://kapi.kakao.com/v1/user/unlink -auth.kakao.client-id=REST_API_KEY -auth.kakao.admin-id=ADMIN_KEY -server.servlet.session.cookie.same-site=none -server.servlet.session.cookie.secure=true -logging.level.org.hibernate.orm.jdbc.bind=trace -logging.slack.webhook-url=https://WEB_HOOK_URL.com -spring.flyway.enabled=false -admin.account=asdf -admin.password=123 -admin.secondPassword=123 -view.image.favicion=https://static.pium.life/prod/favicon.ico -view.image.home=https://static.pium.life/prod/home.png -spring.mvc.hiddenmethod.filter.enabled=true -registration.image.directory=registration -petPlant.image.directory=test -aws.s3.root=https://test.image.storage -aws.s3.bucket=test/ -aws.s3.folder=test/ -aws.s3.directory=test -management.endpoints.enabled-by-default=false -management.endpoint.health.enabled=true - -fcm.key.path=test/ -fcm.key.scope=https://www.googleapis.com/auth/firebase.messaging -fcm.api.url=https://fcm.googleapis.com/v1/projects/project-id/messages:send diff --git a/backend/pium/src/main/resources/application.yml b/backend/pium/src/main/resources/application.yml new file mode 100644 index 000000000..5b1ccb8f7 --- /dev/null +++ b/backend/pium/src/main/resources/application.yml @@ -0,0 +1,85 @@ +spring: + flyway: + enabled: false + sql: + init: + schema-locations: classpath:sql/schema.sql + data-locations: classpath:sql/data.sql + mode: always + mvc: + hiddenmethod: + filter: + enabled: true + jpa: + hibernate: + ddl-auto: validate + properties: + hibernate: + format_sql: true + show-sql: true + datasource: + username: sa + url: jdbc:h2:mem:test;MODE=MYSQL +management: + endpoint: + health: + enabled: true + endpoints: + enabled-by-default: false +fcm: + key: + path: test/ + scope: https://www.googleapis.com/auth/firebase.messaging + api: + url: https://fcm.googleapis.com/v1/projects/project-id/messages:send +petPlant: + image: + directory: test +dictPlant: + image: + directory: /dict +server: + servlet: + session: + cookie: + secure: true + same-site: none +logging: + level: + org: + hibernate: + orm: + jdbc: + bind: trace + slack: + webhook-url: https://WEB_HOOK_URL.com +admin: + secondPassword: 123 + password: 123 + account: asdf +aws: + s3: + root: https://test.image.storage + directory: test + folder: test/ + bucket: test/ + +local: + image: + root: src/test/resources + web: https://static.pium.life +auth: + kakao: + member-info-request-uri: https://kapi.kakao.com/v2/user/me + unlink-uri: https://kapi.kakao.com/v1/user/unlink + redirect-uri: http://localhost:8282/authorization + client-id: REST_API_KEY + token-request-uri: https://kauth.kakao.com/oauth/token + admin-id: ADMIN_KEY +registration: + image: + directory: registration +view: + image: + favicion: https://static.pium.life/prod/favicon.ico + home: https://static.pium.life/prod/home.png diff --git a/backend/pium/src/test/java/com/official/pium/acceptance/DictionaryPlantApiTest.java b/backend/pium/src/test/java/com/official/pium/acceptance/DictionaryPlantApiTest.java index 8d7a53d8f..8db7a286e 100644 --- a/backend/pium/src/test/java/com/official/pium/acceptance/DictionaryPlantApiTest.java +++ b/backend/pium/src/test/java/com/official/pium/acceptance/DictionaryPlantApiTest.java @@ -9,7 +9,11 @@ import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; + +import java.time.LocalDateTime; import java.util.List; + +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Nested; diff --git a/backend/pium/src/test/java/com/official/pium/config/ImageCleaner.java b/backend/pium/src/test/java/com/official/pium/config/ImageCleaner.java new file mode 100644 index 000000000..c2a753ef9 --- /dev/null +++ b/backend/pium/src/test/java/com/official/pium/config/ImageCleaner.java @@ -0,0 +1,39 @@ +package com.official.pium.config; + +import org.springframework.stereotype.Component; + +import java.io.File; + +@Component +public class ImageCleaner { + + private static final String TEST_FILE_ROOT_PATH = "src/test/resources/"; + + public static void cleanUp() { + deleteFolder(TEST_FILE_ROOT_PATH); + } + + private static void deleteFolder(String path) { + File folder = new File(path); + if (folder.exists()) { + deleteFileRecursive(folder); + } + } + + private static void deleteFileRecursive(File file) { + if (file.isDirectory()) { + File[] files = file.listFiles(); + if (files == null) { + return; + } + + for (File child : files) { + deleteFileRecursive(child); + } + } + if (file.getName().contains("application.yml")) { + return; + } + file.delete(); + } +} diff --git a/backend/pium/src/test/java/com/official/pium/config/ImageCleanerExtension.java b/backend/pium/src/test/java/com/official/pium/config/ImageCleanerExtension.java new file mode 100644 index 000000000..8cb78738c --- /dev/null +++ b/backend/pium/src/test/java/com/official/pium/config/ImageCleanerExtension.java @@ -0,0 +1,12 @@ +package com.official.pium.config; + +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +public class ImageCleanerExtension implements AfterEachCallback { + + @Override + public void afterEach(ExtensionContext context) throws Exception { + ImageCleaner.cleanUp(); + } +} diff --git a/backend/pium/src/test/java/com/official/pium/fixture/FileFixture.java b/backend/pium/src/test/java/com/official/pium/fixture/FileFixture.java index 02f498c37..1f64814c3 100644 --- a/backend/pium/src/test/java/com/official/pium/fixture/FileFixture.java +++ b/backend/pium/src/test/java/com/official/pium/fixture/FileFixture.java @@ -2,6 +2,7 @@ import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import org.springframework.http.MediaType; @@ -10,6 +11,8 @@ public class FileFixture { + public static final String IMAGE_PATH = "image"; + private static final byte[] IMAGE = generateMockImage(); public static byte[] generateMockImage() { @@ -25,10 +28,10 @@ public static byte[] generateMockImage() { public static MultipartFile generateMultiPartFile() { return new MockMultipartFile( - "image", - "pium.jpg", - MediaType.IMAGE_JPEG_VALUE, - IMAGE + "image", + "pium.jpg", + MediaType.IMAGE_JPEG_VALUE, + IMAGE ); } } diff --git a/backend/pium/src/test/java/com/official/pium/service/PetPlantServiceTest.java b/backend/pium/src/test/java/com/official/pium/service/PetPlantServiceTest.java index f951e9c3b..f81032c88 100644 --- a/backend/pium/src/test/java/com/official/pium/service/PetPlantServiceTest.java +++ b/backend/pium/src/test/java/com/official/pium/service/PetPlantServiceTest.java @@ -1,22 +1,7 @@ package com.official.pium.service; -import static com.official.pium.fixture.PetPlantFixture.REQUEST.피우미_수정_요청; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.SoftAssertions.assertSoftly; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; - -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.PutObjectRequest; -import com.amazonaws.services.s3.model.PutObjectResult; import com.official.pium.IntegrationTest; +import com.official.pium.config.ImageCleanerExtension; import com.official.pium.domain.DictionaryPlant; import com.official.pium.domain.HistoryType; import com.official.pium.domain.Member; @@ -24,29 +9,35 @@ import com.official.pium.fixture.FileFixture; import com.official.pium.repository.HistoryRepository; import com.official.pium.repository.PetPlantRepository; -import com.official.pium.service.dto.DataResponse; -import com.official.pium.service.dto.PetPlantCreateRequest; -import com.official.pium.service.dto.PetPlantResponse; -import com.official.pium.service.dto.PetPlantUpdateRequest; -import com.official.pium.service.dto.ReminderCreateRequest; -import com.official.pium.service.dto.SinglePetPlantResponse; -import java.time.LocalDate; -import java.util.List; -import java.util.NoSuchElementException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; +import com.official.pium.service.dto.*; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.data.domain.PageRequest; import org.springframework.web.multipart.MultipartFile; +import java.time.LocalDate; +import java.util.List; +import java.util.NoSuchElementException; + +import static com.official.pium.fixture.PetPlantFixture.REQUEST.피우미_수정_요청; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +@ExtendWith(ImageCleanerExtension.class) @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") class PetPlantServiceTest extends IntegrationTest { + @Value("${local.image.web}") + private String webPath; private Member member; private DictionaryPlant dictionaryPlant; @@ -62,8 +53,8 @@ class PetPlantServiceTest extends IntegrationTest { @Autowired private HistoryRepository historyRepository; - @MockBean - private AmazonS3 amazonS3; + @SpyBean + private PhotoManager photoManager; @BeforeEach void setUp() { @@ -115,8 +106,6 @@ class 반려_식물_등록_시 { .lastWaterDate(LocalDate.of(2020, 2, 1)) .build(); MultipartFile multipartFile = FileFixture.generateMultiPartFile(); - given(amazonS3.putObject(any(PutObjectRequest.class))) - .willReturn(new PutObjectResult()); PetPlantResponse petPlantResponse = petPlantService.create(request, multipartFile, member); @@ -194,13 +183,11 @@ class 반려_식물_수정_시 { @Test void 정보_수정() { - PetPlant petPlant = petPlantSupport.builder().member(member).build(); + PetPlant petPlant = petPlantSupport.builder().member(member).imageUrl(webPath + "/test/아무거나.jpg").build(); String petPlantImageUrl = petPlant.getImageUrl(); PetPlantUpdateRequest updateRequest = 피우미_수정_요청; MultipartFile multipartFile = FileFixture.generateMultiPartFile(); - given(amazonS3.putObject(any(PutObjectRequest.class))) - .willReturn(new PutObjectResult()); petPlantService.update(petPlant.getId(), updateRequest, multipartFile, member); PetPlant updatedPetPlant = petPlantRepository.findById(petPlant.getId()).get(); @@ -227,7 +214,7 @@ class 반려_식물_수정_시 { .member(member) .dictionaryPlant(dictionaryPlant) .flowerpot("유리") - .imageUrl("https://test.image.storage/test/test/34234.jpg") + .imageUrl(webPath + "/test/test/34234.jpg") .wind("바람이 안 통해요") .light("일반 조명") .lastWaterDate(LocalDate.now()) @@ -238,15 +225,12 @@ class 반려_식물_수정_시 { .build()); PetPlantUpdateRequest updateRequest = 피우미_수정_요청; MultipartFile multipartFile = FileFixture.generateMultiPartFile(); - given(amazonS3.putObject(any(PutObjectRequest.class))) - .willReturn(new PutObjectResult()); petPlantService.update(petPlant.getId(), updateRequest, multipartFile, member); assertSoftly( softly -> { - verify(amazonS3, atLeastOnce()).putObject(any()); - verify(amazonS3, atLeastOnce()).deleteObject(any(), anyString()); + verify(photoManager, atLeastOnce()).delete(anyString(), anyString()); } ); } @@ -257,8 +241,6 @@ class 반려_식물_수정_시 { String petPlantImageUrl = petPlant.getImageUrl(); PetPlantUpdateRequest updateRequest = 피우미_수정_요청; - given(amazonS3.putObject(any(PutObjectRequest.class))) - .willReturn(new PutObjectResult()); petPlantService.update(petPlant.getId(), updateRequest, null, member); PetPlant updatedPetPlant = petPlantRepository.findById(petPlant.getId()).get(); @@ -266,8 +248,7 @@ class 반려_식물_수정_시 { assertSoftly( softly -> { assertThat(updatedPetPlant.getImageUrl()).isEqualTo(petPlantImageUrl); - verify(amazonS3, never()).putObject(any()); - verify(amazonS3, never()).deleteObject(any(), anyString()); + verify(photoManager, never()).delete(anyString(), anyString()); } ); } @@ -279,7 +260,7 @@ class 반려_식물_수정_시 { .member(member) .dictionaryPlant(dictionaryPlant) .flowerpot("유리") - .imageUrl("static/dictionary-plant/34234.jpg") + .imageUrl(webPath + "/dictionary-plant/34234.jpg") .wind("바람이 안 통해요") .light("일반 조명") .lastWaterDate(LocalDate.now()) @@ -290,8 +271,6 @@ class 반려_식물_수정_시 { .build()); PetPlantUpdateRequest updateRequest = 피우미_수정_요청; - given(amazonS3.putObject(any(PutObjectRequest.class))) - .willReturn(new PutObjectResult()); MultipartFile multipartFile = FileFixture.generateMultiPartFile(); @@ -299,8 +278,7 @@ class 반려_식물_수정_시 { assertSoftly( softly -> { - verify(amazonS3, atLeastOnce()).putObject(any()); - verify(amazonS3, never()).deleteObject(any(), anyString()); + verify(photoManager, never()).delete(anyString(), anyString()); } ); } diff --git a/backend/pium/src/test/java/com/official/pium/support/PetPlantSupport.java b/backend/pium/src/test/java/com/official/pium/support/PetPlantSupport.java index 1113d0916..c29a9a558 100644 --- a/backend/pium/src/test/java/com/official/pium/support/PetPlantSupport.java +++ b/backend/pium/src/test/java/com/official/pium/support/PetPlantSupport.java @@ -10,6 +10,7 @@ import com.official.pium.repository.PetPlantRepository; import java.time.LocalDate; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component @@ -29,6 +30,7 @@ public final class PetPlantBuilder { private Member member; private DictionaryPlant dictionaryPlant; private LocalDate lastWaterDate; + private String imageUrl; public PetPlantBuilder member(Member member) { this.member = member; @@ -45,6 +47,11 @@ public PetPlantBuilder lastWaterDate(LocalDate lastWaterDate) { return this; } + public PetPlantBuilder imageUrl(String imageUrl) { + this.imageUrl = imageUrl; + return this; + } + public PetPlant build() { return petPlantRepository.save( PetPlant.builder() @@ -52,7 +59,7 @@ public PetPlant build() { DictionaryPlantFixture.generateDictionaryPlant()) : dictionaryPlant) .member(member == null ? memberRepository.save(MemberFixture.generateMember()) : member) .nickname("testNickName") - .imageUrl("testImageUrl") + .imageUrl(imageUrl == null ? "testImageUrl" : imageUrl) .location("testLocation") .flowerpot("testFlowerpot") .light("testLight") diff --git a/backend/pium/src/test/java/com/official/pium/util/PhotoManagerTest.java b/backend/pium/src/test/java/com/official/pium/util/PhotoManagerTest.java new file mode 100644 index 000000000..a41782aa1 --- /dev/null +++ b/backend/pium/src/test/java/com/official/pium/util/PhotoManagerTest.java @@ -0,0 +1,34 @@ +package com.official.pium.util; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.official.pium.IntegrationTest; +import com.official.pium.config.ImageCleanerExtension; +import com.official.pium.fixture.FileFixture; +import com.official.pium.service.PhotoManager; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.web.multipart.MultipartFile; + +@ExtendWith(ImageCleanerExtension.class) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class PhotoManagerTest extends IntegrationTest { + + @Autowired + private PhotoManager photoManager; + + @Test + void 이미지를_업로드한다() { + MultipartFile multipartFile = FileFixture.generateMultiPartFile(); + String imagePath = FileFixture.IMAGE_PATH; + String imageUrl = photoManager.upload(multipartFile, imagePath); + + assertThat(imageUrl).isNotBlank(); + } +} diff --git a/backend/pium/src/test/java/com/official/pium/util/PhotoS3ManagerTest.java b/backend/pium/src/test/java/com/official/pium/util/PhotoS3ManagerTest.java deleted file mode 100644 index 8e45a63b8..000000000 --- a/backend/pium/src/test/java/com/official/pium/util/PhotoS3ManagerTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.official.pium.util; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.BDDMockito.willDoNothing; - -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.PutObjectRequest; -import com.amazonaws.services.s3.model.PutObjectResult; -import com.official.pium.fixture.FileFixture; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.web.multipart.MultipartFile; - -@ExtendWith({MockitoExtension.class}) -@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -@SuppressWarnings("NonAsciiCharacters") -class PhotoS3ManagerTest { - - @InjectMocks - private PhotoS3Manager photoS3Manager; - - @Mock - private AmazonS3 s3Client; - - @Test - void 이미지를_업로드한다() { - MultipartFile multipartFile = FileFixture.generateMultiPartFile(); - given(s3Client.putObject(any(PutObjectRequest.class))) - .willReturn(new PutObjectResult()); - - String imageUrl = photoS3Manager.upload(multipartFile, "test"); - - assertThat(imageUrl).isNotBlank(); - } -} diff --git a/backend/pium/src/test/resources/application.properties b/backend/pium/src/test/resources/application.properties deleted file mode 100644 index 894a5246f..000000000 --- a/backend/pium/src/test/resources/application.properties +++ /dev/null @@ -1,26 +0,0 @@ -server.port=8888 -spring.datasource.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL -auth.kakao.token-request-uri=http://localhost:8888/oauth/token -auth.kakao.member-info-request-uri=http://localhost:8888/user/me -auth.kakao.unlink-uri=http://localhost:8888/v1/user/unlink -auth.kakao.redirect-uri=http://localhost:8888/authorization -auth.kakao.client-id=clientId -auth.kakao.admin-id=adminId -spring.flyway.enabled=false -aws.s3.root=https://test.image.storage -aws.s3.bucket=test/ -aws.s3.folder=test/ -aws.s3.directory=test/ -petPlant.image.directory=test -admin.account=account -admin.password=password -admin.secondPassword=password! -spring.mvc.hiddenmethod.filter.enabled=true -registration.image.directory=registration - -spring.jpa.show-sql=true -spring.jpa.properties.hibernate.format_sql=true - -fcm.key.path=test/ -fcm.key.scope=https://www.googleapis.com/auth/firebase.messaging -fcm.api.url=https://fcm.googleapis.com/v1/projects/project-id/messages:send diff --git a/backend/pium/src/test/resources/application.yml b/backend/pium/src/test/resources/application.yml new file mode 100644 index 000000000..f25c91992 --- /dev/null +++ b/backend/pium/src/test/resources/application.yml @@ -0,0 +1,78 @@ +spring: + flyway: + enabled: false + mvc: + hiddenmethod: + filter: + enabled: true + servlet: + multipart: + max-file-size: 10MB + max-request-size: 10MB + datasource: + url: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL + hikari: + connectionTimeout: 3000 + maximum-pool-size: 4 + +registration: + image: + directory: registration + +auth: + kakao: + token-request-uri: http://localhost:8888/oauth/token + admin-id: adminId + member-info-request-uri: http://localhost:8888/user/me + unlink-uri: http://localhost:8888/v1/user/unlink + redirect-uri: http://localhost:8888/authorization + client-id: clientId + +petPlant: + image: + directory: test + +dictPlant: + image: + directory: /dict + +aws: + s3: + root: https://static.pium.life + directory: test/ + folder: test/ + bucket: test/ + +local: + image: + root: src/test/resources + web: https://static.pium.life + +admin: + secondPassword: 123 + account: asdf + password: 123 + +view: + image: + favicion: https://static.pium.life/prod/favicon.ico + home: https://static.pium.life/prod/home.png + +server: + port: 8888 + tomcat: + max-http-form-post-size: 10MB + +fcm: + key: + path: config/pium-fcm.json + scope: https://www.googleapis.com/auth/firebase.messaging + api: + url: https://fcm.googleapis.com/v1/projects/pium-test/messages:send + +management: + endpoint: + health: + enabled: true + endpoints: + enabled-by-default: false