diff --git a/walkguide-backend/demo/src/test/java/com/walkguide/service/UserSettingServiceTest.java b/walkguide-backend/demo/src/test/java/com/walkguide/service/UserSettingServiceTest.java new file mode 100644 index 0000000..59e4461 --- /dev/null +++ b/walkguide-backend/demo/src/test/java/com/walkguide/service/UserSettingServiceTest.java @@ -0,0 +1,202 @@ +package com.walkguide.service; + +import com.walkguide.dto.request.UserSettingsUpdateRequest; +import com.walkguide.dto.response.UserSettingsResponse; +import com.walkguide.entity.UserSettings; +import com.walkguide.repository.UserSettingsRepository; +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.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@DisplayName("UserSettingsService Unit Tests") +class UserSettingsServiceTest { + + @Mock + UserSettingsRepository userSettingsRepository; + + @InjectMocks + UserSettingsService userSettingsService; + + private UserSettings existingSettings; + + @BeforeEach + void setUp() { + existingSettings = UserSettings.builder() + .id(1L) + .userId(10L) + .ttsLanguage("id-ID") + .ttsPitch(1.0) + .ttsSpeed(0.9) + .warnNoGuardian(true) + .hapticEnabled(true) + .build(); + } + + // ===== getSettings TESTS ===== + + @Test + @DisplayName("getSettings - harus return settings yang ada di DB") + void getSettings_existingSettings_shouldReturnExistingData() { + when(userSettingsRepository.findByUserId(10L)).thenReturn(Optional.of(existingSettings)); + + UserSettingsResponse result = userSettingsService.getSettings(10L); + + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(1L); + assertThat(result.getTtsLanguage()).isEqualTo("id-ID"); + assertThat(result.getTtsPitch()).isEqualTo(1.0); + assertThat(result.getTtsSpeed()).isEqualTo(0.9); + assertThat(result.getWarnNoGuardian()).isTrue(); + assertThat(result.getHapticEnabled()).isTrue(); + + // Tidak boleh membuat settings baru jika sudah ada + verify(userSettingsRepository, never()).save(any()); + } + + @Test + @DisplayName("getSettings - jika belum ada, harus auto-create settings default dan simpan ke DB") + void getSettings_noExistingSettings_shouldCreateDefaultAndSave() { + UserSettings defaultSettings = UserSettings.builder() + .id(2L).userId(20L) + .ttsLanguage("id-ID").ttsPitch(1.0).ttsSpeed(0.9) + .warnNoGuardian(true).hapticEnabled(true).build(); + + when(userSettingsRepository.findByUserId(20L)).thenReturn(Optional.empty()); + when(userSettingsRepository.save(any(UserSettings.class))).thenReturn(defaultSettings); + + UserSettingsResponse result = userSettingsService.getSettings(20L); + + assertThat(result).isNotNull(); + verify(userSettingsRepository).save(any(UserSettings.class)); + } + + @Test + @DisplayName("getSettings - settings yang di-auto-create harus memiliki userId yang benar") + void getSettings_autoCreate_shouldHaveCorrectUserId() { + when(userSettingsRepository.findByUserId(30L)).thenReturn(Optional.empty()); + when(userSettingsRepository.save(any(UserSettings.class))) + .thenAnswer(inv -> inv.getArgument(0)); + + userSettingsService.getSettings(30L); + + ArgumentCaptor captor = ArgumentCaptor.forClass(UserSettings.class); + verify(userSettingsRepository).save(captor.capture()); + assertThat(captor.getValue().getUserId()).isEqualTo(30L); + } + + // ===== updateSettings TESTS ===== + + @Test + @DisplayName("updateSettings - semua field diisi harus mengupdate semua nilai") + void updateSettings_allFields_shouldUpdateAllValues() { + UserSettingsUpdateRequest req = new UserSettingsUpdateRequest(); + req.setTtsLanguage("en-US"); + req.setTtsPitch(1.5); + req.setTtsSpeed(1.2); + req.setWarnNoGuardian(false); + req.setHapticEnabled(false); + + when(userSettingsRepository.findByUserId(10L)).thenReturn(Optional.of(existingSettings)); + when(userSettingsRepository.save(any(UserSettings.class))) + .thenAnswer(inv -> inv.getArgument(0)); + + UserSettingsResponse result = userSettingsService.updateSettings(10L, req); + + assertThat(result.getTtsLanguage()).isEqualTo("en-US"); + assertThat(result.getTtsPitch()).isEqualTo(1.5); + assertThat(result.getTtsSpeed()).isEqualTo(1.2); + assertThat(result.getWarnNoGuardian()).isFalse(); + assertThat(result.getHapticEnabled()).isFalse(); + } + + @Test + @DisplayName("updateSettings - field null tidak boleh mengubah nilai yang sudah ada (partial update)") + void updateSettings_partialUpdate_nullFieldsShouldNotOverride() { + UserSettingsUpdateRequest req = new UserSettingsUpdateRequest(); + req.setTtsLanguage("en-US"); // hanya ini yang diubah + req.setTtsPitch(null); + req.setTtsSpeed(null); + req.setWarnNoGuardian(null); + req.setHapticEnabled(null); + + when(userSettingsRepository.findByUserId(10L)).thenReturn(Optional.of(existingSettings)); + when(userSettingsRepository.save(any(UserSettings.class))) + .thenAnswer(inv -> inv.getArgument(0)); + + UserSettingsResponse result = userSettingsService.updateSettings(10L, req); + + assertThat(result.getTtsLanguage()).isEqualTo("en-US"); + // Nilai lama harus tetap + assertThat(result.getTtsPitch()).isEqualTo(1.0); + assertThat(result.getTtsSpeed()).isEqualTo(0.9); + assertThat(result.getWarnNoGuardian()).isTrue(); + assertThat(result.getHapticEnabled()).isTrue(); + } + + @Test + @DisplayName("updateSettings - jika belum ada settings, harus buat baru dan update") + void updateSettings_noExistingSettings_shouldCreateNewAndUpdate() { + UserSettingsUpdateRequest req = new UserSettingsUpdateRequest(); + req.setTtsLanguage("ja-JP"); + req.setHapticEnabled(false); + + when(userSettingsRepository.findByUserId(40L)).thenReturn(Optional.empty()); + when(userSettingsRepository.save(any(UserSettings.class))) + .thenAnswer(inv -> inv.getArgument(0)); + + UserSettingsResponse result = userSettingsService.updateSettings(40L, req); + + ArgumentCaptor captor = ArgumentCaptor.forClass(UserSettings.class); + verify(userSettingsRepository).save(captor.capture()); + + assertThat(captor.getValue().getUserId()).isEqualTo(40L); + assertThat(captor.getValue().getTtsLanguage()).isEqualTo("ja-JP"); + assertThat(captor.getValue().getHapticEnabled()).isFalse(); + } + + @Test + @DisplayName("updateSettings - harus memanggil repository.save dan return hasil yang diupdate") + void updateSettings_shouldPersistAndReturnUpdatedResponse() { + UserSettingsUpdateRequest req = new UserSettingsUpdateRequest(); + req.setTtsSpeed(0.5); + + when(userSettingsRepository.findByUserId(10L)).thenReturn(Optional.of(existingSettings)); + when(userSettingsRepository.save(any(UserSettings.class))) + .thenAnswer(inv -> inv.getArgument(0)); + + userSettingsService.updateSettings(10L, req); + + verify(userSettingsRepository, times(1)).save(any(UserSettings.class)); + } + + @Test + @DisplayName("updateSettings - semua field null harus tidak mengubah apapun") + void updateSettings_allNull_shouldNotChangeAnything() { + UserSettingsUpdateRequest req = new UserSettingsUpdateRequest(); + // semua null + + when(userSettingsRepository.findByUserId(10L)).thenReturn(Optional.of(existingSettings)); + when(userSettingsRepository.save(any(UserSettings.class))) + .thenAnswer(inv -> inv.getArgument(0)); + + UserSettingsResponse result = userSettingsService.updateSettings(10L, req); + + assertThat(result.getTtsLanguage()).isEqualTo("id-ID"); + assertThat(result.getTtsPitch()).isEqualTo(1.0); + assertThat(result.getTtsSpeed()).isEqualTo(0.9); + assertThat(result.getWarnNoGuardian()).isTrue(); + assertThat(result.getHapticEnabled()).isTrue(); + } +} \ No newline at end of file