Finished registration Process and added tests

This commit is contained in:
lobo committed 2026-04-06 09:12:45 +02:00
1 parent 5dcdf7bd61
commit afbe950ba4
14 files changed
+274 -42

No files matched your search

@@ -30,6 +30,6 @@ public class MinimalUserEntity {
@Column(nullable = false) @Column(nullable = false)
private boolean enabled; private boolean enabled;
@Column(name = "last_login_date", nullable = false) @Column(name = "last_login_date")
private LocalDateTime lastLoginDate; private LocalDateTime lastLoginDate;
} }
@@ -0,0 +1,28 @@
package auth_service.domain.registration.controller;
import auth_service.domain.registration.controller.dto.mapper.ApiMapper;
import auth_service.domain.registration.controller.dto.request.RegistrationRequest;
import auth_service.domain.registration.controller.dto.response.RegistrationResponse;
import auth_service.domain.registration.service.RegistrationService;
import io.micrometer.core.annotation.Timed;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/registration")
@RequiredArgsConstructor
public class RegistrationController {
private final RegistrationService registrationService;
@PostMapping(value = "/new", version = "1.0", produces = "application/json", consumes = "application/json")
@Timed(value = "auth.registration.new.requests.time", description = "Time taken to process registration requests")
public ResponseEntity<RegistrationResponse> registration(@RequestBody RegistrationRequest request) {
var userId = registrationService.register(ApiMapper.mapToModel(request));
return ResponseEntity.ok(RegistrationResponse.builder().userId(userId).build());
}
}
@@ -0,0 +1,19 @@
package auth_service.domain.registration.controller.dto.mapper;
import auth_service.domain.registration.controller.dto.request.RegistrationRequest;
import auth_service.domain.registration.model.Registration;
public abstract class ApiMapper {
private ApiMapper() {
}
public static Registration mapToModel(RegistrationRequest registrationRequest) {
return Registration.builder().
email(registrationRequest.email()).
rawPassword(registrationRequest.password()).
inviteCode(registrationRequest.inviteCode()).
steamId(registrationRequest.steamId()).
build();
}
}
@@ -0,0 +1,7 @@
package auth_service.domain.registration.controller.dto.request;
import lombok.Builder;
@Builder
public record RegistrationRequest(String email, String password, String inviteCode, long steamId) {
}
@@ -0,0 +1,7 @@
package auth_service.domain.registration.controller.dto.response;
import lombok.Builder;
@Builder
public record RegistrationResponse(long userId) {
}
@@ -1,7 +0,0 @@
package auth_service.domain.registration.model;
public record RegisteredUser(
String id,
String email
) {
}
@@ -1,6 +1,5 @@
package auth_service.domain.registration.persistence; package auth_service.domain.registration.persistence;
import auth_service.domain.registration.model.RegisteredUser;
import auth_service.domain.registration.model.Registration; import auth_service.domain.registration.model.Registration;
public interface RegisteredUserRepository { public interface RegisteredUserRepository {
@@ -8,5 +7,5 @@ public interface RegisteredUserRepository {
boolean existsBySteamId(long steamId); boolean existsBySteamId(long steamId);
RegisteredUser save(Registration registration); long save(Registration registration);
} }
@@ -1,23 +1,43 @@
package auth_service.domain.registration.persistence; package auth_service.domain.registration.persistence;
import auth_service.domain.registration.model.RegisteredUser;
import auth_service.domain.registration.model.Registration; import auth_service.domain.registration.model.Registration;
import auth_service.domain.registration.persistence.mapper.EntityMapper;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@Repository @Repository
@Transactional
public class RegisteredUserRepositoryImpl implements RegisteredUserRepository { public class RegisteredUserRepositoryImpl implements RegisteredUserRepository {
@PersistenceContext
private EntityManager entityManager;
@Override @Override
public boolean existsByEmail(String email) { public boolean existsByEmail(String email) {
return false; var jpql = "SELECT COUNT(u) FROM UserEntity u WHERE u.email = :email";
var count = entityManager.createQuery(jpql, Long.class)
.setParameter("email", email)
.getSingleResult();
return count != null && count > 0;
} }
@Override @Override
public boolean existsBySteamId(long steamId) { public boolean existsBySteamId(long steamId) {
return false; var jpql = "SELECT COUNT(u) FROM UserEntity u WHERE u.steamId = :steamId";
var count = entityManager.createQuery(jpql, Long.class)
.setParameter("steamId", steamId)
.getSingleResult();
return count != null && count > 0;
} }
@Override @Override
public RegisteredUser save(Registration registration) { public long save(Registration registration) {
return null; var userToRegisterEntity = EntityMapper.mapToPersistence(registration);
entityManager.persist(userToRegisterEntity);
return userToRegisterEntity.getId();
} }
} }
@@ -0,0 +1,37 @@
package auth_service.domain.registration.persistence.entities;
import jakarta.persistence.*;
import lombok.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "users")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class RegisteredUserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String email;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private boolean enabled;
@Column(name = "steam_id", unique = true, nullable = false)
private Long steamId;
@Column(name = "registration_date", nullable = false)
private LocalDateTime registrationDate;
@Column(name = "last_login_date")
private LocalDateTime lastLoginDate;
}
@@ -0,0 +1,24 @@
package auth_service.domain.registration.persistence.mapper;
import auth_service.domain.registration.model.Registration;
import auth_service.domain.registration.persistence.entities.RegisteredUserEntity;
import java.time.Clock;
import java.time.LocalDateTime;
public abstract class EntityMapper {
private EntityMapper() {
}
public static RegisteredUserEntity mapToPersistence(Registration userModel) {
return RegisteredUserEntity.builder().
email(userModel.getEmail()).
registrationDate(LocalDateTime.now(Clock.systemUTC())).
password(userModel.getEncryptedPassword()).
enabled(true).
steamId(userModel.getSteamId()).
lastLoginDate(null).
build();
}
}
@@ -3,7 +3,6 @@ package auth_service.domain.registration.service;
import auth_service.domain.registration.exception.EmailAlreadyInUseException; import auth_service.domain.registration.exception.EmailAlreadyInUseException;
import auth_service.domain.registration.exception.InvalidInvitationCodeException; import auth_service.domain.registration.exception.InvalidInvitationCodeException;
import auth_service.domain.registration.exception.SteamIdAlreadyRegisteredException; import auth_service.domain.registration.exception.SteamIdAlreadyRegisteredException;
import auth_service.domain.registration.model.RegisteredUser;
import auth_service.domain.registration.model.Registration; import auth_service.domain.registration.model.Registration;
import auth_service.domain.registration.persistence.RegisteredUserRepository; import auth_service.domain.registration.persistence.RegisteredUserRepository;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -18,7 +17,7 @@ public class RegistrationService {
private final RegisteredUserRepository registeredUserRepository; private final RegisteredUserRepository registeredUserRepository;
private final PasswordEncoder passwordEncoder; private final PasswordEncoder passwordEncoder;
public RegisteredUser register(Registration registration) { public long register(Registration registration) {
if (!codeValidationService.isCodeValid(registration.getInviteCode())) { if (!codeValidationService.isCodeValid(registration.getInviteCode())) {
throw new InvalidInvitationCodeException("The provided invite code is invalid or has already been used."); throw new InvalidInvitationCodeException("The provided invite code is invalid or has already been used.");
} }
@@ -36,6 +36,6 @@ public class UserEntity {
@Column(name = "registration_date", nullable = false) @Column(name = "registration_date", nullable = false)
private LocalDateTime registrationDate; private LocalDateTime registrationDate;
@Column(name = "last_login_date", nullable = false) @Column(name = "last_login_date")
private LocalDateTime lastLoginDate; private LocalDateTime lastLoginDate;
} }
@@ -0,0 +1,93 @@
package auth_service.domain.registration.controller;
import auth_service.configuration.security.JwtAuthenticationFilter;
import auth_service.configuration.security.JwtService;
import auth_service.domain.registration.controller.dto.request.RegistrationRequest;
import auth_service.domain.registration.exception.EmailAlreadyInUseException;
import auth_service.domain.registration.model.Registration;
import auth_service.domain.registration.service.RegistrationService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters;
import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.ArgumentMatchers.any;
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;
/**
* Tests for RegistrationController.
*/
@WebMvcTest(RegistrationController.class)
@AutoConfigureMockMvc(addFilters = false) // Skip security filters for simple controller test
@AutoConfigureJsonTesters
class RegistrationControllerTest {
@Autowired
private MockMvc mockMvc;
@MockitoBean
private RegistrationService registrationService;
// Needed because they are likely used in SecurityConfig and required by the ApplicationContext
@MockitoBean
private JwtService jwtService;
@MockitoBean
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Autowired
private JacksonTester<RegistrationRequest> registrationRequestJson;
@Test
void register_Success() throws Exception {
// Arrange
var request = RegistrationRequest.builder()
.email("test@example.com")
.password("SuperSecret123!")
.inviteCode("test-invite-code")
.steamId(314159L)
.build();
long expectedUserId = 42L;
// Mock the service call, ignoring the exact static mapping output
when(registrationService.register(any(Registration.class))).thenReturn(expectedUserId);
// Act & Assert
mockMvc.perform(post("/api/registration/new")
.contentType(MediaType.APPLICATION_JSON)
.content(registrationRequestJson.write(request).getJson()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.userId").value(expectedUserId));
}
@Test
void register_Failure_EmailAlreadyInUse() throws Exception {
// Arrange
var request = RegistrationRequest.builder()
.email("test@example.com")
.password("SuperSecret123!")
.inviteCode("test-invite-code")
.steamId(314159L)
.build();
when(registrationService.register(any(Registration.class)))
.thenThrow(new EmailAlreadyInUseException("The provided email address is already in use."));
// Act & Assert
// Depending on your GlobalExceptionHandler, this could also be 400 or 409.
// Assuming standard behavior without advice or mapping to 400 for now.
mockMvc.perform(post("/api/registration/new")
.contentType(MediaType.APPLICATION_JSON)
.content(registrationRequestJson.write(request).getJson()))
.andExpect(status().isBadRequest()); // Adjust status based on your @ExceptionHandler
}
}
@@ -3,7 +3,6 @@ package auth_service.domain.registration.service;
import auth_service.domain.registration.exception.EmailAlreadyInUseException; import auth_service.domain.registration.exception.EmailAlreadyInUseException;
import auth_service.domain.registration.exception.InvalidInvitationCodeException; import auth_service.domain.registration.exception.InvalidInvitationCodeException;
import auth_service.domain.registration.exception.SteamIdAlreadyRegisteredException; import auth_service.domain.registration.exception.SteamIdAlreadyRegisteredException;
import auth_service.domain.registration.model.RegisteredUser;
import auth_service.domain.registration.model.Registration; import auth_service.domain.registration.model.Registration;
import auth_service.domain.registration.persistence.RegisteredUserRepository; import auth_service.domain.registration.persistence.RegisteredUserRepository;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@@ -14,9 +13,14 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
/**
* Tests for RegistrationService.
*/
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class RegistrationServiceTest { class RegistrationServiceTest {
@@ -39,60 +43,62 @@ class RegistrationServiceTest {
validRegistration = Registration.builder() validRegistration = Registration.builder()
.email("test@example.com") .email("test@example.com")
.rawPassword("SuperSecret123!") .rawPassword("SuperSecret123!")
.encryptedPassword("") .inviteCode("test-invite-code")
.inviteCode("TBP-INVITE-2026")
.steamId(314159L) .steamId(314159L)
.build(); .build();
} }
@Test @Test
void register_withValidData_shouldReturnRegisteredUserAndMarkCodeAsUsed() { void register_Success() {
// Arrange // Arrange
when(codeValidationService.isCodeValid("TBP-INVITE-2026")).thenReturn(true); long expectedUserId = 42L;
when(registeredUserRepository.existsByEmail("test@example.com")).thenReturn(false); when(codeValidationService.isCodeValid(validRegistration.getInviteCode())).thenReturn(true);
when(passwordEncoder.encode("SuperSecret123!")).thenReturn("hashedPasswordXYZ"); when(registeredUserRepository.existsByEmail(validRegistration.getEmail())).thenReturn(false);
when(registeredUserRepository.existsBySteamId(validRegistration.getSteamId())).thenReturn(false);
RegisteredUser expectedUser = new RegisteredUser("uuid-1234", "test@example.com"); when(passwordEncoder.encode(validRegistration.getRawPassword())).thenReturn("hashedPasswordXYZ");
when(registeredUserRepository.save(validRegistration)).thenReturn(expectedUser); when(registeredUserRepository.save(any(Registration.class))).thenReturn(expectedUserId);
// Act // Act
RegisteredUser result = registrationService.register(validRegistration); long result = registrationService.register(validRegistration);
// Assert // Assert
assertNotNull(result); assertEquals(expectedUserId, result);
assertEquals("uuid-1234", result.id()); verify(passwordEncoder).encode("SuperSecret123!");
assertEquals("test@example.com", result.email()); verify(registeredUserRepository).save(validRegistration);
assertEquals("hashedPasswordXYZ", validRegistration.getEncryptedPassword());
} }
@Test @Test
void register_withInvalidInviteCode_shouldThrowException() { void register_InvalidInviteCode() {
// Arrange // Arrange
when(codeValidationService.isCodeValid("TBP-INVITE-2026")).thenReturn(false); when(codeValidationService.isCodeValid(validRegistration.getInviteCode())).thenReturn(false);
// Act & Assert // Act & Assert
assertThrows(InvalidInvitationCodeException.class, () -> registrationService.register(validRegistration)); assertThrows(InvalidInvitationCodeException.class, () -> registrationService.register(validRegistration));
verify(registeredUserRepository, never()).existsByEmail(anyString());
verify(registeredUserRepository, never()).save(any()); verify(registeredUserRepository, never()).save(any());
} }
@Test @Test
void register_withExistingEmail_shouldThrowException() { void register_EmailAlreadyInUse() {
// Arrange // Arrange
when(codeValidationService.isCodeValid("TBP-INVITE-2026")).thenReturn(true); when(codeValidationService.isCodeValid(validRegistration.getInviteCode())).thenReturn(true);
when(registeredUserRepository.existsByEmail("test@example.com")).thenReturn(true); when(registeredUserRepository.existsByEmail(validRegistration.getEmail())).thenReturn(true);
// Act & Assert // Act & Assert
assertThrows(EmailAlreadyInUseException.class, () -> registrationService.register(validRegistration)); assertThrows(EmailAlreadyInUseException.class, () -> registrationService.register(validRegistration));
verify(registeredUserRepository, never()).existsBySteamId(anyLong());
verify(registeredUserRepository, never()).save(any()); verify(registeredUserRepository, never()).save(any());
} }
@Test @Test
void register_withExistingSteamId_shouldThrowException() { void register_SteamIdAlreadyRegistered() {
// Arrange // Arrange
when(codeValidationService.isCodeValid("TBP-INVITE-2026")).thenReturn(true); when(codeValidationService.isCodeValid(validRegistration.getInviteCode())).thenReturn(true);
when(registeredUserRepository.existsByEmail("test@example.com")).thenReturn(false); when(registeredUserRepository.existsByEmail(validRegistration.getEmail())).thenReturn(false);
when(registeredUserRepository.existsBySteamId(314159L)).thenReturn(true); when(registeredUserRepository.existsBySteamId(validRegistration.getSteamId())).thenReturn(true);
// Act & Assert // Act & Assert
assertThrows(SteamIdAlreadyRegisteredException.class, () -> registrationService.register(validRegistration)); assertThrows(SteamIdAlreadyRegisteredException.class, () -> registrationService.register(validRegistration));