AdapterErrorMessageHandler 프러퍼티 그룹에 default template 기능 추가
- default.template - default.in.template - default.sys.template
This commit is contained in:
@@ -128,6 +128,9 @@ public class TemplateAdapterErrorMsgHandler implements AdapterErrorMessageHandle
|
|||||||
String templateKey = inboundAdapterGroupName + ".template";
|
String templateKey = inboundAdapterGroupName + ".template";
|
||||||
String template = PropManager.getInstance().getProperty(PROP_GROUP, templateKey);
|
String template = PropManager.getInstance().getProperty(PROP_GROUP, templateKey);
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(template))
|
||||||
|
template = PropManager.getInstance().getProperty(PROP_GROUP, "default.template");
|
||||||
|
|
||||||
if (StringUtils.isBlank(template)) {
|
if (StringUtils.isBlank(template)) {
|
||||||
if (logger.isWarn()) {
|
if (logger.isWarn()) {
|
||||||
logger.warn("TemplateAdapterErrorMsgHandler] template not found. group=" + PROP_GROUP
|
logger.warn("TemplateAdapterErrorMsgHandler] template not found. group=" + PROP_GROUP
|
||||||
@@ -320,6 +323,8 @@ public class TemplateAdapterErrorMsgHandler implements AdapterErrorMessageHandle
|
|||||||
? expr.substring(closeIdx + 2)
|
? expr.substring(closeIdx + 2)
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
|
if (msg == null) return defaultVal;
|
||||||
|
|
||||||
String key = msg.findItemValue(msgPath);
|
String key = msg.findItemValue(msgPath);
|
||||||
if (StringUtils.isBlank(key)) return defaultVal;
|
if (StringUtils.isBlank(key)) return defaultVal;
|
||||||
if (callProp == null) return defaultVal;
|
if (callProp == null) return defaultVal;
|
||||||
@@ -409,6 +414,9 @@ public class TemplateAdapterErrorMsgHandler implements AdapterErrorMessageHandle
|
|||||||
String templateKey = inboudnAdapterGroupName + ".sys.template";
|
String templateKey = inboudnAdapterGroupName + ".sys.template";
|
||||||
String template = PropManager.getInstance().getProperty(PROP_GROUP, templateKey);
|
String template = PropManager.getInstance().getProperty(PROP_GROUP, templateKey);
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(template))
|
||||||
|
template = PropManager.getInstance().getProperty(PROP_GROUP, "default.sys.template");
|
||||||
|
|
||||||
if (StringUtils.isBlank(template)) {
|
if (StringUtils.isBlank(template)) {
|
||||||
if (logger.isWarn()) {
|
if (logger.isWarn()) {
|
||||||
logger.warn("TemplateAdapterErrorMsgHandler] template not found. group=" + PROP_GROUP
|
logger.warn("TemplateAdapterErrorMsgHandler] template not found. group=" + PROP_GROUP
|
||||||
@@ -456,6 +464,10 @@ public class TemplateAdapterErrorMsgHandler implements AdapterErrorMessageHandle
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
String templateKey = outboundadapterGroupName + ".template";
|
String templateKey = outboundadapterGroupName + ".template";
|
||||||
String template = PropManager.getInstance().getProperty(PROP_GROUP, templateKey);
|
String template = PropManager.getInstance().getProperty(PROP_GROUP, templateKey);
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(template))
|
||||||
|
template = PropManager.getInstance().getProperty(PROP_GROUP, "default.template");
|
||||||
|
|
||||||
if (StringUtils.isBlank(template)) {
|
if (StringUtils.isBlank(template)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -466,15 +478,27 @@ public class TemplateAdapterErrorMsgHandler implements AdapterErrorMessageHandle
|
|||||||
String adptMsgType, String encode, Throwable e1, int httpCode) {
|
String adptMsgType, String encode, Throwable e1, int httpCode) {
|
||||||
String templateKey = adapterGroupName + ".in." + httpCode + ".template";
|
String templateKey = adapterGroupName + ".in." + httpCode + ".template";
|
||||||
String template = PropManager.getInstance().getProperty(PROP_GROUP, templateKey);
|
String template = PropManager.getInstance().getProperty(PROP_GROUP, templateKey);
|
||||||
|
if (StringUtils.isBlank(template)) {
|
||||||
|
templateKey = "default.in." + httpCode + ".template";
|
||||||
|
template = PropManager.getInstance().getProperty(PROP_GROUP, templateKey);
|
||||||
|
}
|
||||||
|
|
||||||
if (StringUtils.isBlank(template)) {
|
if (StringUtils.isBlank(template)) {
|
||||||
templateKey = adapterGroupName + ".in.template";
|
templateKey = adapterGroupName + ".in.template";
|
||||||
template = PropManager.getInstance().getProperty(PROP_GROUP, templateKey);
|
template = PropManager.getInstance().getProperty(PROP_GROUP, templateKey);
|
||||||
if (StringUtils.isBlank(template)) {
|
|
||||||
String errorResponseFormat = httpProp.getProperty("ERROR_RESPONSE_FORMAT");
|
|
||||||
return MessageUtil.makeErrorMessageByMessageType(adptMsgType, encode,
|
|
||||||
MessageUtil.ERROR_CODE_AP_ERROR, e1.getMessage(), errorResponseFormat);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(template)) {
|
||||||
|
templateKey = "default.in.template";
|
||||||
|
template = PropManager.getInstance().getProperty(PROP_GROUP, templateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(template)) {
|
||||||
|
String errorResponseFormat = httpProp.getProperty("ERROR_RESPONSE_FORMAT");
|
||||||
|
return MessageUtil.makeErrorMessageByMessageType(adptMsgType, encode,
|
||||||
|
MessageUtil.ERROR_CODE_AP_ERROR, e1.getMessage(), errorResponseFormat);
|
||||||
|
}
|
||||||
|
|
||||||
return render(template, null, callProp, e1);
|
return render(template, null, callProp, e1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+424
@@ -1383,4 +1383,428 @@ class TemplateAdapterErrorMsgHandlerTest {
|
|||||||
assertNotNull(result);
|
assertNotNull(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// default.* 템플릿 폴백
|
||||||
|
//
|
||||||
|
// 그룹 전용 키가 미설정/공백이면 "default." 로 시작하는 공통 키를 재조회한다.
|
||||||
|
// generateNonStandardErrorResponseMessage : {group}.template → default.template
|
||||||
|
// generateOutboundErrorResponseMessage : {group}.template → default.template
|
||||||
|
// generateNonStandardInternalErrorResponseMessage : {group}.sys.template → default.sys.template
|
||||||
|
// generateNonStandardInboundErrorResponseMessage : {group}.in.{code}.template
|
||||||
|
// → default.in.{code}.template
|
||||||
|
// → {group}.in.template
|
||||||
|
// → default.in.template
|
||||||
|
// → MessageUtil 폴백
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("default.* 템플릿 폴백")
|
||||||
|
class DefaultTemplateFallback {
|
||||||
|
|
||||||
|
private static final String G = TemplateAdapterErrorMsgHandler.PROP_GROUP;
|
||||||
|
|
||||||
|
private PropManager mockPropManager;
|
||||||
|
private AdapterManager mockAdapterManager;
|
||||||
|
private AdapterPropManager mockAdapterPropManager;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUpDefaults() throws Exception {
|
||||||
|
mockPropManager = Mockito.mock(PropManager.class);
|
||||||
|
mockAdapterManager = Mockito.mock(AdapterManager.class);
|
||||||
|
mockAdapterPropManager = Mockito.mock(AdapterPropManager.class);
|
||||||
|
|
||||||
|
// 명시적으로 stub 하지 않은 모든 키는 미설정(null) 로 간주
|
||||||
|
Mockito.when(mockPropManager.getProperty(anyString(), anyString())).thenReturn(null);
|
||||||
|
Mockito.when(mockPropManager.getProperty(anyString(), anyString(), anyString()))
|
||||||
|
.thenAnswer(invocation -> invocation.getArgument(2));
|
||||||
|
|
||||||
|
AdapterGroupVO adapterGroupVO = Mockito.mock(AdapterGroupVO.class);
|
||||||
|
AdapterVO adapterVO = Mockito.mock(AdapterVO.class);
|
||||||
|
Mockito.when(adapterGroupVO.getMessageType()).thenReturn("JSON");
|
||||||
|
Mockito.when(adapterGroupVO.getMessageEncode()).thenReturn("UTF-8");
|
||||||
|
Mockito.when(adapterVO.getPropGroupName()).thenReturn("TEST_PROP");
|
||||||
|
Mockito.when(adapterVO.getAdapterGroupVO()).thenReturn(adapterGroupVO);
|
||||||
|
Mockito.when(mockAdapterManager.getAdapterGroupVO("MY_GROUP")).thenReturn(adapterGroupVO);
|
||||||
|
Mockito.when(mockAdapterManager.getAdapterVO("MY_GROUP", "MY_ADAPTER")).thenReturn(adapterVO);
|
||||||
|
|
||||||
|
Properties httpProp = new Properties();
|
||||||
|
httpProp.setProperty("ERROR_RESPONSE_FORMAT", "");
|
||||||
|
Mockito.when(mockAdapterPropManager.getProperties("TEST_PROP")).thenReturn(httpProp);
|
||||||
|
|
||||||
|
ApplicationContext mockCtx = Mockito.mock(ApplicationContext.class);
|
||||||
|
Mockito.when(mockCtx.getBean(PropManager.class)).thenReturn(mockPropManager);
|
||||||
|
Mockito.when(mockCtx.getBean(AdapterManager.class)).thenReturn(mockAdapterManager);
|
||||||
|
Mockito.when(mockCtx.getBean(AdapterPropManager.class)).thenReturn(mockAdapterPropManager);
|
||||||
|
Field ctxField = ApplicationContextProvider.class.getDeclaredField("context");
|
||||||
|
ctxField.setAccessible(true);
|
||||||
|
ctxField.set(null, mockCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prop(String key, String value) {
|
||||||
|
Mockito.when(mockPropManager.getProperty(eq(G), eq(key))).thenReturn(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// generateNonStandardErrorResponseMessage : default.template
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("generateNonStandardErrorResponseMessage – default.template")
|
||||||
|
class NonStandardError {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("그룹 템플릿이 있으면 default.template 을 조회하지 않는다")
|
||||||
|
void 그룹_템플릿_우선() throws Exception {
|
||||||
|
prop("MY_GROUP.template", "{\"code\":\"${MSG.MAIN_MSG.outp_msg_cd}\"}");
|
||||||
|
|
||||||
|
Object result = handler.generateNonStandardErrorResponseMessage(
|
||||||
|
"MY_GROUP", "MY_ADAPTER", null, null,
|
||||||
|
toEaiMessage(buildMsg("E001", "오류", "", null)));
|
||||||
|
|
||||||
|
assertEquals("{\"code\":\"E001\"}", result);
|
||||||
|
Mockito.verify(mockPropManager, Mockito.never()).getProperty(G, "default.template");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("그룹 템플릿 미설정 시 default.template 으로 폴백")
|
||||||
|
void default_폴백() throws Exception {
|
||||||
|
prop("default.template", "{\"code\":\"${MSG.MAIN_MSG.outp_msg_cd}\",\"src\":\"default\"}");
|
||||||
|
|
||||||
|
Object result = handler.generateNonStandardErrorResponseMessage(
|
||||||
|
"MY_GROUP", "MY_ADAPTER", null, null,
|
||||||
|
toEaiMessage(buildMsg("E777", "오류", "", null)));
|
||||||
|
|
||||||
|
assertEquals("{\"code\":\"E777\",\"src\":\"default\"}", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("그룹 템플릿이 공백 문자열이어도 default.template 으로 폴백")
|
||||||
|
void 공백_템플릿도_폴백() throws Exception {
|
||||||
|
prop("MY_GROUP.template", " ");
|
||||||
|
prop("default.template", "{\"src\":\"default\"}");
|
||||||
|
|
||||||
|
Object result = handler.generateNonStandardErrorResponseMessage(
|
||||||
|
"MY_GROUP", "MY_ADAPTER", null, null,
|
||||||
|
toEaiMessage(buildMsg("E001", "오류", "", null)));
|
||||||
|
|
||||||
|
assertEquals("{\"src\":\"default\"}", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("그룹/default 둘 다 없으면 기존대로 null 반환")
|
||||||
|
void 둘다_없으면_null() throws Exception {
|
||||||
|
Object result = handler.generateNonStandardErrorResponseMessage(
|
||||||
|
"MY_GROUP", "MY_ADAPTER", null, null,
|
||||||
|
toEaiMessage(buildMsg("E001", "오류", "", null)));
|
||||||
|
|
||||||
|
assertNull(result);
|
||||||
|
// 폴백 조회가 실제로 시도되었는지 확인
|
||||||
|
Mockito.verify(mockPropManager).getProperty(G, "MY_GROUP.template");
|
||||||
|
Mockito.verify(mockPropManager).getProperty(G, "default.template");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("조회 순서: {group}.template → default.template")
|
||||||
|
void 조회_순서() throws Exception {
|
||||||
|
handler.generateNonStandardErrorResponseMessage(
|
||||||
|
"MY_GROUP", "MY_ADAPTER", null, null,
|
||||||
|
toEaiMessage(buildMsg("E001", "오류", "", null)));
|
||||||
|
|
||||||
|
InOrder inOrder = Mockito.inOrder(mockPropManager);
|
||||||
|
inOrder.verify(mockPropManager).getProperty(G, "MY_GROUP.template");
|
||||||
|
inOrder.verify(mockPropManager).getProperty(G, "default.template");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("default.template 에서도 callProp/foreach 치환이 동일하게 동작")
|
||||||
|
void default_템플릿_전체_치환() throws Exception {
|
||||||
|
prop("default.template",
|
||||||
|
"{\"adapter\":\"${callprop.ADAPTER_NAME}\"," +
|
||||||
|
"\"code\":\"${MSG.MAIN_MSG.outp_msg_cd}\"," +
|
||||||
|
"\"errors\":[{{#foreach MSG.MSG_LIST}}{\"c\":\"${outp_msg_cd}\"}{{/foreach}}]}");
|
||||||
|
|
||||||
|
Object result = handler.generateNonStandardErrorResponseMessage(
|
||||||
|
"MY_GROUP", "MY_ADAPTER", props("ADAPTER_NAME", "REAL"), null,
|
||||||
|
toEaiMessage(buildMsg("E001", "대표오류", "",
|
||||||
|
new String[][]{{"E001", "오류A", "", ""}, {"E002", "오류B", "", ""}})));
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
"{\"adapter\":\"REAL\",\"code\":\"E001\"," +
|
||||||
|
"\"errors\":[{\"c\":\"E001\"},{\"c\":\"E002\"}]}",
|
||||||
|
result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// generateNonStandardInternalErrorResponseMessage : default.sys.template
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("generateNonStandardInternalErrorResponseMessage – default.sys.template")
|
||||||
|
class InternalError {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("그룹 sys 템플릿이 있으면 default.sys.template 을 조회하지 않는다")
|
||||||
|
void 그룹_템플릿_우선() throws Exception {
|
||||||
|
prop("MY_GROUP.sys.template", "{\"code\":\"${MSG.MAIN_MSG.outp_msg_cd}\"}");
|
||||||
|
|
||||||
|
Object result = handler.generateNonStandardInternalErrorResponseMessage(
|
||||||
|
"MY_GROUP", "MY_ADAPTER", null, null,
|
||||||
|
toEaiMessage(buildMsg("E500", "내부오류", "", null)));
|
||||||
|
|
||||||
|
assertEquals("{\"code\":\"E500\"}", result);
|
||||||
|
Mockito.verify(mockPropManager, Mockito.never()).getProperty(G, "default.sys.template");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("그룹 sys 템플릿 미설정 시 default.sys.template 으로 폴백")
|
||||||
|
void default_폴백() throws Exception {
|
||||||
|
prop("default.sys.template",
|
||||||
|
"{\"code\":\"${MSG.MAIN_MSG.outp_msg_cd}\",\"msg\":\"${MSG.MAIN_MSG.outp_msg_ctnt}\"}");
|
||||||
|
|
||||||
|
Object result = handler.generateNonStandardInternalErrorResponseMessage(
|
||||||
|
"MY_GROUP", "MY_ADAPTER", null, null,
|
||||||
|
toEaiMessage(buildMsg("E500", "시스템오류", "", null)));
|
||||||
|
|
||||||
|
assertEquals("{\"code\":\"E500\",\"msg\":\"시스템오류\"}", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("내부오류는 default.template 이 아니라 default.sys.template 을 조회한다")
|
||||||
|
void sys_전용_키_사용() throws Exception {
|
||||||
|
// default.template 만 설정된 상태 → 내부오류에는 적용되지 않아야 한다
|
||||||
|
prop("default.template", "{\"src\":\"non-sys\"}");
|
||||||
|
|
||||||
|
Object result = handler.generateNonStandardInternalErrorResponseMessage(
|
||||||
|
"MY_GROUP", "MY_ADAPTER", null, null,
|
||||||
|
toEaiMessage(buildMsg("E500", "시스템오류", "", null)));
|
||||||
|
|
||||||
|
assertNull(result);
|
||||||
|
Mockito.verify(mockPropManager).getProperty(G, "default.sys.template");
|
||||||
|
Mockito.verify(mockPropManager, Mockito.never()).getProperty(G, "default.template");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("그룹/default 둘 다 없으면 기존대로 null 반환")
|
||||||
|
void 둘다_없으면_null() throws Exception {
|
||||||
|
Object result = handler.generateNonStandardInternalErrorResponseMessage(
|
||||||
|
"MY_GROUP", "MY_ADAPTER", null, null,
|
||||||
|
toEaiMessage(buildMsg("E500", "오류", "", null)));
|
||||||
|
|
||||||
|
assertNull(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("조회 순서: {group}.sys.template → default.sys.template")
|
||||||
|
void 조회_순서() throws Exception {
|
||||||
|
handler.generateNonStandardInternalErrorResponseMessage(
|
||||||
|
"MY_GROUP", "MY_ADAPTER", null, null,
|
||||||
|
toEaiMessage(buildMsg("E500", "오류", "", null)));
|
||||||
|
|
||||||
|
InOrder inOrder = Mockito.inOrder(mockPropManager);
|
||||||
|
inOrder.verify(mockPropManager).getProperty(G, "MY_GROUP.sys.template");
|
||||||
|
inOrder.verify(mockPropManager).getProperty(G, "default.sys.template");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// generateOutboundErrorResponseMessage : default.template
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("generateOutboundErrorResponseMessage – default.template")
|
||||||
|
class OutboundError {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("그룹 템플릿이 있으면 default.template 을 조회하지 않는다")
|
||||||
|
void 그룹_템플릿_우선() throws Exception {
|
||||||
|
prop("OUT_GROUP.template", "{\"code\":\"${MSG.MAIN_MSG.outp_msg_cd}\"}");
|
||||||
|
|
||||||
|
Object result = handler.generateOutboundErrorResponseMessage(
|
||||||
|
"OUT_GROUP", "OUT_ADAPTER", null, null,
|
||||||
|
toEaiMessage(buildMsg("E001", "오류", "", null)));
|
||||||
|
|
||||||
|
assertEquals("{\"code\":\"E001\"}", result);
|
||||||
|
Mockito.verify(mockPropManager, Mockito.never()).getProperty(G, "default.template");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("그룹 템플릿 미설정 시 default.template 으로 폴백")
|
||||||
|
void default_폴백() throws Exception {
|
||||||
|
prop("default.template", "{\"code\":\"${MSG.MAIN_MSG.outp_msg_cd}\",\"src\":\"default\"}");
|
||||||
|
|
||||||
|
Object result = handler.generateOutboundErrorResponseMessage(
|
||||||
|
"OUT_GROUP", "OUT_ADAPTER", null, null,
|
||||||
|
toEaiMessage(buildMsg("E888", "오류", "", null)));
|
||||||
|
|
||||||
|
assertEquals("{\"code\":\"E888\",\"src\":\"default\"}", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("아웃바운드/인바운드가 동일한 default.template 을 공유한다")
|
||||||
|
void inbound_outbound_default_공유() throws Exception {
|
||||||
|
prop("default.template", "{\"shared\":\"${MSG.MAIN_MSG.outp_msg_cd}\"}");
|
||||||
|
|
||||||
|
Object outbound = handler.generateOutboundErrorResponseMessage(
|
||||||
|
"OUT_GROUP", "OUT_ADAPTER", null, null,
|
||||||
|
toEaiMessage(buildMsg("E001", "오류", "", null)));
|
||||||
|
Object inbound = handler.generateNonStandardErrorResponseMessage(
|
||||||
|
"IN_GROUP", "IN_ADAPTER", null, null,
|
||||||
|
toEaiMessage(buildMsg("E001", "오류", "", null)));
|
||||||
|
|
||||||
|
// 두 경로 모두 같은 키를 쓰므로 결과가 동일하다.
|
||||||
|
// 방향별로 다른 기본 응답이 필요하면 그룹 전용 키를 설정해야 한다.
|
||||||
|
assertEquals("{\"shared\":\"E001\"}", outbound);
|
||||||
|
assertEquals(outbound, inbound);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("그룹/default 둘 다 없으면 기존대로 null 반환")
|
||||||
|
void 둘다_없으면_null() throws Exception {
|
||||||
|
Object result = handler.generateOutboundErrorResponseMessage(
|
||||||
|
"OUT_GROUP", "OUT_ADAPTER", null, null,
|
||||||
|
toEaiMessage(buildMsg("E001", "오류", "", null)));
|
||||||
|
|
||||||
|
assertNull(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// generateNonStandardInboundErrorResponseMessage : default.in.*
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("generateNonStandardInboundErrorResponseMessage – default.in.*")
|
||||||
|
class InboundError {
|
||||||
|
|
||||||
|
/** 500 으로 매핑되는 일반 예외로 호출한다. */
|
||||||
|
private Object call500(Properties callProp) throws Exception {
|
||||||
|
return handler.generateNonStandardInboundErrorResponseMessage(
|
||||||
|
"MY_GROUP", "MY_ADAPTER", callProp, null, null,
|
||||||
|
new RuntimeException("처리 중 오류"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("1순위: {group}.in.{code}.template 이 있으면 그것을 사용")
|
||||||
|
void 그룹_코드별_템플릿_우선() throws Exception {
|
||||||
|
prop("MY_GROUP.in.500.template", "{\"src\":\"group-code\"}");
|
||||||
|
prop("default.in.500.template", "{\"src\":\"default-code\"}");
|
||||||
|
prop("MY_GROUP.in.template", "{\"src\":\"group-common\"}");
|
||||||
|
prop("default.in.template", "{\"src\":\"default-common\"}");
|
||||||
|
|
||||||
|
assertEquals("{\"src\":\"group-code\"}", call500(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("2순위: {group}.in.{code} 가 없으면 default.in.{code}.template 사용")
|
||||||
|
void default_코드별_템플릿() throws Exception {
|
||||||
|
prop("default.in.500.template", "{\"src\":\"default-code\"}");
|
||||||
|
prop("MY_GROUP.in.template", "{\"src\":\"group-common\"}");
|
||||||
|
prop("default.in.template", "{\"src\":\"default-common\"}");
|
||||||
|
|
||||||
|
// 주의: 현재 구현은 코드별 default 가 그룹 공통보다 우선한다.
|
||||||
|
assertEquals("{\"src\":\"default-code\"}", call500(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("3순위: 코드별 키가 모두 없으면 {group}.in.template 사용")
|
||||||
|
void 그룹_공통_템플릿() throws Exception {
|
||||||
|
prop("MY_GROUP.in.template", "{\"src\":\"group-common\"}");
|
||||||
|
prop("default.in.template", "{\"src\":\"default-common\"}");
|
||||||
|
|
||||||
|
assertEquals("{\"src\":\"group-common\"}", call500(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("4순위: 앞의 3개가 모두 없으면 default.in.template 사용")
|
||||||
|
void default_공통_템플릿() throws Exception {
|
||||||
|
prop("default.in.template", "{\"src\":\"default-common\"}");
|
||||||
|
|
||||||
|
assertEquals("{\"src\":\"default-common\"}", call500(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("4개 키가 모두 없으면 MessageUtil 폴백")
|
||||||
|
void 전부_없으면_MessageUtil_폴백() throws Exception {
|
||||||
|
Object result = call500(null);
|
||||||
|
|
||||||
|
assertNotNull(result);
|
||||||
|
Mockito.verify(mockPropManager).getProperty(G, "MY_GROUP.in.500.template");
|
||||||
|
Mockito.verify(mockPropManager).getProperty(G, "default.in.500.template");
|
||||||
|
Mockito.verify(mockPropManager).getProperty(G, "MY_GROUP.in.template");
|
||||||
|
Mockito.verify(mockPropManager).getProperty(G, "default.in.template");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("조회 순서: group.code → default.code → group.common → default.common")
|
||||||
|
void 조회_순서_4단계() throws Exception {
|
||||||
|
handler.generateNonStandardInboundErrorResponseMessage(
|
||||||
|
"MY_GROUP", "MY_ADAPTER", null, null, null,
|
||||||
|
new HttpStatusException("Service Unavailable", 503));
|
||||||
|
|
||||||
|
InOrder inOrder = Mockito.inOrder(mockPropManager);
|
||||||
|
inOrder.verify(mockPropManager).getProperty(G, "MY_GROUP.in.503.template");
|
||||||
|
inOrder.verify(mockPropManager).getProperty(G, "default.in.503.template");
|
||||||
|
inOrder.verify(mockPropManager).getProperty(G, "MY_GROUP.in.template");
|
||||||
|
inOrder.verify(mockPropManager).getProperty(G, "default.in.template");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("코드별 default 키 형식은 'default.in.{code}.template' (점 누락 회귀 방지)")
|
||||||
|
void default_코드별_키_형식_검증() throws Exception {
|
||||||
|
handler.generateNonStandardInboundErrorResponseMessage(
|
||||||
|
"MY_GROUP", "MY_ADAPTER", null, null, null,
|
||||||
|
new HttpStatusException("Bad Request", 400));
|
||||||
|
|
||||||
|
Mockito.verify(mockPropManager).getProperty(G, "default.in.400.template");
|
||||||
|
// 점이 빠진 잘못된 키로 조회하지 않아야 한다
|
||||||
|
Mockito.verify(mockPropManager, Mockito.never()).getProperty(G, "default.in400.template");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("JwtAuthException 은 default.in.401.template 로 폴백")
|
||||||
|
void jwt_401_default_폴백() throws Exception {
|
||||||
|
prop("default.in.401.template", "{\"code\":\"${exception.code}\",\"msg\":\"${exception.message}\"}");
|
||||||
|
|
||||||
|
Object result = handler.generateNonStandardInboundErrorResponseMessage(
|
||||||
|
"MY_GROUP", "MY_ADAPTER", null, null, null,
|
||||||
|
new JwtAuthException("JWT_EXPIRED", "토큰이 만료되었습니다"));
|
||||||
|
|
||||||
|
assertEquals("{\"code\":\"JWT_EXPIRED\",\"msg\":\"토큰이 만료되었습니다\"}", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("default.in.template 에서 exception/callProp 치환이 동일하게 동작")
|
||||||
|
void default_공통_템플릿_치환() throws Exception {
|
||||||
|
prop("default.in.template",
|
||||||
|
"{\"adapter\":\"${callprop.ADAPTER_NAME}\",\"msg\":\"${exception.message}\"}");
|
||||||
|
|
||||||
|
Object result = call500(props("ADAPTER_NAME", "REAL"));
|
||||||
|
|
||||||
|
assertEquals("{\"adapter\":\"REAL\",\"msg\":\"처리 중 오류\"}", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("default.in.template 에 ${callprop[경로]} 를 써도 msg=null 로 NPE 없이 기본값 반환")
|
||||||
|
void default_공통_템플릿_간접참조_NPE_회귀방지() throws Exception {
|
||||||
|
// 인바운드 경로는 render(template, null, ...) 로 호출되므로
|
||||||
|
// 간접 참조의 표준전문 경로 조회 대상이 없다 → 기본값으로 처리되어야 한다.
|
||||||
|
prop("default.in.template", "{\"key\":\"${callprop[MSG.cp_key]:NO_MSG}\"}");
|
||||||
|
|
||||||
|
Object result = call500(props("ADAPTER_NAME", "REAL"));
|
||||||
|
|
||||||
|
assertEquals("{\"key\":\"NO_MSG\"}", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("그룹 코드별 템플릿이 공백이면 default 로 폴백")
|
||||||
|
void 공백_템플릿도_폴백() throws Exception {
|
||||||
|
prop("MY_GROUP.in.500.template", " ");
|
||||||
|
prop("default.in.500.template", "{\"src\":\"default-code\"}");
|
||||||
|
|
||||||
|
assertEquals("{\"src\":\"default-code\"}", call500(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user