diff --git a/src/test/java/com/potatocake/everymoment/util/IdExtractorTest.java b/src/test/java/com/potatocake/everymoment/util/IdExtractorTest.java new file mode 100644 index 0000000..01b98c2 --- /dev/null +++ b/src/test/java/com/potatocake/everymoment/util/IdExtractorTest.java @@ -0,0 +1,28 @@ +package com.potatocake.everymoment.util; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.potatocake.everymoment.entity.Member; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class IdExtractorTest { + + @Test + @DisplayName("IdExtractor 가 성공적으로 ID를 추출한다.") + void should_ExtractId_When_ValidInput() { + // given + Member member = Member.builder() + .id(1L) + .build(); + + IdExtractor extractor = Member::getId; + + // when + Long extractedId = extractor.extractId(member); + + // then + assertThat(extractedId).isEqualTo(1L); + } + +} diff --git a/src/test/java/com/potatocake/everymoment/util/JwtUtilTest.java b/src/test/java/com/potatocake/everymoment/util/JwtUtilTest.java new file mode 100644 index 0000000..29fbe19 --- /dev/null +++ b/src/test/java/com/potatocake/everymoment/util/JwtUtilTest.java @@ -0,0 +1,127 @@ +package com.potatocake.everymoment.util; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.test.util.ReflectionTestUtils; + +class JwtUtilTest { + + private JwtUtil jwtUtil; + private static final String TEST_SECRET = "dGVzdF9zZWNyZXRfdGVzdF9zZWNyZXRfdGVzdF9zZWNyZXRfdGVzdF9zZWNyZXRfdGVzdF9zZWNyZXQ="; + + @BeforeEach + void setUp() { + jwtUtil = new JwtUtil(); + ReflectionTestUtils.setField(jwtUtil, "secret", TEST_SECRET); + jwtUtil.init(); + } + + @Test + @DisplayName("토큰이 성공적으로 생성된다.") + void should_CreateToken_When_ValidInput() { + // given + Long id = 1L; + + // when + String token = jwtUtil.create(id); + + // then + assertThat(token).isNotEmpty(); + assertThatCode(() -> jwtUtil.getId(token)) + .doesNotThrowAnyException(); + assertThat(jwtUtil.getId(token)).isEqualTo(id); + } + + @Test + @DisplayName("유효한 토큰에서 ID가 성공적으로 추출된다.") + void should_ExtractId_When_ValidToken() { + // given + Long expectedId = 1L; + String token = jwtUtil.create(expectedId); + + // when + Long extractedId = jwtUtil.getId(token); + + // then + assertThat(extractedId).isEqualTo(expectedId); + } + + @Test + @DisplayName("만료되지 않은 토큰은 유효하다고 판단된다.") + void should_ReturnFalse_When_TokenNotExpired() { + // given + String token = jwtUtil.create(1L); + + // when + boolean isExpired = jwtUtil.isExpired(token); + + // then + assertThat(isExpired).isFalse(); + } + + @Test + @DisplayName("잘못된 형식의 토큰은 만료되었다고 판단된다.") + void should_ReturnTrue_When_InvalidToken() { + // given + String invalidToken = "invalid.token.format"; + + // when + boolean isExpired = jwtUtil.isExpired(invalidToken); + + // then + assertThat(isExpired).isTrue(); + } + + @Test + @DisplayName("Authorization 헤더에서 토큰이 성공적으로 추출된다.") + void should_ResolveToken_When_ValidAuthorizationHeader() { + // given + String token = "valid-token"; + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader(HttpHeaders.AUTHORIZATION, jwtUtil.PREFIX + token); + + // when + Optional resolvedToken = jwtUtil.resolveToken(request); + + // then + assertThat(resolvedToken) + .isPresent() + .contains(token); + } + + @Test + @DisplayName("Authorization 헤더가 없으면 빈 Optional 이 반환된다.") + void should_ReturnEmpty_When_NoAuthorizationHeader() { + // given + HttpServletRequest request = new MockHttpServletRequest(); + + // when + Optional resolvedToken = jwtUtil.resolveToken(request); + + // then + assertThat(resolvedToken).isEmpty(); + } + + @Test + @DisplayName("Bearer 접두사가 없는 Authorization 헤더는 빈 Optional 을 반환한다.") + void should_ReturnEmpty_When_NoBearerPrefix() { + // given + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader(HttpHeaders.AUTHORIZATION, "invalid-format-token"); + + // when + Optional resolvedToken = jwtUtil.resolveToken(request); + + // then + assertThat(resolvedToken).isEmpty(); + } + +} diff --git a/src/test/java/com/potatocake/everymoment/util/PagingUtilTest.java b/src/test/java/com/potatocake/everymoment/util/PagingUtilTest.java index 4808d11..f22effa 100644 --- a/src/test/java/com/potatocake/everymoment/util/PagingUtilTest.java +++ b/src/test/java/com/potatocake/everymoment/util/PagingUtilTest.java @@ -1,16 +1,15 @@ package com.potatocake.everymoment.util; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.data.domain.Sort.Direction.ASC; -import com.potatocake.everymoment.entity.Member; import java.util.List; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.ScrollPosition; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Window; class PagingUtilTest { @@ -24,52 +23,121 @@ void setUp() { @Test @DisplayName("스크롤 위치가 성공적으로 생성된다.") - void should_CreateScrollPosition_When_KeyIsNull() { + void should_CreateScrollPosition_When_GivenKey() { + // given + Long key = 1L; + + // when + ScrollPosition position = pagingUtil.createScrollPosition(key); + + // then + assertThat(position).isNotNull(); + } + + @Test + @DisplayName("key 가 null 일 때 offset 스크롤 위치가 생성된다.") + void should_CreateOffsetPosition_When_KeyIsNull() { + // given + Long key = null; + // when - ScrollPosition position = pagingUtil.createScrollPosition(null); + ScrollPosition position = pagingUtil.createScrollPosition(key); // then - Assertions.assertThat(position).isNotNull(); + assertThat(position).isEqualTo(ScrollPosition.offset()); } @Test @DisplayName("페이지 정보가 성공적으로 생성된다.") - void should_CreatePageable_When_ValidSizeProvided() { + void should_CreatePageable_When_ValidInput() { + // given + int size = 10; + Direction direction = Direction.DESC; + // when - Pageable pageable = pagingUtil.createPageable(10, ASC); + Pageable pageable = pagingUtil.createPageable(size, direction); // then - assertThat(pageable).isNotNull(); - assertThat(pageable.getPageSize()).isEqualTo(10); + assertThat(pageable.getPageSize()).isEqualTo(size); + assertThat(pageable.getSort().getOrderFor("id").getDirection()).isEqualTo(direction); } @Test - @DisplayName("다음 페이지 키가 성공적으로 반환된다.") - void should_ReturnNextKey_When_WindowHasNext() { + @DisplayName("페이지 정보가 올바른 정렬 순서를 가진다.") + void should_CreatePageableWithCorrectSort_When_DirectionGiven() { // given - List members = List.of(Member.builder().id(1L).build()); - Window window = Window.from(members, ScrollPosition::offset, true); + int size = 10; + Direction direction = Direction.ASC; // when - Long nextKey = pagingUtil.getNextKey(window, Member::getId); + Pageable pageable = pagingUtil.createPageable(size, direction); // then - assertThat(nextKey).isNotNull(); - assertThat(nextKey).isEqualTo(1L); + Sort sort = pageable.getSort(); + assertThat(sort.getOrderFor("id")).isNotNull(); + assertThat(sort.getOrderFor("id").getDirection()).isEqualTo(direction); } @Test - @DisplayName("다음 페이지가 존재하지 않을 때, 키 값으로 null 을 반환한다.") - void should_ReturnNull_When_WindowHasNoNext() { + @DisplayName("다음 키가 성공적으로 생성된다.") + void should_GetNextKey_When_ValidWindow() { // given - List members = List.of(Member.builder().id(1L).build()); - Window window = Window.from(members, ScrollPosition::offset, false); + TestEntity entity1 = new TestEntity(1L); + TestEntity entity2 = new TestEntity(2L); + + List content = List.of(entity1, entity2); + ScrollPosition scrollPosition = ScrollPosition.offset(); + + Window window = Window.from(content, i -> scrollPosition, true); // when - Long nextKey = pagingUtil.getNextKey(window, Member::getId); + Long nextKey = pagingUtil.getNextKey(window, TestEntity::getId); + + // then + assertThat(nextKey).isEqualTo(2L); + } + + @Test + @DisplayName("다음 페이지가 없으면 null 을 반환한다.") + void should_ReturnNull_When_NoNextPage() { + // given + TestEntity entity = new TestEntity(1L); + ScrollPosition scrollPosition = ScrollPosition.offset(); + + Window window = Window.from(List.of(entity), i -> scrollPosition, false); + + // when + Long nextKey = pagingUtil.getNextKey(window, TestEntity::getId); + + // then + assertThat(nextKey).isNull(); + } + + @Test + @DisplayName("빈 윈도우에 대해 null 을 반환한다.") + void should_ReturnNull_When_EmptyWindow() { + // given + ScrollPosition scrollPosition = ScrollPosition.offset(); + + Window window = Window.from(List.of(), i -> scrollPosition, false); + + // when + Long nextKey = pagingUtil.getNextKey(window, TestEntity::getId); // then assertThat(nextKey).isNull(); } + private static class TestEntity { + private final Long id; + + TestEntity(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + } + } diff --git a/src/test/java/com/potatocake/everymoment/util/S3FileUploaderTest.java b/src/test/java/com/potatocake/everymoment/util/S3FileUploaderTest.java new file mode 100644 index 0000000..b748018 --- /dev/null +++ b/src/test/java/com/potatocake/everymoment/util/S3FileUploaderTest.java @@ -0,0 +1,104 @@ +package com.potatocake.everymoment.util; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.PutObjectRequest; +import com.potatocake.everymoment.config.AwsS3Properties; +import com.potatocake.everymoment.exception.ErrorCode; +import com.potatocake.everymoment.exception.GlobalException; +import java.io.IOException; +import java.net.URL; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.util.ReflectionTestUtils; + +@ExtendWith(MockitoExtension.class) +class S3FileUploaderTest { + + private S3FileUploader uploader; + + @Mock + private AmazonS3 amazonS3; + + @Mock + private AwsS3Properties properties; + + @BeforeEach + void setUp() { + uploader = new S3FileUploader(properties); + ReflectionTestUtils.setField(uploader, "amazonS3", amazonS3); + } + + @Test + @DisplayName("파일이 성공적으로 업로드된다.") + void should_UploadFile_When_ValidInput() throws IOException { + // given + MockMultipartFile file = new MockMultipartFile( + "file", + "test.jpg", + MediaType.IMAGE_JPEG_VALUE, + "test image".getBytes() + ); + + given(properties.bucket()).willReturn("test-bucket"); + given(amazonS3.getUrl(any(), any())).willReturn(new URL("https://example.com/test.jpg")); + + // when + String url = uploader.uploadFile(file); + + // then + assertThat(url).isEqualTo("https://example.com/test.jpg"); + then(amazonS3).should().putObject(any(PutObjectRequest.class)); + } + + @Test + @DisplayName("지원하지 않는 파일 형식이면 예외가 발생한다.") + void should_ThrowException_When_UnsupportedFileType() { + // given + MockMultipartFile file = new MockMultipartFile( + "file", + "test.txt", + MediaType.TEXT_PLAIN_VALUE, + "test content".getBytes() + ); + + // when & then + assertThatThrownBy(() -> uploader.uploadFile(file)) + .isInstanceOf(GlobalException.class) + .hasFieldOrPropertyWithValue("errorCode", ErrorCode.INVALID_FILE_TYPE); + + then(amazonS3).shouldHaveNoInteractions(); + } + + @Test + @DisplayName("파일 업로드 실패시 예외가 발생한다.") + void should_ThrowException_When_UploadFails() { + // given + MockMultipartFile file = new MockMultipartFile( + "file", + "test.jpg", + MediaType.IMAGE_JPEG_VALUE, + "test image".getBytes() + ); + + given(properties.bucket()).willReturn("test-bucket"); + given(amazonS3.putObject(any(PutObjectRequest.class))) + .willThrow(new RuntimeException("Upload failed")); + + // when & then + assertThatThrownBy(() -> uploader.uploadFile(file)) + .isInstanceOf(RuntimeException.class); + } + +}