package com.recipeapp.admin; import com.recipeapp.admin.dto.*; import com.recipeapp.admin.entity.AdminAuditLog; import com.recipeapp.auth.UserAccountRepository; import com.recipeapp.auth.entity.UserAccount; import com.recipeapp.common.ConflictException; import com.recipeapp.common.ResourceNotFoundException; 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.data.domain.Pageable; import org.springframework.security.crypto.password.PasswordEncoder; import java.time.Instant; import java.util.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class AdminServiceTest { @Mock private UserAccountRepository userAccountRepository; @Mock private AdminAuditLogRepository auditLogRepository; @Mock private AdminUserQueryRepository adminUserQueryRepository; @Mock private PasswordEncoder passwordEncoder; private AdminServiceImpl adminService; private final String adminEmail = "admin@example.com"; private UserAccount adminUser; private UserAccount targetUser; @BeforeEach void setUp() { adminService = new AdminServiceImpl(userAccountRepository, auditLogRepository, adminUserQueryRepository, passwordEncoder); adminUser = new UserAccount("admin@example.com", "Admin", "hashed"); setId(adminUser, UserAccount.class, UUID.randomUUID()); targetUser = new UserAccount("jane@example.com", "Jane", "hashed"); setId(targetUser, UserAccount.class, UUID.randomUUID()); } private void setId(T entity, Class clazz, UUID id) { try { var field = clazz.getDeclaredField("id"); field.setAccessible(true); field.set(entity, id); } catch (Exception e) { throw new RuntimeException(e); } } @Test void listUsers_returnsPaginatedResults() { when(adminUserQueryRepository.findUsersFiltered(isNull(), isNull(), any(Pageable.class))) .thenReturn(List.of(targetUser)); when(adminUserQueryRepository.countUsersFiltered(isNull(), isNull())) .thenReturn(1L); var result = adminService.listUsers(null, null, 50, 0); assertEquals(1, result.users().size()); assertEquals(1L, result.total()); assertEquals("jane@example.com", result.users().getFirst().email()); } @Test void listUsers_withSearchFilter() { when(adminUserQueryRepository.findUsersFiltered(eq("jane"), isNull(), any(Pageable.class))) .thenReturn(List.of(targetUser)); when(adminUserQueryRepository.countUsersFiltered(eq("jane"), isNull())) .thenReturn(1L); var result = adminService.listUsers("jane", null, 50, 0); assertEquals(1, result.users().size()); } @Test void createUser_success() { when(userAccountRepository.existsByEmailIgnoreCase("new@example.com")).thenReturn(false); when(userAccountRepository.findByEmailIgnoreCase(adminEmail)).thenReturn(Optional.of(adminUser)); when(passwordEncoder.encode("TempPass1!")).thenReturn("encoded"); when(userAccountRepository.save(any(UserAccount.class))).thenAnswer(inv -> { var u = inv.getArgument(0, UserAccount.class); setId(u, UserAccount.class, UUID.randomUUID()); return u; }); when(auditLogRepository.save(any(AdminAuditLog.class))).thenAnswer(inv -> inv.getArgument(0)); var result = adminService.createUser( new CreateUserRequest("new@example.com", "New User", "TempPass1!", "user"), adminEmail); assertEquals("new@example.com", result.email()); assertEquals("New User", result.displayName()); verify(auditLogRepository).save(argThat(log -> "create_account".equals(log.getAction()))); } @Test void createUser_duplicateEmail_throwsConflict() { when(userAccountRepository.existsByEmailIgnoreCase("jane@example.com")).thenReturn(true); assertThrows(ConflictException.class, () -> adminService.createUser( new CreateUserRequest("jane@example.com", "Jane", "TempPass1!", "user"), adminEmail)); } @Test void updateUser_success() { when(userAccountRepository.findByEmailIgnoreCase(adminEmail)).thenReturn(Optional.of(adminUser)); when(userAccountRepository.findById(targetUser.getId())).thenReturn(Optional.of(targetUser)); when(userAccountRepository.save(any(UserAccount.class))).thenAnswer(inv -> inv.getArgument(0)); when(auditLogRepository.save(any(AdminAuditLog.class))).thenAnswer(inv -> inv.getArgument(0)); var result = adminService.updateUser(targetUser.getId(), new UpdateUserRequest("Updated Jane", null, null, null), adminEmail); assertEquals("Updated Jane", result.displayName()); verify(auditLogRepository).save(argThat(log -> "update_account".equals(log.getAction()))); } @Test void updateUser_deactivate() { when(userAccountRepository.findByEmailIgnoreCase(adminEmail)).thenReturn(Optional.of(adminUser)); when(userAccountRepository.findById(targetUser.getId())).thenReturn(Optional.of(targetUser)); when(userAccountRepository.save(any(UserAccount.class))).thenAnswer(inv -> inv.getArgument(0)); when(auditLogRepository.save(any(AdminAuditLog.class))).thenAnswer(inv -> inv.getArgument(0)); var result = adminService.updateUser(targetUser.getId(), new UpdateUserRequest(null, null, null, false), adminEmail); assertFalse(result.isActive()); verify(auditLogRepository).save(argThat(log -> "deactivate_account".equals(log.getAction()))); } @Test void resetPassword_success() { when(userAccountRepository.findByEmailIgnoreCase(adminEmail)).thenReturn(Optional.of(adminUser)); when(userAccountRepository.findById(targetUser.getId())).thenReturn(Optional.of(targetUser)); when(passwordEncoder.encode("NewTemp123!")).thenReturn("encoded"); when(userAccountRepository.save(any(UserAccount.class))).thenAnswer(inv -> inv.getArgument(0)); when(auditLogRepository.save(any(AdminAuditLog.class))).thenAnswer(inv -> inv.getArgument(0)); var result = adminService.resetPassword(targetUser.getId(), new ResetPasswordRequest("NewTemp123!", "Forgot password"), adminEmail); assertEquals("Password reset successfully", result.message()); assertTrue(result.mustChangePassword()); verify(auditLogRepository).save(argThat(log -> "reset_password".equals(log.getAction()))); } @Test void listAuditLog_returnsLogs() { var log = new AdminAuditLog(adminUser.getId(), targetUser.getId(), "create_account", Map.of(), null); setId(log, AdminAuditLog.class, UUID.randomUUID()); when(auditLogRepository.findAllByOrderByPerformedAtDesc(any(Pageable.class))) .thenReturn(List.of(log)); when(userAccountRepository.findById(adminUser.getId())).thenReturn(Optional.of(adminUser)); when(userAccountRepository.findById(targetUser.getId())).thenReturn(Optional.of(targetUser)); var result = adminService.listAuditLog(null, 50, 0); assertEquals(1, result.size()); assertEquals("create_account", result.getFirst().action()); assertEquals("admin@example.com", result.getFirst().adminEmail()); } }