test: add AiConfigServiceTest unit test

This commit is contained in:
5803024019 2026-05-15 21:47:23 +07:00
parent 2c5626c0f9
commit 688a3aac7c

View File

@ -0,0 +1,212 @@
package com.walkguide.service;
import com.walkguide.dto.request.AiConfigUpdateRequest;
import com.walkguide.dto.response.AiConfigResponse;
import com.walkguide.entity.AiConfig;
import com.walkguide.entity.PairingRelation;
import com.walkguide.entity.User;
import com.walkguide.enums.PairingStatus;
import com.walkguide.exception.PairingException;
import com.walkguide.repository.AiConfigRepository;
import com.walkguide.repository.PairingRelationRepository;
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 java.util.Optional;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
@DisplayName("AiConfigService Unit Tests")
class AiConfigServiceTest {
@Mock AiConfigRepository aiConfigRepository;
@Mock PairingRelationRepository pairingRelationRepository;
@Mock FcmService fcmService;
@InjectMocks AiConfigService aiConfigService;
private User guardian;
private User user;
private PairingRelation activePairing;
private AiConfig existingConfig;
@BeforeEach
void setUp() {
guardian = User.builder()
.id(1L).email("guardian@test.com").role("ROLE_GUARDIAN")
.displayName("Guardian").fcmToken("guardian-fcm").build();
user = User.builder()
.id(2L).email("user@test.com").role("ROLE_USER")
.displayName("User").fcmToken("user-fcm").build();
activePairing = PairingRelation.builder()
.id(5L).guardian(guardian).user(user).status(PairingStatus.ACTIVE).build();
existingConfig = AiConfig.builder()
.id(10L).userId(2L).guardianId(1L)
.confidenceThreshold(0.5)
.alertDistanceClose(1.5)
.alertDistanceMedium(3.0)
.maxInferenceFps(5)
.build();
}
// ===== GET CONFIG TESTS =====
@Test
@DisplayName("getConfig - konfigurasi ada harus return data dari DB")
void getConfig_configExists_shouldReturnExistingConfig() {
when(aiConfigRepository.findByUserId(2L)).thenReturn(Optional.of(existingConfig));
AiConfigResponse result = aiConfigService.getConfig(2L);
assertThat(result.getId()).isEqualTo(10L);
assertThat(result.getConfidenceThreshold()).isEqualTo(0.5);
assertThat(result.getAlertDistanceClose()).isEqualTo(1.5);
verify(aiConfigRepository, never()).save(any());
}
@Test
@DisplayName("getConfig - konfigurasi belum ada harus buat default dan simpan")
void getConfig_configNotExists_shouldCreateDefaultAndSave() {
AiConfig newConfig = AiConfig.builder().id(99L).userId(2L).build();
when(aiConfigRepository.findByUserId(2L)).thenReturn(Optional.empty());
when(aiConfigRepository.save(any(AiConfig.class))).thenReturn(newConfig);
AiConfigResponse result = aiConfigService.getConfig(2L);
assertThat(result).isNotNull();
verify(aiConfigRepository).save(any(AiConfig.class));
}
// ===== UPDATE CONFIG BY GUARDIAN TESTS =====
@Test
@DisplayName("updateConfigByGuardian - update confidenceThreshold berhasil")
void updateConfigByGuardian_updateThreshold_shouldSaveAndNotify() {
AiConfigUpdateRequest req = new AiConfigUpdateRequest();
req.setConfidenceThreshold(0.75);
when(pairingRelationRepository.findByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE))
.thenReturn(Optional.of(activePairing));
when(aiConfigRepository.findByUserId(2L)).thenReturn(Optional.of(existingConfig));
when(aiConfigRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
AiConfigResponse result = aiConfigService.updateConfigByGuardian(1L, req);
assertThat(result.getConfidenceThreshold()).isEqualTo(0.75);
verify(aiConfigRepository).save(any(AiConfig.class));
verify(fcmService).sendToToken(eq("user-fcm"), anyString(), anyString(), anyMap());
}
@Test
@DisplayName("updateConfigByGuardian - update semua field sekaligus")
void updateConfigByGuardian_updateAllFields_shouldUpdateAll() {
AiConfigUpdateRequest req = new AiConfigUpdateRequest();
req.setConfidenceThreshold(0.8);
req.setAlertDistanceClose(1.0);
req.setAlertDistanceMedium(2.5);
req.setMaxInferenceFps(10);
req.setEnabledLabels("person,car,motorcycle");
when(pairingRelationRepository.findByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE))
.thenReturn(Optional.of(activePairing));
when(aiConfigRepository.findByUserId(2L)).thenReturn(Optional.of(existingConfig));
when(aiConfigRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
AiConfigResponse result = aiConfigService.updateConfigByGuardian(1L, req);
assertThat(result.getConfidenceThreshold()).isEqualTo(0.8);
assertThat(result.getAlertDistanceClose()).isEqualTo(1.0);
assertThat(result.getAlertDistanceMedium()).isEqualTo(2.5);
assertThat(result.getMaxInferenceFps()).isEqualTo(10);
assertThat(result.getEnabledLabels()).isEqualTo("person,car,motorcycle");
}
@Test
@DisplayName("updateConfigByGuardian - field null tidak boleh mengubah nilai existing")
void updateConfigByGuardian_nullFields_shouldNotOverrideExisting() {
// Request dengan semua field null tidak ada yang berubah
AiConfigUpdateRequest req = new AiConfigUpdateRequest();
when(pairingRelationRepository.findByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE))
.thenReturn(Optional.of(activePairing));
when(aiConfigRepository.findByUserId(2L)).thenReturn(Optional.of(existingConfig));
when(aiConfigRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
AiConfigResponse result = aiConfigService.updateConfigByGuardian(1L, req);
// Nilai lama tetap
assertThat(result.getConfidenceThreshold()).isEqualTo(0.5);
assertThat(result.getAlertDistanceClose()).isEqualTo(1.5);
}
@Test
@DisplayName("updateConfigByGuardian - Guardian tanpa pairing aktif harus throw PairingException")
void updateConfigByGuardian_noPairing_shouldThrow() {
when(pairingRelationRepository.findByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE))
.thenReturn(Optional.empty());
assertThatThrownBy(() -> aiConfigService.updateConfigByGuardian(1L, new AiConfigUpdateRequest()))
.isInstanceOf(PairingException.class)
.hasMessageContaining("Tidak ada user yang dipair");
verify(aiConfigRepository, never()).save(any());
verify(fcmService, never()).sendToToken(any(), any(), any(), any());
}
@Test
@DisplayName("updateConfigByGuardian - konfigurasi belum ada harus buat baru lalu update")
void updateConfigByGuardian_configNotExists_shouldCreateAndUpdate() {
AiConfigUpdateRequest req = new AiConfigUpdateRequest();
req.setConfidenceThreshold(0.6);
AiConfig freshConfig = AiConfig.builder().userId(2L).guardianId(1L)
.confidenceThreshold(0.5).build();
when(pairingRelationRepository.findByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE))
.thenReturn(Optional.of(activePairing));
// Pertama kali findByUserId kosong save default kembalikan freshConfig
when(aiConfigRepository.findByUserId(2L)).thenReturn(Optional.empty());
when(aiConfigRepository.save(any())).thenAnswer(inv -> {
AiConfig c = inv.getArgument(0);
c.setId(50L);
return c;
});
AiConfigResponse result = aiConfigService.updateConfigByGuardian(1L, req);
assertThat(result).isNotNull();
// save dipanggil 2x: sekali buat default, sekali update
verify(aiConfigRepository, times(2)).save(any(AiConfig.class));
}
@Test
@DisplayName("updateConfigByGuardian - harus kirim FCM ke user setelah update")
void updateConfigByGuardian_shouldSendFcmToUser() {
AiConfigUpdateRequest req = new AiConfigUpdateRequest();
req.setMaxInferenceFps(8);
when(pairingRelationRepository.findByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE))
.thenReturn(Optional.of(activePairing));
when(aiConfigRepository.findByUserId(2L)).thenReturn(Optional.of(existingConfig));
when(aiConfigRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
aiConfigService.updateConfigByGuardian(1L, req);
ArgumentCaptor<String> tokenCaptor = ArgumentCaptor.forClass(String.class);
verify(fcmService).sendToToken(tokenCaptor.capture(), anyString(), anyString(), anyMap());
assertThat(tokenCaptor.getValue()).isEqualTo("user-fcm");
}
}