diff --git a/build.gradle b/build.gradle index c0bc4db..e7f2833 100644 --- a/build.gradle +++ b/build.gradle @@ -48,6 +48,7 @@ dependencies { implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation', version: '2.7.5' implementation group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '2.7.5' implementation group: 'io.springfox', name: 'springfox-boot-starter', version: '3.0.0' + testImplementation 'junit:junit:4.13.1' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' runtimeOnly 'org.postgresql:postgresql' @@ -55,6 +56,7 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.8.0' + } tasks.named('test') { diff --git a/src/main/java/com/techeer/cokkiri/domain/user/controller/UserController.java b/src/main/java/com/techeer/cokkiri/domain/user/controller/UserController.java index 84c1f6c..bef3d72 100644 --- a/src/main/java/com/techeer/cokkiri/domain/user/controller/UserController.java +++ b/src/main/java/com/techeer/cokkiri/domain/user/controller/UserController.java @@ -1,8 +1,6 @@ package com.techeer.cokkiri.domain.user.controller; -import static com.techeer.cokkiri.global.result.ResultCode.USER_LOGIN_SUCCESS; -import static com.techeer.cokkiri.global.result.ResultCode.USER_REGISTRATION_SUCCESS; -import static com.techeer.cokkiri.global.result.ResultCode.USER_USERNAME_NOT_DUPLICATED; +import static com.techeer.cokkiri.global.result.ResultCode.*; import com.techeer.cokkiri.domain.user.dto.UserDto; import com.techeer.cokkiri.domain.user.entity.User; @@ -53,10 +51,10 @@ public ResponseEntity isDuplicatedUsername(@PathVariable String @PostMapping("/login") public ResponseEntity login( @RequestBody @Valid UserDto.LoginRequest userRequest) { - boolean isValidUser = loginService.isValidUser(userRequest); + boolean isValid = loginService.isValidPassword(userRequest); - if (isValidUser) { - User user = userService.findUserByUsername(userRequest.getUsername()); + if (isValid) { + User user = userService.findByUsername(userRequest.getUsername()); loginService.login(user.getId()); } return ResponseEntity.ok(ResultResponse.of(USER_LOGIN_SUCCESS)); diff --git a/src/main/java/com/techeer/cokkiri/domain/user/exception/UserPasswordWrongException.java b/src/main/java/com/techeer/cokkiri/domain/user/exception/UserPasswordWrongException.java new file mode 100644 index 0000000..b5a363e --- /dev/null +++ b/src/main/java/com/techeer/cokkiri/domain/user/exception/UserPasswordWrongException.java @@ -0,0 +1,10 @@ +package com.techeer.cokkiri.domain.user.exception; + +import com.techeer.cokkiri.global.error.ErrorCode; +import com.techeer.cokkiri.global.error.exception.BusinessException; + +public class UserPasswordWrongException extends BusinessException { + public UserPasswordWrongException() { + super(ErrorCode.USER_PASSWORD_NOT_MATCH); + } +} diff --git a/src/main/java/com/techeer/cokkiri/domain/user/repository/UserRepository.java b/src/main/java/com/techeer/cokkiri/domain/user/repository/UserRepository.java index 9f83ae9..59a8923 100644 --- a/src/main/java/com/techeer/cokkiri/domain/user/repository/UserRepository.java +++ b/src/main/java/com/techeer/cokkiri/domain/user/repository/UserRepository.java @@ -7,5 +7,5 @@ public interface UserRepository extends JpaRepository { boolean existsByUsername(String username); - Optional findUserByUsername(String username); + Optional findByUsername(String username); } diff --git a/src/main/java/com/techeer/cokkiri/domain/user/service/LoginService.java b/src/main/java/com/techeer/cokkiri/domain/user/service/LoginService.java index 238d540..cbde530 100644 --- a/src/main/java/com/techeer/cokkiri/domain/user/service/LoginService.java +++ b/src/main/java/com/techeer/cokkiri/domain/user/service/LoginService.java @@ -2,6 +2,7 @@ import com.techeer.cokkiri.domain.user.dto.UserDto; import com.techeer.cokkiri.domain.user.entity.User; +import com.techeer.cokkiri.domain.user.exception.UserPasswordWrongException; import com.techeer.cokkiri.global.util.PasswordUtil; import javax.servlet.http.HttpSession; import lombok.AccessLevel; @@ -36,8 +37,12 @@ public boolean isUserLogin() { return getLoginUserId() != null; } - public boolean isValidUser(UserDto.LoginRequest userRequest) { - User user = userService.findUserByUsername(userRequest.getUsername()); - return passwordUtil.isSamePassword(userRequest.getPassword(), user.getPassword()); + public boolean isValidPassword(UserDto.LoginRequest userRequest) { + User user = userService.findByUsername(userRequest.getUsername()); + boolean isValidPassword = + passwordUtil.isSamePassword(userRequest.getPassword(), user.getPassword()); + if (isValidPassword) { + return true; + } else throw new UserPasswordWrongException(); } } diff --git a/src/main/java/com/techeer/cokkiri/domain/user/service/UserService.java b/src/main/java/com/techeer/cokkiri/domain/user/service/UserService.java index 728b755..d666db1 100644 --- a/src/main/java/com/techeer/cokkiri/domain/user/service/UserService.java +++ b/src/main/java/com/techeer/cokkiri/domain/user/service/UserService.java @@ -31,7 +31,7 @@ public User findUserById(long id) { return userRepository.findById(id).orElseThrow(UserNotFoundException::new); } - public User findUserByUsername(String username) { - return userRepository.findUserByUsername(username).orElseThrow(UserNotFoundException::new); + public User findByUsername(String username) { + return userRepository.findByUsername(username).orElseThrow(UserNotFoundException::new); } } diff --git a/src/main/java/com/techeer/cokkiri/global/constant/RegExp.java b/src/main/java/com/techeer/cokkiri/global/constant/RegExp.java index 53cab3a..c434d38 100644 --- a/src/main/java/com/techeer/cokkiri/global/constant/RegExp.java +++ b/src/main/java/com/techeer/cokkiri/global/constant/RegExp.java @@ -4,5 +4,5 @@ public class RegExp { public static final String PASSWORD_REGEXP = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#!~$%^&-+=()])(?=\\S+$).{8,16}$"; - public static final String USERNAME_REGEXP = "^[A-Za-z0-9]{5,20}$"; + public static final String USERNAME_REGEXP = "^[A-Za-z0-9]{5,21}$"; } diff --git a/src/main/java/com/techeer/cokkiri/global/entity/BaseEntity.java b/src/main/java/com/techeer/cokkiri/global/entity/BaseEntity.java index 6ef23a8..d19a7dd 100644 --- a/src/main/java/com/techeer/cokkiri/global/entity/BaseEntity.java +++ b/src/main/java/com/techeer/cokkiri/global/entity/BaseEntity.java @@ -4,8 +4,8 @@ import javax.persistence.EntityListeners; import javax.persistence.MappedSuperclass; import lombok.*; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.LastModifiedDate; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @Getter @@ -14,9 +14,9 @@ @EntityListeners(AuditingEntityListener.class) @ToString public class BaseEntity { - @CreatedDate private LocalDateTime createdAt; + @CreationTimestamp private LocalDateTime createdAt; - @LastModifiedDate private LocalDateTime updatedAt; + @UpdateTimestamp private LocalDateTime updatedAt; private boolean isDeleted; diff --git a/src/main/java/com/techeer/cokkiri/global/error/ErrorCode.java b/src/main/java/com/techeer/cokkiri/global/error/ErrorCode.java index 61b9794..9bf6254 100644 --- a/src/main/java/com/techeer/cokkiri/global/error/ErrorCode.java +++ b/src/main/java/com/techeer/cokkiri/global/error/ErrorCode.java @@ -17,6 +17,8 @@ public enum ErrorCode { UNAUTHORIZED_ACCESS_ERROR(403, "U003", "승인되지 않은 접근"), USER_USERNAME_DUPLICATED(409, "U004", "회원 아이디 중복"), + USER_PASSWORD_NOT_MATCH(401, "U005", "비밀번호가 일치하지 않음"), + // Study STUDY_DUPLICATION_ERROR(409, "S001", "스터디의 이름이 중복됨"), STUDY_NOT_FOUND_ERROR(400, "S002", "스터디를 찾을 수 없음"), diff --git a/src/test/java/com/techeer/cokkiri/domain/user/Controller/UserControllerTest.java b/src/test/java/com/techeer/cokkiri/domain/user/Controller/UserControllerTest.java new file mode 100644 index 0000000..20e4de3 --- /dev/null +++ b/src/test/java/com/techeer/cokkiri/domain/user/Controller/UserControllerTest.java @@ -0,0 +1,76 @@ +package com.techeer.cokkiri.domain.user.Controller; + +import static com.techeer.cokkiri.fixture.UserFixtures.USER_LOGIN_REQUEST; +import static com.techeer.cokkiri.global.result.ResultCode.USER_LOGIN_SUCCESS; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.content; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.techeer.cokkiri.domain.user.controller.UserController; +import com.techeer.cokkiri.domain.user.entity.User; +import com.techeer.cokkiri.domain.user.service.LoginService; +import com.techeer.cokkiri.domain.user.service.UserService; +import com.techeer.cokkiri.global.result.ResultResponse; +import com.techeer.cokkiri.global.util.JsonUtil; +import java.nio.charset.StandardCharsets; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +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.result.MockMvcResultMatchers; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.filter.CharacterEncodingFilter; + +@AutoConfigureMockMvc +@WebMvcTest(UserController.class) +public class UserControllerTest { + + private final JsonUtil jsonUtil = new JsonUtil(); + + @MockBean UserService userService; + + @MockBean LoginService loginService; + + @Mock User mockUser; + + private MockMvc mockMvc; + + @BeforeEach + void setUp(WebApplicationContext applicationContext) { + mockMvc = + MockMvcBuilders.webAppContextSetup(applicationContext) + .addFilter(new CharacterEncodingFilter(StandardCharsets.UTF_8.name(), true)) + .build(); + } + + // 미완성 테스트 - login메소드 오류 + @Test + @DisplayName("로그인에 성공할 경우 ResultResponse(") + void loginTest() throws Exception { + // given - 회원가입 + when(loginService.isValidPassword(any())).thenReturn(true); + when(userService.findByUsername(any())).thenReturn(mockUser); + when(mockUser.getId()).thenReturn(1L); + + mockMvc + .perform( + post("/api/v1/users/login") + .contentType(MediaType.APPLICATION_JSON) + .content(jsonUtil.toJsonString(USER_LOGIN_REQUEST))) + .andExpect(status().isOk()) + .andExpect( + MockMvcResultMatchers.content() + .string(jsonUtil.toJsonString(ResultResponse.of(USER_LOGIN_SUCCESS)))) + .andDo(print()); // 전체 메시지 확인 + System.out.println(content()); + } +} diff --git a/src/test/java/com/techeer/cokkiri/domain/user/repository/UserRepositoryTest.java b/src/test/java/com/techeer/cokkiri/domain/user/repository/UserRepositoryTest.java new file mode 100644 index 0000000..c85266c --- /dev/null +++ b/src/test/java/com/techeer/cokkiri/domain/user/repository/UserRepositoryTest.java @@ -0,0 +1,43 @@ +package com.techeer.cokkiri.domain.user.repository; + +import static com.techeer.cokkiri.fixture.UserFixtures.DEFAULT_USER; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.techeer.cokkiri.domain.user.entity.User; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +@DataJpaTest +public class UserRepositoryTest { + + @Autowired private UserRepository userRepository; + + private User user; + + @BeforeEach + void saveUser() {} + + @Test + @DisplayName("user의 존재여부를 username으로 확인한다.") + void existsByUsername() { + user = DEFAULT_USER; + userRepository.save(user); + assertTrue(userRepository.existsByUsername(user.getUsername())); + } + + @Test + @DisplayName("username으로 user를 찾는다.") + void findByUsername() { + // given + user = DEFAULT_USER; + user = userRepository.save(user); + User savedUser = userRepository.findByUsername(user.getUsername()).orElseThrow(); + + // then + assertEquals(user, savedUser); + } +} diff --git a/src/test/java/com/techeer/cokkiri/domain/user/service/UserServiceTest.java b/src/test/java/com/techeer/cokkiri/domain/user/service/UserServiceTest.java new file mode 100644 index 0000000..68def21 --- /dev/null +++ b/src/test/java/com/techeer/cokkiri/domain/user/service/UserServiceTest.java @@ -0,0 +1,82 @@ +package com.techeer.cokkiri.domain.user.service; + +import static com.techeer.cokkiri.fixture.UserFixtures.DEFAULT_USER; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import com.techeer.cokkiri.domain.user.dto.UserDto; +import com.techeer.cokkiri.domain.user.entity.User; +import com.techeer.cokkiri.domain.user.exception.UserPasswordWrongException; +import com.techeer.cokkiri.domain.user.mapper.UserMapper; +import com.techeer.cokkiri.domain.user.repository.UserRepository; +import com.techeer.cokkiri.global.util.PasswordUtil; +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.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class UserServiceTest { + + @Mock private UserRepository userRepository; + + @Mock private UserMapper userMapper; + + @Mock private PasswordUtil passwordUtil; + + @Mock private UserService userService; + + @InjectMocks private LoginService loginService; + + private User user; + private UserDto.LoginRequest loginRequest; + + @BeforeEach + void setup() { + user = DEFAULT_USER; + + loginRequest = + UserDto.LoginRequest.builder() + .username(user.getUsername()) + .password(user.getPassword()) + .build(); + } + + @Nested + class loginTest { + @Test + @DisplayName("username은 존재하나 비밀번호가 다를 경우 로그인 실패한다.") + void wrongPassword() { + // given + when(userService.findByUsername(any())).thenReturn(user); + when(passwordUtil.isSamePassword(any(), any())).thenReturn(false); + + // then + assertThrows( + UserPasswordWrongException.class, + () -> { + loginService.isValidPassword(loginRequest); + }); + } + + @Test + @DisplayName("올바른 username과 비밀번호 입력시 로그인 성공한다.") + void loginSuccess() { + // given + when(userService.findByUsername(any())).thenReturn(user); + when(passwordUtil.isSamePassword(any(), any())).thenReturn(true); + + // when + boolean isValid = loginService.isValidPassword(loginRequest); + + // then + assertTrue(isValid); + } + } +} diff --git a/src/test/java/com/techeer/cokkiri/fixture/UserFixtures.java b/src/test/java/com/techeer/cokkiri/fixture/UserFixtures.java index cfc44e9..cafd396 100644 --- a/src/test/java/com/techeer/cokkiri/fixture/UserFixtures.java +++ b/src/test/java/com/techeer/cokkiri/fixture/UserFixtures.java @@ -1,5 +1,6 @@ package com.techeer.cokkiri.fixture; +import com.techeer.cokkiri.domain.user.dto.UserDto; import com.techeer.cokkiri.domain.user.entity.User; public class UserFixtures { @@ -7,8 +8,28 @@ public class UserFixtures { User.builder() .username("javaStudyManager") .bio("자바를 좋아합니다.") - .imageUrl("url") .nickname("javaMania") .password("qwer123!") .build(); + + public static final User DEFAULT_USER = + User.builder() + .username("DefaultUser1") + .bio("테스트용 디폴트 유저") + .nickname("default") + .password("Test1234!") + .build(); + + public static final UserDto.LoginRequest USER_LOGIN_REQUEST = + UserDto.LoginRequest.builder() + .username(DEFAULT_USER.getUsername()) + .password(DEFAULT_USER.getPassword()) + .build(); + + public static final UserDto.RegisterRequest USER_REGISTER_REQUEST = + UserDto.RegisterRequest.builder() + .username(DEFAULT_USER.getUsername()) + .password(DEFAULT_USER.getPassword()) + .nickname(DEFAULT_USER.getNickname()) + .build(); } diff --git a/src/test/java/com/techeer/cokkiri/global/util/JsonUtil.java b/src/test/java/com/techeer/cokkiri/global/util/JsonUtil.java new file mode 100644 index 0000000..52cc54a --- /dev/null +++ b/src/test/java/com/techeer/cokkiri/global/util/JsonUtil.java @@ -0,0 +1,12 @@ +package com.techeer.cokkiri.global.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JsonUtil { + private final ObjectMapper objectMapper = new ObjectMapper(); + + public String toJsonString(Object object) throws JsonProcessingException { + return objectMapper.writeValueAsString(object); + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 62ec3b4..e51830d 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -17,4 +17,4 @@ spring: generate-ddl: true properties: hibernate: - format_sql: true \ No newline at end of file + format_sql: true