test: add PairingServiceTest unit test

This commit is contained in:
5803024019 2026-05-15 21:36:44 +07:00
parent bfcfd4e0f5
commit 70487e6d0d

View File

@ -0,0 +1,217 @@
package com.walkguide.service;
import com.walkguide.dto.response.PairingStatusResponse;
import com.walkguide.entity.PairingRelation;
import com.walkguide.entity.User;
import com.walkguide.enums.PairingStatus;
import com.walkguide.exception.PairingException;
import com.walkguide.exception.ResourceNotFoundException;
import com.walkguide.repository.*;
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.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
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("PairingService Unit Tests")
class PairingServiceTest {
@Mock PairingRelationRepository pairingRelationRepository;
@Mock UserRepository userRepository;
@Mock VoiceCommandConfigRepository voiceCommandConfigRepository;
@Mock HardwareShortcutRepository hardwareShortcutRepository;
@Mock AiConfigRepository aiConfigRepository;
@Mock ActivityLogService activityLogService;
@Mock FcmService fcmService;
@InjectMocks PairingService pairingService;
private User guardian;
private User user;
@BeforeEach
void setUp() {
guardian = User.builder()
.id(1L)
.email("guardian@test.com")
.role("ROLE_GUARDIAN")
.displayName("Guardian Test")
.fcmToken("guardian-fcm-token")
.build();
user = User.builder()
.id(2L)
.email("user@test.com")
.role("ROLE_USER")
.displayName("User Test")
.uniqueUserId("ABC123DEF456")
.fcmToken("user-fcm-token")
.build();
}
// ===== INVITE USER TESTS =====
@Test
@DisplayName("inviteUser - berhasil mengirim invite ke user yang valid")
void inviteUser_success_shouldSavePairingAndSendFcm() {
when(pairingRelationRepository.existsByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE)).thenReturn(false);
when(pairingRelationRepository.existsByGuardian_IdAndStatus(1L, PairingStatus.PENDING)).thenReturn(false);
when(userRepository.findById(1L)).thenReturn(Optional.of(guardian));
when(userRepository.findByUniqueUserId("ABC123DEF456")).thenReturn(Optional.of(user));
when(pairingRelationRepository.existsByUser_IdAndStatus(2L, PairingStatus.ACTIVE)).thenReturn(false);
when(pairingRelationRepository.save(any(PairingRelation.class))).thenAnswer(inv -> {
PairingRelation p = inv.getArgument(0);
p.setId(10L);
return p;
});
PairingStatusResponse result = pairingService.inviteUser(1L, "ABC123DEF456");
assertThat(result).isNotNull();
verify(pairingRelationRepository).save(any(PairingRelation.class));
verify(fcmService).sendToToken(eq("user-fcm-token"), anyString(), anyString(), anyMap());
verify(activityLogService).createLog(eq(guardian), any(), anyString(), any());
}
@Test
@DisplayName("inviteUser - Guardian sudah ACTIVE pairing harus throw PairingException")
void inviteUser_alreadyActivePairing_shouldThrow() {
when(pairingRelationRepository.existsByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE)).thenReturn(true);
assertThatThrownBy(() -> pairingService.inviteUser(1L, "ABC123DEF456"))
.isInstanceOf(PairingException.class)
.hasMessageContaining("sudah memiliki user yang dipair");
verify(pairingRelationRepository, never()).save(any());
}
@Test
@DisplayName("inviteUser - Guardian sudah PENDING invite harus throw PairingException")
void inviteUser_alreadyPendingInvite_shouldThrow() {
when(pairingRelationRepository.existsByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE)).thenReturn(false);
when(pairingRelationRepository.existsByGuardian_IdAndStatus(1L, PairingStatus.PENDING)).thenReturn(true);
assertThatThrownBy(() -> pairingService.inviteUser(1L, "ABC123DEF456"))
.isInstanceOf(PairingException.class)
.hasMessageContaining("sudah punya invite yang menunggu");
verify(pairingRelationRepository, never()).save(any());
}
@Test
@DisplayName("inviteUser - uniqueUserId tidak ditemukan harus throw ResourceNotFoundException")
void inviteUser_userNotFound_shouldThrow() {
when(pairingRelationRepository.existsByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE)).thenReturn(false);
when(pairingRelationRepository.existsByGuardian_IdAndStatus(1L, PairingStatus.PENDING)).thenReturn(false);
when(userRepository.findById(1L)).thenReturn(Optional.of(guardian));
when(userRepository.findByUniqueUserId("INVALID")).thenReturn(Optional.empty());
assertThatThrownBy(() -> pairingService.inviteUser(1L, "INVALID"))
.isInstanceOf(ResourceNotFoundException.class)
.hasMessageContaining("tidak ditemukan");
}
@Test
@DisplayName("inviteUser - target bukan ROLE_USER harus throw PairingException")
void inviteUser_targetNotUser_shouldThrow() {
User anotherGuardian = User.builder()
.id(3L).role("ROLE_GUARDIAN").uniqueUserId("GRD000000001").build();
when(pairingRelationRepository.existsByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE)).thenReturn(false);
when(pairingRelationRepository.existsByGuardian_IdAndStatus(1L, PairingStatus.PENDING)).thenReturn(false);
when(userRepository.findById(1L)).thenReturn(Optional.of(guardian));
when(userRepository.findByUniqueUserId("GRD000000001")).thenReturn(Optional.of(anotherGuardian));
assertThatThrownBy(() -> pairingService.inviteUser(1L, "GRD000000001"))
.isInstanceOf(PairingException.class)
.hasMessageContaining("bukan milik User");
}
@Test
@DisplayName("inviteUser - User sudah ACTIVE dengan Guardian lain harus throw PairingException")
void inviteUser_userAlreadyPaired_shouldThrow() {
when(pairingRelationRepository.existsByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE)).thenReturn(false);
when(pairingRelationRepository.existsByGuardian_IdAndStatus(1L, PairingStatus.PENDING)).thenReturn(false);
when(userRepository.findById(1L)).thenReturn(Optional.of(guardian));
when(userRepository.findByUniqueUserId("ABC123DEF456")).thenReturn(Optional.of(user));
when(pairingRelationRepository.existsByUser_IdAndStatus(2L, PairingStatus.ACTIVE)).thenReturn(true);
assertThatThrownBy(() -> pairingService.inviteUser(1L, "ABC123DEF456"))
.isInstanceOf(PairingException.class)
.hasMessageContaining("sudah dipair dengan Guardian lain");
}
// ===== RESPOND TO PAIRING TESTS =====
@Test
@DisplayName("respondToPairing - accept: status harus jadi ACTIVE")
void respondToPairing_accept_shouldSetStatusActive() {
PairingRelation pairing = PairingRelation.builder()
.id(10L).guardian(guardian).user(user).status(PairingStatus.PENDING).build();
when(pairingRelationRepository.findById(10L)).thenReturn(Optional.of(pairing));
when(pairingRelationRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
// Stub untuk pembuatan config saat accept
when(voiceCommandConfigRepository.existsByUserId(2L)).thenReturn(false);
when(hardwareShortcutRepository.existsByUserId(2L)).thenReturn(false);
when(aiConfigRepository.findByUserId(2L)).thenReturn(Optional.empty());
when(aiConfigRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
PairingStatusResponse result = pairingService.respondToPairing(2L, 10L, true);
assertThat(result).isNotNull();
assertThat(pairing.getStatus()).isEqualTo(PairingStatus.ACTIVE);
verify(pairingRelationRepository).save(pairing);
}
@Test
@DisplayName("respondToPairing - reject: status harus jadi REJECTED")
void respondToPairing_reject_shouldSetStatusRejected() {
PairingRelation pairing = PairingRelation.builder()
.id(10L).guardian(guardian).user(user).status(PairingStatus.PENDING).build();
when(pairingRelationRepository.findById(10L)).thenReturn(Optional.of(pairing));
when(pairingRelationRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
PairingStatusResponse result = pairingService.respondToPairing(2L, 10L, false);
assertThat(result).isNotNull();
assertThat(pairing.getStatus()).isEqualTo(PairingStatus.REJECTED);
}
@Test
@DisplayName("respondToPairing - user yang salah harus throw PairingException")
void respondToPairing_wrongUser_shouldThrow() {
PairingRelation pairing = PairingRelation.builder()
.id(10L).guardian(guardian).user(user).status(PairingStatus.PENDING).build();
when(pairingRelationRepository.findById(10L)).thenReturn(Optional.of(pairing));
// userId=99 bukan pemilik pairing ini (user.id=2)
assertThatThrownBy(() -> pairingService.respondToPairing(99L, 10L, true))
.isInstanceOf(PairingException.class)
.hasMessageContaining("tidak berhak");
}
@Test
@DisplayName("respondToPairing - pairing yang sudah direspons harus throw PairingException")
void respondToPairing_alreadyResponded_shouldThrow() {
PairingRelation pairing = PairingRelation.builder()
.id(10L).guardian(guardian).user(user).status(PairingStatus.ACTIVE).build();
when(pairingRelationRepository.findById(10L)).thenReturn(Optional.of(pairing));
assertThatThrownBy(() -> pairingService.respondToPairing(2L, 10L, true))
.isInstanceOf(PairingException.class)
.hasMessageContaining("sudah direspons");
}
}