|
|
|
@@ -40,6 +40,7 @@ import static org.mockito.ArgumentMatchers.anyString;
|
|
|
|
|
* 5. 비표준 수신 → 정상 응답 표준 생성 (coordinateAfterRecvNonStdSyncResponse → JSON)
|
|
|
|
|
* 6. 오류 응답 표준 생성 (coordinateSetStandardMessageError → JSON)
|
|
|
|
|
* 7. JSON 왕복(parse → serialize) 일관성
|
|
|
|
|
* 8. 상대 헤더가 표준을 어긴 응답 파싱 (2026-09 사고 회귀 방지)
|
|
|
|
|
*/
|
|
|
|
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
|
|
|
|
class StandardMessageJsonFlowTest {
|
|
|
|
@@ -80,11 +81,11 @@ class StandardMessageJsonFlowTest {
|
|
|
|
|
+ "\"frst_mesg_dman_dt\":\"20260420\","
|
|
|
|
|
+ "\"frst_mesg_dman_time\":\"120000000\""
|
|
|
|
|
+ "},"
|
|
|
|
|
+ "\"DATA\":{"
|
|
|
|
|
+ "\"DATA\":[{"
|
|
|
|
|
+ "\"data_dvcd\":\"IO\","
|
|
|
|
|
+ "\"data_scop_len\":\"37\","
|
|
|
|
|
+ "\"BIZ_DATA\":{\"acct_no\":\"123456789\",\"amt\":\"100000\"}"
|
|
|
|
|
+ "}"
|
|
|
|
|
+ "}]"
|
|
|
|
|
+ "}";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -121,10 +122,58 @@ class StandardMessageJsonFlowTest {
|
|
|
|
|
+ "\"msg_list_rowcnt\":\"1\""
|
|
|
|
|
+ "}"
|
|
|
|
|
+ "},"
|
|
|
|
|
+ "\"DATA\":{"
|
|
|
|
|
+ "\"DATA\":[{"
|
|
|
|
|
+ "\"data_dvcd\":\"IO\","
|
|
|
|
|
+ "\"data_scop_len\":\"37\","
|
|
|
|
|
+ "\"BIZ_DATA\":{\"acct_no\":\"123456789\",\"bal\":\"900000\"}"
|
|
|
|
|
+ "}]"
|
|
|
|
|
+ "}";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 상대 헤더가 표준을 어긴 오류응답 JSON (2026-09 실제 사고 전문 형태)
|
|
|
|
|
*
|
|
|
|
|
* 표준은 응답 시 dman_rspn_dvcd=R 이어야 하는데, 상대가 요청값 S 를 그대로 에코백하면서
|
|
|
|
|
* procs_rslt_dvcd 만 F(오류)로 보냈다. MSG 블록의 레이아웃 조건이 !S 라 예전에는
|
|
|
|
|
* MSG 이하가 통째로 버려졌고, MAIN_MSG 가 레이아웃 기본값으로 남아 오류가
|
|
|
|
|
* "정상처리되었습니다." 로 보고되고 거래로그에서도 MSG 부가 누락됐다.
|
|
|
|
|
*
|
|
|
|
|
* 같은 전문에 들어있던 다른 비표준 요소도 함께 담아 둔다.
|
|
|
|
|
* - msg_list_rowcnt 가 따옴표 없는 선행 0 숫자(00001)
|
|
|
|
|
* - outp_msg_desc 안에 이스케이프되지 않은 개행/탭
|
|
|
|
|
*/
|
|
|
|
|
private static final String RSP_JSON_BAD_HEADER =
|
|
|
|
|
"{"
|
|
|
|
|
+ "\"HEAD\":{"
|
|
|
|
|
+ "\"nxgn_stnd_idfr\":\"JERA\","
|
|
|
|
|
+ "\"guid\":\"" + TEST_GUID + "\","
|
|
|
|
|
+ "\"guid_prgs_no\":\"2\","
|
|
|
|
|
+ "\"stnd_mesg_ver\":\"R10\","
|
|
|
|
|
+ "\"ortr_guid\":\"" + TEST_GUID + "\","
|
|
|
|
|
+ "\"dman_rspn_dvcd\":\"S\"," // 표준 위반: 응답인데 R 이 아님
|
|
|
|
|
+ "\"if_id\":\"" + TEST_IF_ID + "\","
|
|
|
|
|
+ "\"procs_rslt_dvcd\":\"F\","
|
|
|
|
|
+ "\"tx_id\":\"" + TEST_TX_ID + "\","
|
|
|
|
|
+ "\"chnl_tycd\":\"EAI\","
|
|
|
|
|
+ "\"hmab_dvcd\":\"1\","
|
|
|
|
|
+ "\"trnm_sys_dvcd\":\"EAI\","
|
|
|
|
|
+ "\"sys_env_dvcd\":\"D\","
|
|
|
|
|
+ "\"frst_mesg_dman_dt\":\"20260420\","
|
|
|
|
|
+ "\"frst_mesg_dman_time\":\"120000000\","
|
|
|
|
|
+ "\"mesg_rspn_dt\":\"20260420\","
|
|
|
|
|
+ "\"mesg_rspn_time\":\"120100000\","
|
|
|
|
|
+ "\"rprs_msg_cd\":\"900400\""
|
|
|
|
|
+ "},"
|
|
|
|
|
+ "\"MSG\":{"
|
|
|
|
|
+ "\"msg_dvcd\":\"EM\","
|
|
|
|
|
+ "\"msg_scop_len\":1437,"
|
|
|
|
|
+ "\"MAIN_MSG\":{"
|
|
|
|
|
+ "\"outp_atrb_cd\":\"0\","
|
|
|
|
|
+ "\"outp_msg_cd\":\"900400\","
|
|
|
|
|
+ "\"outp_msg_ctnt\":\"\","
|
|
|
|
|
+ "\"outp_msg_desc\":\"[ErrorCode:S9OWP0011].WTC.Adapter.\n\t호출 오류\","
|
|
|
|
|
+ "\"mngm_msg_cd\":\"\","
|
|
|
|
|
+ "\"msg_list_rowcnt\":00001"
|
|
|
|
|
+ "}"
|
|
|
|
|
+ "}"
|
|
|
|
|
+ "}";
|
|
|
|
|
|
|
|
|
@@ -208,7 +257,11 @@ class StandardMessageJsonFlowTest {
|
|
|
|
|
void 요청JSON_파싱_DATA_BIZ_DATA_JSON_오브젝트_저장() throws Exception {
|
|
|
|
|
StandardMessage msg = parseReq();
|
|
|
|
|
|
|
|
|
|
String bizData = msg.findItemValue("DATA.BIZ_DATA");
|
|
|
|
|
// DATA는 GRID(반복부)라 값은 행(row 0)에 들어간다. 인덱스 없는 "DATA.BIZ_DATA"는
|
|
|
|
|
// 직렬화에 쓰이지 않는 템플릿 항목을 가리키므로 빈 값이 나온다.
|
|
|
|
|
// 운영 경로도 인덱스 형태를 쓴다 — standard-message-mapping-config-djb.properties:27
|
|
|
|
|
// BIZ_DATA=DATA[0].BIZ_DATA
|
|
|
|
|
String bizData = msg.findItemValue("DATA[0].BIZ_DATA");
|
|
|
|
|
assertNotNull(bizData, "BIZ_DATA가 null");
|
|
|
|
|
assertFalse(bizData.trim().isEmpty(), "BIZ_DATA가 공백");
|
|
|
|
|
// JSON 오브젝트로 파싱 가능한지 검증
|
|
|
|
@@ -344,9 +397,11 @@ class StandardMessageJsonFlowTest {
|
|
|
|
|
|
|
|
|
|
String json = msg.getDataString(MessageType.JSON, "utf-8");
|
|
|
|
|
JsonNode root = jacksonMapper.readTree(json);
|
|
|
|
|
// DATA는 GRID(반복부)이므로 직렬화하면 JSON 배열이다. 표준 레이아웃상 1건 고정.
|
|
|
|
|
// BIZ_DATA는 JSON 문자열 또는 JSON 오브젝트로 들어갈 수 있음
|
|
|
|
|
assertNotNull(root.path("DATA").get("BIZ_DATA"),
|
|
|
|
|
"직렬화 JSON에 DATA.BIZ_DATA 없음");
|
|
|
|
|
assertTrue(root.path("DATA").isArray(), "직렬화 JSON의 DATA가 배열이 아님");
|
|
|
|
|
assertNotNull(root.path("DATA").path(0).get("BIZ_DATA"),
|
|
|
|
|
"직렬화 JSON에 DATA[0].BIZ_DATA 없음");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ================================================================
|
|
|
|
@@ -490,6 +545,40 @@ class StandardMessageJsonFlowTest {
|
|
|
|
|
"응답 JSON 왕복 후 msg_dvcd 불일치");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ================================================================
|
|
|
|
|
// 8. 상대 헤더가 표준을 어긴 응답 파싱 (2026-09 사고 회귀 방지)
|
|
|
|
|
// ================================================================
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void 상대헤더오류_dman_rspn_dvcd가_S여도_MSG부가_파싱됨() throws Exception {
|
|
|
|
|
StandardMessage msg = parseRspBadHeader();
|
|
|
|
|
|
|
|
|
|
assertEquals("F", msg.findItemValue("HEAD.procs_rslt_dvcd"),
|
|
|
|
|
"HEAD가 파싱되지 않음 — 전제 조건 실패");
|
|
|
|
|
assertEquals("EM", msg.findItemValue("MSG.msg_dvcd"),
|
|
|
|
|
"MSG.msg_dvcd 미반영 — MSG 블록이 버려졌다");
|
|
|
|
|
assertEquals("900400", msg.findItemValue("MSG.MAIN_MSG.outp_msg_cd"),
|
|
|
|
|
"outp_msg_cd가 레이아웃 기본값(NCMM00001) 그대로 — MSG부가 버려졌다");
|
|
|
|
|
assertNotEquals("정상처리되었습니다.", msg.findItemValue("MSG.MAIN_MSG.outp_msg_ctnt"),
|
|
|
|
|
"오류응답인데 레이아웃 기본값 '정상처리되었습니다.'가 그대로 남음");
|
|
|
|
|
assertNotNull(msg.findItemValue("MSG.MAIN_MSG.outp_msg_desc"),
|
|
|
|
|
"outp_msg_desc가 null — 개행/탭이 섞인 값이 파싱되지 않았다");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void 상대헤더오류_재직렬화시_MSG부_보존() throws Exception {
|
|
|
|
|
StandardMessage msg = parseRspBadHeader();
|
|
|
|
|
|
|
|
|
|
// 거래로그(EAILogDAO)는 수신 원문이 아니라 StandardMessage 재직렬화본을 저장한다.
|
|
|
|
|
// MSG가 hidden 이면 로그에서도 MSG부 이후가 통째로 누락된다.
|
|
|
|
|
String json = msg.getDataString(MessageType.JSON, "utf-8");
|
|
|
|
|
JsonNode root = jacksonMapper.readTree(json);
|
|
|
|
|
|
|
|
|
|
assertTrue(root.has("MSG"), "재직렬화 결과에 MSG 블록 없음 — 거래로그에 MSG부 누락");
|
|
|
|
|
assertEquals("900400", root.path("MSG").path("MAIN_MSG").path("outp_msg_cd").asText(),
|
|
|
|
|
"재직렬화 결과의 outp_msg_cd 불일치");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ================================================================
|
|
|
|
|
// helper
|
|
|
|
|
// ================================================================
|
|
|
|
@@ -505,4 +594,10 @@ class StandardMessageJsonFlowTest {
|
|
|
|
|
jsonReader.parse(msg, RSP_JSON_OK);
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private StandardMessage parseRspBadHeader() throws Exception {
|
|
|
|
|
StandardMessage msg = manager.getStandardMessage();
|
|
|
|
|
jsonReader.parse(msg, RSP_JSON_BAD_HEADER);
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|