From 07ba80e4ee95e7af355091ab207b60a52b9bc6c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <51346021+andr30z@users.noreply.github.com> Date: Mon, 12 Feb 2024 17:00:02 -0300 Subject: [PATCH 1/2] fix: auth token expiry date --- .../fgc/combo/companion/service/impl/TokenProviderImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/com/fgc/combo/companion/service/impl/TokenProviderImpl.java b/backend/src/main/java/com/fgc/combo/companion/service/impl/TokenProviderImpl.java index 27f6bcd..5247e5f 100644 --- a/backend/src/main/java/com/fgc/combo/companion/service/impl/TokenProviderImpl.java +++ b/backend/src/main/java/com/fgc/combo/companion/service/impl/TokenProviderImpl.java @@ -44,7 +44,7 @@ public Token generateAccessToken(String subject) { return new Token( Token.TokenType.ACCESS, token, - duration, + tokenExpirationMsec, LocalDateTime.ofInstant(expiryDate.toInstant(), ZoneId.systemDefault())); } @@ -65,7 +65,7 @@ public Token generateRefreshToken(String subject) { return new Token( Token.TokenType.REFRESH, token, - duration, + refreshTokenExpirationMsec, LocalDateTime.ofInstant(expiryDate.toInstant(), ZoneId.systemDefault())); } From 4d439e3e8c8e551e357d228b183c0ce54aeb84c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <51346021+andr30z@users.noreply.github.com> Date: Mon, 12 Feb 2024 17:00:43 -0300 Subject: [PATCH 2/2] test: add test for login feature --- .../service/impl/EmailServiceImpl.java | 1 + .../controller/UserControllerTests.java | 148 ++++++++++++++++++ backend/src/test/resources/application.yml | 6 +- backend/src/test/resources/import.sql | 2 + 4 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 backend/src/test/java/com/fgc/combo/companion/controller/UserControllerTests.java diff --git a/backend/src/main/java/com/fgc/combo/companion/service/impl/EmailServiceImpl.java b/backend/src/main/java/com/fgc/combo/companion/service/impl/EmailServiceImpl.java index b884510..590a178 100644 --- a/backend/src/main/java/com/fgc/combo/companion/service/impl/EmailServiceImpl.java +++ b/backend/src/main/java/com/fgc/combo/companion/service/impl/EmailServiceImpl.java @@ -46,6 +46,7 @@ public Email sendEmail(CreateEmailDto emailDto) { email.setStatus(MailStatus.ERROR.name()); log.error("Failed to send email", mailException); } + return this.emailRepository.save(email); } } diff --git a/backend/src/test/java/com/fgc/combo/companion/controller/UserControllerTests.java b/backend/src/test/java/com/fgc/combo/companion/controller/UserControllerTests.java new file mode 100644 index 0000000..ea1068a --- /dev/null +++ b/backend/src/test/java/com/fgc/combo/companion/controller/UserControllerTests.java @@ -0,0 +1,148 @@ +package com.fgc.combo.companion.controller; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +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.AutoConfigureWebMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fgc.combo.companion.dto.CreateUserDto; +import com.fgc.combo.companion.dto.LoginRequest; +import com.fgc.combo.companion.dto.LoginResponse; +import com.fgc.combo.companion.dto.OAuthLoginRequestDto; +import com.fgc.combo.companion.enums.OAuthTypes; +import com.fgc.combo.companion.model.User; +import com.fgc.combo.companion.repository.UserRepository; +import com.fgc.combo.companion.service.UserService; + +import jakarta.servlet.http.Cookie; +import lombok.extern.slf4j.Slf4j; + +@SpringBootTest +@AutoConfigureWebMvc +@Slf4j +@ExtendWith(MockitoExtension.class) +@TestInstance(Lifecycle.PER_CLASS) +public class UserControllerTests { + + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext applicationContext; + + @Autowired + private UserService userService; + + @Autowired + private UserRepository userRepository; + + @Autowired + private ObjectMapper objectMapper; + + private void assertSuccessResponse(int responseStatus) { + assertThat(responseStatus).isIn(List.of(200, 201)); + } + + @BeforeEach + public final void init() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext).build(); + } + + private MockHttpServletResponse login(String url, Object body, User user) throws Exception { + + MvcResult mvcResult = this.mockMvc.perform( + MockMvcRequestBuilders + .post(url) + .contentType("application/json") + .content( + objectMapper.writeValueAsString(body))) + .andReturn(); + + var mvcResponse = mvcResult.getResponse(); + + assertSuccessResponse(mvcResponse.getStatus()); + + LoginResponse loginResponse = objectMapper.readValue( + mvcResponse.getContentAsString(), + LoginResponse.class); + + Cookie accessToken = mvcResponse.getCookie("accessToken"); + if (user != null) { + assertThat(loginResponse.getUser().getId()).isEqualTo(user.getId()); + } + assertThat(accessToken).isNotNull(); + assertThat(accessToken.getDomain()).isEqualTo("fgc-combo-companion.xyz"); + return mvcResponse; + } + + @Test + @DisplayName("It should login with email and password") + void itShouldLoginSuccessfully() throws Exception { + var user = this.userService.create( + CreateUserDto.builder() + .email("randomuser1@gmail.com") + .password("12345678") + .name("USER") + .build()); + this.login("/api/v1/users/login", new LoginRequest(user.getEmail(), "12345678"), user); + } + + @Test + @DisplayName("It should login and create user if don't exist through OAuth") + void itShouldLoginAndCreateTheUserSuccessfullyThroughOAuth() throws Exception { + + assertThat(userRepository.findUserByEmail("randomuser2@gmail.com")).isEmpty(); + MockHttpServletResponse response = this.login("/api/v1/users/oauth/login", + new OAuthLoginRequestDto("randomuser2@gmail.com", OAuthTypes.GOOGLE.name(), "testname", "testauthkey", "123"), + null); + + LoginResponse loginResponse = objectMapper.readValue( + response.getContentAsString(), + LoginResponse.class); + + Optional user = userRepository.findUserByEmail("randomuser2@gmail.com"); + + assertThat(user).isNotEmpty(); + assertThat(user.get().getId()).isEqualTo(loginResponse.getUser().getId()); + + } + + @Test + @DisplayName("It should login through OAuth") + void itShouldLoginThroughOAuth() throws Exception { + + var user = this.userService.saveUser(User.builder().authProvider(OAuthTypes.GOOGLE).oAuthId("123456") + .email("testemail@gmail.com").emailVerified(true) + .name("test").build()); + + MockHttpServletResponse response = this.login("/api/v1/users/oauth/login", + new OAuthLoginRequestDto(user.getEmail(), OAuthTypes.GOOGLE.name(), user.getOAuthId(), "testauthkey", + user.getName()), + null); + + LoginResponse loginResponse = objectMapper.readValue( + response.getContentAsString(), + LoginResponse.class); + + assertThat(user.getId()).isEqualTo(loginResponse.getUser().getId()); + + } + +} diff --git a/backend/src/test/resources/application.yml b/backend/src/test/resources/application.yml index b55b3f8..9b4b1a4 100644 --- a/backend/src/test/resources/application.yml +++ b/backend/src/test/resources/application.yml @@ -20,9 +20,9 @@ server: authentication: auth: secureKey: test - tokenSecret: SktwcElPMjNKNDIzTkFTREtKSHhjY3hhMjEzM0FTS0RGT0VXMzQzNDQyM04xM3NuZG1hczIxMjNMWENLTEpLcHBJTzIzSjQyM05BU0RLSkh4Y2N4YTIxMzNBU0tERk9FVzM0MzQ0MjNOMTNzbmRtYXMyMTIzTFhDS0xKS3BwSU8yM0o0MjNOQVNES0pIeGNjeGEyMTMzQVNLREZPRVczNDM0NDIzTjEzc25kbWFzMjEyM0xYQ0tMSktwcElPMjNKNDIzTkFTREtKSHhjY3hhMjEzM0FTS0RGT0VXMzQzNDQyM04xM3NuZG1hczIxMjNMWENLTEpLcHBJTzIzSjQyM05BU0RLSkh4Y2N4YTIxMzNBU0tERk9FVzM0MzQ0MjNOMTNzbmRtYXMyMTIzTFhDS0xKS3BwSU8yM0o0MjNOQVNES0pIeGNjeGEyMTMzQVNLREZPRVczNDM0NDIzTjEzc25kbWFzMjEyM0xYQ0tMSktwcElPMjNKNDIzTkFTREtKSHhjY3hhMjEzM0FTS0RGT0VXMzQzNDQyM04xM3NuZG1hczIxMjNMWENLTEpLcHBJTzIzSjQyM05BU0RLSkh4Y2N4YTIxMzNBU0tERk9FVzM0MzQ0MjNOMTNzbmRtYXMyMTIzTFhDS0xKS3BwSU8yM0o0MjNOQVNES0pIeGNjeGEyMTMzQVNLREZPRVczNDM0NDIzTjEzc25kbWFzMjEyM0xYQ0tMSktwcElPMjNKNDIzTkFTREtKSHhjY3hhMjEzM0FTS0RGT0VXMzQzNDQyM04xM3NuZG1hczIxMjNMWENLTEpLcHBJTzIzSjQyM05BU0RLSkh4Y2N4YTIxMzNBU0tERk9FVzM0MzQ0MjNOMTNzbmRtYXMyMTIzTFhDS0xKS3BwSU8yM0o0MjNOQVNES0pIeGNjeGEyMTMzQVNLREZPRVczNDM0NDIzTjEzc25kbWFzMjEyM0xYQ0tMSktwcElPMjNKNDIzTkFTREtKSHhjY3hhMjEzM0FTS0RGT0VXMzQzNDQyM04xM3NuZG1hczIxMjNMWENLTEpLcHBJTzIzSjQyM05BU0RLSkh4Y2N4YTIxMzNBU0tERk9FVzM0MzQ0MjNOMTNzbmRtYXMyMTIzTFhDS0xKS3BwSU8yM0o0MjNOQVNES0pIeGNjeGEyMTMzQVNLREZPRVczNDM0NDIzTjEzc25kbWFzMjEyM0xYQ0tMSktwcElPMjNKNDIzTkFTREtKSHhjY3hhMjEzM0FTS0RGT0VXMzQzNDQyM04xM3NuZG1hczIxMjNMWENLTEpLcHBJTzIzSjQyM05BU0RLSkh4Y2N4YTIxMzNBU0tERk9FVzM0MzQ0MjNOMTNzbmRtYXMyMTIzTFhDS0xKS3BwSU8yM0o0MjNOQVNES0pIeGNjeGEyMTMzQVNLREZPRVczNDM0NDIzTjEzc25kbWFzMjEyM0xYQ0tM - tokenExpirationMsec: 3600000 - refreshTokenExpirationMsec: 7776000000 + tokenSecret: Sk34twcElPMjNKNDIzTkFTREtKSHhjY3hhMjEzM0FTS0RGT0VXMzQzNDQyM04xM3NuZG1hczIxMjNMWENLTEpLcHBJTzIzSjQyM05BU0RLSkh4Y2N4YTIxMzNBU0tERk9FVzM0MzQ0MjNOMTNzbmRtYXMyMTIzTFhDS0xKS3BwSU8yM0o0MjNOQVNES0pIeGNjeGEyMTMzQVNLREZPRVczNDM0NDIzTjEzc25kbWFzMjEyM0xYQ0tMSktwcElPMjNKNDIzTkFTREtKSHhjY3hhMjEzM0FTS0RGT0VXMzQzNDQyM04xM3NuZG1hczIxMjNMWENLTEpLcHBJTzIzSjQyM05BU0RLSkh4Y2N4YTIxMzNBU0tERk9FVzM0MzQ0MjNOMTNzbmRtYXMyMTIzTFhDS0xKS3BwSU8yM0o0MjNOQVNES0pIeGNjeGEyMTMzQVNLREZPRVczNDM0NDIzTjEzc25kbWFzMjEyM0xYQ0tMSktwcElPMjNKNDIzTkFTREtKSHhjY3hhMjEzM0FTS0RGT0VXMzQzNDQyM04xM3NuZG1hczIxMjNMWENLTEpLcHBJTzIzSjQyM05BU0RLSkh4Y2N4YTIxMzNBU0tERk9FVzM0MzQ0MjNOMTNzbmRtYXMyMTIzTFhDS0xKS3BwSU8yM0o0MjNOQVNES0pIeGNjeGEyMTMzQVNLREZPRVczNDM0NDIzTjEzc25kbWFzMjEyM0xYQ0tMSktwcElPMjNKNDIzTkFTREtKSHhjY3hhMjEzM0FTS0RGT0VXMzQzNDQyM04xM3NuZG1hczIxMjNMWENLTEpLcHBJTzIzSjQyM05BU0RLSkh4Y2N4YTIxMzNBU0tERk9FVzM0MzQ0MjNOMTNzbmRtYXMyMTIzTFhDS0xKS3BwSU8yM0o0MjNOQVNES0pIeGNjeGEyMTMzQVNLREZPRVczNDM0NDIzTjEzc25kbWFzMjEyM0xYQ0tMSktwcElPMjNKNDIzTkFTREtKSHhjY3hhMjEzM0FTS0RGT0VXMzQzNDQyM04xM3NuZG1hczIxMjNMWENLTEpLcHBJTzIzSjQyM05BU0RLSkh4Y2N4YTIxMzNBU0tERk9FVzM0MzQ0MjNOMTNzbmRtYXMyMTIzTFhDS0xKS3BwSU8yM0o0MjNOQVNES0pIeGNjeGEyMTMzQVNLREZPRVczNDM0NDIzTjEzc25kbWFzMjEyM0xYQ0tMSktwcElPMjNKNDIzTkFTREtKSHhjY3hhMjEzM0FTS0RGT0VXMzQzNDQyM04xM3NuZG1hczIxMjNMWENLTEpLcHBJTzIzSjQyM05BU0RLSkh4Y2N4YTIxMzNBU0tERk9FVzM0MzQ0MjNOMTNzbmRtYXMyMTIzTFhDS0xKS3BwSU8yM0o0MjNOQVNES0pIeGNjeGEyMTMzQVNLREZPRVczNDM0NDIzTjEzc25kbWFzMjEyM0xYQ0tM + tokenExpirationMsec: 777600 + refreshTokenExpirationMsec: 777600 accessTokenCookieName: accessToken refreshTokenCookieName: refreshToken oauthkey: testauthkey diff --git a/backend/src/test/resources/import.sql b/backend/src/test/resources/import.sql index 6d7c31e..a5df546 100644 --- a/backend/src/test/resources/import.sql +++ b/backend/src/test/resources/import.sql @@ -1,2 +1,4 @@ CREATE TYPE gametypes AS ENUM ('TEKKEN_7', 'SFV', 'STREET_FIGHTER_6','KOF_XV', 'GUILTY_GEAR_STRIVE', 'TEKKEN_8', 'MORTAL_KOMBAT_1') +CREATE TYPE mailstatustypes AS ENUM ('SENT', 'ERROR') +CREATE TYPE userverificationtypes AS ENUM ('PASSWORD_CHANGE', 'EMAIL_VERIFICATION') CREATE TYPE oauthtypes AS ENUM ('GOOGLE'); \ No newline at end of file