test: add AuthServiceTest - register & login
This commit is contained in:
parent
fa1c2ca69a
commit
bfcfd4e0f5
@ -0,0 +1,292 @@
|
|||||||
|
package com.walkguide.service;
|
||||||
|
|
||||||
|
import com.walkguide.dto.request.LoginRequest;
|
||||||
|
import com.walkguide.dto.request.RegisterRequest;
|
||||||
|
import com.walkguide.dto.response.AuthDataResponse;
|
||||||
|
import com.walkguide.entity.RefreshToken;
|
||||||
|
import com.walkguide.entity.User;
|
||||||
|
import com.walkguide.entity.UserSettings;
|
||||||
|
import com.walkguide.enums.ActivityLogType;
|
||||||
|
import com.walkguide.repository.RefreshTokenRepository;
|
||||||
|
import com.walkguide.repository.UserRepository;
|
||||||
|
import com.walkguide.repository.UserSettingsRepository;
|
||||||
|
import com.walkguide.security.JwtUtil;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
@DisplayName("AuthService Unit Tests")
|
||||||
|
class AuthServiceTest {
|
||||||
|
|
||||||
|
@Mock UserRepository userRepository;
|
||||||
|
@Mock RefreshTokenRepository refreshTokenRepository;
|
||||||
|
@Mock UserSettingsRepository userSettingsRepository;
|
||||||
|
@Mock ActivityLogService activityLogService;
|
||||||
|
@Mock JwtUtil jwtUtil;
|
||||||
|
@Mock PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
@InjectMocks AuthService authService;
|
||||||
|
|
||||||
|
private User sampleUser;
|
||||||
|
private User sampleGuardian;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
sampleUser = User.builder()
|
||||||
|
.id(1L)
|
||||||
|
.email("user@test.com")
|
||||||
|
.password("encodedPassword")
|
||||||
|
.role("ROLE_USER")
|
||||||
|
.displayName("Test User")
|
||||||
|
.uniqueUserId("ABC123DEF456")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
sampleGuardian = User.builder()
|
||||||
|
.id(2L)
|
||||||
|
.email("guardian@test.com")
|
||||||
|
.password("encodedPassword")
|
||||||
|
.role("ROLE_GUARDIAN")
|
||||||
|
.displayName("Test Guardian")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== REGISTER TESTS =====
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("register - ROLE_USER: harus generate uniqueUserId dan buat UserSettings")
|
||||||
|
void register_asUser_shouldGenerateUniqueUserIdAndCreateSettings() {
|
||||||
|
RegisterRequest req = new RegisterRequest();
|
||||||
|
req.setEmail("new@test.com");
|
||||||
|
req.setPassword("password123");
|
||||||
|
req.setDisplayName("New User");
|
||||||
|
req.setRole("USER");
|
||||||
|
|
||||||
|
when(userRepository.existsByEmail("new@test.com")).thenReturn(false);
|
||||||
|
when(userRepository.findByUniqueUserId(anyString())).thenReturn(Optional.empty());
|
||||||
|
when(passwordEncoder.encode("password123")).thenReturn("hashed");
|
||||||
|
when(userRepository.save(any(User.class))).thenAnswer(inv -> {
|
||||||
|
User u = inv.getArgument(0);
|
||||||
|
u.setId(10L);
|
||||||
|
return u;
|
||||||
|
});
|
||||||
|
when(jwtUtil.generateAccessToken(anyString(), anyString(), anyLong()))
|
||||||
|
.thenReturn("access-token");
|
||||||
|
when(jwtUtil.generateRefreshToken()).thenReturn("refresh-token");
|
||||||
|
when(refreshTokenRepository.save(any())).thenReturn(new RefreshToken());
|
||||||
|
|
||||||
|
AuthDataResponse result = authService.register(req);
|
||||||
|
|
||||||
|
assertThat(result).isNotNull();
|
||||||
|
assertThat(result.getRole()).isEqualTo("ROLE_USER");
|
||||||
|
assertThat(result.getAccessToken()).isEqualTo("access-token");
|
||||||
|
|
||||||
|
// Verifikasi UserSettings dibuat untuk ROLE_USER
|
||||||
|
verify(userSettingsRepository, times(1)).save(any(UserSettings.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("register - ROLE_GUARDIAN: tidak boleh generate uniqueUserId dan tidak buat UserSettings")
|
||||||
|
void register_asGuardian_shouldNotCreateUserSettings() {
|
||||||
|
RegisterRequest req = new RegisterRequest();
|
||||||
|
req.setEmail("guardian2@test.com");
|
||||||
|
req.setPassword("password123");
|
||||||
|
req.setDisplayName("New Guardian");
|
||||||
|
req.setRole("GUARDIAN");
|
||||||
|
|
||||||
|
when(userRepository.existsByEmail("guardian2@test.com")).thenReturn(false);
|
||||||
|
when(passwordEncoder.encode("password123")).thenReturn("hashed");
|
||||||
|
when(userRepository.save(any(User.class))).thenAnswer(inv -> {
|
||||||
|
User u = inv.getArgument(0);
|
||||||
|
u.setId(11L);
|
||||||
|
return u;
|
||||||
|
});
|
||||||
|
when(jwtUtil.generateAccessToken(anyString(), anyString(), anyLong()))
|
||||||
|
.thenReturn("access-token");
|
||||||
|
when(jwtUtil.generateRefreshToken()).thenReturn("refresh-token");
|
||||||
|
when(refreshTokenRepository.save(any())).thenReturn(new RefreshToken());
|
||||||
|
|
||||||
|
AuthDataResponse result = authService.register(req);
|
||||||
|
|
||||||
|
assertThat(result.getRole()).isEqualTo("ROLE_GUARDIAN");
|
||||||
|
// Tidak boleh buat UserSettings untuk Guardian
|
||||||
|
verify(userSettingsRepository, never()).save(any());
|
||||||
|
|
||||||
|
// Verifikasi uniqueUserId null pada Guardian
|
||||||
|
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
|
||||||
|
verify(userRepository).save(userCaptor.capture());
|
||||||
|
assertThat(userCaptor.getValue().getUniqueUserId()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("register - email duplikat harus throw RuntimeException")
|
||||||
|
void register_duplicateEmail_shouldThrow() {
|
||||||
|
RegisterRequest req = new RegisterRequest();
|
||||||
|
req.setEmail("user@test.com");
|
||||||
|
req.setPassword("password123");
|
||||||
|
req.setDisplayName("User");
|
||||||
|
req.setRole("USER");
|
||||||
|
|
||||||
|
when(userRepository.existsByEmail("user@test.com")).thenReturn(true);
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> authService.register(req))
|
||||||
|
.isInstanceOf(RuntimeException.class)
|
||||||
|
.hasMessageContaining("Email sudah terdaftar");
|
||||||
|
|
||||||
|
verify(userRepository, never()).save(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== LOGIN TESTS =====
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("login - credentials valid harus return AuthDataResponse")
|
||||||
|
void login_validCredentials_shouldReturnToken() {
|
||||||
|
LoginRequest req = new LoginRequest();
|
||||||
|
req.setEmail("user@test.com");
|
||||||
|
req.setPassword("correctPassword");
|
||||||
|
|
||||||
|
when(userRepository.findByEmail("user@test.com")).thenReturn(Optional.of(sampleUser));
|
||||||
|
when(passwordEncoder.matches("correctPassword", "encodedPassword")).thenReturn(true);
|
||||||
|
when(jwtUtil.generateAccessToken(anyString(), anyString(), anyLong()))
|
||||||
|
.thenReturn("access-token");
|
||||||
|
when(jwtUtil.generateRefreshToken()).thenReturn("refresh-token");
|
||||||
|
when(refreshTokenRepository.save(any())).thenReturn(new RefreshToken());
|
||||||
|
|
||||||
|
AuthDataResponse result = authService.login(req);
|
||||||
|
|
||||||
|
assertThat(result).isNotNull();
|
||||||
|
assertThat(result.getAccessToken()).isEqualTo("access-token");
|
||||||
|
assertThat(result.getRefreshToken()).isEqualTo("refresh-token");
|
||||||
|
assertThat(result.getUserId()).isEqualTo(1L);
|
||||||
|
|
||||||
|
// Harus hapus refresh token lama sebelum buat yang baru
|
||||||
|
verify(refreshTokenRepository).deleteByUserId(1L);
|
||||||
|
// Harus catat activity log LOGIN
|
||||||
|
verify(activityLogService).createLog(
|
||||||
|
eq(sampleUser), eq(ActivityLogType.LOGIN), anyString(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("login - email tidak terdaftar harus throw RuntimeException")
|
||||||
|
void login_emailNotFound_shouldThrow() {
|
||||||
|
LoginRequest req = new LoginRequest();
|
||||||
|
req.setEmail("ghost@test.com");
|
||||||
|
req.setPassword("any");
|
||||||
|
|
||||||
|
when(userRepository.findByEmail("ghost@test.com")).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> authService.login(req))
|
||||||
|
.isInstanceOf(RuntimeException.class)
|
||||||
|
.hasMessageContaining("Email tidak terdaftar");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("login - password salah harus throw RuntimeException")
|
||||||
|
void login_wrongPassword_shouldThrow() {
|
||||||
|
LoginRequest req = new LoginRequest();
|
||||||
|
req.setEmail("user@test.com");
|
||||||
|
req.setPassword("wrongPassword");
|
||||||
|
|
||||||
|
when(userRepository.findByEmail("user@test.com")).thenReturn(Optional.of(sampleUser));
|
||||||
|
when(passwordEncoder.matches("wrongPassword", "encodedPassword")).thenReturn(false);
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> authService.login(req))
|
||||||
|
.isInstanceOf(RuntimeException.class)
|
||||||
|
.hasMessageContaining("Password salah");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== REFRESH TOKEN TESTS =====
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("refreshToken - token valid harus return access token baru")
|
||||||
|
void refreshToken_validToken_shouldReturnNewAccessToken() {
|
||||||
|
RefreshToken storedToken = RefreshToken.builder()
|
||||||
|
.token("valid-refresh-token")
|
||||||
|
.userId(1L)
|
||||||
|
.expiresAt(LocalDateTime.now().plusDays(10))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
when(refreshTokenRepository.findByToken("valid-refresh-token"))
|
||||||
|
.thenReturn(Optional.of(storedToken));
|
||||||
|
when(userRepository.findById(1L)).thenReturn(Optional.of(sampleUser));
|
||||||
|
when(jwtUtil.generateAccessToken(anyString(), anyString(), anyLong()))
|
||||||
|
.thenReturn("new-access-token");
|
||||||
|
|
||||||
|
AuthDataResponse result = authService.refreshToken("valid-refresh-token");
|
||||||
|
|
||||||
|
assertThat(result.getAccessToken()).isEqualTo("new-access-token");
|
||||||
|
// Refresh token tidak diganti — tetap sama
|
||||||
|
assertThat(result.getRefreshToken()).isEqualTo("valid-refresh-token");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("refreshToken - token tidak ada di DB harus throw RuntimeException")
|
||||||
|
void refreshToken_invalidToken_shouldThrow() {
|
||||||
|
when(refreshTokenRepository.findByToken("fake-token")).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> authService.refreshToken("fake-token"))
|
||||||
|
.isInstanceOf(RuntimeException.class)
|
||||||
|
.hasMessageContaining("Refresh token tidak valid");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("refreshToken - token kadaluarsa harus throw dan hapus dari DB")
|
||||||
|
void refreshToken_expiredToken_shouldThrowAndDelete() {
|
||||||
|
RefreshToken expiredToken = RefreshToken.builder()
|
||||||
|
.token("expired-token")
|
||||||
|
.userId(1L)
|
||||||
|
.expiresAt(LocalDateTime.now().minusDays(1)) // sudah lewat
|
||||||
|
.build();
|
||||||
|
|
||||||
|
when(refreshTokenRepository.findByToken("expired-token"))
|
||||||
|
.thenReturn(Optional.of(expiredToken));
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> authService.refreshToken("expired-token"))
|
||||||
|
.isInstanceOf(RuntimeException.class)
|
||||||
|
.hasMessageContaining("kadaluarsa");
|
||||||
|
|
||||||
|
// Token kadaluarsa harus dihapus dari DB
|
||||||
|
verify(refreshTokenRepository).delete(expiredToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== LOGOUT TESTS =====
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("logout - harus hapus refresh token dan catat activity log")
|
||||||
|
void logout_shouldDeleteTokenAndLog() {
|
||||||
|
when(userRepository.findById(1L)).thenReturn(Optional.of(sampleUser));
|
||||||
|
|
||||||
|
authService.logout(1L);
|
||||||
|
|
||||||
|
verify(refreshTokenRepository).deleteByUserId(1L);
|
||||||
|
verify(activityLogService).createLog(
|
||||||
|
eq(sampleUser), eq(ActivityLogType.LOGOUT), anyString(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== UPDATE FCM TOKEN TESTS =====
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("updateFcmToken - harus update dan simpan fcmToken user")
|
||||||
|
void updateFcmToken_shouldSaveNewToken() {
|
||||||
|
when(userRepository.findById(1L)).thenReturn(Optional.of(sampleUser));
|
||||||
|
|
||||||
|
authService.updateFcmToken(1L, "new-fcm-token-xyz");
|
||||||
|
|
||||||
|
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
|
||||||
|
verify(userRepository).save(captor.capture());
|
||||||
|
assertThat(captor.getValue().getFcmToken()).isEqualTo("new-fcm-token-xyz");
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user