Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.newzet.api.userinfo.business.service;

import java.net.URI;
import java.util.UUID;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.newzet.api.userinfo.exception.AuthUserFailException;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class AuthUserService {

private final RestTemplate restTemplate;

@Value("${supabase.url}")
private String supabaseUrl;

@Value("${supabase.service-key}")
private String supabaseServiceKey;

public void deleteAuthUser(UUID userId) {
String url = supabaseUrl + "/auth/v1/admin/users/" + userId;

RequestEntity<Void> requestEntity = RequestEntity
.delete(URI.create(url))
.header("apikey", supabaseServiceKey)
.header("Authorization", "Bearer " + supabaseServiceKey)
.build();

try {
// DELETE 요청 보내기
ResponseEntity<String> response = restTemplate.exchange(requestEntity, String.class);
if (!response.getStatusCode().is2xxSuccessful()) {
throw new AuthUserFailException("Supabase 사용자 삭제 실패: " + response.getBody());
}

} catch (Exception e) {
// 네트워크 오류 등 처리
throw new AuthUserFailException("Supabase 요청 중 알 수 없는 오류가 발생하였습니다.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.newzet.api.userinfo.exception;

import com.newzet.api.common.exception.NewzetException;
import com.newzet.api.common.response.ResponseCode;

public class AuthUserFailException extends NewzetException {
private static final ResponseCode responseCode = ResponseCode.SUPABASE_ERROR;

public AuthUserFailException(String message) {
super(message, responseCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.newzet.api.category.domain.Category;
import com.newzet.api.usercategory.business.service.UserCategoryService;
import com.newzet.api.usercategory.domain.UserCategory;
import com.newzet.api.userinfo.business.service.AuthUserService;
import com.newzet.api.userinfo.business.service.UserinfoService;
import com.newzet.api.userinfo.domain.Userinfo;
import com.newzet.api.userinfo.presentation.dto.UniqueMailResponse;
Expand All @@ -25,6 +26,7 @@ public class UserinfoOrchestrator {
private final UserinfoService userinfoService;
private final UserCategoryService userCategoryService;
private final CategoryService categoryService;
private final AuthUserService authUserService;

@Transactional(readOnly = true)
public UserinfoWithCategoryListResponse getUserinfoWithCategoryList(UUID userId) {
Expand Down Expand Up @@ -58,5 +60,6 @@ public UserinfoInitResponse checkUserInitializeCompleted(UUID userId) {
@Transactional
public void deleteUserinfo(UUID userId) {
userinfoService.deleteUserinfoById(userId);
authUserService.deleteAuthUser(userId);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.newzet.api.userinfo.presentation.controller;

import java.util.UUID;

import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
Expand Down Expand Up @@ -76,6 +74,6 @@ public SuccessResponse<UniqueMailResponse> checkEmailUniqueness(
description = "유저 정보를 삭제한다.")
public SuccessResponse<Object> deleteUserinfo(@Login AuthUser authUser) {
userinfoOrchestrator.deleteUserinfo(authUser.getId());
return SuccessResponse.create(ResponseCode.SUCCESS, "유저 정보를 삭제한다.", null);
return SuccessResponse.create(ResponseCode.SUCCESS, "유저정보 삭제 성공", null);
}
}
6 changes: 5 additions & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,8 @@ cloud:
static: ${S3_REGION}
credentials:
access-key: ${S3_ACCESS_KEY}
secret-key: ${S3_SECRET_KEY}
secret-key: ${S3_SECRET_KEY}

supabase:
url: ${SUPABASE_URL}
service-key: ${SUPABASE_SERVICE_KEY}
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@
import com.newzet.api.config.PostgresTestContainerConfig;
import com.newzet.api.config.RedisTestContainerConfig;
import com.newzet.api.config.S3TestConfig;
import com.newzet.api.config.SupabaseConfig;
import com.newzet.api.fcm.orchestrator.FcmSenderOrchestrator;

@ExtendWith({RedisTestContainerConfig.class, PostgresTestContainerConfig.class,
JwtTestConfig.class, FirebaseTestConfig.class, S3TestConfig.class})
JwtTestConfig.class, FirebaseTestConfig.class, S3TestConfig.class, SupabaseConfig.class})
@SpringBootTest
@ComponentScan(basePackages = {"com.newzet.api.article", "com.newzet.api.common"})
class ArticleRedisBatchProcessorIntegrationTest {
Expand Down Expand Up @@ -274,7 +275,8 @@ void getBatchStatus_WhenPendingCountIsNull_ThenReturnZero() {
}

private Article createArticleDto(UUID userId, String title) {
return Article.createNewArticle(userId, "Newsletter", "example.com", "daily", "imageUrl@a.com", title,
return Article.createNewArticle(userId, "Newsletter", "example.com", "daily",
"imageUrl@a.com", title,
"https://example.com/" + title.toLowerCase().replace(' ', '-'));
}
}
16 changes: 16 additions & 0 deletions src/test/java/com/newzet/api/config/SupabaseConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.newzet.api.config;

import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

public class SupabaseConfig implements BeforeAllCallback {

public static final String TEST_SUPABASE_URL = "aaaaaaaaaa";
public static final String TEST_SUPABASE_KEY = "aaaaaaaaaaaaaa";

@Override
public void beforeAll(ExtensionContext context) throws Exception {
System.setProperty("supabase.url", TEST_SUPABASE_URL);
System.setProperty("supabase.service-key", TEST_SUPABASE_KEY);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.newzet.api.userinfo.business.service;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

import java.net.URI;
import java.util.UUID;

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 org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.client.RestTemplate;

import com.newzet.api.userinfo.exception.AuthUserFailException;

@ExtendWith(MockitoExtension.class)
class AuthUserServiceTest {

private final String supabaseUrl = "http://test-supabase.com";
private final String supabaseServiceKey = "test-service-key";
@InjectMocks
private AuthUserService authUserService;
@Mock
private RestTemplate restTemplate;

@BeforeEach
void setUp() {
ReflectionTestUtils.setField(authUserService, "supabaseUrl", supabaseUrl);
ReflectionTestUtils.setField(authUserService, "supabaseServiceKey", supabaseServiceKey);
}

@Test
@DisplayName("Supabase 사용자 삭제 성공")
void deleteAuthUser_Success() {
// given: 테스트 준비
UUID userId = UUID.randomUUID();
ResponseEntity<String> successResponse = new ResponseEntity<>("User deleted",
HttpStatus.OK);
when(restTemplate.exchange(any(RequestEntity.class), eq(String.class)))
.thenReturn(successResponse);

// when & then: 실행 및 검증
assertDoesNotThrow(() -> authUserService.deleteAuthUser(userId));

// verify: RestTemplate이 올바른 인자와 함께 호출되었는지 검증
ArgumentCaptor<RequestEntity<Void>> requestCaptor = ArgumentCaptor.forClass(
RequestEntity.class);
verify(restTemplate).exchange(requestCaptor.capture(), eq(String.class));

RequestEntity<Void> capturedRequest = requestCaptor.getValue();
assertEquals(URI.create(supabaseUrl + "/auth/v1/admin/users/" + userId),
capturedRequest.getUrl());
assertEquals("DELETE", capturedRequest.getMethod().name());
assertEquals(supabaseServiceKey, capturedRequest.getHeaders().getFirst("apikey"));
assertEquals("Bearer " + supabaseServiceKey,
capturedRequest.getHeaders().getFirst("Authorization"));
}

@Test
@DisplayName("Supabase API가 에러를 반환하여 사용자 삭제 실패")
void deleteAuthUser_Fail_WhenSupabaseReturnsError() {
// given: 테스트 준비
UUID userId = UUID.randomUUID();
String errorBody = "{\"error\":\"User not found\"}";
// RestTemplate이 실패(400 Bad Request) 응답을 반환하도록 설정합니다.
ResponseEntity<String> errorResponse = new ResponseEntity<>(errorBody,
HttpStatus.BAD_REQUEST);
when(restTemplate.exchange(any(RequestEntity.class), eq(String.class)))
.thenReturn(errorResponse);

// when & then: 실행 및 검증
assertThrows(AuthUserFailException.class,
() -> authUserService.deleteAuthUser(userId));
}

@Test
@DisplayName("네트워크 오류로 인해 사용자 삭제 실패")
void deleteAuthUser_Fail_WhenNetworkErrorOccurs() {
// given: 테스트 준비
UUID userId = UUID.randomUUID();
// RestTemplate 호출 시 런타임 예외가 발생하도록 설정합니다. (네트워크 문제 시뮬레이션)
when(restTemplate.exchange(any(RequestEntity.class), eq(String.class)))
.thenThrow(new RuntimeException("Network error"));

// when & then: 실행 및 검증
AuthUserFailException exception = assertThrows(AuthUserFailException.class,
() -> authUserService.deleteAuthUser(userId));

// 정의된 일반 오류 메시지와 일치하는지 확인합니다.
assertEquals("Supabase 요청 중 알 수 없는 오류가 발생하였습니다.", exception.getMessage());
}
}
Loading