Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: photo 도메인 테스트 #29

Merged
merged 4 commits into from
Nov 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ build/

### images ###
**/src/main/resources/static/
**/src/test/resources/images/

### STS ###
.apt_generated
Expand Down
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,10 @@ dependencies {
//DB
runtimeOnly 'com.mysql:mysql-connector-j'
runtimeOnly 'com.h2database:h2'

//Redis
implementation('it.ozimov:embedded-redis:0.7.2')
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.redisson:redisson-spring-boot-starter:3.24.3'

//네이버 클라우드
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,19 @@ public List<String> uploadPhotos(final List<MultipartFile> files) {
}

@Transactional
public void deletePhotosByReviewId(final Review review) {
public boolean deletePhotosByReviewId(final Review review) {
deletePhotosInCloud(review);
photoRepository.deleteAllByReview(review.getId());

return true;
}

@Transactional
public void deletePhotosByWriter(final List<Review> reviews) {
public boolean deletePhotosByWriter(final List<Review> reviews) {
reviews.forEach(this::deletePhotosInCloud);
photoRepository.deleteAllByReviews(reviews.stream().map(Review::getId).toList());

return true;
}

private void deletePhotosInCloud(final Review review) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package com.inq.wishhair.wesharewishhair.photo.domain;

import java.util.List;
import java.util.Optional;

public interface PhotoRepository {

Photo save(Photo photo);

Optional<Photo> findById(Long id);

List<Photo> findAll();

void deleteAllByReview(Long reviewId);

void deleteAllByReviews(List<Long> reviewIds);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ public interface PhotoStore {

List<String> uploadFiles(List<MultipartFile> files);

void deleteFiles(List<String> storeUrls);
boolean deleteFiles(List<String> storeUrls);
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ private String uploadFile(final MultipartFile file) {
}
}

public void deleteFiles(final List<String> storeUrls) {
public boolean deleteFiles(final List<String> storeUrls) {
storeUrls.forEach(this::deleteFile);
return true;
}

private void deleteFile(final String storeUrl) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor
public class Review extends BaseEntity {

@Id
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.inq.wishhair.wesharewishhair.common.utils;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public abstract class FileMockingUtils {

private static final String FILE_PATH = "src/test/resources/images/";
private static final String FILE_META_NAME = "files";
private static final String CONTENT_TYPE = "image/bmp";

public static MultipartFile createMockMultipartFile(
final String fileName
) throws IOException {
try (final FileInputStream stream = new FileInputStream(FILE_PATH + fileName)) {
return new MockMultipartFile(FILE_META_NAME, fileName, CONTENT_TYPE, stream);
}
}

public static List<MultipartFile> createMockMultipartFiles() throws IOException {
List<MultipartFile> files = new ArrayList<>();
for (int i = 1; i <= 2; i++) {
files.add(createMockMultipartFile(String.format("hello%s.jpg", i)));
}
return files;
}

public static MultipartFile createEmptyFile() {
return new MockMultipartFile(
"file",
"hello.png",
"image/png",
new byte[] {}
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.inq.wishhair.wesharewishhair.photo.application;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;

import java.io.IOException;
import java.util.List;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.multipart.MultipartFile;

import com.inq.wishhair.wesharewishhair.common.utils.FileMockingUtils;
import com.inq.wishhair.wesharewishhair.photo.domain.Photo;
import com.inq.wishhair.wesharewishhair.photo.domain.PhotoRepository;
import com.inq.wishhair.wesharewishhair.photo.domain.PhotoStore;
import com.inq.wishhair.wesharewishhair.review.domain.entity.Review;
import com.inq.wishhair.wesharewishhair.review.fixture.ReviewFixture;

@DisplayName("[PhotoService 테스트] - Application")
class PhotoServiceTest {

private final PhotoService photoService;
private final PhotoStore photoStore;

public PhotoServiceTest() {
PhotoRepository photoRepository = Mockito.mock(PhotoRepository.class);
this.photoStore = Mockito.mock(PhotoStore.class);
this.photoService = new PhotoService(photoStore, photoRepository);
}

@Test
void uploadPhotos() throws IOException {
//given
List<MultipartFile> files = FileMockingUtils.createMockMultipartFiles();

List<String> urls = List.of("test_url1", "test_url2");
given(photoStore.uploadFiles(anyList()))
.willReturn(urls);

//when
List<String> actual = photoService.uploadPhotos(files);

//then
assertThat(actual).isEqualTo(urls);
}

@Test
void deletePhotosByReviewId() {
//given
Review review = ReviewFixture.getEmptyReview(1L);
Photo photo = Photo.createReviewPhoto("url1", review);

ReflectionTestUtils.setField(review, "photos", List.of(photo));

//when
boolean actual = photoService.deletePhotosByReviewId(review);

//then
assertThat(actual).isTrue();
verify(photoStore, times(1)).deleteFiles(anyList());
}

@Test
void deletePhotosByWriter() {
//given
Review review1 = ReviewFixture.getEmptyReview(1L);
Review review2 = ReviewFixture.getEmptyReview(2L);
Photo photo1 = Photo.createReviewPhoto("url1", review1);
Photo photo2 = Photo.createReviewPhoto("url1", review2);

ReflectionTestUtils.setField(review1, "photos", List.of(photo1));
ReflectionTestUtils.setField(review2, "photos", List.of(photo2));

//when
boolean actual = photoService.deletePhotosByWriter(List.of(review1, review2));

//then
assertThat(actual).isTrue();
verify(photoStore, times(2)).deleteFiles(anyList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.inq.wishhair.wesharewishhair.photo.domain;

import static org.assertj.core.api.Assertions.*;

import java.util.List;
import java.util.Optional;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.util.ReflectionTestUtils;

import com.inq.wishhair.wesharewishhair.common.support.RepositoryTestSupport;
import com.inq.wishhair.wesharewishhair.review.domain.entity.Review;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;

@DisplayName("[PhotoRepository 테스트] - Domain")
class PhotoRepositoryTest extends RepositoryTestSupport {

@PersistenceContext
private EntityManager entityManager;
@Autowired
private PhotoRepository photoRepository;

@Test
@DisplayName("[리뷰 아이디를 가진 Photo 를 삭제한다]")
void deleteAllByReview() {
//given
Review review = new Review();
ReflectionTestUtils.setField(review, "id", 1L);
Photo photo = photoRepository.save(Photo.createReviewPhoto("url", review));

//when
photoRepository.deleteAllByReview(1L);
entityManager.clear();

//then
Optional<Photo> actual = photoRepository.findById(photo.getId());
assertThat(actual).isNotPresent();
}

@Test
@DisplayName("[리뷰 아이디 리스트에 포함된 Photo 를 삭제한다]")
void deleteAllByReviews() {
//given
Review review1 = new Review();
ReflectionTestUtils.setField(review1, "id", 1L);
Review review2 = new Review();
ReflectionTestUtils.setField(review2, "id", 2L);
photoRepository.save(Photo.createReviewPhoto("url1", review1));
photoRepository.save(Photo.createReviewPhoto("url2", review2));

//when
photoRepository.deleteAllByReviews(List.of(1L, 2L));
entityManager.clear();

//then
List<Photo> actual = photoRepository.findAll();
assertThat(actual).isEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.inq.wishhair.wesharewishhair.photo.domain;

import static org.assertj.core.api.Assertions.*;
import static org.assertj.core.api.ThrowableAssert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.BDDMockito.*;

import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.List;
import java.util.UUID;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.web.multipart.MultipartFile;

import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.inq.wishhair.wesharewishhair.common.utils.FileMockingUtils;
import com.inq.wishhair.wesharewishhair.global.exception.ErrorCode;
import com.inq.wishhair.wesharewishhair.global.exception.WishHairException;
import com.inq.wishhair.wesharewishhair.photo.infrastructure.S3PhotoStore;

@DisplayName("[PhotoStore 테스트] - Domain")
class PhotoStoreTest {

private static final String BUCKET_NAME = "bucket";

private final PhotoStore photoStore;
private final AmazonS3Client amazonS3Client;

public PhotoStoreTest() {
this.amazonS3Client = Mockito.mock(AmazonS3Client.class);
this.photoStore = new S3PhotoStore(amazonS3Client, "bucket");
}

@Nested
@DisplayName("[이미지를 업로드한다]")
class uploadFiles {

@Test
@DisplayName("[성공적으로 업로드한다]")
void success() throws IOException {
//given
MultipartFile file = FileMockingUtils.createMockMultipartFile("hello1.jpg");

URL url = URI.create("http://localhost:8080/test/url").toURL();
given(amazonS3Client.getUrl(eq(BUCKET_NAME), anyString()))
.willReturn(url);

//when
List<String> actual = photoStore.uploadFiles(List.of(file));

//then
assertThat(actual).hasSize(1);
assertThat(actual.get(0)).isEqualTo(url.toString());
}

@Test
@DisplayName("[이미지 업로드에 실패한다]")
void fail() throws IOException {
//given
MultipartFile file = FileMockingUtils.createMockMultipartFile("hello1.jpg");

given(amazonS3Client.putObject(any(PutObjectRequest.class)))
.willThrow(new WishHairException(ErrorCode.FILE_TRANSFER_EX));

//when
ThrowingCallable when = () -> photoStore.uploadFiles(List.of(file));

//then
assertThatThrownBy(when)
.isInstanceOf(WishHairException.class)
.hasMessageContaining(ErrorCode.FILE_TRANSFER_EX.getMessage());
}
}

@Test
@DisplayName("[이미지를 삭제한다]")
void fail() {
//given
String url = "http://localhost:8080/" + UUID.randomUUID();

//when
boolean actual = photoStore.deleteFiles(List.of(url));

//then
assertThat(actual).isTrue();
}
}
Loading