Files
cameleer-saas/src/test/java/net/siegeln/cameleer/saas/auth/AuthServiceTest.java
hsiegeln 770f59500d feat: add login with password verification and audit logging
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 10:26:37 +02:00

172 lines
6.5 KiB
Java

package net.siegeln.cameleer.saas.auth;
import net.siegeln.cameleer.saas.audit.AuditAction;
import net.siegeln.cameleer.saas.audit.AuditService;
import net.siegeln.cameleer.saas.auth.dto.LoginRequest;
import net.siegeln.cameleer.saas.auth.dto.RegisterRequest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.util.Optional;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class AuthServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private RoleRepository roleRepository;
@Mock
private PasswordEncoder passwordEncoder;
@Mock
private JwtService jwtService;
@Mock
private AuditService auditService;
private AuthService authService;
@BeforeEach
void setUp() {
authService = new AuthService(userRepository, roleRepository,
passwordEncoder, jwtService, auditService);
}
@Test
void register_createsUserAndReturnsToken() {
var request = new RegisterRequest("user@example.com", "Test User", "password123");
var ownerRole = new RoleEntity();
ownerRole.setName("OWNER");
when(userRepository.existsByEmail("user@example.com")).thenReturn(false);
when(passwordEncoder.encode("password123")).thenReturn("encoded-password");
when(roleRepository.findByName("OWNER")).thenReturn(Optional.of(ownerRole));
when(userRepository.save(any(UserEntity.class))).thenAnswer(invocation -> {
UserEntity user = invocation.getArgument(0);
// simulate ID assignment by persistence
try {
var idField = UserEntity.class.getDeclaredField("id");
idField.setAccessible(true);
idField.set(user, java.util.UUID.randomUUID());
} catch (Exception e) {
throw new RuntimeException(e);
}
return user;
});
when(jwtService.generateToken(any(UserEntity.class))).thenReturn("test-jwt-token");
var response = authService.register(request, "127.0.0.1");
assertNotNull(response);
assertEquals("test-jwt-token", response.token());
assertEquals("user@example.com", response.email());
assertEquals("Test User", response.name());
// Verify audit was logged
verify(auditService).log(
any(), eq("user@example.com"), eq(null),
eq(AuditAction.AUTH_REGISTER), eq(null),
eq(null), eq("127.0.0.1"),
eq("SUCCESS"), eq(null)
);
}
@Test
void register_rejectsDuplicateEmail() {
var request = new RegisterRequest("existing@example.com", "Test User", "password123");
when(userRepository.existsByEmail("existing@example.com")).thenReturn(true);
var exception = assertThrows(IllegalArgumentException.class,
() -> authService.register(request, "127.0.0.1"));
assertEquals("Email already registered", exception.getMessage());
verify(userRepository, never()).save(any());
verify(auditService, never()).log(any(), any(), any(), any(), any(), any(), any(), any(), any());
}
@Test
void login_returnsTokenForValidCredentials() {
var request = new LoginRequest("user@example.com", "password123");
var user = createUserWithId("user@example.com", "encoded-password");
when(userRepository.findByEmail("user@example.com")).thenReturn(Optional.of(user));
when(passwordEncoder.matches("password123", "encoded-password")).thenReturn(true);
when(jwtService.generateToken(user)).thenReturn("login-jwt-token");
var response = authService.login(request, "192.168.1.1");
assertNotNull(response);
assertEquals("login-jwt-token", response.token());
assertEquals("user@example.com", response.email());
verify(auditService).log(
any(), eq("user@example.com"), eq(null),
eq(AuditAction.AUTH_LOGIN), eq(null),
eq(null), eq("192.168.1.1"),
eq("SUCCESS"), eq(null)
);
}
@Test
void login_rejectsInvalidPassword() {
var request = new LoginRequest("user@example.com", "wrong-password");
var user = createUserWithId("user@example.com", "encoded-password");
when(userRepository.findByEmail("user@example.com")).thenReturn(Optional.of(user));
when(passwordEncoder.matches("wrong-password", "encoded-password")).thenReturn(false);
assertThrows(IllegalArgumentException.class,
() -> authService.login(request, "192.168.1.1"));
// Verify AUTH_LOGIN_FAILED audit was logged
verify(auditService).log(
any(), eq("user@example.com"), eq(null),
eq(AuditAction.AUTH_LOGIN_FAILED), eq(null),
eq(null), eq("192.168.1.1"),
eq("FAILURE"), eq(null)
);
}
@Test
void login_rejectsUnknownEmail() {
var request = new LoginRequest("unknown@example.com", "password123");
when(userRepository.findByEmail("unknown@example.com")).thenReturn(Optional.empty());
var exception = assertThrows(IllegalArgumentException.class,
() -> authService.login(request, "192.168.1.1"));
assertEquals("Invalid credentials", exception.getMessage());
verify(auditService, never()).log(any(), any(), any(), any(), any(), any(), any(), any(), any());
}
private UserEntity createUserWithId(String email, String password) {
var user = new UserEntity();
user.setEmail(email);
user.setName("Test User");
user.setPassword(password);
try {
var idField = UserEntity.class.getDeclaredField("id");
idField.setAccessible(true);
idField.set(user, UUID.randomUUID());
} catch (Exception e) {
throw new RuntimeException(e);
}
return user;
}
}