diff --git a/backend/src/main/java/harustudy/backend/auth/util/JwtTokenProvider.java b/backend/src/main/java/harustudy/backend/auth/util/JwtTokenProvider.java index 99281506..ee6bbee2 100644 --- a/backend/src/main/java/harustudy/backend/auth/util/JwtTokenProvider.java +++ b/backend/src/main/java/harustudy/backend/auth/util/JwtTokenProvider.java @@ -1,6 +1,7 @@ package harustudy.backend.auth.util; import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Clock; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; @@ -14,6 +15,8 @@ @Component public class JwtTokenProvider { + private final Clock clock; + @Builder public String createAccessToken(String subject, Long accessTokenExpireLength, String secretKey) { Claims claims = generateClaims(subject, accessTokenExpireLength); @@ -28,7 +31,7 @@ public String createAccessToken(String subject, Long accessTokenExpireLength, St } private Claims generateClaims(String subject, Long accessTokenExpireLength) { - Date now = new Date(); + Date now = clock.now(); Date expiredAt = new Date(now.getTime() + accessTokenExpireLength); return Jwts.claims() diff --git a/backend/src/main/java/harustudy/backend/config/BeanConfig.java b/backend/src/main/java/harustudy/backend/config/BeanConfig.java index d1ec9568..a24644e8 100644 --- a/backend/src/main/java/harustudy/backend/config/BeanConfig.java +++ b/backend/src/main/java/harustudy/backend/config/BeanConfig.java @@ -2,6 +2,8 @@ import harustudy.backend.participantcode.domain.CodeGenerationStrategy; import harustudy.backend.participantcode.domain.GenerationStrategy; +import io.jsonwebtoken.Clock; +import io.jsonwebtoken.impl.DefaultClock; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -12,4 +14,9 @@ public class BeanConfig { public GenerationStrategy generationStrategy() { return new CodeGenerationStrategy(); } + + @Bean + public Clock clock() { + return new DefaultClock(); + } } diff --git a/backend/src/test/java/harustudy/backend/HaruStudyApplicationTests.java b/backend/src/test/java/harustudy/backend/HaruStudyApplicationTests.java deleted file mode 100644 index 4a513b3a..00000000 --- a/backend/src/test/java/harustudy/backend/HaruStudyApplicationTests.java +++ /dev/null @@ -1,12 +0,0 @@ -package harustudy.backend; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class HaruStudyApplicationTests { - - @Test - void contextLoads() { - } -} diff --git a/backend/src/test/java/harustudy/backend/auth/AuthArgumentResolverTest.java b/backend/src/test/java/harustudy/backend/auth/AuthArgumentResolverTest.java index bcc70182..793c6069 100644 --- a/backend/src/test/java/harustudy/backend/auth/AuthArgumentResolverTest.java +++ b/backend/src/test/java/harustudy/backend/auth/AuthArgumentResolverTest.java @@ -6,13 +6,14 @@ import harustudy.backend.auth.dto.AuthMember; import harustudy.backend.auth.service.AuthService; +import harustudy.backend.auth.util.BearerAuthorizationParser; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; import org.mockito.Mock; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.MethodParameter; import org.springframework.http.HttpHeaders; import org.springframework.mock.web.MockHttpServletRequest; @@ -23,21 +24,15 @@ @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores.class) -@SpringBootTest +@ExtendWith(MockitoExtension.class) class AuthArgumentResolverTest { - @Autowired + @InjectMocks private AuthArgumentResolver authArgumentResolver; - - @MockBean - private AuthService authService; - @Mock - private MethodParameter methodParameter; - @Mock - private ModelAndViewContainer modelAndViewContainer; + private AuthService authService; @Mock - private WebDataBinderFactory webDataBinderFactory; + private BearerAuthorizationParser bearerAuthorizationParser; @Test void 액세스_토큰의_파싱된_아이디에_해당하는_인증_멤버가_반환된다() { @@ -48,12 +43,13 @@ class AuthArgumentResolverTest { request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken); NativeWebRequest nativeWebRequest = new ServletWebRequest(request); + given(bearerAuthorizationParser.parse(any(String.class))) + .willReturn(accessToken); given(authService.parseMemberId(any(String.class))) .willReturn(mockedAuthMemberId); // when - AuthMember authMember = authArgumentResolver.resolveArgument(methodParameter, - modelAndViewContainer, nativeWebRequest, webDataBinderFactory); + AuthMember authMember = authArgumentResolver.resolveArgument(null, null, nativeWebRequest, null); // then assertThat(authMember.id()).isEqualTo(Long.valueOf(mockedAuthMemberId)); diff --git a/backend/src/test/java/harustudy/backend/auth/AuthInterceptorTest.java b/backend/src/test/java/harustudy/backend/auth/AuthInterceptorTest.java index 9f1b428f..c20f48eb 100644 --- a/backend/src/test/java/harustudy/backend/auth/AuthInterceptorTest.java +++ b/backend/src/test/java/harustudy/backend/auth/AuthInterceptorTest.java @@ -7,9 +7,15 @@ import static org.mockito.Mockito.times; import harustudy.backend.auth.service.AuthService; +import harustudy.backend.auth.util.BearerAuthorizationParser; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -19,14 +25,16 @@ @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores.class) -@SpringBootTest +@ExtendWith(MockitoExtension.class) class AuthInterceptorTest { - @Autowired + @InjectMocks private AuthInterceptor authInterceptor; - @MockBean + @Mock private AuthService authService; + @Spy + private BearerAuthorizationParser bearerAuthorizationParser = new BearerAuthorizationParser(); @Test void preflight_요청시_예외를_던지지_않는다() { diff --git a/backend/src/test/java/harustudy/backend/integration/AuthIntegrationTest.java b/backend/src/test/java/harustudy/backend/integration/AuthIntegrationTest.java index 97190a33..a32790e2 100644 --- a/backend/src/test/java/harustudy/backend/integration/AuthIntegrationTest.java +++ b/backend/src/test/java/harustudy/backend/integration/AuthIntegrationTest.java @@ -1,26 +1,27 @@ package harustudy.backend.integration; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + import harustudy.backend.auth.domain.RefreshToken; import harustudy.backend.auth.dto.OauthLoginRequest; import harustudy.backend.auth.dto.OauthTokenResponse; import harustudy.backend.auth.dto.TokenResponse; import harustudy.backend.member.domain.Member; import jakarta.servlet.http.Cookie; -import org.junit.jupiter.api.BeforeEach; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.Map; import org.junit.jupiter.api.Test; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.web.servlet.MvcResult; -import java.nio.charset.StandardCharsets; -import java.util.Map; - -import static org.assertj.core.api.SoftAssertions.assertSoftly; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SuppressWarnings("NonAsciiCharacters") class AuthIntegrationTest extends IntegrationTest { @@ -63,9 +64,8 @@ class AuthIntegrationTest extends IntegrationTest { // given MemberDto memberDto = createMember("member1"); - // access token을 재발급 하더라도 Date는 초 단위의 시간 정보를 담은 액세스 토큰을 생성하기 때문에 - // 같은 access token이 만들어지는 문제가 있어서 갱신된다는 것을 검증하기 위해 사용 - Thread.sleep(1000); + Date now = new Date(); + when(customClock.now()).thenReturn(new Date(now.getTime() + 1000L)); // when MvcResult result = mockMvc.perform( diff --git a/backend/src/test/java/harustudy/backend/integration/IntegrationTest.java b/backend/src/test/java/harustudy/backend/integration/IntegrationTest.java index 7f63f4b8..f7eb8464 100644 --- a/backend/src/test/java/harustudy/backend/integration/IntegrationTest.java +++ b/backend/src/test/java/harustudy/backend/integration/IntegrationTest.java @@ -1,37 +1,29 @@ package harustudy.backend.integration; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.mockito.Mockito.when; import com.fasterxml.jackson.databind.ObjectMapper; import harustudy.backend.auth.config.TokenConfig; import harustudy.backend.auth.domain.RefreshToken; import harustudy.backend.auth.domain.oauth.OauthClients; -import harustudy.backend.auth.dto.OauthLoginRequest; -import harustudy.backend.auth.dto.OauthTokenResponse; -import harustudy.backend.auth.dto.TokenResponse; import harustudy.backend.auth.util.JwtTokenProvider; import harustudy.backend.member.domain.LoginType; import harustudy.backend.member.domain.Member; import harustudy.backend.participantcode.domain.GenerationStrategy; +import io.jsonwebtoken.Clock; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.servlet.http.Cookie; -import java.nio.charset.StandardCharsets; -import java.util.Map; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.context.WebApplicationContext; +import java.util.Date; @DisplayNameGeneration(ReplaceUnderscores.class) @@ -55,14 +47,23 @@ class IntegrationTest { @Autowired private TokenConfig tokenConfig; + @MockBean + protected OauthClients oauthClients; + + @MockBean + protected Clock customClock; + @Autowired protected GenerationStrategy generationStrategy; - @MockBean - protected OauthClients oauthClients; + @BeforeEach + void setUp() { + when(customClock.now()).thenReturn(new Date()); + } protected MemberDto createMember(String name) { Member member = generateAndSaveMemberNamedWith(name); + when(customClock.now()).thenReturn(new Date()); String accessToken = jwtTokenProvider.createAccessToken(String.valueOf(member.getId()), tokenConfig.accessTokenExpireLength(), tokenConfig.secretKey()); RefreshToken refreshToken = generateAndSaveRefreshTokenOf(member);