Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a5a072fef4 | |||
| 078331b3b8 | |||
| 428ab1103a | |||
| 4813a2ef0e | |||
| 752156b86f | |||
| 5732ddf962 | |||
| 5ffc48c039 | |||
| cbb6f33ce5 |
Binary file not shown.
+6
-7
@@ -527,8 +527,8 @@ public class HttpClient5AdapterServiceRest extends HttpClient5AdapterServiceSupp
|
|||||||
// 이경우 정상으로 처리함
|
// 이경우 정상으로 처리함
|
||||||
} else if (status >= 200 && status <= 207) {
|
} else if (status >= 200 && status <= 207) {
|
||||||
if (useAdapterToken && StringUtils.equals(tokenErrorHttpStatusCode, "200")) {
|
if (useAdapterToken && StringUtils.equals(tokenErrorHttpStatusCode, "200")) {
|
||||||
needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues,
|
needReissue = checkTokenRetry(status, responseMessage, tokenErrorHttpStatusCode, tokenErrorCodeKey,
|
||||||
vo.getEncode());
|
tokenErrorCodeValues, vo.getEncode());
|
||||||
}
|
}
|
||||||
} else if (status == 302) {
|
} else if (status == 302) {
|
||||||
if (!StringUtils.contains(relayResponseHeaderKeys, "Location")) {
|
if (!StringUtils.contains(relayResponseHeaderKeys, "Location")) {
|
||||||
@@ -542,15 +542,14 @@ public class HttpClient5AdapterServiceRest extends HttpClient5AdapterServiceSupp
|
|||||||
|
|
||||||
if (status >= 400 && status < 500) {
|
if (status >= 400 && status < 500) {
|
||||||
if (useAdapterToken) {
|
if (useAdapterToken) {
|
||||||
needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues,
|
needReissue = checkTokenRetry(status, responseMessage, tokenErrorHttpStatusCode,
|
||||||
vo.getEncode());
|
tokenErrorCodeKey, tokenErrorCodeValues, vo.getEncode());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!needReissue) {
|
if (!needReissue) {
|
||||||
throw new Exception(errMsg);
|
throw new Exception(errMsg);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
throw new Exception(errMsg);
|
throw new Exception(errMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -882,8 +881,8 @@ public class HttpClient5AdapterServiceRest extends HttpClient5AdapterServiceSupp
|
|||||||
String.format("%s %s", AccessTokenVO.BEARER_TYPE, accessToken.getAccessToken()));
|
String.format("%s %s", AccessTokenVO.BEARER_TYPE, accessToken.getAccessToken()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkTokenRetry(byte[] responseMessage, String tokenErrorCodeKey, String tokenErrorCodeValues,
|
protected boolean checkTokenRetry(int status, byte[] responseMessage, String tokenErrorHttpStatusCode,
|
||||||
String encode) {
|
String tokenErrorCodeKey, String tokenErrorCodeValues, String encode) {
|
||||||
if (responseMessage == null) {
|
if (responseMessage == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilterFactory;
|
|||||||
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilterType;
|
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilterType;
|
||||||
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException;
|
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException;
|
||||||
import com.eactive.eai.common.TransactionContextKeys;
|
import com.eactive.eai.common.TransactionContextKeys;
|
||||||
|
import com.eactive.eai.common.server.EAIServerManager;
|
||||||
import com.eactive.eai.common.util.Logger;
|
import com.eactive.eai.common.util.Logger;
|
||||||
import com.eactive.eai.common.util.TxSiftContext;
|
import com.eactive.eai.common.util.TxSiftContext;
|
||||||
import com.eactive.eai.common.util.UUIDGenerator;
|
import com.eactive.eai.common.util.UUIDGenerator;
|
||||||
@@ -31,13 +32,20 @@ public abstract class HttpAdapterServiceSupport implements HttpAdapterService, H
|
|||||||
public static final String PROPERTIES_NAME_CLIENT_ID = "clientId";
|
public static final String PROPERTIES_NAME_CLIENT_ID = "clientId";
|
||||||
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
|
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
|
||||||
private ObjectMapper mapper = new ObjectMapper();
|
private ObjectMapper mapper = new ObjectMapper();
|
||||||
|
EAIServerManager eaiServerManager;
|
||||||
|
String instid = null;
|
||||||
|
|
||||||
public Object service(String adptGrpName, String adptName, Object message, Properties prop,
|
public Object service(String adptGrpName, String adptName, Object message, Properties prop,
|
||||||
HttpServletRequest request, HttpServletResponse response) throws Exception {
|
HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
String uuid = prop.getProperty(TransactionContextKeys.TRANSACTION_UUID);
|
String uuid = prop.getProperty(TransactionContextKeys.TRANSACTION_UUID);
|
||||||
boolean bMDCput = false;
|
boolean bMDCput = false;
|
||||||
|
|
||||||
if(uuid == null) {
|
if(uuid == null) {
|
||||||
uuid = UUIDGenerator.getUUID().toString().replaceAll("-", "");
|
if(instid == null) {
|
||||||
|
eaiServerManager = EAIServerManager.getInstance();
|
||||||
|
instid = eaiServerManager.getGroupInstId();
|
||||||
|
}
|
||||||
|
uuid = instid + UUIDGenerator.getUUID().toString().replaceAll("-", "");
|
||||||
prop.setProperty(TransactionContextKeys.TRANSACTION_UUID, uuid);
|
prop.setProperty(TransactionContextKeys.TRANSACTION_UUID, uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.eactive.eai.adapter.http.dynamic.filter;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import com.eactive.eai.common.property.PropManager;
|
||||||
|
import com.eactive.eai.common.util.Logger;
|
||||||
|
import com.eactive.eai.util.JsonPathUtil;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
|
public class JsonToSetStatusFilter implements HttpAdapterFilter {
|
||||||
|
private static final String PROPGROUP = "JsonToSetStatusFilter";
|
||||||
|
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
|
||||||
|
/** HTTP 상태코드 유효 범위 (RFC 7231) */
|
||||||
|
private static final int MIN_HTTP_STATUS = 100;
|
||||||
|
private static final int MAX_HTTP_STATUS = 599;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
|
||||||
|
HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
|
// do nothing
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object doPostFilter(String adptGrpName, String adptName, Object resultMessage, Properties prop,
|
||||||
|
HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
|
String fieldName = "";
|
||||||
|
String statusParam = "";
|
||||||
|
try {
|
||||||
|
JsonNode rootNode = parseJson(adptGrpName, resultMessage);
|
||||||
|
fieldName = getFieldName(adptGrpName);
|
||||||
|
if (rootNode != null && StringUtils.isNotBlank(fieldName) && rootNode.has(fieldName)) {
|
||||||
|
statusParam = rootNode.get(fieldName).asText();
|
||||||
|
int httpStatus = Integer.parseInt(statusParam);
|
||||||
|
if (isValidHttpStatus(httpStatus)) {
|
||||||
|
response.setStatus(httpStatus);
|
||||||
|
} else {
|
||||||
|
logger.warn("유효하지 않은 HTTP 상태코드. 상태코드를 설정하지 않음. fieldName={}, value={}", fieldName, statusParam);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn("설정과 맞지 않는 메시지. 상태코드를 설정하지 않음. fieldName={}", fieldName);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("상태코드 추출 실패. fieldName={}, value={}", fieldName, statusParam, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** HTTP 상태코드로 사용 가능한 값인지 확인한다. (100 ~ 599) */
|
||||||
|
private boolean isValidHttpStatus(int httpStatus) {
|
||||||
|
return httpStatus >= MIN_HTTP_STATUS && httpStatus <= MAX_HTTP_STATUS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFieldName(String adptGrpName) {
|
||||||
|
return PropManager.getInstance().getProperty(PROPGROUP, adptGrpName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonNode parseJson(String adptGrpName, Object message) throws Exception {
|
||||||
|
return JsonPathUtil.toTree(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,10 +2,8 @@ package com.eactive.eai.common.util;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -63,13 +61,17 @@ public class HttpAdapterExtraLogUtil {
|
|||||||
httpAdapterExtraLogVo.setHttpMethod(httpMethod);
|
httpAdapterExtraLogVo.setHttpMethod(httpMethod);
|
||||||
|
|
||||||
for (HttpAdapterExtraHeaderVo httpAdapterExtraHeaderVo : headerVoList) {
|
for (HttpAdapterExtraHeaderVo httpAdapterExtraHeaderVo : headerVoList) {
|
||||||
// String name = httpAdapterExtraHeaderVo.getName();
|
String name = httpAdapterExtraHeaderVo.getName();
|
||||||
// if(StringUtils.isNotBlank(name) && "authorization".equals(name.toLowerCase())) {
|
// if(StringUtils.isNotBlank(name) && "authorization".equals(name.toLowerCase())) {
|
||||||
// httpAdapterExtraHeaderVo.setValue("{hidden}");
|
// httpAdapterExtraHeaderVo.setValue("{hidden}");
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
if(StringUtils.isNotBlank(name) && BODY_FIELD_NAME.equals(name))
|
||||||
|
continue;
|
||||||
|
|
||||||
String value = httpAdapterExtraHeaderVo.getValue();
|
String value = httpAdapterExtraHeaderVo.getValue();
|
||||||
if(StringUtils.isNotBlank(value) && value.length() > MAX_HEADER_VALUE_SIZE) {
|
if(StringUtils.isNotBlank(value) && value.length() > MAX_HEADER_VALUE_SIZE) {
|
||||||
value = value.substring(0, 400) + "...";
|
value = value.substring(0, MAX_HEADER_VALUE_SIZE) + "...";
|
||||||
httpAdapterExtraHeaderVo.setValue(value);
|
httpAdapterExtraHeaderVo.setValue(value);
|
||||||
}else if(value == null){
|
}else if(value == null){
|
||||||
httpAdapterExtraHeaderVo.setValue(" ");
|
httpAdapterExtraHeaderVo.setValue(" ");
|
||||||
@@ -116,7 +118,7 @@ public class HttpAdapterExtraLogUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static List<HttpAdapterExtraHeaderVo> convertHeaderToListOfHttpAdapterExtraHeaderVo(Header[] headers) {
|
public static List<HttpAdapterExtraHeaderVo> convertHeaderToListOfHttpAdapterExtraHeaderVo(Header[] headers) {
|
||||||
Set<String> seenNames = new HashSet<>();
|
// Set<String> seenNames = new HashSet<>();
|
||||||
return Arrays.stream(headers)
|
return Arrays.stream(headers)
|
||||||
// .filter(header -> seenNames.add(header.getName())) // 중복된 이름을 스킵
|
// .filter(header -> seenNames.add(header.getName())) // 중복된 이름을 스킵
|
||||||
.map(header -> new HttpAdapterExtraHeaderVo(header.getName(), header.getValue()))
|
.map(header -> new HttpAdapterExtraHeaderVo(header.getName(), header.getValue()))
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ public class RequestProcessor extends RequestProcessorSupport {
|
|||||||
|
|
||||||
// UUID 생성 : UUID에서 - 없는 32자리
|
// UUID 생성 : UUID에서 - 없는 32자리
|
||||||
String uuid = prop.getProperty(TransactionContextKeys.TRANSACTION_UUID);
|
String uuid = prop.getProperty(TransactionContextKeys.TRANSACTION_UUID);
|
||||||
uuid = uuid == null ? UUIDGenerator.getUUID().toString().replaceAll("-", "") : uuid;
|
uuid = uuid == null ? instid+UUIDGenerator.getUUID().toString().replaceAll("-", "") : uuid;
|
||||||
// UUID 생성 : UUID = server구분4자리 + UUID
|
// UUID 생성 : UUID = server구분4자리 + UUID
|
||||||
/*
|
/*
|
||||||
String uuid = "";
|
String uuid = "";
|
||||||
|
|||||||
+319
@@ -0,0 +1,319 @@
|
|||||||
|
package com.eactive.eai.adapter.http.dynamic.filter;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.json.simple.JSONObject;
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.TestMethodOrder;
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
|
import org.springframework.context.support.GenericApplicationContext;
|
||||||
|
|
||||||
|
import com.eactive.eai.common.property.PropManager;
|
||||||
|
import com.eactive.eai.common.util.ApplicationContextProvider;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JsonToSetStatusFilter 단위 테스트.
|
||||||
|
*
|
||||||
|
* 상태코드 필드명은 PropManager(그룹: JsonToSetStatusFilter, 키: 어댑터그룹명)에서 조회하므로
|
||||||
|
* PropManager 를 Mock 으로 등록하고 필터 로직만 검증한다.
|
||||||
|
*
|
||||||
|
* 4번 그룹은 "현재 구현의 동작을 그대로 고정(characterization)"한 테스트로,
|
||||||
|
* 개선 여부 판단용이다. 구현을 보완하면 해당 테스트도 함께 수정해야 한다.
|
||||||
|
*/
|
||||||
|
@TestMethodOrder(MethodOrderer.DisplayName.class)
|
||||||
|
class JsonToSetStatusFilterTest {
|
||||||
|
|
||||||
|
private static final String GRP = "TEST_GRP";
|
||||||
|
private static final String ADPT = "TEST_ADPT";
|
||||||
|
private static final String FIELD = "apiRsltCd";
|
||||||
|
private static final String PROP_GROUP = "JsonToSetStatusFilter";
|
||||||
|
|
||||||
|
private static GenericApplicationContext ctx;
|
||||||
|
private static PropManager mockPropManager;
|
||||||
|
private static JsonToSetStatusFilter filter;
|
||||||
|
|
||||||
|
private HttpServletRequest mockRequest;
|
||||||
|
private HttpServletResponse mockResponse;
|
||||||
|
private Properties prop;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void setUpClass() {
|
||||||
|
mockPropManager = mock(PropManager.class);
|
||||||
|
|
||||||
|
ctx = new GenericApplicationContext();
|
||||||
|
ctx.getBeanFactory().registerSingleton("propManager", mockPropManager);
|
||||||
|
ctx.registerBeanDefinition("applicationContextProvider",
|
||||||
|
BeanDefinitionBuilder.genericBeanDefinition(ApplicationContextProvider.class)
|
||||||
|
.getBeanDefinition());
|
||||||
|
ctx.refresh();
|
||||||
|
|
||||||
|
filter = new JsonToSetStatusFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
static void tearDownClass() {
|
||||||
|
if (ctx != null) ctx.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
reset(mockPropManager);
|
||||||
|
mockRequest = mock(HttpServletRequest.class);
|
||||||
|
mockResponse = mock(HttpServletResponse.class);
|
||||||
|
prop = new Properties();
|
||||||
|
|
||||||
|
when(mockPropManager.getProperty(PROP_GROUP, GRP)).thenReturn(FIELD);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String body(String rsltCd) {
|
||||||
|
return "{\"" + FIELD + "\":\"" + rsltCd + "\",\"msg\":\"OK\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// 1. 상태코드 정상 반영
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("1-1. 문자열 body의 상태코드 필드값을 HTTP 상태코드로 설정한다")
|
||||||
|
void testPostFilter_string_setsStatus() throws Exception {
|
||||||
|
Object result = filter.doPostFilter(GRP, ADPT, body("404"), prop, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
verify(mockResponse).setStatus(404);
|
||||||
|
assertEquals(body("404"), result, "원 메시지를 그대로 반환해야 한다");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("1-2. 숫자 타입 필드값도 상태코드로 설정한다")
|
||||||
|
void testPostFilter_numericNode_setsStatus() throws Exception {
|
||||||
|
filter.doPostFilter(GRP, ADPT, "{\"" + FIELD + "\":503}", prop, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
verify(mockResponse).setStatus(503);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("1-3. 업무팀 주 사용 케이스 - 2xx 상태코드를 설정한다")
|
||||||
|
void testPostFilter_successStatusCodes() throws Exception {
|
||||||
|
for (int status : new int[] { 200, 201, 202, 204 }) {
|
||||||
|
reset(mockResponse);
|
||||||
|
|
||||||
|
filter.doPostFilter(GRP, ADPT, body(String.valueOf(status)), prop, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
verify(mockResponse).setStatus(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("1-4. 어댑터 그룹마다 다른 필드명을 사용할 수 있다")
|
||||||
|
void testPostFilter_perGroupFieldName() throws Exception {
|
||||||
|
when(mockPropManager.getProperty(PROP_GROUP, GRP)).thenReturn("rspCd");
|
||||||
|
|
||||||
|
filter.doPostFilter(GRP, ADPT, "{\"rspCd\":\"403\",\"" + FIELD + "\":\"500\"}", prop,
|
||||||
|
mockRequest, mockResponse);
|
||||||
|
|
||||||
|
verify(mockResponse).setStatus(403);
|
||||||
|
verify(mockResponse, never()).setStatus(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("1-5. JSONObject 타입 응답도 처리한다")
|
||||||
|
void testPostFilter_jsonObjectMessage_setsStatus() throws Exception {
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
json.put(FIELD, "404");
|
||||||
|
|
||||||
|
Object result = filter.doPostFilter(GRP, ADPT, json, prop, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
verify(mockResponse).setStatus(404);
|
||||||
|
assertSame(json, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("1-6. JsonNode 타입 응답도 처리한다")
|
||||||
|
void testPostFilter_jsonNodeMessage_setsStatus() throws Exception {
|
||||||
|
JsonNode node = new ObjectMapper().readTree(body("401"));
|
||||||
|
|
||||||
|
Object result = filter.doPostFilter(GRP, ADPT, node, prop, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
verify(mockResponse).setStatus(401);
|
||||||
|
assertSame(node, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("1-7. 경계값 100 / 599는 설정한다")
|
||||||
|
void testPostFilter_boundaryValues() throws Exception {
|
||||||
|
filter.doPostFilter(GRP, ADPT, body("100"), prop, mockRequest, mockResponse);
|
||||||
|
verify(mockResponse).setStatus(100);
|
||||||
|
|
||||||
|
reset(mockResponse);
|
||||||
|
filter.doPostFilter(GRP, ADPT, body("599"), prop, mockRequest, mockResponse);
|
||||||
|
verify(mockResponse).setStatus(599);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// 2. 상태코드를 변경하지 않는 경우
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("2-1. 상태코드 필드가 없으면 setStatus를 호출하지 않는다")
|
||||||
|
void testPostFilter_fieldAbsent_noStatusChange() throws Exception {
|
||||||
|
filter.doPostFilter(GRP, ADPT, "{\"msg\":\"OK\"}", prop, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
verify(mockResponse, never()).setStatus(anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("2-2. 상태코드 필드값이 빈 문자열이면 setStatus를 호출하지 않는다")
|
||||||
|
void testPostFilter_emptyValue_noStatusChange() throws Exception {
|
||||||
|
filter.doPostFilter(GRP, ADPT, body(""), prop, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
verify(mockResponse, never()).setStatus(anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("2-3. 프로퍼티에 필드명 설정이 없으면(null) 예외 없이 통과한다")
|
||||||
|
void testPostFilter_noConfiguredField_noStatusChange() throws Exception {
|
||||||
|
when(mockPropManager.getProperty(PROP_GROUP, GRP)).thenReturn(null);
|
||||||
|
|
||||||
|
Object result = filter.doPostFilter(GRP, ADPT, body("404"), prop, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
verify(mockResponse, never()).setStatus(anyInt());
|
||||||
|
assertEquals(body("404"), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("2-4. 상태코드 필드값이 숫자가 아니면 로그만 남기고 상태코드를 변경하지 않는다")
|
||||||
|
void testPostFilter_nonNumericValue_noStatusChange() throws Exception {
|
||||||
|
Object result = filter.doPostFilter(GRP, ADPT, body("E0001"), prop, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
verify(mockResponse, never()).setStatus(anyInt());
|
||||||
|
assertEquals(body("E0001"), result, "파싱 실패해도 원 메시지는 그대로 반환되어야 한다");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("2-5. 업무결과코드 0000은 유효한 상태코드가 아니므로 설정하지 않는다")
|
||||||
|
void testPostFilter_businessCode0000_noStatusChange() throws Exception {
|
||||||
|
filter.doPostFilter(GRP, ADPT, body("0000"), prop, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
verify(mockResponse, never()).setStatus(anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("2-6. HTTP 상태코드 범위(100~599)를 벗어난 값은 설정하지 않는다")
|
||||||
|
void testPostFilter_outOfRangeStatus_noStatusChange() throws Exception {
|
||||||
|
for (String value : new String[] { "0", "99", "600", "9999", "-200" }) {
|
||||||
|
reset(mockResponse);
|
||||||
|
|
||||||
|
filter.doPostFilter(GRP, ADPT, body(value), prop, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
verify(mockResponse, never()).setStatus(anyInt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("2-7. JSON 배열 응답이면 상태코드를 변경하지 않는다")
|
||||||
|
void testPostFilter_jsonArray_noStatusChange() throws Exception {
|
||||||
|
filter.doPostFilter(GRP, ADPT, "[{\"" + FIELD + "\":\"404\"}]", prop, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
verify(mockResponse, never()).setStatus(anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("2-8. 필드값이 JSON null이면 상태코드를 변경하지 않는다")
|
||||||
|
void testPostFilter_jsonNullValue_noStatusChange() throws Exception {
|
||||||
|
filter.doPostFilter(GRP, ADPT, "{\"" + FIELD + "\":null}", prop, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
verify(mockResponse, never()).setStatus(anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// 3. 비정상 입력에서도 예외를 던지지 않는다 (거래 실패 방지)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("3-1. JSON 형식이 아닌 응답이어도 예외 없이 통과한다")
|
||||||
|
void testPostFilter_nonJsonBody_noException() throws Exception {
|
||||||
|
String xml = "<xml><rslt>0000</rslt></xml>";
|
||||||
|
|
||||||
|
Object result = assertDoesNotThrow(
|
||||||
|
() -> filter.doPostFilter(GRP, ADPT, xml, prop, mockRequest, mockResponse));
|
||||||
|
|
||||||
|
assertSame(xml, result);
|
||||||
|
verify(mockResponse, never()).setStatus(anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("3-2. 깨진 JSON 응답이어도 예외 없이 통과한다")
|
||||||
|
void testPostFilter_malformedJson_noException() {
|
||||||
|
String broken = "{\"" + FIELD + "\":\"404\"";
|
||||||
|
|
||||||
|
assertDoesNotThrow(() -> filter.doPostFilter(GRP, ADPT, broken, prop, mockRequest, mockResponse));
|
||||||
|
verify(mockResponse, never()).setStatus(anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("3-3. null 응답이어도 예외 없이 null을 그대로 반환한다")
|
||||||
|
void testPostFilter_nullMessage_noException() {
|
||||||
|
Object result = assertDoesNotThrow(
|
||||||
|
() -> filter.doPostFilter(GRP, ADPT, null, prop, mockRequest, mockResponse));
|
||||||
|
|
||||||
|
assertNull(result);
|
||||||
|
verify(mockResponse, never()).setStatus(anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("3-4. 빈 문자열 응답이어도 예외 없이 통과한다")
|
||||||
|
void testPostFilter_emptyBody_noException() {
|
||||||
|
assertDoesNotThrow(() -> filter.doPostFilter(GRP, ADPT, "", prop, mockRequest, mockResponse));
|
||||||
|
verify(mockResponse, never()).setStatus(anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("3-5. 프로퍼티 그룹 미등록(PropManager가 null 반환)이어도 예외 없이 통과한다")
|
||||||
|
void testPostFilter_propGroupMissing_noException() {
|
||||||
|
when(mockPropManager.getProperty(anyString(), anyString())).thenReturn(null);
|
||||||
|
|
||||||
|
assertDoesNotThrow(() -> filter.doPostFilter(GRP, ADPT, body("404"), prop, mockRequest, mockResponse));
|
||||||
|
verify(mockResponse, never()).setStatus(anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// 4. doPreFilter / 현재 동작 고정 (개선 검토 대상)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("4-1. doPreFilter는 아무 것도 하지 않고 요청 메시지를 그대로 반환한다")
|
||||||
|
void testPreFilter_doesNothing() throws Exception {
|
||||||
|
String message = body("404");
|
||||||
|
|
||||||
|
Object result = filter.doPreFilter(GRP, ADPT, message, prop, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
assertSame(message, result);
|
||||||
|
verify(mockResponse, never()).setStatus(anyInt());
|
||||||
|
verifyNoInteractions(mockPropManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("4-2. [확인필요] byte[] 응답은 JSON으로 파싱되지 않아 상태코드가 설정되지 않는다")
|
||||||
|
void testPostFilter_byteArray_notSupported() throws Exception {
|
||||||
|
byte[] msg = body("404").getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
Object result = filter.doPostFilter(GRP, ADPT, msg, prop, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
// JsonPathUtil.toTree 가 byte[] 를 toString() 처리하므로 "[B@..." 가 되어 파싱에 실패한다.
|
||||||
|
verify(mockResponse, never()).setStatus(anyInt());
|
||||||
|
assertSame(msg, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user