-
Notifications
You must be signed in to change notification settings - Fork 0
Add authentication module test coverage #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat/authentication
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| package com.workastra.authentication.controller; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
| import static org.junit.jupiter.api.Assertions.assertNull; | ||
| import static org.junit.jupiter.api.Assertions.assertSame; | ||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
|
||
| import com.workastra.common.api.ApiResponse; | ||
| import org.junit.jupiter.api.AfterEach; | ||
| import org.junit.jupiter.api.BeforeEach; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
| import org.springframework.security.core.Authentication; | ||
| import org.springframework.security.web.csrf.DefaultCsrfToken; | ||
| import org.springframework.security.web.csrf.CsrfToken; | ||
| import org.springframework.mock.web.MockHttpServletRequest; | ||
| import org.springframework.web.context.request.RequestContextHolder; | ||
| import org.springframework.web.context.request.ServletRequestAttributes; | ||
|
|
||
| class AuthControllerTest { | ||
|
|
||
| private final AuthController controller = new AuthController(); | ||
| private MockHttpServletRequest request; | ||
|
|
||
| @BeforeEach | ||
| void setUpRequestContext() { | ||
| request = new MockHttpServletRequest(); | ||
| request.addHeader("X-Request-Id", "test-request"); | ||
| RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); | ||
| } | ||
|
|
||
| @AfterEach | ||
| void clearRequestContext() { | ||
| RequestContextHolder.resetRequestAttributes(); | ||
| } | ||
|
|
||
| @Test | ||
| void getCsrfWrapsTokenInApiResponse() { | ||
| CsrfToken csrfToken = new DefaultCsrfToken("X-CSRF", "_csrf", "token-123"); | ||
|
|
||
| ApiResponse<CsrfToken> response = controller.getCsrf(csrfToken); | ||
|
|
||
| assertTrue(response.success()); | ||
| assertEquals("OK", response.code()); | ||
| assertNull(response.message()); | ||
| assertSame(csrfToken, response.data()); | ||
| assertNull(response.errors()); | ||
| assertNotNull(response.meta()); | ||
| } | ||
|
|
||
| @Test | ||
| void meReturnsAuthenticatedPrincipal() { | ||
| Authentication authentication = new UsernamePasswordAuthenticationToken("user", "password"); | ||
|
|
||
| ApiResponse<Authentication> response = controller.me(authentication); | ||
|
|
||
| assertTrue(response.success()); | ||
| assertEquals("OK", response.code()); | ||
| assertSame(authentication, response.data()); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| @NullMarked | ||
| package com.workastra.authentication.controller; | ||
|
|
||
| import org.jspecify.annotations.NullMarked; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| package com.workastra.authentication.infrastructure.filter; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| 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.ArgumentMatchers.argThat; | ||
| import static org.mockito.Mockito.verify; | ||
| import static org.mockito.Mockito.when; | ||
|
|
||
| import jakarta.servlet.http.HttpServletResponse; | ||
| import java.nio.charset.StandardCharsets; | ||
| import org.junit.jupiter.api.BeforeEach; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.springframework.http.HttpMethod; | ||
| import org.springframework.mock.web.MockHttpServletRequest; | ||
| import org.springframework.mock.web.MockHttpServletResponse; | ||
| import org.springframework.security.authentication.AuthenticationManager; | ||
| import org.springframework.security.authentication.AuthenticationServiceException; | ||
| import org.springframework.security.authentication.BadCredentialsException; | ||
| import org.springframework.security.authentication.TestingAuthenticationToken; | ||
| import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
| import org.springframework.security.core.Authentication; | ||
| import org.springframework.util.Assert; | ||
| import org.mockito.Mockito; | ||
| import tools.jackson.databind.ObjectMapper; | ||
|
|
||
| class JsonLoginAuthenticationFilterTest { | ||
|
|
||
| private AuthenticationManager authenticationManager; | ||
|
|
||
| private JsonLoginAuthenticationFilter filter; | ||
|
|
||
| @BeforeEach | ||
| void setUp() { | ||
| authenticationManager = Mockito.mock(AuthenticationManager.class); | ||
| Assert.notNull(authenticationManager, "authenticationManager"); | ||
| filter = new JsonLoginAuthenticationFilter(authenticationManager, new ObjectMapper()); | ||
| } | ||
|
|
||
| @Test | ||
| void attemptAuthenticationDelegatesToManagerWhenJsonPayloadIsValid() { | ||
| Authentication authenticated = new TestingAuthenticationToken("alice", "password"); | ||
| when(authenticationManager.authenticate(any(Authentication.class))).thenReturn(authenticated); | ||
|
|
||
| MockHttpServletRequest request = new MockHttpServletRequest(HttpMethod.POST.name(), "/api/v1/auth/login"); | ||
| request.setContent("{\"username\":\"alice\",\"password\":\"secret\"}".getBytes(StandardCharsets.UTF_8)); | ||
| MockHttpServletResponse response = new MockHttpServletResponse(); | ||
|
|
||
| Authentication result = filter.attemptAuthentication(request, response); | ||
|
|
||
| verify(authenticationManager).authenticate( | ||
| argThat(auth -> { | ||
| assertTrue(auth instanceof UsernamePasswordAuthenticationToken); | ||
| assertEquals("alice", auth.getPrincipal()); | ||
| assertEquals("secret", auth.getCredentials()); | ||
| return true; | ||
| }) | ||
| ); | ||
| assertEquals(authenticated, result); | ||
| } | ||
|
|
||
| @Test | ||
| void attemptAuthenticationRejectsNonPostMethods() { | ||
| MockHttpServletRequest request = new MockHttpServletRequest(HttpMethod.GET.name(), "/api/v1/auth/login"); | ||
| MockHttpServletResponse response = new MockHttpServletResponse(); | ||
|
|
||
| AuthenticationServiceException exception = assertThrows( | ||
| AuthenticationServiceException.class, | ||
| () -> filter.attemptAuthentication(request, response) | ||
| ); | ||
|
|
||
| assertEquals("Authentication method not supported: GET", exception.getMessage()); | ||
| } | ||
|
|
||
| @Test | ||
| void attemptAuthenticationRejectsInvalidJson() { | ||
| MockHttpServletRequest request = new MockHttpServletRequest(HttpMethod.POST.name(), "/api/v1/auth/login"); | ||
| request.setContent("not-a-json".getBytes(StandardCharsets.UTF_8)); | ||
| MockHttpServletResponse response = new MockHttpServletResponse(); | ||
|
|
||
| BadCredentialsException exception = assertThrows( | ||
| BadCredentialsException.class, | ||
| () -> filter.attemptAuthentication(request, response) | ||
| ); | ||
|
|
||
| assertEquals("Invalid login request", exception.getMessage()); | ||
| assertEquals(HttpServletResponse.SC_OK, response.getStatus()); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This assertion is misleading and should be removed. When |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| @NullMarked | ||
| package com.workastra.authentication.infrastructure.filter; | ||
|
|
||
| import org.jspecify.annotations.NullMarked; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| package com.workastra.authentication.infrastructure.handler; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertFalse; | ||
| import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
|
||
| import jakarta.servlet.http.HttpServletResponse; | ||
| import java.util.Map; | ||
| import org.junit.jupiter.api.AfterEach; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.springframework.mock.web.MockHttpServletRequest; | ||
| import org.springframework.mock.web.MockHttpServletResponse; | ||
| import org.springframework.security.access.AccessDeniedException; | ||
| import org.springframework.web.context.request.RequestContextHolder; | ||
| import org.springframework.web.context.request.ServletRequestAttributes; | ||
| import tools.jackson.core.type.TypeReference; | ||
| import tools.jackson.databind.ObjectMapper; | ||
|
|
||
| class JsonAccessDeniedHandlerTest { | ||
|
|
||
| private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<>() {}; | ||
|
|
||
| @AfterEach | ||
| void tearDown() { | ||
| RequestContextHolder.resetRequestAttributes(); | ||
| } | ||
|
|
||
| @Test | ||
| void handleWritesForbiddenApiResponse() throws Exception { | ||
| MockHttpServletRequest request = new MockHttpServletRequest(); | ||
| request.addHeader("X-Request-Id", "request-456"); | ||
| RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); | ||
|
|
||
| MockHttpServletResponse response = new MockHttpServletResponse(); | ||
| JsonAccessDeniedHandler handler = new JsonAccessDeniedHandler(new ObjectMapper()); | ||
|
|
||
| handler.handle(request, response, new AccessDeniedException("denied")); | ||
|
|
||
| assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); | ||
| assertEquals("application/json", response.getContentType()); | ||
|
|
||
| Map<String, Object> body = new ObjectMapper().readValue(response.getContentAsString(), MAP_TYPE); | ||
| Object success = body.get("success"); | ||
| assertTrue(success instanceof Boolean); | ||
| assertFalse(((Boolean) success).booleanValue()); | ||
| assertEquals("FORBIDDEN", body.get("code")); | ||
| assertEquals("You do not have permission to access this resource.", body.get("message")); | ||
|
|
||
| Object metaObj = body.get("meta"); | ||
| assertTrue(metaObj instanceof Map); | ||
| Map<?, ?> meta = (Map<?, ?>) metaObj; | ||
| assertEquals("request-456", meta.get("requestId")); | ||
| assertNotNull(meta.get("timestamp")); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| package com.workastra.authentication.infrastructure.handler; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertFalse; | ||
| import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
|
||
| import jakarta.servlet.http.HttpServletResponse; | ||
| import java.util.Map; | ||
| import org.junit.jupiter.api.AfterEach; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.springframework.mock.web.MockHttpServletRequest; | ||
| import org.springframework.mock.web.MockHttpServletResponse; | ||
| import org.springframework.security.authentication.BadCredentialsException; | ||
| import org.springframework.web.context.request.RequestContextHolder; | ||
| import org.springframework.web.context.request.ServletRequestAttributes; | ||
| import tools.jackson.core.type.TypeReference; | ||
| import tools.jackson.databind.ObjectMapper; | ||
|
|
||
| class JsonAuthenticationEntryPointTest { | ||
|
|
||
| private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<>() {}; | ||
|
|
||
| @AfterEach | ||
| void tearDown() { | ||
| RequestContextHolder.resetRequestAttributes(); | ||
| } | ||
|
|
||
| @Test | ||
| void commenceWritesUnauthorizedApiResponse() throws Exception { | ||
| MockHttpServletRequest request = new MockHttpServletRequest(); | ||
| request.addHeader("X-Request-Id", "request-123"); | ||
| RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); | ||
|
|
||
| MockHttpServletResponse response = new MockHttpServletResponse(); | ||
| JsonAuthenticationEntryPoint entryPoint = new JsonAuthenticationEntryPoint(new ObjectMapper()); | ||
|
|
||
| entryPoint.commence(request, response, new BadCredentialsException("bad")); | ||
|
|
||
| assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getStatus()); | ||
| assertEquals("application/json", response.getContentType()); | ||
|
|
||
| Map<String, Object> body = new ObjectMapper().readValue(response.getContentAsString(), MAP_TYPE); | ||
| Object success = body.get("success"); | ||
| assertTrue(success instanceof Boolean); | ||
| assertFalse(((Boolean) success).booleanValue()); | ||
| assertEquals("UNAUTHORIZED", body.get("code")); | ||
| assertEquals("Authentication is required to access this resource.", body.get("message")); | ||
|
|
||
| Object metaObj = body.get("meta"); | ||
| assertTrue(metaObj instanceof Map); | ||
| Map<?, ?> meta = (Map<?, ?>) metaObj; | ||
| assertEquals("request-123", meta.get("requestId")); | ||
| assertNotNull(meta.get("timestamp")); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| @NullMarked | ||
| package com.workastra.authentication.infrastructure.handler; | ||
|
|
||
| import org.jspecify.annotations.NullMarked; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is missing some assertions to fully verify the structure of the
ApiResponse. For consistency withgetCsrfWrapsTokenInApiResponseand to make the test more robust, you should also assert thatmessageanderrorsare null, and thatmetais not null.