Merge branch 'feature/template-adapter-error-msg-handler'

This commit is contained in:
curry772
2026-05-12 13:06:08 +09:00
2 changed files with 433 additions and 25 deletions
@@ -9,7 +9,9 @@ import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.adapter.AdapterPropManager;
import com.eactive.eai.common.property.PropManager;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.message.StandardItem;
import com.eactive.eai.message.StandardMessage;
@@ -23,10 +25,13 @@ import com.eactive.eai.message.StandardMessage;
* StandardMessage 의 필드 값으로 치환한 결과를 반환한다.
*
* [변수 치환 규칙]
* 1. "${callprop.키}" → callProp.getProperty("키") 로 치환
* 2. "${경로}" → StandardMessage.findItemValue("경로") 로 치
* callprop 접두어가 있으면 callProp 을 우선 참조하고,
* 없으면 StandardMessage 경로로 해석한다.
* 1. "${callprop.키}" → callProp.getProperty("키") 로 치환
* "${callprop.키:기본값}" → 키 없거나 callProp null 이면 기본값 반
* 2. "${callprop[경로]}" → StandardMessage 경로 값을 키로 callProp 간접 조회
* "${callprop[경로]:기본값}" → 키 결정 실패 또는 키 없으면 기본값 반환
* 3. "${경로}" → StandardMessage.findItemValue("경로") 로 치환
* "${경로:기본값}" → 경로 값이 null/공백이면 기본값 반환
* 모든 형태에서 기본값 미지정 시 빈 문자열을 반환한다.
*
* [배열 반복 문법] (MSG_LIST 등 GRID 타입)
* {{#foreach MSG.MSG_LIST}}
@@ -62,6 +67,12 @@ import com.eactive.eai.message.StandardMessage;
* {{/foreach}}
* </errors>
* </error>
*
* [callProp 중첩 Properties 참조]
* callProp 의 값이 Properties 인 경우 점(.) 으로 체인 탐색이 가능하다.
* callProp.put("OUTBOUND", subProps) // subProps.setProperty("IF_ID", "SVC001")
* → 템플릿에서 ${callprop.OUTBOUND.IF_ID} 로 참조
* 중간 값이 Properties 가 아닌 경우 전체 keyPath 를 키로 폴백 조회한다.
*/
public class TemplateAdapterErrorMsgHandler implements AdapterErrorMessageHandler {
@@ -73,6 +84,9 @@ public class TemplateAdapterErrorMsgHandler implements AdapterErrorMessageHandle
/** callProp 참조 변수 접두어: ${callprop.키} */
static final String CALLPROP_PREFIX = "callprop.";
/** callProp 간접 참조 접두어: ${callprop[표준전문경로]} */
static final String CALLPROP_INDIRECT_PREFIX = "callprop[";
/** ${path} 또는 ${callprop.key} 스칼라 변수 패턴 */
private static final Pattern VAR_PATTERN =
Pattern.compile("\\$\\{([^}]+)\\}");
@@ -91,7 +105,7 @@ public class TemplateAdapterErrorMsgHandler implements AdapterErrorMessageHandle
StandardMessage resStandardMessage) throws Exception {
String templateKey = inboundAdapterGroupName + ".template";
String template = AdapterPropManager.getInstance().getProperty(PROP_GROUP, templateKey);
String template = PropManager.getInstance().getProperty(PROP_GROUP, templateKey);
if (StringUtils.isBlank(template)) {
if (logger.isWarn()) {
@@ -135,18 +149,126 @@ public class TemplateAdapterErrorMsgHandler implements AdapterErrorMessageHandle
/**
* 변수 표현식을 해석해 값을 반환한다.
* - "callprop.키" → callProp.getProperty("키")
* - 그 외 → StandardMessage.findItemValue(expr)
* 모든 형태에서 ':기본값' 접미사를 지원한다. 기본값 미지정 시 빈 문자열.
* - "callprop[경로][:기본값]" → 표준전문 경로 값을 키로 callProp 간접 조회
* - "callprop.키[:기본값]" → callProp 직접/중첩 조회
* - "경로[:기본값]" → StandardMessage.findItemValue(경로)
*/
private String resolveScalar(String expr, StandardMessage msg, Properties callProp) {
if (expr.startsWith(CALLPROP_PREFIX)) {
String key = expr.substring(CALLPROP_PREFIX.length());
if (callProp != null) {
return StringUtils.defaultString(callProp.getProperty(key));
}
return "";
if (expr.startsWith(CALLPROP_INDIRECT_PREFIX)) {
return resolveIndirectCallProp(expr, msg, callProp);
}
return StringUtils.defaultString(msg.findItemValue(expr));
if (expr.startsWith(CALLPROP_PREFIX)) {
String rest = expr.substring(CALLPROP_PREFIX.length());
int colonIdx = rest.indexOf(':');
String keyPath = colonIdx >= 0 ? rest.substring(0, colonIdx) : rest;
String defaultVal = colonIdx >= 0 ? rest.substring(colonIdx + 1) : "";
if (callProp == null) return defaultVal;
String value = resolveCallProp(callProp, keyPath);
return value != null ? value : defaultVal;
}
int colonIdx = expr.indexOf(':');
String path = colonIdx >= 0 ? expr.substring(0, colonIdx) : expr;
String defaultVal = colonIdx >= 0 ? expr.substring(colonIdx + 1) : "";
String value = resolveMessagePath(msg, path);
return StringUtils.isNotEmpty(value) ? value : defaultVal;
}
/**
* StandardMessage 경로를 해석한다.
* 1. findItemValue(path) 로 직접 조회한다.
* 2. null 이면 경로를 뒤에서부터 분리하며, 부모 경로 값이 JSON 문자열인 경우
* 나머지 경로(subPath)를 키로 JSON 필드를 추출한다.
* 예) DATA.BIZDATA.acctNo → findItemValue("DATA.BIZDATA") → JSON 파싱 → acctNo
* DATA.BIZDATA.addr.city → findItemValue("DATA.BIZDATA") → JSON 파싱 → addr.city
*/
private String resolveMessagePath(StandardMessage msg, String path) {
String value = msg.findItemValue(path);
if (value != null) return value;
int dotIdx = path.lastIndexOf('.');
while (dotIdx > 0) {
String parentPath = path.substring(0, dotIdx);
String subPath = path.substring(dotIdx + 1);
String parentVal = msg.findItemValue(parentPath);
if (StringUtils.isNotBlank(parentVal)) {
String extracted = extractFromJson(parentVal, subPath);
if (extracted != null) return extracted;
}
dotIdx = parentPath.lastIndexOf('.');
}
return null;
}
/**
* JSON 문자열에서 dot 구분 경로로 필드 값을 추출한다.
* 중간 경로가 JSON 오브젝트면 계속 탐색하고, 최종 값이 primitive 면 문자열로,
* 오브젝트/배열이면 JSON 문자열 그대로 반환한다. 파싱 실패 시 null 반환.
*/
private String extractFromJson(String json, String fieldPath) {
try {
JsonElement el = new JsonParser().parse(json);
for (String part : fieldPath.split("\\.", -1)) {
if (!el.isJsonObject()) return null;
el = el.getAsJsonObject().get(part);
if (el == null) return null;
}
return el.isJsonPrimitive() ? el.getAsString() : el.toString();
} catch (Exception e) {
return null;
}
}
/**
* ${callprop[경로]} 또는 ${callprop[경로]:기본값} 처리.
* 1. 표준전문 경로로 callProp 키를 동적으로 결정한다.
* 2. 결정된 키로 callProp 에서 값을 조회한다.
* 3. 키 또는 값을 찾지 못하면 기본값(없으면 빈 문자열)을 반환한다.
*/
private String resolveIndirectCallProp(String expr, StandardMessage msg, Properties callProp) {
int closeIdx = expr.indexOf(']');
if (closeIdx < 0) return "";
String msgPath = expr.substring(CALLPROP_INDIRECT_PREFIX.length(), closeIdx);
String defaultVal = (closeIdx + 1 < expr.length() && expr.charAt(closeIdx + 1) == ':')
? expr.substring(closeIdx + 2)
: "";
String key = msg.findItemValue(msgPath);
if (StringUtils.isBlank(key)) return defaultVal;
if (callProp == null) return defaultVal;
String value = resolveCallProp(callProp, key);
return value != null ? value : defaultVal;
}
/**
* Properties 체인을 점(.) 구분자로 재귀 탐색한다.
* - 첫 세그먼트의 값이 Properties 이면 나머지 경로로 재귀
* - String 이면 반환, 그 외 Object 이면 toString() 반환
* - 첫 세그먼트 값이 Properties 가 아닌 경우 전체 keyPath 로 폴백 조회
*/
private String resolveCallProp(Properties props, String keyPath) {
int dotIdx = keyPath.indexOf('.');
if (dotIdx < 0) {
Object val = props.get(keyPath);
if (val == null) return null;
if (val instanceof String) return (String) val;
return val.toString();
}
String first = keyPath.substring(0, dotIdx);
String rest = keyPath.substring(dotIdx + 1);
Object val = props.get(first);
if (val instanceof Properties) {
return resolveCallProp((Properties) val, rest);
}
// 중간 값이 Properties 가 아닌 경우: 전체 keyPath 를 키로 폴백
Object direct = props.get(keyPath);
if (direct == null) return null;
if (direct instanceof String) return (String) direct;
return direct.toString();
}
/**
@@ -15,7 +15,7 @@ import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.context.ApplicationContext;
import com.eactive.eai.adapter.AdapterPropManager;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.ApplicationContextProvider;
import com.eactive.eai.message.StandardItem;
import com.eactive.eai.message.StandardMessage;
@@ -26,7 +26,7 @@ import com.eactive.eai.message.StandardType;
*
* - render() : package-private 이므로 같은 패키지에서 직접 호출
* - generateNonStandardErrorResponseMessage() : ApplicationContextProvider 에
* mock ApplicationContext 를 리플렉션으로 주입하여 AdapterPropManager 격리
* mock ApplicationContext 를 리플렉션으로 주입하여 PropManager 격리
*/
class TemplateAdapterErrorMsgHandlerTest {
@@ -160,6 +160,30 @@ class TemplateAdapterErrorMsgHandlerTest {
assertEquals("상세설명입니다", handler.render("${MSG.MAIN_MSG.outp_msg_desc}", msg, null));
}
@Test
@DisplayName("경로:기본값 – 경로 존재 시 값 반환")
void 표준전문_기본값_경로존재() {
StandardMessage msg = buildMsg("E001", "오류", "", null);
assertEquals("E001", handler.render("${MSG.MAIN_MSG.outp_msg_cd:DEFAULT}", msg, null));
}
@Test
@DisplayName("경로:기본값 – 경로 없으면 기본값 반환")
void 표준전문_기본값_경로없음() {
StandardMessage msg = buildMsg("E001", "오류", "", null);
assertEquals("UNKNOWN", handler.render("${NONE.PATH:UNKNOWN}", msg, null));
}
@Test
@DisplayName("경로:기본값 기본값이 빈 문자열")
void 표준전문_기본값_빈문자열() {
StandardMessage msg = buildMsg("E001", "오류", "", null);
assertEquals("", handler.render("${NONE.PATH:}", msg, null));
}
}
// ================================================================
@@ -210,12 +234,274 @@ class TemplateAdapterErrorMsgHandlerTest {
assertEquals("", handler.render("${callprop.ADAPTER_NAME}", msg, null));
}
@Test
@DisplayName("callProp 값이 Properties 인 경우 중첩 탐색")
void callProp_중첩_Properties() {
Properties sub = new Properties();
sub.setProperty("IF_ID", "SVC001");
sub.setProperty("SVC_NM", "조회서비스");
Properties callProp = new Properties();
callProp.put("OUTBOUND", sub);
callProp.setProperty("ADAPTER_NAME", "MY_ADAPTER");
StandardMessage msg = buildMsg("E001", "오류", "", null);
String template = "{\"adapter\":\"${callprop.ADAPTER_NAME}\",\"ifId\":\"${callprop.OUTBOUND.IF_ID}\",\"svcNm\":\"${callprop.OUTBOUND.SVC_NM}\"}";
String result = handler.render(template, msg, callProp);
assertEquals("{\"adapter\":\"MY_ADAPTER\",\"ifId\":\"SVC001\",\"svcNm\":\"조회서비스\"}", result);
}
@Test
@DisplayName("중첩 Properties 3단계 탐색")
void callProp_3단계_중첩() {
Properties level2 = new Properties();
level2.setProperty("CODE", "L2CODE");
Properties level1 = new Properties();
level1.put("LEVEL2", level2);
Properties callProp = new Properties();
callProp.put("LEVEL1", level1);
StandardMessage msg = buildMsg("E001", "오류", "", null);
assertEquals("L2CODE", handler.render("${callprop.LEVEL1.LEVEL2.CODE}", msg, callProp));
}
@Test
@DisplayName("중간 값이 Properties 가 아니면 전체 keyPath 로 폴백 조회")
void callProp_중간값_비Properties_폴백() {
Properties callProp = new Properties();
callProp.setProperty("A.B", "FALLBACK_VALUE"); // 점 포함 키
callProp.setProperty("A", "NOT_PROPS"); // String 값
StandardMessage msg = buildMsg("E001", "오류", "", null);
// A 는 String 이므로 Properties 가 아님 → "A.B" 전체 키로 폴백
assertEquals("FALLBACK_VALUE", handler.render("${callprop.A.B}", msg, callProp));
}
@Test
@DisplayName("중첩 Properties 에서 없는 키는 빈 문자열")
void callProp_중첩_없는키() {
Properties sub = new Properties();
sub.setProperty("EXIST", "VALUE");
Properties callProp = new Properties();
callProp.put("SUB", sub);
StandardMessage msg = buildMsg("E001", "오류", "", null);
assertEquals("", handler.render("${callprop.SUB.NONE_KEY}", msg, callProp));
}
@Test
@DisplayName("callprop.키:기본값 – 키 존재 시 값 반환")
void callProp_직접_기본값_키존재() {
StandardMessage msg = buildMsg("E001", "오류", "", null);
Properties callProp = props("ADAPTER_NAME", "MY_ADAPTER");
assertEquals("MY_ADAPTER", handler.render("${callprop.ADAPTER_NAME:DEFAULT}", msg, callProp));
}
@Test
@DisplayName("callprop.키:기본값 – 키 없으면 기본값 반환")
void callProp_직접_기본값_키없음() {
StandardMessage msg = buildMsg("E001", "오류", "", null);
Properties callProp = new Properties();
assertEquals("DEFAULT_VAL", handler.render("${callprop.NONE_KEY:DEFAULT_VAL}", msg, callProp));
}
@Test
@DisplayName("callprop.키:기본값 callProp null 이면 기본값 반환")
void callProp_직접_기본값_null() {
StandardMessage msg = buildMsg("E001", "오류", "", null);
assertEquals("FALLBACK", handler.render("${callprop.ADAPTER_NAME:FALLBACK}", msg, null));
}
}
// ================================================================
// callProp 간접 참조 ${callprop[표준전문경로]} / ${callprop[경로]:기본값}
// ================================================================
@Nested
@DisplayName("callProp 간접 참조 ${callprop[경로]}")
class IndirectCallPropSubstitution {
/** 표준전문에 cp_key 필드가 있는 메시지 */
private StandardMessage buildMsgWithKey(String cpKey, String errCode) {
StandardMessage msg = buildMsg(errCode, "오류", "", null);
// MSG 그룹 아래 cp_key 필드 추가
StandardItem msgGroup = msg.findItem("MSG");
if (msgGroup != null) {
msgGroup.addItem(field("cp_key", cpKey));
}
return msg;
}
@Test
@DisplayName("표준전문 경로 값으로 callProp 키를 결정해 값 조회")
void 간접_참조_정상() {
StandardMessage msg = buildMsgWithKey("ADAPTER_NAME", "E001");
Properties callProp = props("ADAPTER_NAME", "MY_ADAPTER");
String template = "${callprop[MSG.cp_key]}";
assertEquals("MY_ADAPTER", handler.render(template, msg, callProp));
}
@Test
@DisplayName("표준전문 경로 값이 없으면 기본값 반환")
void 표준전문_경로_없으면_기본값() {
StandardMessage msg = buildMsg("E001", "오류", "", null);
Properties callProp = props("ADAPTER_NAME", "MY_ADAPTER");
String template = "${callprop[MSG.NONE_KEY]:DEFAULT_ADAPTER}";
assertEquals("DEFAULT_ADAPTER", handler.render(template, msg, callProp));
}
@Test
@DisplayName("callProp 에 해당 키 없으면 기본값 반환")
void callProp_키_없으면_기본값() {
StandardMessage msg = buildMsgWithKey("MISSING_KEY", "E001");
Properties callProp = props("OTHER_KEY", "OTHER_VALUE");
String template = "${callprop[MSG.cp_key]:FALLBACK}";
assertEquals("FALLBACK", handler.render(template, msg, callProp));
}
@Test
@DisplayName("기본값 미지정 시 빈 문자열")
void 기본값_없으면_빈문자열() {
StandardMessage msg = buildMsg("E001", "오류", "", null);
Properties callProp = props("ADAPTER_NAME", "MY_ADAPTER");
String template = "${callprop[MSG.NONE_KEY]}";
assertEquals("", handler.render(template, msg, callProp));
}
@Test
@DisplayName("callProp 이 null 이면 기본값 반환")
void callProp_null_기본값() {
StandardMessage msg = buildMsgWithKey("ADAPTER_NAME", "E001");
String template = "${callprop[MSG.cp_key]:NO_PROP}";
assertEquals("NO_PROP", handler.render(template, msg, null));
}
@Test
@DisplayName("기본값이 빈 문자열인 경우")
void 기본값이_빈문자열() {
StandardMessage msg = buildMsg("E001", "오류", "", null);
Properties callProp = new Properties();
String template = "${callprop[MSG.NONE_KEY]:}";
assertEquals("", handler.render(template, msg, callProp));
}
@Test
@DisplayName("간접 참조와 직접 참조 혼합")
void 간접_직접_혼합() {
StandardMessage msg = buildMsgWithKey("IF_ID_KEY", "E001");
Properties callProp = props("IF_ID_KEY", "SVC_001", "ADAPTER_NAME", "MY_ADAPTER");
String template = "{\"adapter\":\"${callprop.ADAPTER_NAME}\",\"ifId\":\"${callprop[MSG.cp_key]}\"}";
assertEquals("{\"adapter\":\"MY_ADAPTER\",\"ifId\":\"SVC_001\"}", handler.render(template, msg, callProp));
}
}
// ================================================================
// 배열 반복 {{#foreach path}}...{{/foreach}}
// ================================================================
// ================================================================
// DATA 영역 변수 치환 ${DATA.xxx}
// ================================================================
@Nested
@DisplayName("DATA 영역 변수 치환 ${DATA.xxx}")
class DataSectionSubstitution {
/**
* DATA > BIZDATA(FIELD, JSON String value) 구조의 StandardMessage 생성.
* BIZDATA 는 파싱 없이 JSON 문자열 그대로 세팅되는 형태.
*/
private StandardMessage buildMsgWithBizData(String bizDataJson) {
StandardMessage msg = buildMsg("E001", "오류", "", null);
StandardItem dataGroup = group("DATA");
dataGroup.addItem(field("BIZDATA", bizDataJson));
msg.addItem(dataGroup);
return msg;
}
@Test
@DisplayName("DATA.BIZDATA JSON 필드 치환 단일 depth")
void DATA_BIZDATA_JSON_필드_치환() {
StandardMessage msg = buildMsgWithBizData("{\"acctNo\":\"1234567890\",\"custNm\":\"홍길동\"}");
String template = "{\"acctNo\":\"${DATA.BIZDATA.acctNo}\",\"custNm\":\"${DATA.BIZDATA.custNm}\"}";
assertEquals("{\"acctNo\":\"1234567890\",\"custNm\":\"홍길동\"}",
handler.render(template, msg, null));
}
@Test
@DisplayName("DATA.BIZDATA JSON 필드 치환 중첩 depth")
void DATA_BIZDATA_JSON_중첩_필드_치환() {
StandardMessage msg = buildMsgWithBizData("{\"addr\":{\"city\":\"서울\",\"zip\":\"12345\"}}");
assertEquals("서울", handler.render("${DATA.BIZDATA.addr.city}", msg, null));
}
@Test
@DisplayName("DATA.BIZDATA 원본 JSON 문자열 치환")
void DATA_BIZDATA_원본_치환() {
String rawJson = "{\"acctNo\":\"1234567890\"}";
StandardMessage msg = buildMsgWithBizData(rawJson);
assertEquals(rawJson, handler.render("${DATA.BIZDATA}", msg, null));
}
@Test
@DisplayName("DATA.BIZDATA 와 MSG 영역 혼합 치환")
void DATA_MSG_혼합_치환() {
StandardMessage msg = buildMsgWithBizData("{\"acctNo\":\"9876543210\"}");
String template = "{\"code\":\"${MSG.MAIN_MSG.outp_msg_cd}\",\"acctNo\":\"${DATA.BIZDATA.acctNo}\"}";
assertEquals("{\"code\":\"E001\",\"acctNo\":\"9876543210\"}",
handler.render(template, msg, null));
}
@Test
@DisplayName("DATA.BIZDATA JSON 에 없는 필드는 빈 문자열")
void DATA_BIZDATA_없는_JSON_필드_빈문자열() {
StandardMessage msg = buildMsgWithBizData("{\"acctNo\":\"1234567890\"}");
assertEquals("", handler.render("${DATA.BIZDATA.noneField}", msg, null));
}
@Test
@DisplayName("DATA.BIZDATA JSON 에 없는 필드에 기본값 지정")
void DATA_BIZDATA_없는_JSON_필드_기본값() {
StandardMessage msg = buildMsgWithBizData("{\"acctNo\":\"1234567890\"}");
assertEquals("UNKNOWN", handler.render("${DATA.BIZDATA.noneField:UNKNOWN}", msg, null));
}
@Test
@DisplayName("DATA 영역 미설정 시 기본값 반환")
void DATA_없으면_기본값() {
StandardMessage msg = buildMsg("E001", "오류", "", null);
assertEquals("{}", handler.render("${DATA.BIZDATA:{}}", msg, null));
}
}
@Nested
@DisplayName("배열 반복 블록 {{#foreach}}")
class ForeachBlock {
@@ -341,16 +627,16 @@ class TemplateAdapterErrorMsgHandlerTest {
}
// ================================================================
// generateNonStandardErrorResponseMessage AdapterPropManager mock
// generateNonStandardErrorResponseMessage PropManager mock
// ================================================================
@Nested
@DisplayName("generateNonStandardErrorResponseMessage")
class GenerateMethod {
private void injectMockPropManager(AdapterPropManager mockPropManager) throws Exception {
private void injectMockPropManager(PropManager mockPropManager) throws Exception {
ApplicationContext mockCtx = Mockito.mock(ApplicationContext.class);
Mockito.when(mockCtx.getBean(AdapterPropManager.class)).thenReturn(mockPropManager);
Mockito.when(mockCtx.getBean(PropManager.class)).thenReturn(mockPropManager);
Field ctxField = ApplicationContextProvider.class.getDeclaredField("context");
ctxField.setAccessible(true);
ctxField.set(null, mockCtx);
@@ -359,7 +645,7 @@ class TemplateAdapterErrorMsgHandlerTest {
@Test
@DisplayName("템플릿 미설정 시 null 반환")
void 템플릿_없으면_null() throws Exception {
AdapterPropManager mockProp = Mockito.mock(AdapterPropManager.class);
PropManager mockProp = Mockito.mock(PropManager.class);
Mockito.when(mockProp.getProperty(anyString(), anyString())).thenReturn(null);
injectMockPropManager(mockProp);
@@ -373,7 +659,7 @@ class TemplateAdapterErrorMsgHandlerTest {
@Test
@DisplayName("빈 템플릿 시 null 반환")
void 빈_템플릿_null() throws Exception {
AdapterPropManager mockProp = Mockito.mock(AdapterPropManager.class);
PropManager mockProp = Mockito.mock(PropManager.class);
Mockito.when(mockProp.getProperty(anyString(), anyString())).thenReturn(" ");
injectMockPropManager(mockProp);
@@ -388,7 +674,7 @@ class TemplateAdapterErrorMsgHandlerTest {
@DisplayName("템플릿 설정 시 StandardMessage 치환 결과 반환")
void 템플릿_stdmsg_치환() throws Exception {
String template = "{\"code\":\"${MSG.MAIN_MSG.outp_msg_cd}\"}";
AdapterPropManager mockProp = Mockito.mock(AdapterPropManager.class);
PropManager mockProp = Mockito.mock(PropManager.class);
Mockito.when(mockProp.getProperty(
eq(TemplateAdapterErrorMsgHandler.PROP_GROUP),
eq("MY_GROUP.template"))).thenReturn(template);
@@ -405,7 +691,7 @@ class TemplateAdapterErrorMsgHandlerTest {
@DisplayName("callProp 값이 템플릿에 치환되어 반환")
void 템플릿_callProp_치환() throws Exception {
String template = "{\"adapter\":\"${callprop.ADAPTER_NAME}\",\"code\":\"${MSG.MAIN_MSG.outp_msg_cd}\"}";
AdapterPropManager mockProp = Mockito.mock(AdapterPropManager.class);
PropManager mockProp = Mockito.mock(PropManager.class);
Mockito.when(mockProp.getProperty(
eq(TemplateAdapterErrorMsgHandler.PROP_GROUP),
eq("MY_GROUP.template"))).thenReturn(template);
@@ -423,7 +709,7 @@ class TemplateAdapterErrorMsgHandlerTest {
@Test
@DisplayName("템플릿 키는 {adapterGroupId}.template 형식으로 조회")
void 템플릿_키_형식_검증() throws Exception {
AdapterPropManager mockProp = Mockito.mock(AdapterPropManager.class);
PropManager mockProp = Mockito.mock(PropManager.class);
Mockito.when(mockProp.getProperty(anyString(), anyString())).thenReturn(null);
injectMockPropManager(mockProp);