feat: 표준전문 스펙 필수 처리 항목 구현 및 테스트 추가

요청 처리:
- ERP 채널(corp_tlwn_virt_brcd=ERP) HEAD 지점코드 자동 설정 (tx_cmgr_cd, mgmt_brcd 등)
- ERP 채널 EZDATA 전자금융공통영역 자동 설정 (elfn_bnkn_dvcd=10, svr_tx_seqno, elfn_mac, IP)

응답 처리:
- coordinateBeforeResponse: guid_prgs_no +1, dman_rspn_dvcd="R" 설정 추가
- coordinateAfterRecvNonStdSyncResponse: 정상 MSG msg_list_rowcnt=0으로 수정, 정상처리 코드/메시지 업데이트

오류 처리:
- coordinateSetStandardMessageError: MSG_LIST에 outp_msg_desc, err_occu_loct 추가

테스트:
- StandardMessageSpecTest: 요청/응답 스펙 전체 62개 항목 검증
- StandardMessageEzdataJsonFlowTest: EZDATA JSON 흐름 22개 검증

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curry772
2026-04-21 10:45:08 +09:00
parent 557a071b1e
commit 17d533fe06
5 changed files with 1243 additions and 45 deletions
@@ -0,0 +1,422 @@
package com.eactive.eai.custom.message;
import static org.junit.jupiter.api.Assertions.*;
import java.lang.reflect.Field;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.context.ApplicationContext;
import com.eactive.eai.common.monitor.EAIServiceMonitor;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.util.ApplicationContextProvider;
import com.eactive.eai.message.StandardMessage;
import com.eactive.eai.message.manager.StandardMessageManager;
import com.eactive.eai.message.parser.JsonReader;
import com.eactive.eai.message.service.InterfaceMapper;
import com.eactive.eai.common.message.MessageType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import static org.mockito.ArgumentMatchers.anyString;
/**
* DJBank 표준전문 EZDATA(전자금융공통영역) JSON 처리 흐름 통합 테스트
*
* 검증 시나리오:
* 1. EZDATA 포함 요청 JSON 파싱 — corp_tlwn_virt_brcd=ERP 조건 활성
* 2. EZDATA 핵심 필드 값 검증
* 3. EZDATA 조건 미충족(비ERP) 시 JSON 직렬화 제외 검증
* 4. EZDATA 포함 JSON 왕복(parse → serialize) 일관성
* 5. coordinateAfterCreateMessageByRule 후 EZDATA 보존
* 6. coordinateBeforeResponse 후 EZDATA 포함 직렬화
* 7. 오류 응답 생성 시 EZDATA 보존
*/
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class StandardMessageEzdataJsonFlowTest {
private static final String TEST_GUID =
"20260420" + "120000" + "000" + "EAI" + "01" + "123456789012" + "000001";
private static final String TEST_IF_ID = "IF_DJBTEST_EZ001";
private static final String TEST_TX_ID = "TXTEST000002";
/**
* ERP 채널 요청 JSON — corp_tlwn_virt_brcd=ERP → EZDATA 블록 활성화
*/
private static final String ERP_REQ_JSON =
"{"
+ "\"HEAD\":{"
+ "\"nxgn_stnd_idfr\":\"JERA\","
+ "\"guid\":\"" + TEST_GUID + "\","
+ "\"guid_prgs_no\":\"1\","
+ "\"stnd_mesg_ver\":\"R10\","
+ "\"ortr_guid\":\"" + TEST_GUID + "\","
+ "\"dman_rspn_dvcd\":\"S\","
+ "\"if_id\":\"" + TEST_IF_ID + "\","
+ "\"procs_rslt_dvcd\":\"S\","
+ "\"tx_id\":\"" + TEST_TX_ID + "\","
+ "\"chnl_tycd\":\"EAI\","
+ "\"hmab_dvcd\":\"1\","
+ "\"corp_tlwn_virt_brcd\":\"ERP\","
+ "\"trnm_sys_dvcd\":\"OPA\","
+ "\"frst_trnm_sys_dvcd\":\"OPA\","
+ "\"sys_env_dvcd\":\"D\","
+ "\"frst_mesg_dman_dt\":\"20260420\","
+ "\"frst_mesg_dman_time\":\"120000000\""
+ "},"
+ "\"EZDATA\":{"
+ "\"login_mhod_dvcd\":\"1\","
+ "\"elfn_bnkn_dvcd\":\"01\","
+ "\"usr_no\":\"USR0000000000001\","
+ "\"rgsr_id\":\"REG_ID_000000001\","
+ "\"secu_media_dvcd\":\"00\","
+ "\"secu_media_sta_dvcd\":\"00\","
+ "\"brll_srcd_yn\":\"N\","
+ "\"next_data_exis_yn\":\"N\","
+ "\"page_no\":\"00001\","
+ "\"page_size\":\"00020\","
+ "\"dup_tx_yn\":\"N\""
+ "},"
+ "\"DATA\":{"
+ "\"data_dvcd\":\"IO\","
+ "\"data_scop_len\":\"37\","
+ "\"BIZ_DATA\":{\"acct_no\":\"987654321\",\"amt\":\"500000\"}"
+ "}"
+ "}";
/**
* 비ERP 요청 JSON — corp_tlwn_virt_brcd 미설정 → EZDATA 블록 비활성
*/
private static final String NON_ERP_REQ_JSON =
"{"
+ "\"HEAD\":{"
+ "\"nxgn_stnd_idfr\":\"JERA\","
+ "\"guid\":\"" + TEST_GUID + "\","
+ "\"guid_prgs_no\":\"1\","
+ "\"stnd_mesg_ver\":\"R10\","
+ "\"ortr_guid\":\"" + TEST_GUID + "\","
+ "\"dman_rspn_dvcd\":\"S\","
+ "\"if_id\":\"" + TEST_IF_ID + "\","
+ "\"procs_rslt_dvcd\":\"S\","
+ "\"tx_id\":\"" + TEST_TX_ID + "\","
+ "\"chnl_tycd\":\"EAI\","
+ "\"hmab_dvcd\":\"1\","
+ "\"trnm_sys_dvcd\":\"OPA\","
+ "\"frst_trnm_sys_dvcd\":\"OPA\","
+ "\"sys_env_dvcd\":\"D\","
+ "\"frst_mesg_dman_dt\":\"20260420\","
+ "\"frst_mesg_dman_time\":\"120000000\""
+ "},"
+ "\"DATA\":{"
+ "\"data_dvcd\":\"IO\","
+ "\"data_scop_len\":\"20\","
+ "\"BIZ_DATA\":{\"acct_no\":\"111222333\"}"
+ "}"
+ "}";
private StandardMessageManager manager;
private InterfaceMapper mapper;
private JsonReader jsonReader;
private StandardMessageCoordinatorDJB coordinator;
private ObjectMapper jacksonMapper;
@BeforeAll
void setUpAll() throws Exception {
PropManager mockPropManager = Mockito.mock(PropManager.class);
Mockito.when(mockPropManager.getProperty(anyString(), anyString(), anyString()))
.thenAnswer(new Answer<String>() {
public String answer(InvocationOnMock inv) { return (String) inv.getArguments()[2]; }
});
EAIServerManager mockServer = Mockito.mock(EAIServerManager.class);
Mockito.when(mockServer.isPEAIServer()).thenReturn(false);
Mockito.when(mockServer.isSEAIServer()).thenReturn(false);
Mockito.when(mockServer.isMCI()).thenReturn(false);
Mockito.when(mockServer.getLocalServerName()).thenReturn("TEST_SERVER");
Mockito.when(mockServer.getGroupInstId()).thenReturn("TEST_INST");
EAIServiceMonitor mockMonitor = Mockito.mock(EAIServiceMonitor.class);
Mockito.when(mockMonitor.isTimeOutCodes(anyString())).thenReturn(false);
Mockito.when(mockMonitor.isBizErrorCodes(anyString())).thenReturn(false);
ApplicationContext mockCtx = Mockito.mock(ApplicationContext.class);
Mockito.when(mockCtx.getBean(EAIServerManager.class)).thenReturn(mockServer);
Mockito.when(mockCtx.getBean(PropManager.class)).thenReturn(mockPropManager);
Mockito.when(mockCtx.getBean(EAIServiceMonitor.class)).thenReturn(mockMonitor);
Field ctxField = ApplicationContextProvider.class.getDeclaredField("context");
ctxField.setAccessible(true);
ctxField.set(null, mockCtx);
System.setProperty(StandardMessageManager.STANDARD_MESSAGE_CONFIG,
"src/test/resources/standard-message-config-test.properties");
manager = StandardMessageManager.getInstance();
manager.init();
}
@BeforeEach
void setUp() {
mapper = manager.getMapper();
jsonReader = (JsonReader) manager.getReader("JSON");
coordinator = new StandardMessageCoordinatorDJB();
jacksonMapper = new ObjectMapper();
}
// ================================================================
// 1. EZDATA 포함 요청 JSON 파싱
// ================================================================
@Test
void ERP요청_파싱_EZDATA_블록_활성() throws Exception {
StandardMessage msg = parseErpReq();
assertNotNull(msg.findItem("EZDATA"), "EZDATA 블록이 null");
assertFalse(msg.findItem("EZDATA").isHidden(), "EZDATA 블록이 hidden 상태");
}
@Test
void ERP요청_파싱_HEAD_corp_tlwn_virt_brcd_ERP() throws Exception {
StandardMessage msg = parseErpReq();
assertEquals("ERP", msg.findItemValue("HEAD.corp_tlwn_virt_brcd"),
"corp_tlwn_virt_brcd가 ERP가 아님");
}
// ================================================================
// 2. EZDATA 핵심 필드 값 검증
// ================================================================
@Test
void EZDATA_login_mhod_dvcd_파싱() throws Exception {
StandardMessage msg = parseErpReq();
assertEquals("1", msg.findItemValue("EZDATA.login_mhod_dvcd"),
"EZDATA.login_mhod_dvcd 불일치");
}
@Test
void EZDATA_elfn_bnkn_dvcd_파싱() throws Exception {
StandardMessage msg = parseErpReq();
assertEquals("01", msg.findItemValue("EZDATA.elfn_bnkn_dvcd"),
"EZDATA.elfn_bnkn_dvcd 불일치");
}
@Test
void EZDATA_usr_no_파싱() throws Exception {
StandardMessage msg = parseErpReq();
assertEquals("USR0000000000001", msg.findItemValue("EZDATA.usr_no").trim(),
"EZDATA.usr_no 불일치");
}
@Test
void EZDATA_rgsr_id_파싱() throws Exception {
StandardMessage msg = parseErpReq();
assertEquals("REG_ID_000000001", msg.findItemValue("EZDATA.rgsr_id").trim(),
"EZDATA.rgsr_id 불일치");
}
@Test
void EZDATA_secu_media_dvcd_파싱() throws Exception {
StandardMessage msg = parseErpReq();
assertEquals("00", msg.findItemValue("EZDATA.secu_media_dvcd"),
"EZDATA.secu_media_dvcd 불일치");
}
@Test
void EZDATA_dup_tx_yn_파싱() throws Exception {
StandardMessage msg = parseErpReq();
assertEquals("N", msg.findItemValue("EZDATA.dup_tx_yn"),
"EZDATA.dup_tx_yn 불일치");
}
// ================================================================
// 3. EZDATA 조건 미충족(비ERP) — 입력값 미설정 검증
// EZDATA 블록은 기본값으로 직렬화되므로, 입력 JSON에 없는 ERP 전용
// 필드(usr_no, rgsr_id 등)는 파싱 후 빈 값(기본값)이어야 함
// ================================================================
@Test
void 비ERP요청_파싱_EZDATA_usr_no_미설정() throws Exception {
StandardMessage msg = parseNonErpReq();
String usrNo = msg.findItemValue("EZDATA.usr_no");
assertTrue(usrNo == null || usrNo.trim().isEmpty(),
"비ERP 요청에서 EZDATA.usr_no는 빈 값이어야 함: [" + usrNo + "]");
}
@Test
void 비ERP요청_파싱_EZDATA_rgsr_id_미설정() throws Exception {
StandardMessage msg = parseNonErpReq();
String rgsrId = msg.findItemValue("EZDATA.rgsr_id");
assertTrue(rgsrId == null || rgsrId.trim().isEmpty(),
"비ERP 요청에서 EZDATA.rgsr_id는 빈 값이어야 함: [" + rgsrId + "]");
}
@Test
void 비ERP요청_파싱_BIZ_DATA_정상파싱() throws Exception {
StandardMessage msg = parseNonErpReq();
String bizData = msg.findItemValue("DATA.BIZ_DATA");
assertNotNull(bizData, "비ERP 요청의 BIZ_DATA가 null");
JsonNode node = jacksonMapper.readTree(bizData);
assertEquals("111222333", node.path("acct_no").asText());
}
// ================================================================
// 4. EZDATA 포함 JSON 왕복(parse → serialize) 일관성
// ================================================================
@Test
void JSON_왕복_EZDATA_포함_직렬화() throws Exception {
StandardMessage msg = parseErpReq();
String json = msg.getDataString(MessageType.JSON, "utf-8");
JsonNode root = jacksonMapper.readTree(json);
assertTrue(root.has("EZDATA"), "JSON 왕복 후 EZDATA 블록 소실");
}
@Test
void JSON_왕복_EZDATA_usr_no_보존() throws Exception {
StandardMessage msg = parseErpReq();
String json = msg.getDataString(MessageType.JSON, "utf-8");
JsonNode root = jacksonMapper.readTree(json);
assertEquals("USR0000000000001",
root.path("EZDATA").path("usr_no").asText().trim(),
"JSON 왕복 후 EZDATA.usr_no 불일치");
}
@Test
void JSON_왕복_HEAD_guid_보존() throws Exception {
StandardMessage msg = parseErpReq();
String json = msg.getDataString(MessageType.JSON, "utf-8");
JsonNode root = jacksonMapper.readTree(json);
assertEquals(TEST_GUID, root.path("HEAD").path("guid").asText(),
"JSON 왕복 후 guid 불일치");
}
@Test
void JSON_왕복_BIZ_DATA_보존() throws Exception {
StandardMessage msg = parseErpReq();
String json = msg.getDataString(MessageType.JSON, "utf-8");
JsonNode root = jacksonMapper.readTree(json);
JsonNode bizData = root.path("DATA").get("BIZ_DATA");
assertNotNull(bizData, "JSON 왕복 후 BIZ_DATA 소실");
assertEquals("987654321", bizData.path("acct_no").asText(),
"JSON 왕복 후 BIZ_DATA.acct_no 불일치");
}
// ================================================================
// 5. coordinateAfterCreateMessageByRule 후 EZDATA 보존
// ================================================================
@Test
void 표준수신_coordinateAfterCreateMessageByRule_EZDATA_보존() throws Exception {
StandardMessage msg = parseErpReq();
coordinator.coordinateAfterCreateMessageByRule(msg, null, null);
String json = msg.getDataString(MessageType.JSON, "utf-8");
JsonNode root = jacksonMapper.readTree(json);
assertTrue(root.has("EZDATA"), "coordinateAfterCreateMessageByRule 후 EZDATA 소실");
}
@Test
void 표준수신_coordinateAfterCreateMessageByRule_EZDATA_usr_no_보존() throws Exception {
StandardMessage msg = parseErpReq();
coordinator.coordinateAfterCreateMessageByRule(msg, null, null);
assertEquals("USR0000000000001", msg.findItemValue("EZDATA.usr_no").trim(),
"coordinateAfterCreateMessageByRule 후 EZDATA.usr_no 변경됨");
}
// ================================================================
// 6. coordinateBeforeResponse 후 EZDATA 포함 직렬화
// ================================================================
@Test
void 표준응답생성_coordinateBeforeResponse_EZDATA_포함() throws Exception {
StandardMessage msg = parseErpReq();
coordinator.coordinateBeforeResponse(msg, null, null, "utf-8");
String json = msg.getDataString(MessageType.JSON, "utf-8");
JsonNode root = jacksonMapper.readTree(json);
assertTrue(root.has("EZDATA"), "coordinateBeforeResponse 후 EZDATA 소실");
}
@Test
void 표준응답생성_coordinateBeforeResponse_HEAD_mesg_rspn_dt_설정() throws Exception {
StandardMessage msg = parseErpReq();
coordinator.coordinateBeforeResponse(msg, null, null, "utf-8");
String dt = msg.findItemValue("HEAD.mesg_rspn_dt");
assertNotNull(dt, "mesg_rspn_dt가 null");
assertTrue(dt.matches("\\d{8}"), "mesg_rspn_dt 형식 오류: " + dt);
}
// ================================================================
// 7. 오류 응답 생성 시 EZDATA 보존
// ================================================================
@Test
void 오류응답_EZDATA_포함_JSON_직렬화() throws Exception {
StandardMessage msg = parseErpReq();
coordinator.coordinateSetStandardMessageError(msg, mapper, "RECEAICMM999", "테스트 오류");
String json = msg.getDataString(MessageType.JSON, "utf-8");
JsonNode root = jacksonMapper.readTree(json);
assertTrue(root.has("EZDATA"), "오류응답 JSON에 EZDATA 블록 소실");
}
@Test
void 오류응답_EZDATA_procs_rslt_dvcd_F() throws Exception {
StandardMessage msg = parseErpReq();
coordinator.coordinateSetStandardMessageError(msg, mapper, "RECEAICMM999", "테스트 오류");
assertEquals("F", msg.findItemValue("HEAD.procs_rslt_dvcd"),
"오류 응답의 procs_rslt_dvcd는 F여야 함");
}
@Test
void 오류응답_EZDATA_MSG_블록_포함() throws Exception {
StandardMessage msg = parseErpReq();
coordinator.coordinateSetStandardMessageError(msg, mapper, "RECEAICMM999", "테스트 오류");
String json = msg.getDataString(MessageType.JSON, "utf-8");
JsonNode root = jacksonMapper.readTree(json);
assertTrue(root.has("MSG"), "오류응답 JSON에 MSG 블록 없음");
assertEquals("EM", root.path("MSG").path("msg_dvcd").asText(),
"오류응답 msg_dvcd가 EM이 아님");
}
// ================================================================
// helper
// ================================================================
private StandardMessage parseErpReq() throws Exception {
StandardMessage msg = manager.getStandardMessage();
jsonReader.parse(msg, ERP_REQ_JSON);
return msg;
}
private StandardMessage parseNonErpReq() throws Exception {
StandardMessage msg = manager.getStandardMessage();
jsonReader.parse(msg, NON_ERP_REQ_JSON);
return msg;
}
}