From 291521f220f0a92d15fa35b124b5e1c439f6a35d Mon Sep 17 00:00:00 2001 From: nastiausenko Date: Thu, 18 Apr 2024 19:22:48 +0300 Subject: [PATCH 1/6] refactor AuthController and UserController tests --- .../urlshortener/auth/AuthControllerTest.java | 92 +++++++++---------- .../urlshortener/user/UserControllerTest.java | 15 ++- 2 files changed, 48 insertions(+), 59 deletions(-) diff --git a/src/test/java/com/linkurlshorter/urlshortener/auth/AuthControllerTest.java b/src/test/java/com/linkurlshorter/urlshortener/auth/AuthControllerTest.java index 9c4ff63..5b260e1 100644 --- a/src/test/java/com/linkurlshorter/urlshortener/auth/AuthControllerTest.java +++ b/src/test/java/com/linkurlshorter/urlshortener/auth/AuthControllerTest.java @@ -2,49 +2,57 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.linkurlshorter.urlshortener.auth.dto.AuthRequest; +import com.linkurlshorter.urlshortener.auth.exception.EmailAlreadyTakenException; +import com.linkurlshorter.urlshortener.TestConfig; +import com.linkurlshorter.urlshortener.security.SecurityConfig; +import com.linkurlshorter.urlshortener.security.UnauthorizedException; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; 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.testcontainers.service.connection.ServiceConnection; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; + +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; -import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -import org.testcontainers.containers.PostgreSQLContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; +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.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** * Unit tests for {@link AuthController} class. * * @author Anastasiia Usenko */ -@SpringBootTest -@AutoConfigureMockMvc(addFilters = false) -@ExtendWith(MockitoExtension.class) +@WebMvcTest(controllers = AuthController.class) +@Import({SecurityConfig.class, TestConfig.class}) class AuthControllerTest { - @Autowired private MockMvc mockMvc; @Autowired private ObjectMapper objectMapper; + @MockBean + private AuthService authService; + /** * Test case for the {@link AuthController#register(AuthRequest)} method. */ @Test void registrationSuccessfulTest() throws Exception { AuthRequest request = new AuthRequest("test@email.com", "Password1"); - performRegistration(request) - .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("User registered successfully!")) - .andExpect(MockMvcResultMatchers.jsonPath("$.jwtToken").exists()); + when(authService.registerUser(request)).thenReturn(String.valueOf(request)); + + ResultActions resultActions = mockMvc.perform(post("/api/V1/auth/register") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))); + + resultActions.andExpect(jsonPath("$.message").value("User registered successfully!")) + .andExpect(jsonPath("$.jwtToken").exists()); } /** @@ -54,12 +62,13 @@ void registrationSuccessfulTest() throws Exception { @Test void registrationFailedTest() throws Exception { AuthRequest request = new AuthRequest("test1@email.com", "Password1"); - performRegistration(request) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("User registered successfully!")) - .andExpect(MockMvcResultMatchers.jsonPath("$.jwtToken").exists()); + when(authService.registerUser(request)).thenThrow(EmailAlreadyTakenException.class); - performRegistration(request).andExpect(MockMvcResultMatchers.status().isBadRequest()); + ResultActions resultActions = mockMvc.perform(post("/api/V1/auth/register") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))); + + resultActions.andExpect(status().isBadRequest()); } /** @@ -68,12 +77,15 @@ void registrationFailedTest() throws Exception { @Test void loginSuccessfulTest() throws Exception { AuthRequest request = new AuthRequest("test2@email.com", "Password1"); - performRegistration(request).andExpect(MockMvcResultMatchers.status().isOk()); + when(authService.loginUser(request)).thenReturn(String.valueOf(request)); - performLogin(request) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("User logged in successfully!")) - .andExpect(MockMvcResultMatchers.jsonPath("$.jwtToken").exists()); + ResultActions resultActions = mockMvc.perform(post("/api/V1/auth/login") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))); + + resultActions.andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("User logged in successfully!")) + .andExpect(jsonPath("$.jwtToken").exists()); } /** @@ -82,32 +94,12 @@ void loginSuccessfulTest() throws Exception { @Test void loginFailedTest() throws Exception { AuthRequest request = new AuthRequest("test3@email.com", "Password1"); - performLogin(request).andExpect(MockMvcResultMatchers.status().isUnauthorized()); - } + when(authService.loginUser(request)).thenThrow(UnauthorizedException.class); - /** - * Performs registration request. - * - * @param request the registration request - * @return the result actions after performing registration - * @throws Exception if an error occurs during the registration process - */ - private ResultActions performRegistration(AuthRequest request) throws Exception { - return mockMvc.perform(post("/api/V1/auth/register") + ResultActions resultActions = mockMvc.perform(post("/api/V1/auth/login") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))); - } - /** - * Performs login request. - * - * @param request the login request - * @return the result actions after performing login - * @throws Exception if an error occurs during the login process - */ - private ResultActions performLogin(AuthRequest request) throws Exception { - return mockMvc.perform(post("/api/V1/auth/login") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))); + resultActions.andExpect(status().isUnauthorized()); } -} \ No newline at end of file +} diff --git a/src/test/java/com/linkurlshorter/urlshortener/user/UserControllerTest.java b/src/test/java/com/linkurlshorter/urlshortener/user/UserControllerTest.java index 029f08e..7c81c3e 100644 --- a/src/test/java/com/linkurlshorter/urlshortener/user/UserControllerTest.java +++ b/src/test/java/com/linkurlshorter/urlshortener/user/UserControllerTest.java @@ -3,16 +3,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.linkurlshorter.urlshortener.jwt.JwtUtil; import com.linkurlshorter.urlshortener.security.CustomUserDetailsService; +import com.linkurlshorter.urlshortener.security.SecurityConfig; import com.linkurlshorter.urlshortener.security.SecurityUserDetails; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.test.context.support.WithMockUser; @@ -33,9 +31,8 @@ * * @author Anastasiia Usenko */ -@SpringBootTest -@AutoConfigureMockMvc -@ExtendWith(MockitoExtension.class) +@WebMvcTest(controllers = UserController.class) +@Import(SecurityConfig.class) class UserControllerTest { @Autowired private MockMvc mockMvc; @@ -98,8 +95,8 @@ void changePasswordFailedTest() throws Exception { given(userService.updateByEmailDynamically(any(User.class), any(String.class))).willReturn(0); ResultActions resultActions = mockMvc.perform(post("/api/V1/user/change-password") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))); + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))); resultActions.andExpect(status().isNotFound()); } From cb37c934db40abb52490324a833d380d99426b95 Mon Sep 17 00:00:00 2001 From: nastiausenko Date: Thu, 18 Apr 2024 19:23:17 +0300 Subject: [PATCH 2/6] add TestConfig class --- .../urlshortener/TestConfig.java | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 src/test/java/com/linkurlshorter/urlshortener/TestConfig.java diff --git a/src/test/java/com/linkurlshorter/urlshortener/TestConfig.java b/src/test/java/com/linkurlshorter/urlshortener/TestConfig.java new file mode 100644 index 0000000..94abfed --- /dev/null +++ b/src/test/java/com/linkurlshorter/urlshortener/TestConfig.java @@ -0,0 +1,118 @@ +package com.linkurlshorter.urlshortener; + +import com.linkurlshorter.urlshortener.auth.AuthService; +import com.linkurlshorter.urlshortener.jwt.JwtUtil; +import com.linkurlshorter.urlshortener.link.LinkInfoDtoMapper; +import com.linkurlshorter.urlshortener.link.LinkRepository; +import com.linkurlshorter.urlshortener.link.LinkService; +import com.linkurlshorter.urlshortener.security.CustomUserDetailsService; +import com.linkurlshorter.urlshortener.user.UserRepository; +import com.linkurlshorter.urlshortener.user.UserService; +import jakarta.persistence.EntityManager; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; + +import static org.mockito.Mockito.mock; + +/** + * Configuration class for test environment. + * Provides bean definitions for mocked services and repositories used in testing. + * + * @author Anastasiia Usenko + */ +@TestConfiguration +public class TestConfig { + + /** + * Creates a mock bean for JwtUtil. + * + * @return JwtUtil mock bean + */ + @Bean + public JwtUtil jwtUtil() { + return new JwtUtil(); + } + + /** + * Creates a bean for CustomUserDetailsService with a mocked UserService dependency. + * + * @param userService UserService mock bean + * @return CustomUserDetailsService bean with mocked UserService dependency + */ + @Bean + public CustomUserDetailsService customUserDetailsService(UserService userService) { + return new CustomUserDetailsService(userService); + } + + /** + * Creates a bean for LinkInfoDtoMapper. + * + * @return LinkInfoDtoMapper bean + */ + @Bean + public LinkInfoDtoMapper linkInfoDtoMapper() { + return new LinkInfoDtoMapper(); + } + + /** + * Creates a mock bean for EntityManager. + * + * @return EntityManager mock bean + */ + @Bean + public EntityManager entityManager() { + return mock(EntityManager.class); + } + + /** + * Creates a bean for LinkService with a mocked LinkRepository dependency. + * + * @param linkRepository LinkRepository mock bean + * @return LinkService bean with mocked LinkRepository dependency + */ + @Bean + public LinkService linkService(LinkRepository linkRepository) { + return new LinkService(linkRepository); + } + + /** + * Creates a bean for UserService with a mocked UserRepository dependency. + * + * @param userRepository UserRepository mock bean + * @return UserService bean with mocked UserRepository dependency + */ + @Bean + public UserService userService(UserRepository userRepository) { + return new UserService(userRepository); + } + + /** + * Creates a mock bean for LinkRepository. + * + * @return LinkRepository mock bean + */ + @Bean + public LinkRepository linkRepository() { + return mock(LinkRepository.class); + } + + /** + * Creates a mock bean for UserRepository. + * + * @return UserRepository mock bean + */ + @Bean + public UserRepository userRepository() { + return mock(UserRepository.class); + } + + /** + * Creates a mock bean for AuthService. + * + * @return AuthService mock bean + */ + @Bean + public AuthService authService() { + return mock(AuthService.class); + } +} From a90498f6463d6ba28ac5c7ea84fe8d4f64e21a13 Mon Sep 17 00:00:00 2001 From: nastiausenko Date: Thu, 18 Apr 2024 19:24:18 +0300 Subject: [PATCH 3/6] add LinkController tests --- .../urlshortener/link/LinkControllerTest.java | 292 ++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 src/test/java/com/linkurlshorter/urlshortener/link/LinkControllerTest.java diff --git a/src/test/java/com/linkurlshorter/urlshortener/link/LinkControllerTest.java b/src/test/java/com/linkurlshorter/urlshortener/link/LinkControllerTest.java new file mode 100644 index 0000000..ac4e4e7 --- /dev/null +++ b/src/test/java/com/linkurlshorter/urlshortener/link/LinkControllerTest.java @@ -0,0 +1,292 @@ +package com.linkurlshorter.urlshortener.link; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.linkurlshorter.urlshortener.TestConfig; +import com.linkurlshorter.urlshortener.security.SecurityConfig; +import com.linkurlshorter.urlshortener.user.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithAnonymousUser; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +@WebMvcTest(controllers = LinkController.class) +@Import({SecurityConfig.class, TestConfig.class}) +class LinkControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @MockBean + private LinkService linkService; + + @MockBean + private UserService userService; + + + private User user; + private Link link; + + /** + * Set up method to initialize test data before each test method. + */ + @BeforeEach + void setUp() { + user = User.builder() + .id(UUID.fromString("84991c79-f6a9-4b7b-b1b4-0d66c0b92c81")) + .email("test1@gmail.com") + .password("Password1") + .role(UserRole.USER) + .build(); + + link = Link.builder() + .id(UUID.fromString("3053e49b-6da3-4389-9d06-23b2d57b6f25")) + .longLink("https://www.youtube.com") + .shortLink("short-link-1") + .user(user) + .createdTime(LocalDateTime.of(2024, 4, 13, 10, 0)) + .expirationTime(LocalDateTime.of(2024, 5, 16, 8, 0)) + .statistics(100) + .status(LinkStatus.ACTIVE) + .build(); + } + + /** + * Test case for the {@link LinkController#createLink(CreateLinkRequest)} method. + */ + @Test + @WithMockUser + void createLinkTest() throws Exception { + when(userService.findByEmail(any())).thenReturn(user); + when(linkService.save(any())).thenReturn(link); + + CreateLinkRequest request = new CreateLinkRequest("https://www.example.com"); + + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/create") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))); + + resultActions.andExpect(status().isOk()) + .andExpect(jsonPath("$.error").value("ok")) + .andExpect(jsonPath("$.shortLink").exists()); + } + + //TODO test for InternalServerLinkException() for create + + /** + * Test case for the {@link LinkController#deleteLink(UUID)} method. + */ + @Test + @WithMockUser + void deleteLinkTest() throws Exception { + when(userService.findByEmail(any())).thenReturn(user); + when(linkService.findById(link.getId())).thenReturn(link); + doNothing().when(linkService).deleteById(link.getId()); + + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/delete") + .contentType(MediaType.APPLICATION_JSON) + .param("id", String.valueOf(link.getId()))); + + resultActions.andExpect(status().isOk()) + .andExpect(jsonPath("$.error").value("ok")); + } + + /** + * Test case for the {@link LinkController#deleteLink(UUID)} method when + * the authenticated user does not have rights. + */ + @Test + @WithAnonymousUser + void deleteLinkForbiddenTest() throws Exception { + when(userService.findByEmail(any())).thenReturn(user); + when(linkService.findById(link.getId())).thenReturn(link); + doNothing().when(linkService).deleteById(link.getId()); + + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/delete") + .contentType(MediaType.APPLICATION_JSON) + .param("id", String.valueOf(link.getId()))); + + resultActions.andExpect(status().isForbidden()); + } + + /** + * Test case for the {@link LinkController#getAllLinksForUser()} method. + */ + @Test + @WithMockUser + void getAllLinksForUserTest() throws Exception { + List userLinks = Arrays.asList( + Link.builder().id(UUID.randomUUID()).user(user).build(), + Link.builder().id(UUID.randomUUID()).user(user).build(), + Link.builder().id(UUID.randomUUID()).user(user).build()); + + when(userService.findByEmail(any())).thenReturn(user); + when(linkService.findAllByUserId(user.getId())).thenReturn(userLinks); + + ResultActions resultActions = mockMvc.perform(get("/api/V1/link/all-links-info") + .contentType(MediaType.APPLICATION_JSON) + .param("userId", String.valueOf(user.getId()))); + + resultActions.andExpect(status().isOk()) + .andExpect(jsonPath("$.error").value("ok")) + .andExpect(jsonPath("$.linkDtoList").isArray()) + .andExpect(jsonPath("$.linkDtoList.length()").value(userLinks.size())); + } + + //TODO test for InternalServerLinkException() for getAll + + @Test + @WithMockUser + void refreshLinkTest() throws Exception { + when(userService.findByEmail(any())).thenReturn(user); + when(linkService.findById(link.getId())).thenReturn(link); + when(linkService.update(link)).thenReturn(link); + + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/refresh") + .contentType(MediaType.APPLICATION_JSON) + .param("id", String.valueOf(link.getId()))); + + resultActions.andExpect(status().isOk()) + .andExpect(jsonPath("$.error").value("ok")); + } + + @Test + @WithAnonymousUser + void refreshLinkForbiddenTest() throws Exception { + when(userService.findByEmail(any())).thenReturn(user); + when(linkService.findById(link.getId())).thenReturn(link); + when(linkService.update(link)).thenReturn(link); + + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/refresh") + .contentType(MediaType.APPLICATION_JSON) + .param("id", String.valueOf(link.getId()))); + + resultActions.andExpect(status().isForbidden()); + } + + @Test + @WithMockUser + void refreshDeletedLinkTest() throws Exception { + link.setStatus(LinkStatus.DELETED); + when(userService.findByEmail(any())).thenReturn(user); + when(linkService.findById(link.getId())).thenReturn(link); + when(linkService.update(link)).thenReturn(link); + + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/refresh") + .contentType(MediaType.APPLICATION_JSON) + .param("id", String.valueOf(link.getId()))); + + resultActions.andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.exceptionMessage").value("The link has already been deleted, " + + "no operations are allowed")); + } + + @Test + @WithMockUser + void editLinkContentTest() throws Exception { + when(userService.findByEmail(any())).thenReturn(user); + when(linkService.findById(link.getId())).thenReturn(link); + + EditLinkContentRequest request = new EditLinkContentRequest(link.getId(), "short-link-2"); + when(linkService.update(link)).thenReturn(link); + + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/content") + .contentType(MediaType.APPLICATION_JSON) + .param("id", String.valueOf(link.getId())) + .content(objectMapper.writeValueAsString(request))); + + resultActions.andExpect(status().isOk()) + .andExpect(jsonPath("$.error").value("ok")); + } + + @Test + @WithAnonymousUser + void editLinkContentForbiddenTest() throws Exception { + when(userService.findByEmail(any())).thenReturn(user); + when(linkService.findById(link.getId())).thenReturn(link); + + EditLinkContentRequest request = new EditLinkContentRequest(link.getId(), "short-link-2"); + when(linkService.update(link)).thenReturn(link); + + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/content") + .contentType(MediaType.APPLICATION_JSON) + .param("id", String.valueOf(link.getId())) + .content(objectMapper.writeValueAsString(request))); + + resultActions.andExpect(status().isForbidden()); + } + + @Test + @WithMockUser + void editDeletedLinkContentTest() throws Exception { + link.setStatus(LinkStatus.DELETED); + when(userService.findByEmail(any())).thenReturn(user); + when(linkService.findById(link.getId())).thenReturn(link); + + EditLinkContentRequest request = new EditLinkContentRequest(link.getId(), "short-link-2"); + when(linkService.update(link)).thenReturn(link); + + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/content") + .contentType(MediaType.APPLICATION_JSON) + .param("id", String.valueOf(link.getId())) + .content(objectMapper.writeValueAsString(request))); + + resultActions.andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.exceptionMessage").value("Link status is invalid for the operation")); + } + + @Test + @WithMockUser + void getInfoByShortLinkTest() throws Exception { + when(userService.findByEmail(any())).thenReturn(user); + when(linkService.findById(any())).thenReturn(link); + when(linkService.findByShortLink(link.getShortLink())).thenReturn(link); + + ResultActions resultActions = mockMvc.perform(get("/api/V1/link/info") + .contentType(MediaType.APPLICATION_JSON) + .param("shortLink", link.getShortLink())); + + resultActions.andExpect(status().isOk()) + .andExpect(jsonPath("$.error").value("ok")) + .andExpect(jsonPath("$.linkDtoList").isArray()); + + } + + @Test + @WithAnonymousUser + void getInfoByShortLinkForbiddenTest() throws Exception { + when(userService.findByEmail(any())).thenReturn(user); + when(linkService.findById(any())).thenReturn(link); + when(linkService.findByShortLink(link.getShortLink())).thenReturn(link); + + ResultActions resultActions = mockMvc.perform(get("/api/V1/link/info") + .contentType(MediaType.APPLICATION_JSON) + .param("shortLink", link.getShortLink())); + + resultActions.andExpect(status().isForbidden()); + + } +} From b558c5fd55aaa01dae8d2ee7f751b771ccc7aaa0 Mon Sep 17 00:00:00 2001 From: nastiausenko Date: Fri, 19 Apr 2024 14:04:20 +0300 Subject: [PATCH 4/6] complete LinkController tests --- .../urlshortener/link/LinkControllerTest.java | 188 +++++++++++------- 1 file changed, 119 insertions(+), 69 deletions(-) diff --git a/src/test/java/com/linkurlshorter/urlshortener/link/LinkControllerTest.java b/src/test/java/com/linkurlshorter/urlshortener/link/LinkControllerTest.java index ac4e4e7..d9c1035 100644 --- a/src/test/java/com/linkurlshorter/urlshortener/link/LinkControllerTest.java +++ b/src/test/java/com/linkurlshorter/urlshortener/link/LinkControllerTest.java @@ -11,7 +11,6 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; -import org.springframework.security.test.context.support.WithAnonymousUser; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; @@ -94,7 +93,20 @@ void createLinkTest() throws Exception { .andExpect(jsonPath("$.shortLink").exists()); } - //TODO test for InternalServerLinkException() for create + @Test + @WithMockUser + void createLinkFailedTest() throws Exception { + CreateLinkRequest request = new CreateLinkRequest("https://www.example.com"); + + when(userService.findByEmail(any())).thenReturn(user); + when(linkService.save(any())).thenThrow(new RuntimeException("Short link already exists")); + + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/create") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))); + + resultActions.andExpect(status().isInternalServerError()); + } /** * Test case for the {@link LinkController#deleteLink(UUID)} method. @@ -119,9 +131,15 @@ void deleteLinkTest() throws Exception { * the authenticated user does not have rights. */ @Test - @WithAnonymousUser + @WithMockUser void deleteLinkForbiddenTest() throws Exception { - when(userService.findByEmail(any())).thenReturn(user); + User newUser = User.builder() + .id(UUID.fromString("84991c79-f6a9-4b7b-b1b4-0d66c0b92c83")) + .email("test3@gmail.com") + .password("Password3") + .role(UserRole.USER) + .build(); + when(userService.findByEmail(any())).thenReturn(newUser); when(linkService.findById(link.getId())).thenReturn(link); doNothing().when(linkService).deleteById(link.getId()); @@ -132,130 +150,116 @@ void deleteLinkForbiddenTest() throws Exception { resultActions.andExpect(status().isForbidden()); } - /** - * Test case for the {@link LinkController#getAllLinksForUser()} method. - */ @Test @WithMockUser - void getAllLinksForUserTest() throws Exception { - List userLinks = Arrays.asList( - Link.builder().id(UUID.randomUUID()).user(user).build(), - Link.builder().id(UUID.randomUUID()).user(user).build(), - Link.builder().id(UUID.randomUUID()).user(user).build()); - - when(userService.findByEmail(any())).thenReturn(user); - when(linkService.findAllByUserId(user.getId())).thenReturn(userLinks); - - ResultActions resultActions = mockMvc.perform(get("/api/V1/link/all-links-info") - .contentType(MediaType.APPLICATION_JSON) - .param("userId", String.valueOf(user.getId()))); - - resultActions.andExpect(status().isOk()) - .andExpect(jsonPath("$.error").value("ok")) - .andExpect(jsonPath("$.linkDtoList").isArray()) - .andExpect(jsonPath("$.linkDtoList.length()").value(userLinks.size())); - } - - //TODO test for InternalServerLinkException() for getAll - - @Test - @WithMockUser - void refreshLinkTest() throws Exception { + void editLinkContentTest() throws Exception { when(userService.findByEmail(any())).thenReturn(user); when(linkService.findById(link.getId())).thenReturn(link); + + EditLinkContentRequest request = new EditLinkContentRequest(link.getId(), "short-link-2"); when(linkService.update(link)).thenReturn(link); - ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/refresh") - .contentType(MediaType.APPLICATION_JSON) - .param("id", String.valueOf(link.getId()))); + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/content") + .contentType(MediaType.APPLICATION_JSON) + .param("id", String.valueOf(link.getId())) + .content(objectMapper.writeValueAsString(request))); resultActions.andExpect(status().isOk()) .andExpect(jsonPath("$.error").value("ok")); } @Test - @WithAnonymousUser - void refreshLinkForbiddenTest() throws Exception { - when(userService.findByEmail(any())).thenReturn(user); + @WithMockUser + void editLinkContentForbiddenTest() throws Exception { + User newUser = User.builder() + .id(UUID.fromString("84991c79-f6a9-4b7b-b1b4-0d66c0b92c83")) + .email("test3@gmail.com") + .password("Password3") + .role(UserRole.USER) + .build(); + when(userService.findByEmail(any())).thenReturn(newUser); when(linkService.findById(link.getId())).thenReturn(link); + + EditLinkContentRequest request = new EditLinkContentRequest(link.getId(), "short-link-2"); when(linkService.update(link)).thenReturn(link); - ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/refresh") + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/content") .contentType(MediaType.APPLICATION_JSON) - .param("id", String.valueOf(link.getId()))); + .param("id", String.valueOf(link.getId())) + .content(objectMapper.writeValueAsString(request))); resultActions.andExpect(status().isForbidden()); } @Test @WithMockUser - void refreshDeletedLinkTest() throws Exception { + void editDeletedLinkContentTest() throws Exception { link.setStatus(LinkStatus.DELETED); when(userService.findByEmail(any())).thenReturn(user); when(linkService.findById(link.getId())).thenReturn(link); + + EditLinkContentRequest request = new EditLinkContentRequest(link.getId(), "short-link-2"); when(linkService.update(link)).thenReturn(link); - ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/refresh") + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/content") .contentType(MediaType.APPLICATION_JSON) - .param("id", String.valueOf(link.getId()))); + .param("id", String.valueOf(link.getId())) + .content(objectMapper.writeValueAsString(request))); resultActions.andExpect(status().isInternalServerError()) - .andExpect(jsonPath("$.exceptionMessage").value("The link has already been deleted, " + - "no operations are allowed")); + .andExpect(jsonPath("$.exceptionMessage").value("Link status is invalid for the operation")); } @Test @WithMockUser - void editLinkContentTest() throws Exception { + void refreshLinkTest() throws Exception { when(userService.findByEmail(any())).thenReturn(user); when(linkService.findById(link.getId())).thenReturn(link); - - EditLinkContentRequest request = new EditLinkContentRequest(link.getId(), "short-link-2"); when(linkService.update(link)).thenReturn(link); - ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/content") - .contentType(MediaType.APPLICATION_JSON) - .param("id", String.valueOf(link.getId())) - .content(objectMapper.writeValueAsString(request))); + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/refresh") + .contentType(MediaType.APPLICATION_JSON) + .param("id", String.valueOf(link.getId()))); resultActions.andExpect(status().isOk()) .andExpect(jsonPath("$.error").value("ok")); } @Test - @WithAnonymousUser - void editLinkContentForbiddenTest() throws Exception { - when(userService.findByEmail(any())).thenReturn(user); + @WithMockUser + void refreshLinkForbiddenTest() throws Exception { + User newUser = User.builder() + .id(UUID.fromString("84991c79-f6a9-4b7b-b1b4-0d66c0b92c83")) + .email("test3@gmail.com") + .password("Password3") + .role(UserRole.USER) + .build(); + when(userService.findByEmail(any())).thenReturn(newUser); when(linkService.findById(link.getId())).thenReturn(link); - - EditLinkContentRequest request = new EditLinkContentRequest(link.getId(), "short-link-2"); when(linkService.update(link)).thenReturn(link); - ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/content") + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/refresh") .contentType(MediaType.APPLICATION_JSON) - .param("id", String.valueOf(link.getId())) - .content(objectMapper.writeValueAsString(request))); + .param("id", String.valueOf(link.getId()))); resultActions.andExpect(status().isForbidden()); } @Test @WithMockUser - void editDeletedLinkContentTest() throws Exception { + void refreshDeletedLinkTest() throws Exception { link.setStatus(LinkStatus.DELETED); when(userService.findByEmail(any())).thenReturn(user); when(linkService.findById(link.getId())).thenReturn(link); - - EditLinkContentRequest request = new EditLinkContentRequest(link.getId(), "short-link-2"); when(linkService.update(link)).thenReturn(link); - ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/content") + ResultActions resultActions = mockMvc.perform(post("/api/V1/link/edit/refresh") .contentType(MediaType.APPLICATION_JSON) - .param("id", String.valueOf(link.getId())) - .content(objectMapper.writeValueAsString(request))); + .param("id", String.valueOf(link.getId()))); resultActions.andExpect(status().isInternalServerError()) - .andExpect(jsonPath("$.exceptionMessage").value("Link status is invalid for the operation")); + .andExpect(jsonPath("$.exceptionMessage").value("The link has already been deleted, " + + "no operations are allowed")); } @Test @@ -272,13 +276,18 @@ void getInfoByShortLinkTest() throws Exception { resultActions.andExpect(status().isOk()) .andExpect(jsonPath("$.error").value("ok")) .andExpect(jsonPath("$.linkDtoList").isArray()); - } @Test - @WithAnonymousUser + @WithMockUser void getInfoByShortLinkForbiddenTest() throws Exception { - when(userService.findByEmail(any())).thenReturn(user); + User newUser = User.builder() + .id(UUID.fromString("84991c79-f6a9-4b7b-b1b4-0d66c0b92c83")) + .email("test3@gmail.com") + .password("Password3") + .role(UserRole.USER) + .build(); + when(userService.findByEmail(any())).thenReturn(newUser); when(linkService.findById(any())).thenReturn(link); when(linkService.findByShortLink(link.getShortLink())).thenReturn(link); @@ -287,6 +296,47 @@ void getInfoByShortLinkForbiddenTest() throws Exception { .param("shortLink", link.getShortLink())); resultActions.andExpect(status().isForbidden()); + } + /** + * Test case for the {@link LinkController#getAllLinksForUser()} method. + */ + @Test + @WithMockUser + void getAllLinksForUserTest() throws Exception { + List userLinks = Arrays.asList( + Link.builder().id(UUID.randomUUID()).user(user).build(), + Link.builder().id(UUID.randomUUID()).user(user).build(), + Link.builder().id(UUID.randomUUID()).user(user).build()); + + when(userService.findByEmail(any())).thenReturn(user); + when(linkService.findAllByUserId(user.getId())).thenReturn(userLinks); + + ResultActions resultActions = mockMvc.perform(get("/api/V1/link/all-links-info") + .contentType(MediaType.APPLICATION_JSON) + .param("userId", String.valueOf(user.getId()))); + + resultActions.andExpect(status().isOk()) + .andExpect(jsonPath("$.error").value("ok")) + .andExpect(jsonPath("$.linkDtoList").isArray()) + .andExpect(jsonPath("$.linkDtoList.length()").value(userLinks.size())); + } + + @Test + @WithMockUser + void getLinksStatsForUserTest() throws Exception { + List stats = Arrays.asList( + new LinkStatisticsDto(UUID.randomUUID(),"link1", 10), + new LinkStatisticsDto(UUID.randomUUID(),"link2", 20) + ); + + when(userService.findByEmail(any())).thenReturn(user); + when(linkService.getLinkUsageStatsByUserId(user.getId())).thenReturn(stats); + ResultActions resultActions = mockMvc.perform(get("/api/V1/link/url-usage-top-for-user") + .contentType(MediaType.APPLICATION_JSON)); + + resultActions.andExpect(status().isOk()) + .andExpect(jsonPath("$.linksStatsList").isArray()) + .andExpect(jsonPath("$.error").value("ok")); } } From 2ec3db3fb41232a201ca8758289e6a39e7f32c38 Mon Sep 17 00:00:00 2001 From: nastiausenko Date: Fri, 19 Apr 2024 16:13:42 +0300 Subject: [PATCH 5/6] add LinkRedirectController tests --- .../redirect/LinkRedirectControllerTest.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/test/java/com/linkurlshorter/urlshortener/redirect/LinkRedirectControllerTest.java diff --git a/src/test/java/com/linkurlshorter/urlshortener/redirect/LinkRedirectControllerTest.java b/src/test/java/com/linkurlshorter/urlshortener/redirect/LinkRedirectControllerTest.java new file mode 100644 index 0000000..37a94a1 --- /dev/null +++ b/src/test/java/com/linkurlshorter/urlshortener/redirect/LinkRedirectControllerTest.java @@ -0,0 +1,85 @@ +package com.linkurlshorter.urlshortener.redirect; + +import com.linkurlshorter.urlshortener.TestConfig; +import com.linkurlshorter.urlshortener.link.Link; +import com.linkurlshorter.urlshortener.link.LinkService; +import com.linkurlshorter.urlshortener.link.LinkStatus; +import com.linkurlshorter.urlshortener.security.SecurityConfig; +import com.linkurlshorter.urlshortener.user.User; +import com.linkurlshorter.urlshortener.user.UserRole; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import java.time.LocalDateTime; +import java.util.UUID; + +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(controllers = LinkRedirectController.class) +@Import({SecurityConfig.class, TestConfig.class}) +class LinkRedirectControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private LinkCache linkCache; + + @MockBean + private LinkService linkService; + + private Link link; + + @BeforeEach + void setUp() { + link = Link.builder() + .id(UUID.fromString("3053e49b-6da3-4389-9d06-23b2d57b6f25")) + .longLink("https://www.youtube.com") + .shortLink("short-link-1") + .user(User.builder() + .id(UUID.fromString("84991c79-f6a9-4b7b-b1b4-0d66c0b92c81")) + .email("user1@example.com") + .password("$2a$12$7Cp4On1DBNyCkz4TaZYc3O.A.CBKi4WXgXnlI4SD0yn7CgBX5Gd6O") + .role(UserRole.USER) + .build()) + .createdTime(LocalDateTime.of(2024, 4, 13, 10, 0)) + .expirationTime(LocalDateTime.of(2024, 5, 16, 8, 0)) + .statistics(100) + .status(LinkStatus.ACTIVE) + .build(); + } + + @Test + void redirectToOriginalLinkTest() throws Exception { + when(linkCache.containsShortLink(link.getShortLink())).thenReturn(true); + when(linkCache.getByShortLink(link.getShortLink())).thenReturn(link); + + ResultActions resultActions = mockMvc.perform(get("/" + link.getShortLink()) + .contentType(MediaType.APPLICATION_JSON)); + + resultActions.andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl(link.getLongLink())); + } + + @Test + void redirectToOriginalLinkNotInLinkCacheTest() throws Exception { + when(linkCache.containsShortLink(link.getShortLink())).thenReturn(false); + when(linkService.findByShortLink(link.getShortLink())).thenReturn(link); + + ResultActions resultActions = mockMvc.perform(get("/" + link.getShortLink()) + .contentType(MediaType.APPLICATION_JSON)); + + resultActions.andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl(link.getLongLink())); + } +} From 796449f36cb2630be0ed167a9be59d41ab17a789 Mon Sep 17 00:00:00 2001 From: nastiausenko Date: Fri, 19 Apr 2024 16:32:46 +0300 Subject: [PATCH 6/6] add javadoc --- .../urlshortener/link/LinkControllerTest.java | 42 ++++++++++++++++++- .../redirect/LinkRedirectControllerTest.java | 16 +++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/linkurlshorter/urlshortener/link/LinkControllerTest.java b/src/test/java/com/linkurlshorter/urlshortener/link/LinkControllerTest.java index d9c1035..f136364 100644 --- a/src/test/java/com/linkurlshorter/urlshortener/link/LinkControllerTest.java +++ b/src/test/java/com/linkurlshorter/urlshortener/link/LinkControllerTest.java @@ -28,7 +28,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - +/** + * Unit tests for {@link LinkController} class. + * + * @author Anastasiia Usenko + */ @WebMvcTest(controllers = LinkController.class) @Import({SecurityConfig.class, TestConfig.class}) class LinkControllerTest { @@ -93,6 +97,10 @@ void createLinkTest() throws Exception { .andExpect(jsonPath("$.shortLink").exists()); } + /** + * Test case for the {@link LinkController#createLink(CreateLinkRequest)} method when + * an error occurs during the link creation process. + */ @Test @WithMockUser void createLinkFailedTest() throws Exception { @@ -150,6 +158,9 @@ void deleteLinkForbiddenTest() throws Exception { resultActions.andExpect(status().isForbidden()); } + /** + * Test case for the {@link LinkController#editLinkContent(EditLinkContentRequest)} method. + */ @Test @WithMockUser void editLinkContentTest() throws Exception { @@ -168,6 +179,10 @@ void editLinkContentTest() throws Exception { .andExpect(jsonPath("$.error").value("ok")); } + /** + * Test case for the {@link LinkController#editLinkContent(EditLinkContentRequest)} method when + * the authenticated user does not have rights. + */ @Test @WithMockUser void editLinkContentForbiddenTest() throws Exception { @@ -191,6 +206,10 @@ void editLinkContentForbiddenTest() throws Exception { resultActions.andExpect(status().isForbidden()); } + /** + * Test case for the {@link LinkController#editLinkContent(EditLinkContentRequest)} method when + * the status of the link is not ACTIVE. + */ @Test @WithMockUser void editDeletedLinkContentTest() throws Exception { @@ -210,6 +229,9 @@ void editDeletedLinkContentTest() throws Exception { .andExpect(jsonPath("$.exceptionMessage").value("Link status is invalid for the operation")); } + /** + * Test case for the {@link LinkController#refreshLink(UUID)} method. + */ @Test @WithMockUser void refreshLinkTest() throws Exception { @@ -225,6 +247,10 @@ void refreshLinkTest() throws Exception { .andExpect(jsonPath("$.error").value("ok")); } + /** + * Test case for the {@link LinkController#refreshLink(UUID)} method when + * the authenticated user does not have rights. + */ @Test @WithMockUser void refreshLinkForbiddenTest() throws Exception { @@ -245,6 +271,10 @@ void refreshLinkForbiddenTest() throws Exception { resultActions.andExpect(status().isForbidden()); } + /** + * Test case for the {@link LinkController#refreshLink(UUID)} method when + * the status of the link is not ACTIVE. + */ @Test @WithMockUser void refreshDeletedLinkTest() throws Exception { @@ -262,6 +292,9 @@ void refreshDeletedLinkTest() throws Exception { "no operations are allowed")); } + /** + * Test case for the {@link LinkController#getInfoByShortLink(String)} method. + */ @Test @WithMockUser void getInfoByShortLinkTest() throws Exception { @@ -278,6 +311,10 @@ void getInfoByShortLinkTest() throws Exception { .andExpect(jsonPath("$.linkDtoList").isArray()); } + /** + * Test case for the {@link LinkController#getInfoByShortLink(String)} method when + * the authenticated user does not have rights. + */ @Test @WithMockUser void getInfoByShortLinkForbiddenTest() throws Exception { @@ -322,6 +359,9 @@ void getAllLinksForUserTest() throws Exception { .andExpect(jsonPath("$.linkDtoList.length()").value(userLinks.size())); } + /** + * Test case for the {@link LinkController#getLinksStatsForUser()} method. + */ @Test @WithMockUser void getLinksStatsForUserTest() throws Exception { diff --git a/src/test/java/com/linkurlshorter/urlshortener/redirect/LinkRedirectControllerTest.java b/src/test/java/com/linkurlshorter/urlshortener/redirect/LinkRedirectControllerTest.java index 37a94a1..3178ddb 100644 --- a/src/test/java/com/linkurlshorter/urlshortener/redirect/LinkRedirectControllerTest.java +++ b/src/test/java/com/linkurlshorter/urlshortener/redirect/LinkRedirectControllerTest.java @@ -25,6 +25,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +/** + * Unit tests for {@link LinkRedirectController} class. + * + * @author Anastasiia Usenko + */ @WebMvcTest(controllers = LinkRedirectController.class) @Import({SecurityConfig.class, TestConfig.class}) class LinkRedirectControllerTest { @@ -40,6 +45,9 @@ class LinkRedirectControllerTest { private Link link; + /** + * Set up method to initialize test data before each test method. + */ @BeforeEach void setUp() { link = Link.builder() @@ -59,6 +67,10 @@ void setUp() { .build(); } + /** + * Test case for the {@link LinkRedirectController#redirectToOriginalLink(String)} method + * when there is a short link in the cache. + */ @Test void redirectToOriginalLinkTest() throws Exception { when(linkCache.containsShortLink(link.getShortLink())).thenReturn(true); @@ -71,6 +83,10 @@ void redirectToOriginalLinkTest() throws Exception { .andExpect(redirectedUrl(link.getLongLink())); } + /** + * Test case for the {@link LinkRedirectController#redirectToOriginalLink(String)} method + * when there is no short link in the cache. + */ @Test void redirectToOriginalLinkNotInLinkCacheTest() throws Exception { when(linkCache.containsShortLink(link.getShortLink())).thenReturn(false);