Fix local dev config and ALOTTTT OFF FLUTTER, im tired boss..
This commit is contained in:
+17
-2
@@ -98,6 +98,13 @@ public class GuardianController {
|
||||
"SOS diakui"));
|
||||
}
|
||||
|
||||
@PutMapping("/sos/{id}/resolve")
|
||||
public ResponseEntity<ApiResponse<SosEventResponse>> resolveSos(@PathVariable Long id) {
|
||||
return ResponseEntity.ok(ApiResponse.ok(
|
||||
sosService.resolveSos(SecurityHelper.getCurrentUserId(), id),
|
||||
"SOS ditangani"));
|
||||
}
|
||||
|
||||
@GetMapping("/ai-config")
|
||||
public ResponseEntity<ApiResponse<AiConfigResponse>> getAiConfig() {
|
||||
// Guardian lihat config user yang dipair
|
||||
@@ -117,7 +124,7 @@ public class GuardianController {
|
||||
@GetMapping("/voice-commands")
|
||||
public ResponseEntity<ApiResponse<?>> getVoiceCommands() {
|
||||
return ResponseEntity.ok(ApiResponse.ok(
|
||||
voiceCommandService.getAll(SecurityHelper.getCurrentUserId()),
|
||||
voiceCommandService.getAllForGuardian(SecurityHelper.getCurrentUserId()),
|
||||
"Voice commands"));
|
||||
}
|
||||
|
||||
@@ -132,10 +139,18 @@ public class GuardianController {
|
||||
@GetMapping("/shortcuts")
|
||||
public ResponseEntity<ApiResponse<?>> getShortcuts() {
|
||||
return ResponseEntity.ok(ApiResponse.ok(
|
||||
hardwareShortcutService.getAll(SecurityHelper.getCurrentUserId()),
|
||||
hardwareShortcutService.getAllForGuardian(SecurityHelper.getCurrentUserId()),
|
||||
"Hardware shortcuts"));
|
||||
}
|
||||
|
||||
@PutMapping("/shortcuts")
|
||||
public ResponseEntity<ApiResponse<HardwareShortcutResponse>> updateShortcut(
|
||||
@RequestBody HardwareShortcutUpdateRequest req) {
|
||||
return ResponseEntity.ok(ApiResponse.ok(
|
||||
hardwareShortcutService.updateByGuardian(SecurityHelper.getCurrentUserId(), req),
|
||||
"Hardware shortcut diperbarui"));
|
||||
}
|
||||
|
||||
@GetMapping("/geofence")
|
||||
public ResponseEntity<ApiResponse<GeofenceResponse>> getGeofence() {
|
||||
return ResponseEntity.ok(ApiResponse.ok(
|
||||
|
||||
@@ -3,8 +3,6 @@ package com.walkguide.controller;
|
||||
import com.walkguide.dto.ApiResponse;
|
||||
import com.walkguide.dto.request.*;
|
||||
import com.walkguide.dto.response.*;
|
||||
import com.walkguide.enums.ActivityLogType;
|
||||
import com.walkguide.repository.UserRepository;
|
||||
import com.walkguide.security.SecurityHelper;
|
||||
import com.walkguide.service.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -29,21 +27,13 @@ public class UserController {
|
||||
private final AiConfigService aiConfigService;
|
||||
private final VoiceCommandService voiceCommandService;
|
||||
private final HardwareShortcutService hardwareShortcutService;
|
||||
private final UserRepository userRepository;
|
||||
private final UserService userService;
|
||||
|
||||
@GetMapping("/profile")
|
||||
public ResponseEntity<ApiResponse<?>> getProfile() {
|
||||
Long userId = SecurityHelper.getCurrentUserId();
|
||||
var user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new RuntimeException("User tidak ditemukan"));
|
||||
var profile = java.util.Map.of(
|
||||
"id", user.getId(),
|
||||
"email", user.getEmail(),
|
||||
"displayName", user.getDisplayName() != null ? user.getDisplayName() : "",
|
||||
"role", user.getRole(),
|
||||
"uniqueUserId", user.getUniqueUserId() != null ? user.getUniqueUserId() : ""
|
||||
);
|
||||
return ResponseEntity.ok(ApiResponse.ok(profile, "Profil user"));
|
||||
return ResponseEntity.ok(ApiResponse.ok(
|
||||
userService.getProfile(SecurityHelper.getCurrentUserId()),
|
||||
"Profil user"));
|
||||
}
|
||||
|
||||
@GetMapping("/settings")
|
||||
@@ -163,19 +153,13 @@ public class UserController {
|
||||
|
||||
@PostMapping("/walkguide/start")
|
||||
public ResponseEntity<ApiResponse<Void>> walkGuideStart() {
|
||||
Long userId = SecurityHelper.getCurrentUserId();
|
||||
userRepository.findById(userId).ifPresent(u ->
|
||||
activityLogService.createLog(u, ActivityLogType.WALKGUIDE_START,
|
||||
"WalkGuide dimulai", null));
|
||||
userService.logWalkGuideStart(SecurityHelper.getCurrentUserId());
|
||||
return ResponseEntity.ok(ApiResponse.ok(null, "WalkGuide dimulai"));
|
||||
}
|
||||
|
||||
@PostMapping("/walkguide/stop")
|
||||
public ResponseEntity<ApiResponse<Void>> walkGuideStop() {
|
||||
Long userId = SecurityHelper.getCurrentUserId();
|
||||
userRepository.findById(userId).ifPresent(u ->
|
||||
activityLogService.createLog(u, ActivityLogType.WALKGUIDE_STOP,
|
||||
"WalkGuide dihentikan", null));
|
||||
userService.logWalkGuideStop(SecurityHelper.getCurrentUserId());
|
||||
return ResponseEntity.ok(ApiResponse.ok(null, "WalkGuide dihentikan"));
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -2,9 +2,12 @@ package com.walkguide.service;
|
||||
|
||||
import com.walkguide.dto.request.HardwareShortcutUpdateRequest;
|
||||
import com.walkguide.dto.response.HardwareShortcutResponse;
|
||||
import com.walkguide.enums.PairingStatus;
|
||||
import com.walkguide.enums.HardwareShortcutKey;
|
||||
import com.walkguide.exception.PairingException;
|
||||
import com.walkguide.exception.ResourceNotFoundException;
|
||||
import com.walkguide.repository.HardwareShortcutRepository;
|
||||
import com.walkguide.repository.PairingRelationRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.List;
|
||||
@@ -15,6 +18,7 @@ import java.util.stream.Collectors;
|
||||
public class HardwareShortcutService {
|
||||
|
||||
private final HardwareShortcutRepository hardwareShortcutRepository;
|
||||
private final PairingRelationRepository pairingRelationRepository;
|
||||
|
||||
public List<HardwareShortcutResponse> getAll(Long userId) {
|
||||
return hardwareShortcutRepository.findByUserId(userId).stream()
|
||||
@@ -25,6 +29,13 @@ public class HardwareShortcutService {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<HardwareShortcutResponse> getAllForGuardian(Long guardianId) {
|
||||
var pairing = pairingRelationRepository
|
||||
.findByGuardian_IdAndStatus(guardianId, PairingStatus.ACTIVE)
|
||||
.orElseThrow(() -> new PairingException("Tidak ada user yang dipair"));
|
||||
return getAll(pairing.getUser().getId());
|
||||
}
|
||||
|
||||
// Bisa dipanggil dari HP User langsung (capture button) atau dari Guardian
|
||||
public HardwareShortcutResponse update(Long userId, HardwareShortcutUpdateRequest req) {
|
||||
HardwareShortcutKey key = HardwareShortcutKey.valueOf(req.getShortcutKey());
|
||||
@@ -43,4 +54,11 @@ public class HardwareShortcutService {
|
||||
.buttonName(shortcut.getButtonName()).buttonCode(shortcut.getButtonCode())
|
||||
.enabled(shortcut.getEnabled()).build();
|
||||
}
|
||||
|
||||
public HardwareShortcutResponse updateByGuardian(Long guardianId, HardwareShortcutUpdateRequest req) {
|
||||
var pairing = pairingRelationRepository
|
||||
.findByGuardian_IdAndStatus(guardianId, PairingStatus.ACTIVE)
|
||||
.orElseThrow(() -> new PairingException("Tidak ada user yang dipair"));
|
||||
return update(pairing.getUser().getId(), req);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ public class SosService {
|
||||
public SosEventResponse acknowledgeSos(Long guardianId, Long sosId) {
|
||||
SosEvent sos = sosEventRepository.findById(sosId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("SOS event tidak ditemukan"));
|
||||
assertGuardianOwnsSos(guardianId, sos.getUserId());
|
||||
|
||||
sos.setStatus(SosStatus.ACKNOWLEDGED);
|
||||
sos.setAcknowledgedAt(LocalDateTime.now());
|
||||
@@ -111,6 +112,33 @@ public class SosService {
|
||||
return toResponse(sos);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public SosEventResponse resolveSos(Long guardianId, Long sosId) {
|
||||
SosEvent sos = sosEventRepository.findById(sosId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("SOS event tidak ditemukan"));
|
||||
assertGuardianOwnsSos(guardianId, sos.getUserId());
|
||||
|
||||
if (sos.getAcknowledgedAt() == null) {
|
||||
sos.setAcknowledgedAt(LocalDateTime.now());
|
||||
}
|
||||
sos.setStatus(SosStatus.RESOLVED);
|
||||
sosEventRepository.save(sos);
|
||||
|
||||
User user = userRepository.findById(sos.getUserId())
|
||||
.orElseThrow(() -> new ResourceNotFoundException("User tidak ditemukan"));
|
||||
activityLogService.createLog(user, ActivityLogType.SOS_ACKNOWLEDGED,
|
||||
"Guardian menandai SOS sudah ditangani", null);
|
||||
|
||||
pairingRelationRepository.findByUser_IdAndStatus(sos.getUserId(), PairingStatus.ACTIVE)
|
||||
.ifPresent(pairing -> fcmService.sendToToken(
|
||||
pairing.getUser().getFcmToken(),
|
||||
"SOS Sudah Ditangani",
|
||||
"Guardian kamu sudah menandai SOS sebagai selesai.",
|
||||
Map.of("type", "SOS_RESOLVED")));
|
||||
|
||||
return toResponse(sos);
|
||||
}
|
||||
|
||||
public Page<SosEventResponse> getSosEvents(Long userId, Pageable pageable) {
|
||||
return sosEventRepository.findByUserIdOrderByCreatedAtDesc(userId, pageable)
|
||||
.map(this::toResponse);
|
||||
@@ -125,6 +153,15 @@ public class SosService {
|
||||
.map(this::toResponse);
|
||||
}
|
||||
|
||||
private void assertGuardianOwnsSos(Long guardianId, Long userId) {
|
||||
var pairing = pairingRelationRepository
|
||||
.findByGuardian_IdAndStatus(guardianId, PairingStatus.ACTIVE)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Tidak ada user yang dipair"));
|
||||
if (!pairing.getUser().getId().equals(userId)) {
|
||||
throw new ResourceNotFoundException("SOS event tidak ditemukan untuk guardian ini");
|
||||
}
|
||||
}
|
||||
|
||||
private SosEventResponse toResponse(SosEvent s) {
|
||||
return SosEventResponse.builder()
|
||||
.id(s.getId())
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.walkguide.service;
|
||||
|
||||
import com.walkguide.enums.ActivityLogType;
|
||||
import com.walkguide.repository.UserRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserService {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final ActivityLogService activityLogService;
|
||||
|
||||
public Map<String, Object> getProfile(Long userId) {
|
||||
var user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new RuntimeException("User tidak ditemukan"));
|
||||
return Map.of(
|
||||
"id", user.getId(),
|
||||
"email", user.getEmail(),
|
||||
"displayName", user.getDisplayName() != null ? user.getDisplayName() : "",
|
||||
"role", user.getRole(),
|
||||
"uniqueUserId", user.getUniqueUserId() != null ? user.getUniqueUserId() : ""
|
||||
);
|
||||
}
|
||||
|
||||
public void logWalkGuideStart(Long userId) {
|
||||
userRepository.findById(userId).ifPresent(user ->
|
||||
activityLogService.createLog(user, ActivityLogType.WALKGUIDE_START,
|
||||
"WalkGuide dimulai", null));
|
||||
}
|
||||
|
||||
public void logWalkGuideStop(Long userId) {
|
||||
userRepository.findById(userId).ifPresent(user ->
|
||||
activityLogService.createLog(user, ActivityLogType.WALKGUIDE_STOP,
|
||||
"WalkGuide dihentikan", null));
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,13 @@ public class VoiceCommandService {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<VoiceCommandResponse> getAllForGuardian(Long guardianId) {
|
||||
var pairing = pairingRelationRepository
|
||||
.findByGuardian_IdAndStatus(guardianId, PairingStatus.ACTIVE)
|
||||
.orElseThrow(() -> new PairingException("Tidak ada user yang dipair"));
|
||||
return getAll(pairing.getUser().getId());
|
||||
}
|
||||
|
||||
public VoiceCommandResponse updateByGuardian(Long guardianId, VoiceCommandUpdateRequest req) {
|
||||
var pairing = pairingRelationRepository
|
||||
.findByGuardian_IdAndStatus(guardianId, PairingStatus.ACTIVE)
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
url: ${DB_URL}
|
||||
username: ${DB_USERNAME}
|
||||
password: ${DB_PASSWORD}
|
||||
url: ${DB_URL:jdbc:postgresql://202.46.28.160:2002/uas_5803024001}
|
||||
username: ${DB_USERNAME:5803024001}
|
||||
password: ${DB_PASSWORD:pw5803024001}
|
||||
|
||||
jpa:
|
||||
show-sql: true
|
||||
@@ -17,7 +17,7 @@ spring:
|
||||
format_sql: true
|
||||
|
||||
jwt:
|
||||
secret: ${JWT_SECRET}
|
||||
secret: ${JWT_SECRET:d2Fsa2d1aWRlLWRldi1qd3Qtc2VjcmV0LWtleS0zMi1ieXRlcy1taW5pbXVt}
|
||||
expiration: ${JWT_EXPIRATION:86400000}
|
||||
|
||||
agora:
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
server.port=${SERVER_PORT:8080}
|
||||
|
||||
# ===== POSTGRESQL CONNECTION =====
|
||||
spring.datasource.url=${DB_URL}
|
||||
spring.datasource.username=${DB_USERNAME}
|
||||
spring.datasource.password=${DB_PASSWORD}
|
||||
spring.datasource.url=${DB_URL:jdbc:postgresql://202.46.28.160:2002/uas_5803024001}
|
||||
spring.datasource.username=${DB_USERNAME:5803024001}
|
||||
spring.datasource.password=${DB_PASSWORD:pw5803024001}
|
||||
spring.datasource.driver-class-name=org.postgresql.Driver
|
||||
|
||||
# ===== JPA / HIBERNATE =====
|
||||
@@ -19,7 +19,7 @@ spring.flyway.locations=classpath:db/migration
|
||||
spring.flyway.baseline-on-migrate=true
|
||||
|
||||
# ===== JWT =====
|
||||
jwt.secret=${JWT_SECRET}
|
||||
jwt.secret=${JWT_SECRET:d2Fsa2d1aWRlLWRldi1qd3Qtc2VjcmV0LWtleS0zMi1ieXRlcy1taW5pbXVt}
|
||||
jwt.expiration=${JWT_EXPIRATION:86400000}
|
||||
|
||||
# ===== SWAGGER =====
|
||||
@@ -38,4 +38,4 @@ logging.level.com.walkguide=DEBUG
|
||||
logging.level.org.springframework.messaging=INFO
|
||||
logging.level.org.springframework.web.socket=INFO
|
||||
|
||||
spring.profiles.active=dev
|
||||
spring.profiles.active=dev
|
||||
|
||||
@@ -273,6 +273,15 @@ paths:
|
||||
schema: { type: integer, format: int64 }
|
||||
responses:
|
||||
"200": { description: SOS acknowledged }
|
||||
/guardian/sos/{id}/resolve:
|
||||
put:
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema: { type: integer, format: int64 }
|
||||
responses:
|
||||
"200": { description: SOS handled and resolved }
|
||||
/guardian/ai-config:
|
||||
get:
|
||||
responses:
|
||||
@@ -291,6 +300,9 @@ paths:
|
||||
get:
|
||||
responses:
|
||||
"200": { description: Paired user shortcuts }
|
||||
put:
|
||||
responses:
|
||||
"200": { description: Paired user shortcut updated }
|
||||
/guardian/geofence:
|
||||
get:
|
||||
responses:
|
||||
|
||||
@@ -8,10 +8,10 @@ import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
@SpringBootTest(properties = {
|
||||
"spring.datasource.url=jdbc:postgresql://localhost:5432/walkguide_test",
|
||||
"spring.datasource.username=test",
|
||||
"spring.datasource.password=test",
|
||||
"spring.datasource.password=${TEST_DB_PASSWORD}",
|
||||
"spring.flyway.enabled=false",
|
||||
"spring.jpa.hibernate.ddl-auto=none",
|
||||
"jwt.secret=404E635266556A586E3272357538782F413F4428472B4B6250645367566B5970"
|
||||
"jwt.secret=${TEST_JWT_SECRET}"
|
||||
})
|
||||
class DemoApplicationTests {
|
||||
|
||||
|
||||
+41
-2
@@ -211,6 +211,24 @@ class GuardianControllerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("PUT /api/v1/guardian/sos/{id}/resolve - harus tandai SOS ditangani")
|
||||
void resolveSos_shouldReturn200() throws Exception {
|
||||
try (MockedStatic<SecurityHelper> sh = mockStatic(SecurityHelper.class)) {
|
||||
sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L);
|
||||
|
||||
SosEventResponse resp = new SosEventResponse();
|
||||
when(sosService.resolveSos(2L, 10L)).thenReturn(resp);
|
||||
|
||||
mockMvc.perform(put("/api/v1/guardian/sos/10/resolve")
|
||||
.with(csrf()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.message").value("SOS ditangani"));
|
||||
|
||||
verify(sosService).resolveSos(2L, 10L);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== AI CONFIG =====
|
||||
|
||||
@Test
|
||||
@@ -254,7 +272,7 @@ class GuardianControllerTest {
|
||||
void getVoiceCommands_shouldReturn200() throws Exception {
|
||||
try (MockedStatic<SecurityHelper> sh = mockStatic(SecurityHelper.class)) {
|
||||
sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L);
|
||||
when(voiceCommandService.getAll(2L)).thenReturn(List.of());
|
||||
when(voiceCommandService.getAllForGuardian(2L)).thenReturn(List.of());
|
||||
|
||||
mockMvc.perform(get("/api/v1/guardian/voice-commands"))
|
||||
.andExpect(status().isOk())
|
||||
@@ -288,7 +306,7 @@ class GuardianControllerTest {
|
||||
void getShortcuts_shouldReturn200() throws Exception {
|
||||
try (MockedStatic<SecurityHelper> sh = mockStatic(SecurityHelper.class)) {
|
||||
sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L);
|
||||
when(hardwareShortcutService.getAll(2L)).thenReturn(List.of());
|
||||
when(hardwareShortcutService.getAllForGuardian(2L)).thenReturn(List.of());
|
||||
|
||||
mockMvc.perform(get("/api/v1/guardian/shortcuts"))
|
||||
.andExpect(status().isOk())
|
||||
@@ -296,6 +314,27 @@ class GuardianControllerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("PUT /api/v1/guardian/shortcuts - harus update shortcut user yang dipair")
|
||||
void updateShortcut_shouldReturn200() throws Exception {
|
||||
try (MockedStatic<SecurityHelper> sh = mockStatic(SecurityHelper.class)) {
|
||||
sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L);
|
||||
|
||||
HardwareShortcutUpdateRequest req = new HardwareShortcutUpdateRequest();
|
||||
HardwareShortcutResponse resp = new HardwareShortcutResponse();
|
||||
when(hardwareShortcutService.updateByGuardian(eq(2L), any())).thenReturn(resp);
|
||||
|
||||
mockMvc.perform(put("/api/v1/guardian/shortcuts")
|
||||
.with(csrf())
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(req)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.message").value("Hardware shortcut diperbarui"));
|
||||
|
||||
verify(hardwareShortcutService).updateByGuardian(eq(2L), any(HardwareShortcutUpdateRequest.class));
|
||||
}
|
||||
}
|
||||
|
||||
// ===== GEOFENCE =====
|
||||
|
||||
@Test
|
||||
|
||||
+2
-5
@@ -1,7 +1,5 @@
|
||||
package com.walkguide.integration;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.walkguide.dto.request.LoginRequest;
|
||||
import com.walkguide.dto.request.RefreshTokenRequest;
|
||||
@@ -25,8 +23,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
*/
|
||||
@DisplayName("Integration Test — Auth Flow (Testcontainers)")
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
@Disabled("Requires Docker/Testcontainers")
|
||||
class AuthIntegrationTest extends AbstractIntegrationTest {
|
||||
class AuthIntegrationTest extends AbstractIntegrationTest {
|
||||
|
||||
private static final String USER_EMAIL = "integ_user@walkguide.test";
|
||||
private static final String USER_PASS = "password123";
|
||||
@@ -294,4 +291,4 @@ class AuthIntegrationTest extends AbstractIntegrationTest {
|
||||
assertThat(result.getResponse().getStatus())
|
||||
.isBetween(200, 399));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-5
@@ -1,7 +1,5 @@
|
||||
package com.walkguide.integration;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.walkguide.dto.request.InviteUserRequest;
|
||||
import com.walkguide.dto.request.PairingResponseRequest;
|
||||
@@ -26,8 +24,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
*/
|
||||
@DisplayName("Integration Test — Pairing Flow (Testcontainers)")
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
@Disabled("Requires Docker/Testcontainers")
|
||||
class PairingIntegrationTest extends AbstractIntegrationTest {
|
||||
class PairingIntegrationTest extends AbstractIntegrationTest {
|
||||
|
||||
private static final String USER_EMAIL = "pairing_user@walkguide.test";
|
||||
private static final String USER_PASS = "userpass123";
|
||||
@@ -286,4 +283,4 @@ class PairingIntegrationTest extends AbstractIntegrationTest {
|
||||
mockMvc.perform(get("/api/v1/shared/pairing/status"))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-5
@@ -1,7 +1,5 @@
|
||||
package com.walkguide.integration;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.walkguide.dto.request.*;
|
||||
import org.junit.jupiter.api.*;
|
||||
@@ -28,8 +26,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
*/
|
||||
@DisplayName("Integration Test — User Core Features (Testcontainers)")
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
@Disabled("Requires Docker/Testcontainers")
|
||||
class UserFeatureIntegrationTest extends AbstractIntegrationTest {
|
||||
class UserFeatureIntegrationTest extends AbstractIntegrationTest {
|
||||
|
||||
private static final String USER_EMAIL = "feature_user@walkguide.test";
|
||||
private static final String USER_PASS = "userpass123";
|
||||
@@ -488,4 +485,4 @@ class UserFeatureIntegrationTest extends AbstractIntegrationTest {
|
||||
.header("Authorization", bearerToken(userToken)))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+46
-14
@@ -1,11 +1,15 @@
|
||||
package com.walkguide.service;
|
||||
|
||||
import com.walkguide.dto.request.HardwareShortcutUpdateRequest;
|
||||
import com.walkguide.dto.response.HardwareShortcutResponse;
|
||||
import com.walkguide.entity.HardwareShortcut;
|
||||
import com.walkguide.enums.HardwareShortcutKey;
|
||||
import com.walkguide.exception.ResourceNotFoundException;
|
||||
import com.walkguide.repository.HardwareShortcutRepository;
|
||||
import com.walkguide.dto.request.HardwareShortcutUpdateRequest;
|
||||
import com.walkguide.dto.response.HardwareShortcutResponse;
|
||||
import com.walkguide.entity.PairingRelation;
|
||||
import com.walkguide.entity.HardwareShortcut;
|
||||
import com.walkguide.entity.User;
|
||||
import com.walkguide.enums.HardwareShortcutKey;
|
||||
import com.walkguide.enums.PairingStatus;
|
||||
import com.walkguide.exception.ResourceNotFoundException;
|
||||
import com.walkguide.repository.HardwareShortcutRepository;
|
||||
import com.walkguide.repository.PairingRelationRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -15,7 +19,8 @@ import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -25,8 +30,10 @@ import static org.mockito.Mockito.*;
|
||||
@DisplayName("HardwareShortcutService Unit Tests")
|
||||
class HardwareShortcutServiceTest {
|
||||
|
||||
@Mock
|
||||
HardwareShortcutRepository hardwareShortcutRepository;
|
||||
@Mock
|
||||
HardwareShortcutRepository hardwareShortcutRepository;
|
||||
@Mock
|
||||
PairingRelationRepository pairingRelationRepository;
|
||||
|
||||
@InjectMocks
|
||||
HardwareShortcutService hardwareShortcutService;
|
||||
@@ -173,11 +180,36 @@ class HardwareShortcutServiceTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("update - shortcutKey enum invalid harus throw IllegalArgumentException")
|
||||
void update_invalidEnumKey_shouldThrow() {
|
||||
void update_invalidEnumKey_shouldThrow() {
|
||||
HardwareShortcutUpdateRequest req = new HardwareShortcutUpdateRequest();
|
||||
req.setShortcutKey("INVALID_KEY_XYZ");
|
||||
|
||||
assertThatThrownBy(() -> hardwareShortcutService.update(10L, req))
|
||||
.isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
}
|
||||
assertThatThrownBy(() -> hardwareShortcutService.update(10L, req))
|
||||
.isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("updateByGuardian - harus update shortcut milik user yang dipair")
|
||||
void updateByGuardian_shouldUpdatePairedUserShortcut() {
|
||||
HardwareShortcutUpdateRequest req = new HardwareShortcutUpdateRequest();
|
||||
req.setShortcutKey("CALL_GUARDIAN");
|
||||
req.setButtonName("Power Double Tap");
|
||||
|
||||
User guardian = User.builder().id(1L).build();
|
||||
User user = User.builder().id(10L).build();
|
||||
PairingRelation pairing = PairingRelation.builder()
|
||||
.guardian(guardian).user(user).status(PairingStatus.ACTIVE).build();
|
||||
|
||||
when(pairingRelationRepository.findByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE))
|
||||
.thenReturn(Optional.of(pairing));
|
||||
when(hardwareShortcutRepository.findByUserId(10L))
|
||||
.thenReturn(List.of(shortcutCallGuardian));
|
||||
when(hardwareShortcutRepository.save(any(HardwareShortcut.class)))
|
||||
.thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
HardwareShortcutResponse result = hardwareShortcutService.updateByGuardian(1L, req);
|
||||
|
||||
assertThat(result.getButtonName()).isEqualTo("Power Double Tap");
|
||||
verify(hardwareShortcutRepository).findByUserId(10L);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,8 +159,10 @@ class SosServiceTest {
|
||||
@Test
|
||||
@DisplayName("acknowledgeSos - SOS ditemukan: harus ubah status ke ACKNOWLEDGED")
|
||||
void acknowledgeSos_sosFound_shouldChangeStatusToAcknowledged() {
|
||||
when(sosEventRepository.findById(50L)).thenReturn(Optional.of(savedSos));
|
||||
when(userRepository.findById(2L)).thenReturn(Optional.of(user));
|
||||
when(sosEventRepository.findById(50L)).thenReturn(Optional.of(savedSos));
|
||||
when(pairingRelationRepository.findByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE))
|
||||
.thenReturn(Optional.of(activePairing));
|
||||
when(userRepository.findById(2L)).thenReturn(Optional.of(user));
|
||||
when(sosEventRepository.save(any(SosEvent.class))).thenAnswer(inv -> inv.getArgument(0));
|
||||
when(pairingRelationRepository.findByUser_IdAndStatus(2L, PairingStatus.ACTIVE))
|
||||
.thenReturn(Optional.empty());
|
||||
@@ -179,8 +181,10 @@ class SosServiceTest {
|
||||
@Test
|
||||
@DisplayName("acknowledgeSos - ada pairing user: harus kirim FCM ke user bahwa guardian sudah respon")
|
||||
void acknowledgeSos_activePairingForUser_shouldNotifyUser() {
|
||||
when(sosEventRepository.findById(50L)).thenReturn(Optional.of(savedSos));
|
||||
when(userRepository.findById(2L)).thenReturn(Optional.of(user));
|
||||
when(sosEventRepository.findById(50L)).thenReturn(Optional.of(savedSos));
|
||||
when(pairingRelationRepository.findByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE))
|
||||
.thenReturn(Optional.of(activePairing));
|
||||
when(userRepository.findById(2L)).thenReturn(Optional.of(user));
|
||||
when(sosEventRepository.save(any(SosEvent.class))).thenAnswer(inv -> inv.getArgument(0));
|
||||
when(pairingRelationRepository.findByUser_IdAndStatus(2L, PairingStatus.ACTIVE))
|
||||
.thenReturn(Optional.of(activePairing));
|
||||
@@ -198,13 +202,35 @@ class SosServiceTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("acknowledgeSos - SOS tidak ditemukan: harus throw ResourceNotFoundException")
|
||||
void acknowledgeSos_sosNotFound_shouldThrowException() {
|
||||
when(sosEventRepository.findById(999L)).thenReturn(Optional.empty());
|
||||
|
||||
assertThatThrownBy(() -> sosService.acknowledgeSos(1L, 999L))
|
||||
.isInstanceOf(ResourceNotFoundException.class)
|
||||
.hasMessageContaining("SOS event tidak ditemukan");
|
||||
}
|
||||
void acknowledgeSos_sosNotFound_shouldThrowException() {
|
||||
when(sosEventRepository.findById(999L)).thenReturn(Optional.empty());
|
||||
|
||||
assertThatThrownBy(() -> sosService.acknowledgeSos(1L, 999L))
|
||||
.isInstanceOf(ResourceNotFoundException.class)
|
||||
.hasMessageContaining("SOS event tidak ditemukan");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("resolveSos - SOS ditemukan: harus ubah status ke RESOLVED")
|
||||
void resolveSos_sosFound_shouldChangeStatusToResolved() {
|
||||
when(sosEventRepository.findById(50L)).thenReturn(Optional.of(savedSos));
|
||||
when(pairingRelationRepository.findByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE))
|
||||
.thenReturn(Optional.of(activePairing));
|
||||
when(userRepository.findById(2L)).thenReturn(Optional.of(user));
|
||||
when(sosEventRepository.save(any(SosEvent.class))).thenAnswer(inv -> inv.getArgument(0));
|
||||
when(pairingRelationRepository.findByUser_IdAndStatus(2L, PairingStatus.ACTIVE))
|
||||
.thenReturn(Optional.empty());
|
||||
doNothing().when(activityLogService).createLog(any(), any(), any(), any());
|
||||
|
||||
SosEventResponse result = sosService.resolveSos(1L, 50L);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getStatus()).isEqualTo("RESOLVED");
|
||||
ArgumentCaptor<SosEvent> captor = ArgumentCaptor.forClass(SosEvent.class);
|
||||
verify(sosEventRepository).save(captor.capture());
|
||||
assertThat(captor.getValue().getStatus()).isEqualTo(SosStatus.RESOLVED);
|
||||
assertThat(captor.getValue().getAcknowledgedAt()).isNotNull();
|
||||
}
|
||||
|
||||
// ===== getSosEvents TESTS =====
|
||||
|
||||
@@ -250,4 +276,4 @@ class SosServiceTest {
|
||||
assertThatThrownBy(() -> sosService.getSosEventsForGuardian(1L, PageRequest.of(0, 10)))
|
||||
.isInstanceOf(ResourceNotFoundException.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user