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.StandardItem; 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() { 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 블록 직렬화 제외 검증 // size=0 + refPath/refValue 조건 미충족 → isHidden=true → JSON 제외 // ================================================================ @Test void 비ERP요청_파싱_EZDATA_size_0() throws Exception { StandardMessage msg = parseNonErpReq(); StandardItem ezdataItem = msg.findItem("EZDATA"); assertTrue(ezdataItem == null || ezdataItem.getSize() == 0, "비ERP 요청에서 EZDATA 블록 size는 0이어야 함"); } @Test void 비ERP요청_파싱_JSON_직렬화_EZDATA_제외() throws Exception { StandardMessage msg = parseNonErpReq(); String json = msg.toJson(false, false); JsonNode root = jacksonMapper.readTree(json); assertFalse(root.has("EZDATA"), "비ERP 요청 직렬화 JSON에 EZDATA 블록이 없어야 함"); } @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; } }