diff --git a/pom.xml b/pom.xml index f8cc255..e3c5616 100644 --- a/pom.xml +++ b/pom.xml @@ -31,6 +31,9 @@ 3.2.2 15.0.0 20.2 + 5.10.0 + 5.4.0 + 0.8.8 @@ -65,6 +68,12 @@ spring-boot-starter-graphql + + org.springframework.boot + spring-boot-starter-test + test + + com.graphql-java-kickstart graphql-java-kickstart @@ -164,6 +173,20 @@ ${minio.version} + + org.junit.jupiter + junit-jupiter-api + ${junit-jupiter.version} + test + + + + org.mockito + mockito-core + ${mockito.version} + test + + @@ -223,6 +246,49 @@ + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + default-prepare-agent + + prepare-agent + + + + report + prepare-package + + report + + + + check + + check + + + + + PACKAGE + + + INSTRUCTION + COVEREDRATIO + 0.60 + + + + com.example.tasklist.service.impl + + + + + + + diff --git a/src/test/java/com/example/tasklist/config/TestConfig.java b/src/test/java/com/example/tasklist/config/TestConfig.java new file mode 100644 index 0000000..2cb4650 --- /dev/null +++ b/src/test/java/com/example/tasklist/config/TestConfig.java @@ -0,0 +1,103 @@ +package com.example.tasklist.config; + +import com.example.tasklist.repository.TaskRepository; +import com.example.tasklist.repository.UserRepository; +import com.example.tasklist.service.AuthService; +import com.example.tasklist.service.ImageService; +import com.example.tasklist.service.TaskService; +import com.example.tasklist.service.UserService; +import com.example.tasklist.service.impl.AuthServiceImpl; +import com.example.tasklist.service.impl.ImageServiceImpl; +import com.example.tasklist.service.impl.TaskServiceImpl; +import com.example.tasklist.service.impl.UserServiceImpl; +import com.example.tasklist.service.props.JwtProperties; +import com.example.tasklist.service.props.MinioProperties; +import com.example.tasklist.web.security.JwtTokenProvider; +import com.example.tasklist.web.security.JwtUserDetailsService; +import io.minio.MinioClient; +import lombok.RequiredArgsConstructor; +import org.mockito.Mockito; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@TestConfiguration +@RequiredArgsConstructor +public class TestConfig { + + private final UserRepository userRepository; + private final TaskRepository taskRepository; + private final AuthenticationManager authenticationManager; + + @Bean + @Primary + public PasswordEncoder testPasswordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public JwtProperties jwtProperties() { + JwtProperties jwtProperties = new JwtProperties(); + jwtProperties.setSecret( + "dmdqdmphdmpndmVjdmplZ2VzamdjdnJzY2p2cnZyY2dzYWNia2hh" + ); + return jwtProperties; + } + + @Bean + @Primary + public UserDetailsService userDetailsService() { + return new JwtUserDetailsService(userService()); + } + + @Bean + public MinioClient minioClient() { + return Mockito.mock(MinioClient.class); + } + + @Bean + public MinioProperties minioProperties() { + MinioProperties properties = new MinioProperties(); + properties.setBucket("images"); + return properties; + } + + @Bean + @Primary + public ImageService imageService() { + return new ImageServiceImpl(minioClient(), minioProperties()); + } + + @Bean + public JwtTokenProvider tokenProvider() { + return new JwtTokenProvider(jwtProperties(), + userDetailsService(), + userService()); + } + + @Bean + @Primary + public UserService userService() { + return new UserServiceImpl(userRepository, testPasswordEncoder()); + } + + @Bean + @Primary + public TaskService taskService() { + return new TaskServiceImpl(taskRepository, + imageService()); + } + + @Bean + @Primary + public AuthService authService() { + return new AuthServiceImpl(authenticationManager, + userService(), + tokenProvider()); + } + +} diff --git a/src/test/java/com/example/tasklist/service/impl/AuthServiceImplTest.java b/src/test/java/com/example/tasklist/service/impl/AuthServiceImplTest.java new file mode 100644 index 0000000..f2e94ec --- /dev/null +++ b/src/test/java/com/example/tasklist/service/impl/AuthServiceImplTest.java @@ -0,0 +1,117 @@ +package com.example.tasklist.service.impl; + +import com.example.tasklist.config.TestConfig; +import com.example.tasklist.domain.exception.ResourceNotFoundException; +import com.example.tasklist.domain.user.Role; +import com.example.tasklist.domain.user.User; +import com.example.tasklist.repository.TaskRepository; +import com.example.tasklist.repository.UserRepository; +import com.example.tasklist.service.UserService; +import com.example.tasklist.web.dto.auth.JwtRequest; +import com.example.tasklist.web.dto.auth.JwtResponse; +import com.example.tasklist.web.security.JwtTokenProvider; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.Collections; +import java.util.Set; + +@ExtendWith(SpringExtension.class) +@ActiveProfiles("test") +@Import(TestConfig.class) +@ExtendWith(MockitoExtension.class) +public class AuthServiceImplTest { + + @MockBean + private AuthenticationManager authenticationManager; + + @MockBean + private UserService userService; + + @MockBean + private UserRepository userRepository; + + @MockBean + private TaskRepository taskRepository; + + @MockBean + private JwtTokenProvider tokenProvider; + + @Autowired + private AuthServiceImpl authService; + + @Test + void login() { + Long userId = 1L; + String username = "username"; + String password = "password"; + Set roles = Collections.emptySet(); + String accessToken = "accessToken"; + String refreshToken = "refreshToken"; + JwtRequest request = new JwtRequest(); + request.setUsername(username); + request.setPassword(password); + User user = new User(); + user.setId(userId); + user.setUsername(username); + user.setRoles(roles); + Mockito.when(userService.getByUsername(username)) + .thenReturn(user); + Mockito.when(tokenProvider.createAccessToken(userId, username, roles)) + .thenReturn(accessToken); + Mockito.when(tokenProvider.createRefreshToken(userId, username)) + .thenReturn(refreshToken); + JwtResponse response = authService.login(request); + Mockito.verify(authenticationManager) + .authenticate( + new UsernamePasswordAuthenticationToken( + request.getUsername(), + request.getPassword() + ) + ); + Assertions.assertEquals(username, response.getUsername()); + Assertions.assertEquals(userId, response.getId()); + Assertions.assertNotNull(response.getAccessToken()); + Assertions.assertNotNull(response.getRefreshToken()); + } + + @Test + void loginWithIncorrectUsername() { + String username = "username"; + String password = "password"; + JwtRequest request = new JwtRequest(); + request.setUsername(username); + request.setPassword(password); + Mockito.when(userService.getByUsername(username)) + .thenThrow(ResourceNotFoundException.class); + Mockito.verifyNoInteractions(tokenProvider); + Assertions.assertThrows(ResourceNotFoundException.class, + () -> authService.login(request)); + } + + @Test + void refresh() { + String accessToken = "accessToken"; + String refreshToken = "refreshToken"; + String newRefreshToken = "newRefreshToken"; + JwtResponse response = new JwtResponse(); + response.setAccessToken(accessToken); + response.setRefreshToken(newRefreshToken); + Mockito.when(tokenProvider.refreshUserTokens(refreshToken)) + .thenReturn(response); + JwtResponse testResponse = authService.refresh(refreshToken); + Mockito.verify(tokenProvider).refreshUserTokens(refreshToken); + Assertions.assertEquals(response, testResponse); + } + +} diff --git a/src/test/java/com/example/tasklist/service/impl/TaskServiceImplTest.java b/src/test/java/com/example/tasklist/service/impl/TaskServiceImplTest.java new file mode 100644 index 0000000..d9d833b --- /dev/null +++ b/src/test/java/com/example/tasklist/service/impl/TaskServiceImplTest.java @@ -0,0 +1,145 @@ +package com.example.tasklist.service.impl; + +import com.example.tasklist.config.TestConfig; +import com.example.tasklist.domain.exception.ResourceNotFoundException; +import com.example.tasklist.domain.task.Status; +import com.example.tasklist.domain.task.Task; +import com.example.tasklist.domain.task.TaskImage; +import com.example.tasklist.repository.TaskRepository; +import com.example.tasklist.repository.UserRepository; +import com.example.tasklist.service.ImageService; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +@ExtendWith(SpringExtension.class) +@ActiveProfiles("test") +@Import(TestConfig.class) +@ExtendWith(MockitoExtension.class) +public class TaskServiceImplTest { + + @MockBean + private TaskRepository taskRepository; + + @MockBean + private ImageService imageService; + + @MockBean + private UserRepository userRepository; + + @MockBean + private AuthenticationManager authenticationManager; + + @Autowired + private TaskServiceImpl taskService; + + @Test + void getById() { + Long id = 1L; + Task task = new Task(); + task.setId(id); + Mockito.when(taskRepository.findById(id)) + .thenReturn(Optional.of(task)); + Task testTask = taskService.getById(id); + Mockito.verify(taskRepository).findById(id); + Assertions.assertEquals(task, testTask); + } + + @Test + void getByIdWithNotExistingId() { + Long id = 1L; + Mockito.when(taskRepository.findById(id)) + .thenReturn(Optional.empty()); + Assertions.assertThrows(ResourceNotFoundException.class, + () -> taskService.getById(id)); + Mockito.verify(taskRepository).findById(id); + } + + @Test + void getAllByUserId() { + Long userId = 1L; + List tasks = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + tasks.add(new Task()); + } + Mockito.when(taskRepository.findAllByUserId(userId)) + .thenReturn(tasks); + List testTasks = taskService.getAllByUserId(userId); + Mockito.verify(taskRepository).findAllByUserId(userId); + Assertions.assertEquals(tasks, testTasks); + } + + @Test + void update() { + Task task = new Task(); + task.setId(1L); + task.setTitle("title"); + task.setDescription("description"); + task.setExpirationDate(LocalDateTime.now()); + task.setStatus(Status.DONE); + Task testTask = taskService.update(task); + Mockito.verify(taskRepository).save(task); + Assertions.assertEquals(task, testTask); + } + + @Test + void updateWithNullStatus() { + Task task = new Task(); + task.setId(1L); + task.setTitle("title"); + task.setDescription("description"); + task.setExpirationDate(LocalDateTime.now()); + Task testTask = taskService.update(task); + Mockito.verify(taskRepository).save(task); + Assertions.assertEquals(Status.TODO, testTask.getStatus()); + } + + @Test + void create() { + Long taskId = 1L; + Long userId = 1L; + Task task = new Task(); + Mockito.doAnswer(invocationOnMock -> { + Task savedTask = invocationOnMock.getArgument(0); + savedTask.setId(taskId); + return savedTask; + }) + .when(taskRepository).save(task); + Task testTask = taskService.create(task, userId); + Mockito.verify(taskRepository).save(task); + Assertions.assertNotNull(testTask.getId()); + Mockito.verify(taskRepository).assignTask(userId, task.getId()); + } + + @Test + void delete() { + Long id = 1L; + taskService.delete(id); + Mockito.verify(taskRepository).deleteById(id); + } + + @Test + void uploadImage() { + Long id = 1L; + String imageName = "imageName"; + TaskImage taskImage = new TaskImage(); + Mockito.when(imageService.upload(taskImage)) + .thenReturn(imageName); + taskService.uploadImage(id, taskImage); + Mockito.verify(taskRepository).addImage(id, imageName); + } + +} diff --git a/src/test/java/com/example/tasklist/service/impl/UserServiceImplTest.java b/src/test/java/com/example/tasklist/service/impl/UserServiceImplTest.java new file mode 100644 index 0000000..fd9b1cf --- /dev/null +++ b/src/test/java/com/example/tasklist/service/impl/UserServiceImplTest.java @@ -0,0 +1,165 @@ +package com.example.tasklist.service.impl; + +import com.example.tasklist.config.TestConfig; +import com.example.tasklist.domain.exception.ResourceNotFoundException; +import com.example.tasklist.domain.user.Role; +import com.example.tasklist.domain.user.User; +import com.example.tasklist.repository.TaskRepository; +import com.example.tasklist.repository.UserRepository; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.Optional; +import java.util.Set; + +@ExtendWith(SpringExtension.class) +@ActiveProfiles("test") +@Import(TestConfig.class) +@ExtendWith(MockitoExtension.class) +public class UserServiceImplTest { + + @MockBean + private UserRepository userRepository; + + @MockBean + private TaskRepository taskRepository; + + @MockBean + private AuthenticationManager authenticationManager; + + @MockBean + private PasswordEncoder passwordEncoder; + + @Autowired + private UserServiceImpl userService; + + @Test + void getById() { + Long id = 1L; + User user = new User(); + user.setId(id); + Mockito.when(userRepository.findById(id)) + .thenReturn(Optional.of(user)); + User testUser = userService.getById(id); + Mockito.verify(userRepository).findById(id); + Assertions.assertEquals(user, testUser); + } + + @Test + void getByIdWithNotExistingId() { + Long id = 1L; + Mockito.when(userRepository.findById(id)) + .thenReturn(Optional.empty()); + Assertions.assertThrows(ResourceNotFoundException.class, + () -> userService.getById(id)); + Mockito.verify(userRepository).findById(id); + } + + @Test + void getByUsername() { + String username = "username"; + User user = new User(); + user.setUsername(username); + Mockito.when(userRepository.findByUsername(username)) + .thenReturn(Optional.of(user)); + User testUser = userService.getByUsername(username); + Mockito.verify(userRepository).findByUsername(username); + Assertions.assertEquals(user, testUser); + } + + @Test + void getByUsernameWithNotExistingUsername() { + String username = "username"; + Mockito.when(userRepository.findByUsername(username)) + .thenReturn(Optional.empty()); + Assertions.assertThrows(ResourceNotFoundException.class, + () -> userService.getByUsername(username)); + Mockito.verify(userRepository).findByUsername(username); + } + + @Test + void update() { + String password = "password"; + User user = new User(); + user.setPassword(password); + userService.update(user); + Mockito.verify(passwordEncoder).encode(password); + Mockito.verify(userRepository).save(user); + } + + @Test + void isTaskOwner() { + Long userId = 1L; + Long taskId = 1L; + Mockito.when(userRepository.isTaskOwner(userId, taskId)) + .thenReturn(true); + boolean isOwner = userService.isTaskOwner(userId, taskId); + Mockito.verify(userRepository).isTaskOwner(userId, taskId); + Assertions.assertTrue(isOwner); + } + + @Test + void create() { + String username = "username"; + String password = "password"; + User user = new User(); + user.setUsername(username); + user.setPassword(password); + user.setPasswordConfirmation(password); + Mockito.when(userRepository.findByUsername(username)) + .thenReturn(Optional.empty()); + User testUser = userService.create(user); + Mockito.verify(userRepository).save(user); + Mockito.verify(passwordEncoder).encode(password); + Assertions.assertEquals(Set.of(Role.ROLE_USER), testUser.getRoles()); + } + + @Test + void createWithExistingUsername() { + String username = "username"; + String password = "password"; + User user = new User(); + user.setUsername(username); + user.setPassword(password); + user.setPasswordConfirmation(password); + Mockito.when(userRepository.findByUsername(username)) + .thenReturn(Optional.of(new User())); + Assertions.assertThrows(IllegalStateException.class, + () -> userService.create(user)); + Mockito.verify(userRepository, Mockito.never()).save(user); + } + + @Test + void createWithDifferentPasswords() { + String username = "username"; + String password = "password"; + String passwordConfirmation = "passwordConfirmation"; + User user = new User(); + user.setUsername(username); + user.setPassword(password); + user.setPasswordConfirmation(passwordConfirmation); + Mockito.when(userRepository.findByUsername(username)) + .thenReturn(Optional.empty()); + Assertions.assertThrows(IllegalStateException.class, + () -> userService.create(user)); + Mockito.verify(userRepository, Mockito.never()).save(user); + } + + @Test + void delete() { + Long id = 1L; + userService.delete(id); + Mockito.verify(userRepository).deleteById(id); + } + +}