diff --git a/backend/emm-sale/src/documentTest/asciidoc/index.adoc b/backend/emm-sale/src/documentTest/asciidoc/index.adoc index ac21f6ced..23c61117c 100644 --- a/backend/emm-sale/src/documentTest/asciidoc/index.adoc +++ b/backend/emm-sale/src/documentTest/asciidoc/index.adoc @@ -178,6 +178,18 @@ include::{snippets}/delete-interest-tag/http-response.adoc[] .HTTP response 설명 include::{snippets}/delete-interest-tag/response-fields.adoc[] +=== `PATCH` : 사용자의 프로필을 변경한다. + +.HTTP request +```http +POST /members/2/profile HTTP/1.1 +Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm +Host: localhost:8080 +``` + +.HTTP response +include::{snippets}/update-profile/http-response.adoc[] + == Event === `GET` : 행사 상세정보 조회 diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java index fb5427075..0875c101c 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java @@ -10,6 +10,7 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; @@ -23,16 +24,21 @@ import com.emmsale.member.application.dto.MemberActivityResponses; import com.emmsale.member.application.dto.MemberProfileResponse; import com.emmsale.member.application.dto.OpenProfileUrlRequest; +import com.emmsale.member.domain.Member; +import java.io.IOException; import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.restdocs.payload.RequestFieldsSnippet; import org.springframework.restdocs.payload.ResponseFieldsSnippet; import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; +import org.springframework.web.multipart.MultipartFile; @WebMvcTest(MemberApi.class) class MemberApiTest extends MockMvcTestHelper { @@ -255,4 +261,46 @@ void deleteMemberTest() throws Exception { .andDo(print()) .andDo(document("delete-member")); } + + @Test + @DisplayName("멤버 프로필을 변경할 수 있다.") + void updateProfile() throws Exception { + //given + final String imageUrl = "http://imageUrl.png"; + final Long memberId = 1L; + final String accessToken = "access_token"; + final MockMultipartHttpServletRequestBuilder builder = createUpdateProfileBuilder(memberId); + + when(memberUpdateService.updateMemberProfile + (any(MultipartFile.class), anyLong(), any(Member.class))) + .thenReturn(imageUrl); + + //when + mockMvc.perform(builder + .header("Authorization", accessToken)) + .andExpect(status().isOk()) + .andDo(print()) + .andDo(document("update-profile")); + } + + private MockMultipartHttpServletRequestBuilder createUpdateProfileBuilder(final Long memberId) + throws IOException { + final MockMultipartFile image = new MockMultipartFile( + "picture", + "picture.jpg", + MediaType.TEXT_PLAIN_VALUE, + "test data".getBytes() + ); + + final MockMultipartHttpServletRequestBuilder builder = + multipart("/members/{member-id}/profile", memberId) + .file("image", image.getBytes()); + + builder.with((mockHttpServletRequest) -> { + mockHttpServletRequest.setMethod("PATCH"); + return mockHttpServletRequest; + }); + + return builder; + } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/image/application/S3Client.java b/backend/emm-sale/src/main/java/com/emmsale/image/application/S3Client.java index 326ccf6ac..c42ad3bf5 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/image/application/S3Client.java +++ b/backend/emm-sale/src/main/java/com/emmsale/image/application/S3Client.java @@ -13,30 +13,32 @@ import java.util.Objects; import java.util.UUID; import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; +import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; -@Service -@RequiredArgsConstructor +@Component public class S3Client { private static final String EXTENSION_DELIMITER = "."; private static final List ALLOWED_FILE_EXTENSIONS = List.of(".jpg", ".png", ".jpeg"); private static final int MIN_EXTENSION_SEPARATOR_INDEX = 0; - - @Value("${cloud.aws.s3.bucket}") - private String bucket; - + private static final String URL_DELIMITER = "/"; + + private final String bucket; private final AmazonS3 amazonS3; - + + public S3Client(@Value("${cloud.aws.s3.bucket}") final String bucket, final AmazonS3 amazonS3) { + this.bucket = bucket; + this.amazonS3 = amazonS3; + } + public List uploadImages(final List multipartFiles) { return multipartFiles.stream().map(this::uploadImage) .collect(Collectors.toList()); } - private String uploadImage(final MultipartFile file) { + public String uploadImage(final MultipartFile file) { final String fileExtension = extractFileExtension(file); final String newFileName = UUID.randomUUID().toString().concat(fileExtension); final ObjectMetadata objectMetadata = configureObjectMetadata(file); @@ -80,4 +82,12 @@ public void deleteImages(final List fileNames) { throw new ImageException(ImageExceptionType.FAIL_S3_DELETE_IMAGE); } } + + public String convertImageUrl(final String imageName) { + return String.join(URL_DELIMITER, bucket, imageName); + } + + public String convertImageName(final String imageUrl) { + return imageUrl.split(bucket + URL_DELIMITER, 2)[1]; + } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java b/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java index 63c640ea9..87e3243ad 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java @@ -17,12 +17,14 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; @RestController @RequiredArgsConstructor @@ -95,4 +97,14 @@ public ResponseEntity deleteMember( memberUpdateService.deleteMember(member, memberId); return ResponseEntity.noContent().build(); } + + @PatchMapping("/members/{memberId}/profile") + public ResponseEntity updateProfile( + @PathVariable final Long memberId, + final MultipartFile image, + final Member member + ) { + final String imageUrl = memberUpdateService.updateMemberProfile(image, memberId, member); + return ResponseEntity.ok(imageUrl); + } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberUpdateService.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberUpdateService.java index e038ed799..603ca02cb 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberUpdateService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberUpdateService.java @@ -1,14 +1,17 @@ package com.emmsale.member.application; +import com.emmsale.image.application.S3Client; import com.emmsale.member.application.dto.DescriptionRequest; import com.emmsale.member.application.dto.OpenProfileUrlRequest; import com.emmsale.member.domain.Member; import com.emmsale.member.domain.MemberRepository; import com.emmsale.member.exception.MemberException; import com.emmsale.member.exception.MemberExceptionType; +import java.util.List; import javax.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; @Service @Transactional @@ -16,6 +19,7 @@ public class MemberUpdateService { private final MemberRepository memberRepository; + private final S3Client s3Client; public void updateOpenProfileUrl( final Member member, @@ -43,4 +47,25 @@ public void deleteMember(final Member member, final Long memberId) { memberRepository.deleteById(memberId); } + + public String updateMemberProfile( + final MultipartFile image, + final Long memberId, + final Member member + ) { + if (member.isNotMe(memberId)) { + throw new MemberException(MemberExceptionType.FORBIDDEN_UPDATE_PROFILE_IMAGE); + } + + if (member.isNotGithubProfile()) { + final String imageName = s3Client.convertImageName(member.getImageUrl()); + s3Client.deleteImages(List.of(imageName)); + } + + final String imageName = s3Client.uploadImage(image); + final String imageUrl = s3Client.convertImageUrl(imageName); + member.updateProfile(imageUrl); + + return imageUrl; + } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/domain/Member.java b/backend/emm-sale/src/main/java/com/emmsale/member/domain/Member.java index e1f37e53e..314ccb927 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/domain/Member.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/domain/Member.java @@ -18,6 +18,7 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Member extends BaseEntity { + private static final String GITHUB_PROFILE_DOMAIN = "https://avatars.githubusercontent.com"; private static final int MAX_DESCRIPTION_LENGTH = 100; private static final String DEFAULT_DESCRIPTION = ""; @@ -37,7 +38,6 @@ public class Member extends BaseEntity { @Column(nullable = false) private String githubUsername; - public Member(final Long id, final Long githubId, final String imageUrl, final String name, final String githubUsername) { this.id = id; @@ -73,6 +73,10 @@ public void updateDescription(final String description) { this.description = description; } + public void updateProfile(final String imageUrl) { + this.imageUrl = imageUrl; + } + private void validateDescriptionNull(final String description) { if (description == null) { throw new MemberException(MemberExceptionType.NULL_DESCRIPTION); @@ -97,7 +101,6 @@ public boolean isNotMe(final Member member) { return isNotMe(member.getId()); } - public boolean isNotMe(final Long id) { return !this.id.equals(id); } @@ -106,6 +109,10 @@ public boolean isOnboarded() { return name != null; } + public boolean isNotGithubProfile() { + return !imageUrl.startsWith(GITHUB_PROFILE_DOMAIN); + } + public Optional getOptionalOpenProfileUrl() { return Optional.ofNullable(openProfileUrl); } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/exception/MemberExceptionType.java b/backend/emm-sale/src/main/java/com/emmsale/member/exception/MemberExceptionType.java index b3c7f56ea..70c627eaf 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/exception/MemberExceptionType.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/exception/MemberExceptionType.java @@ -57,6 +57,11 @@ public enum MemberExceptionType implements BaseExceptionType { NOT_MATCHING_TOKEN_AND_LOGIN_MEMBER( HttpStatus.UNAUTHORIZED, "사용자가 일치하지 않습니다." + ), + + FORBIDDEN_UPDATE_PROFILE_IMAGE( + HttpStatus.FORBIDDEN, + "해당하는 멤버의 프로필을 바꿀 권한이 없습니다." ); private final HttpStatus httpStatus; diff --git a/backend/emm-sale/src/test/java/com/emmsale/helper/ServiceIntegrationTestHelper.java b/backend/emm-sale/src/test/java/com/emmsale/helper/ServiceIntegrationTestHelper.java index bd14b8b5e..1d91a4475 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/helper/ServiceIntegrationTestHelper.java +++ b/backend/emm-sale/src/test/java/com/emmsale/helper/ServiceIntegrationTestHelper.java @@ -1,6 +1,7 @@ package com.emmsale.helper; import com.emmsale.image.application.ImageCommandService; +import com.emmsale.image.application.S3Client; import com.emmsale.login.utils.GithubClient; import com.emmsale.notification.application.FirebaseCloudMessageClient; import org.springframework.boot.test.context.SpringBootTest; @@ -18,4 +19,6 @@ public abstract class ServiceIntegrationTestHelper { protected FirebaseCloudMessageClient firebaseCloudMessageClient; @MockBean protected ImageCommandService imageCommandService; + @MockBean + protected S3Client s3Client; } diff --git a/backend/emm-sale/src/test/java/com/emmsale/image/application/S3ClientTest.java b/backend/emm-sale/src/test/java/com/emmsale/image/application/S3ClientTest.java index 3dbf7004f..cf4a8c7d4 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/image/application/S3ClientTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/image/application/S3ClientTest.java @@ -1,5 +1,6 @@ package com.emmsale.image.application; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -9,7 +10,6 @@ import com.amazonaws.services.s3.model.DeleteObjectRequest; import com.amazonaws.services.s3.model.PutObjectRequest; import com.amazonaws.services.s3.model.PutObjectResult; -import com.emmsale.helper.ServiceIntegrationTestHelper; import com.emmsale.image.exception.ImageException; import com.emmsale.image.exception.ImageExceptionType; import java.util.List; @@ -24,17 +24,19 @@ import org.springframework.mock.web.MockMultipartFile; import org.springframework.web.multipart.MultipartFile; -class S3ClientTest extends ServiceIntegrationTestHelper { - +class S3ClientTest { + + private static final String TEST_BUCKET = "Test"; + private S3Client s3Client; private AmazonS3 mockingAmazonS3; - + @BeforeEach void setUp() { mockingAmazonS3 = mock(AmazonS3.class); - s3Client = new S3Client(mockingAmazonS3); + s3Client = new S3Client(TEST_BUCKET, mockingAmazonS3); } - + @Test @DisplayName("uploadImages(): 올바른 확장자를 갖는 파일을 입력받으면 정상적으로 파일을 S3에 업로드한다.") void uploadImages_success() { @@ -45,16 +47,16 @@ void uploadImages_success() { new MockMultipartFile("test", "test.jpeg", "", new byte[]{})); BDDMockito.given(mockingAmazonS3.putObject(any(PutObjectRequest.class))) .willReturn(new PutObjectResult()); - + //when s3Client.uploadImages(files); - + //then verify(mockingAmazonS3, times(3)) .putObject(any(PutObjectRequest.class)); - + } - + @ParameterizedTest @ValueSource(strings = {"test.txt", "test"}) @DisplayName("uploadImages(): 지원하지 않는 확장자를 갖는 파일을 입력받으면 예외를 반환한다.") @@ -62,28 +64,51 @@ void uploadImages_fail_extension(final String fileName) { //given final List files = List.of( new MockMultipartFile("test", fileName, "", new byte[]{})); - + //when final ThrowingCallable actual = () -> s3Client.uploadImages(files); - + //then Assertions.assertThatThrownBy(actual) .isInstanceOf(ImageException.class) .hasMessage(ImageExceptionType.INVALID_FILE_FORMAT.errorMessage()); } - + @Test @DisplayName("deleteImages(): 올바른 확장자를 갖는 파일을 입력받으면 정상적으로 파일을 S3에 업로드한다.") void deleteImages_success() { //given final List fileNames = List.of("test1", "test2", "test3"); - + //when s3Client.deleteImages(fileNames); - + //then verify(mockingAmazonS3, times(3)) .deleteObject(any(DeleteObjectRequest.class)); - + } + + @Test + @DisplayName("convertImageUrl(): 이미지 이름을 imageUrl로 바꾼다.") + void convertImageUrl() { + final String imageName = "image.png"; + final String expected = "Test/image.png"; + + final String actual = s3Client.convertImageUrl(imageName); + + assertThat(actual) + .isEqualTo(expected); + } + + @Test + @DisplayName("convertImageName(): 이미지 이름을 imageName로 바꾼다.") + void convertImageName() { + final String imageUrl = "Test/image.png"; + final String expected = "image.png"; + + final String actual = s3Client.convertImageName(imageUrl); + + assertThat(actual) + .isEqualTo(expected); } } diff --git a/backend/emm-sale/src/test/java/com/emmsale/member/MemberFixture.java b/backend/emm-sale/src/test/java/com/emmsale/member/MemberFixture.java index 66d04f20c..b6520bb38 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/member/MemberFixture.java +++ b/backend/emm-sale/src/test/java/com/emmsale/member/MemberFixture.java @@ -7,7 +7,7 @@ public class MemberFixture { public static Member memberFixture() { final Member member = new Member( 1234L, - "https://image-url.com", + "https://avatars.githubusercontent.com/0/4", "아마란스" ); member.updateName("우르"); diff --git a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberUpdateServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberUpdateServiceTest.java index dad1caa02..a854c0ee2 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberUpdateServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberUpdateServiceTest.java @@ -1,10 +1,17 @@ package com.emmsale.member.application; +import static com.emmsale.member.MemberFixture.memberFixture; +import static com.emmsale.member.exception.MemberExceptionType.FORBIDDEN_UPDATE_PROFILE_IMAGE; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import com.emmsale.helper.ServiceIntegrationTestHelper; -import com.emmsale.member.MemberFixture; import com.emmsale.member.application.dto.DescriptionRequest; import com.emmsale.member.application.dto.OpenProfileUrlRequest; import com.emmsale.member.domain.Member; @@ -12,13 +19,18 @@ import com.emmsale.member.exception.MemberException; import com.emmsale.member.exception.MemberExceptionType; import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EmptySource; import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.multipart.MultipartFile; class MemberUpdateServiceTest extends ServiceIntegrationTestHelper { @@ -31,7 +43,7 @@ class MemberUpdateServiceTest extends ServiceIntegrationTestHelper { @DisplayName("오픈 프로필 URL을 업데이트한다.") void updateOpenProfileUrlTest() { // given - final Member member = memberRepository.save(MemberFixture.memberFixture()); + final Member member = memberRepository.save(memberFixture()); final String expectOpenProfileUrl = "https://open.kakao.com/new/profile/url"; final OpenProfileUrlRequest request = new OpenProfileUrlRequest(expectOpenProfileUrl); @@ -54,7 +66,7 @@ class UpdateDescription { @DisplayName("정상적으로 업데이트된다.") void updateDescription_success(final String inputDescription) { // given - final Member member = memberRepository.save(MemberFixture.memberFixture()); + final Member member = memberRepository.save(memberFixture()); final String expectDescription = inputDescription; final DescriptionRequest request = new DescriptionRequest(expectDescription); @@ -72,7 +84,7 @@ void updateDescription_success(final String inputDescription) { @DisplayName("한줄 자기소개가 100자를 초과하면 예외를 반환한다.") void updateDescription_fail() { // given - final Member member = memberRepository.save(MemberFixture.memberFixture()); + final Member member = memberRepository.save(memberFixture()); final String invalidDescription = "안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요!"; final DescriptionRequest request = new DescriptionRequest(invalidDescription); @@ -92,7 +104,7 @@ void updateDescription_fail() { @DisplayName("한줄 자기소개에 공백만 들어오면 빈 문자열로 업데이트한다.") void updateDescription_trim(final String inputDescription) { // given - final Member member = memberRepository.save(MemberFixture.memberFixture()); + final Member member = memberRepository.save(memberFixture()); final String expectDescription = ""; final DescriptionRequest request = new DescriptionRequest(inputDescription); @@ -139,4 +151,79 @@ void forbbidenDeleteMemberException() { .hasMessage(MemberExceptionType.FORBIDDEN_DELETE_MEMBER.errorMessage()); } } + + @Nested + @DisplayName("member의 프로필 이미지가 업데이트 되는 경우, 기존 이미지 값을 지우고 새 이미지 값을 받는다.") + class UpdateProfile { + + private final String imageName = "imageName"; + private final MultipartFile image = Mockito.mock(MultipartFile.class); + + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + @BeforeEach + void setUp() { + ReflectionTestUtils.setField(s3Client, "bucket", bucket); + when(s3Client.uploadImage(image)).thenReturn(imageName); + when(s3Client.convertImageUrl(anyString())).thenCallRealMethod(); + when(s3Client.convertImageName(anyString())).thenCallRealMethod(); + } + + @Test + @DisplayName("기존 이미지는 깃허브 url인 경우 새로운 이미지를 s3 추가한다.") + void pastGithubUrl() { + //given + final Member fixture = memberFixture(); + final Member member = memberRepository.save(fixture); + + //when + memberUpdateService.updateMemberProfile(image, member.getId(), member); + + //then + assertAll( + () -> assertThat(member.getImageUrl()) + .isEqualTo(s3Client.convertImageUrl(imageName)), + () -> verify(s3Client, times(1)) + .uploadImage(image) + ); + } + + @Test + @DisplayName("기존 이미지가 s3에 저장된 이미지인 경우, 기존 이미지를 지우고, 새로운 이미지를 추가한다.") + void pastCustomImage() { + //given + final Member fixture = memberFixture(); + fixture.updateProfile(s3Client.convertImageUrl("already")); + final Member member = memberRepository.save(fixture); + + //when + memberUpdateService.updateMemberProfile(image, member.getId(), member); + + //then + assertAll( + () -> assertThat(member.getImageUrl()) + .isEqualTo(s3Client.convertImageUrl(imageName)), + () -> verify(s3Client, times(1)) + .uploadImage(image), + () -> verify(s3Client, times(1)) + .deleteImages(anyList()) + ); + } + + @Test + @DisplayName("member와 memberId가 다른 경우 Exception을 throw한다.") + void validateMemberIsNotMe() { + //given + final Member member = memberRepository.save(memberFixture()); + + //when && then + final ThrowingCallable testTarget = () -> + memberUpdateService.updateMemberProfile(image, member.getId() + 1, member); + + assertThatThrownBy(testTarget) + .isInstanceOf(MemberException.class) + .hasMessage(FORBIDDEN_UPDATE_PROFILE_IMAGE.errorMessage()); + } + } } diff --git a/backend/emm-sale/src/test/java/com/emmsale/member/domain/MemberTest.java b/backend/emm-sale/src/test/java/com/emmsale/member/domain/MemberTest.java index f142bdcb3..06b7532c5 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/member/domain/MemberTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/member/domain/MemberTest.java @@ -1,11 +1,13 @@ package com.emmsale.member.domain; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.emmsale.member.MemberFixture; import com.emmsale.member.exception.MemberException; import com.emmsale.member.exception.MemberExceptionType; -import org.assertj.core.api.Assertions; import org.assertj.core.api.ThrowableAssert.ThrowingCallable; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -32,7 +34,7 @@ void updateDescription_success(final String inputDescription) { String actual = member.getDescription(); // then - Assertions.assertThat(actual).isEqualTo(inputDescription); + assertThat(actual).isEqualTo(inputDescription); } @Test @@ -58,14 +60,35 @@ void updateDescription_fail() { @DisplayName("한줄 자기소개에 공백만 들어오면 빈 문자열로 업데이트한다.") void updateDescription_trim(final String inputDescription) { // given - Member member = MemberFixture.memberFixture(); + final Member member = MemberFixture.memberFixture(); //when member.updateDescription(inputDescription); - String actual = member.getDescription(); + final String actual = member.getDescription(); // then - Assertions.assertThat(actual).isEmpty(); + assertThat(actual).isEmpty(); + } + } + + @Nested + @DisplayName("멤버가 자신의 프로필이미지가 github인지 판단한다.") + class IsNotGithubProfile { + + @Test + void falseCase() { + final Member member = MemberFixture.memberFixture(); + + assertFalse(member.isNotGithubProfile()); + } + + @Test + void trueCase() { + final Member member = MemberFixture.memberFixture(); + member.updateProfile("custom profile"); + + assertTrue(member.isNotGithubProfile()); } } -} \ No newline at end of file + +}