From 858d72c4def529889b9e6dbfb9f534a4f3ec26f9 Mon Sep 17 00:00:00 2001 From: curry772 Date: Wed, 20 May 2026 13:15:32 +0900 Subject: [PATCH] =?UTF-8?q?XMLMessage=20BIGDECIMAL=20validation=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20isValid()=20=EA=B3=B5=ED=86=B5?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Converter.isValid(): JSONMessage의 private isValid()를 static 공통 메서드로 이동 (지수형 정규화 포함, 빈 문자열은 검증 대상 아님) - JSONMessage: Converter.isValid() 위임, private isValid() 제거 - XMLMessage: doSetValue()에서 TYPE_BIGDECIMAL 필드 validation 추가 (try-catch 예외 삼킴 블록 앞에 위치시켜 예외가 전파되도록 함) - BytesMessageValidationTest: VALID_RAW 길이 오류 수정(32→35바이트), TYPE_BYTEARRAY isolation 검증 - XMLMessageValidationTest: validation 정상/오류/경계 케이스 10개 추가 (C-1 assertThrows 포함) --- .../eai/transformer/layout/Converter.java | 26 ++ .../eai/transformer/message/JSONMessage.java | 43 +-- .../eai/transformer/message/XMLMessage.java | 12 +- .../BytesMessageValidationTest.java | 193 +++++++++++++ .../transformer/XMLMessageValidationTest.java | 253 ++++++++++++++++++ .../eai/transformer/init_bytes_validation.sql | 17 ++ .../eai/transformer/init_xml_validation.sql | 14 + 7 files changed, 517 insertions(+), 41 deletions(-) create mode 100644 src/test/java/com/eactive/eai/transformer/BytesMessageValidationTest.java create mode 100644 src/test/java/com/eactive/eai/transformer/XMLMessageValidationTest.java create mode 100644 src/test/resources/com/eactive/eai/transformer/init_bytes_validation.sql create mode 100644 src/test/resources/com/eactive/eai/transformer/init_xml_validation.sql diff --git a/src/main/java/com/eactive/eai/transformer/layout/Converter.java b/src/main/java/com/eactive/eai/transformer/layout/Converter.java index 4cc9732..12630e5 100644 --- a/src/main/java/com/eactive/eai/transformer/layout/Converter.java +++ b/src/main/java/com/eactive/eai/transformer/layout/Converter.java @@ -211,6 +211,32 @@ public class Converter { } } + public static boolean isValid(String value, int length, int pointLength) { + String normalized = value.replaceAll(" ", ""); + if (SystemKeys.isScientificNormalizeEnabled()) { + try { + normalized = new BigDecimal(normalized).toPlainString(); + } catch (NumberFormatException e) { + return false; + } + } + String[] data = normalized.split("[.]"); + if (pointLength > 0) { + if (data.length == 1) { + if (data[0].length() > length - pointLength - 1) return false; + } else if (data.length == 2) { + if (data[0].length() > length - pointLength - 1) return false; + if (data[1].length() > pointLength) return false; + } else { + return false; + } + } else { + if (data.length != 1) return false; + if (data[0].length() > length) return false; + } + return true; + } + public static String addPoint(String dpNum, int tLen , int dLen) { if (dpNum == null || dpNum.trim().length() ==0 ){ return "0"; diff --git a/src/main/java/com/eactive/eai/transformer/message/JSONMessage.java b/src/main/java/com/eactive/eai/transformer/message/JSONMessage.java index 5166aad..8957d22 100644 --- a/src/main/java/com/eactive/eai/transformer/message/JSONMessage.java +++ b/src/main/java/com/eactive/eai/transformer/message/JSONMessage.java @@ -9,6 +9,7 @@ import java.util.Stack; import org.apache.commons.lang3.StringUtils; import com.eactive.eai.transformer.SystemKeys; +import com.eactive.eai.transformer.layout.Converter; import com.eactive.eai.transformer.layout.InvalidNodeException; import com.eactive.eai.transformer.layout.Item; import com.eactive.eai.transformer.layout.Types; @@ -268,7 +269,7 @@ public class JSONMessage extends Message { setLong( itemPath, new Long(value) ); break; case Types.TYPE_BIGDECIMAL : - if (SystemKeys.isValidationEnabled() && !isValid(value, item.getLength(), item.getDecimalPointLength())) { + if (SystemKeys.isValidationEnabled() && !Converter.isValid(value, item.getLength(), item.getDecimalPointLength())) { throw new InvalidAccessException( String.format("Invalid decimal point format Error value=%s,length=%d,pointLength=%d", value, item.getLength(), item.getDecimalPointLength()), @@ -662,7 +663,7 @@ public class JSONMessage extends Message { private String getFormat(String value, int length, int pointLength, boolean isValidation) throws Exception{ if(isValidation) { - if (!isValid(value,length,pointLength)){ + if (!Converter.isValid(value,length,pointLength)){ throw new Exception(String.format("Invalid decimal point format Error value=%s,length=%d,pointLength=%d" , value,length,pointLength) ); @@ -702,44 +703,6 @@ public class JSONMessage extends Message { } } - private boolean isValid(String value,int length,int pointLength){ - String normalized = value.replaceAll(" ", ""); - if (SystemKeys.isScientificNormalizeEnabled()) { - try { - normalized = new BigDecimal(normalized).toPlainString(); - } catch (NumberFormatException e) { - return false; - } - } - String[] data = normalized.split("[.]"); - if (pointLength > 0 ){ - if (data.length ==1){ - if (data[0].length() > length - pointLength - 1 ){ //정수부 오류 - return false; - } - }else if (data.length ==2){ - if (data[0].length() > length - pointLength - 1 ){ //정수부 오류 - return false; - } - if (data[1].length() > pointLength ){ //실수부 오류 - return false; - } - }else{ - //오류 - return false; - } - - }else{ - if (data.length != 1){ //소수점이 들어와서 오류 - return false; - } - if (data[0].length() > length){ //정수부 오류 - return false; - } - } - return true; - } - // private Object toJsonObjectType(int itemType, String value) { // Object jsonValueObject = null; // diff --git a/src/main/java/com/eactive/eai/transformer/message/XMLMessage.java b/src/main/java/com/eactive/eai/transformer/message/XMLMessage.java index 8ff8730..fafb687 100644 --- a/src/main/java/com/eactive/eai/transformer/message/XMLMessage.java +++ b/src/main/java/com/eactive/eai/transformer/message/XMLMessage.java @@ -12,6 +12,7 @@ import org.dom4j.Namespace; import org.dom4j.QName; import com.eactive.eai.transformer.SystemKeys; +import com.eactive.eai.transformer.layout.Converter; import com.eactive.eai.transformer.layout.InvalidNodeException; import com.eactive.eai.transformer.layout.Item; import com.eactive.eai.transformer.layout.Layout; @@ -198,8 +199,17 @@ public class XMLMessage extends Message { continue; } } - else if (item.isField()) { + else if (item.isField()) { Object data = child.getData(); + if (data instanceof String + && item.getDataTypeId() == Types.TYPE_BIGDECIMAL + && SystemKeys.isValidationEnabled() + && !Converter.isValid((String) data, item.getLength(), item.getDecimalPointLength())) { + throw new InvalidAccessException( + String.format("Invalid decimal point format Error value=%s,length=%d,pointLength=%d", + data, item.getLength(), item.getDecimalPointLength()), + childPath); + } try { if (data instanceof String){ setString(childPath, (String) data); diff --git a/src/test/java/com/eactive/eai/transformer/BytesMessageValidationTest.java b/src/test/java/com/eactive/eai/transformer/BytesMessageValidationTest.java new file mode 100644 index 0000000..2185366 --- /dev/null +++ b/src/test/java/com/eactive/eai/transformer/BytesMessageValidationTest.java @@ -0,0 +1,193 @@ +package com.eactive.eai.transformer; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.jdbc.Sql; + +import com.eactive.eai.transformer.engine.TransformEngine; +import com.eactive.eai.transformer.function.jpa.TransformJPATestConfiguration; +import com.eactive.eai.transformer.message.Message; +import com.eactive.eai.transformer.message.MessageFactory; + +/** + * BytesMessage 필드 validation 영향도 단위테스트 + * + * 테스트 대상 레이아웃: EAI_UBYTESTYPE_REQ1 (BYTES 타입, 전문길이=35) + * - INT_VALUE : TYPE_BYTEARRAY(2113), length=5, decimal=0 + * - BIG_VALUE : TYPE_BYTEARRAY(2113), length=10, decimal=5 + * - STR_VALUE : TYPE_BYTEARRAY(2113), length=20, decimal=0 + * + * 검증 목적 + * BytesMessage 필드는 Converter.convert()의 TYPE_BYTEARRAY(2113) 분기를 사용하며, + * 변경 대상인 TYPE_BIGDECIMAL(2118) + String 분기와 완전히 분리되어 있음을 확인한다. + * + * 시나리오 + * A) setData(byte[]) 경로 — TYPE_BYTEARRAY 분기, 변경 전후 동일 + * B) setString() 경로 — TYPE_BYTEARRAY 분기, 변경 전후 동일 + * B-1) 정상 숫자 문자열 → 예외 없음 (bytes로 저장) + * B-2) 비숫자 문자열 → 예외 없음 (bytes로 저장, BigDecimal 변환 없음) + * B-3) 자릿수 초과 문자열, validation=true → 예외 없음 (TYPE_BYTEARRAY는 validation 대상 아님) + * B-4) 자릿수 초과 문자열, validation=false → 예외 없음 + */ +@DataJpaTest +@ContextConfiguration(classes = { TransformJPATestConfiguration.class }) +@Sql({ + "/com/eactive/eai/transformer/init_tseaitr06.sql", + "/com/eactive/eai/transformer/init_bytes_validation.sql" +}) +@DisplayName("BytesMessage Validation 영향도 테스트") +class BytesMessageValidationTest { + + @Autowired + TransformEngine engine; + + // 전문 구조: INT_VALUE(5) + BIG_VALUE(10) + STR_VALUE(20) = 35 bytes + // BytesMessage는 소수점 없이 숫자만 저장 (decimal=5이면 끝 5자리가 소수부) + private static final String VALID_RAW = + "00042" // INT_VALUE : 5자리 정수 + + "0012312345" // BIG_VALUE : 10자리 (정수부5 + 소수부5, 소수점 제외) + + "HELLO WORLD "; // STR_VALUE : 20자리 문자열 + + @BeforeEach + void setUp() { + try { + if (!engine.isStarted()) { + engine.start(); + } + } catch (Exception e) { + assumeTrue(false, "TransformEngine 시작 실패: " + e.getMessage()); + } + } + + @AfterEach + void tearDown() { + System.setProperty(SystemKeys.TRANSFROMER_VALIDATION, "false"); + System.setProperty(SystemKeys.TRANSFROMER_SCIENTIFIC_NORMALIZE, "false"); + SystemKeys.reload(); + } + + // ───────────────────────────────────────────────────────────────── + // A. setData(byte[]) 경로 — TYPE_BYTEARRAY 분기 직접 사용 + // Converter TYPE_BIGDECIMAL 분기와 완전히 무관 + // ───────────────────────────────────────────────────────────────── + + @Test + @DisplayName("[A] 정상 전문(byte[]) setData → 정상 처리") + void testSetDataWithValidBytes() { + byte[] raw = VALID_RAW.getBytes(); + Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1"); + + assertDoesNotThrow(() -> msg.setData(raw), + "정상 전문 byte[] setData는 예외가 발생하지 않아야 합니다"); + + byte[] result = (byte[]) msg.getData(); + assertNotNull(result, "getData() 결과가 null이 아니어야 합니다"); + assertArrayEquals(raw, result, "입력 전문과 출력 전문이 동일해야 합니다"); + } + + @Test + @DisplayName("[A] validation=true 상태에서 byte[] setData → 영향 없음") + void testSetDataWithValidationEnabled() { + enableValidation(); + byte[] raw = VALID_RAW.getBytes(); + Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1"); + + assertDoesNotThrow(() -> msg.setData(raw), + "validation=true 설정이 BytesMessage setData(byte[])에 영향을 주지 않아야 합니다"); + } + + // ───────────────────────────────────────────────────────────────── + // B-1. setString() — 정상 숫자 문자열 + // TYPE_BYTEARRAY 분기 → String.getBytes() 저장, BigDecimal 변환 없음 + // ───────────────────────────────────────────────────────────────── + + @Test + @DisplayName("[B-1] BIG_VALUE에 정상 숫자 문자열 setString → 예외 없음") + void testSetStringWithValidDecimal() { + byte[] raw = VALID_RAW.getBytes(); + Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1"); + assertDoesNotThrow(() -> msg.setData(raw)); + + assertDoesNotThrow( + () -> msg.setString("EAI_UBYTESTYPE_REQ1.BIG_VALUE[0]", "1234.12345"), + "정상 숫자 문자열 setString은 예외가 발생하지 않아야 합니다"); + } + + // ───────────────────────────────────────────────────────────────── + // B-2. setString() — 비숫자 문자열 + // TYPE_BYTEARRAY 분기 → String.getBytes() 저장 + // BigDecimal 변환이 없으므로 NumberFormatException 발생 안 함 + // (JSONMessage와 다른 동작: JSONMessage는 Converter TYPE_BIGDECIMAL 분기 → 예외) + // ───────────────────────────────────────────────────────────────── + + @Test + @DisplayName("[B-2] BIG_VALUE에 비숫자 문자열 setString → TYPE_BYTEARRAY이므로 예외 없음") + void testSetStringWithNonNumeric() { + byte[] raw = VALID_RAW.getBytes(); + Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1"); + assertDoesNotThrow(() -> msg.setData(raw)); + + assertDoesNotThrow( + () -> msg.setString("EAI_UBYTESTYPE_REQ1.BIG_VALUE[0]", "not-a-number"), + "TYPE_BYTEARRAY 필드는 비숫자 문자열도 bytes로 저장하므로 예외가 발생하지 않아야 합니다"); + } + + // ───────────────────────────────────────────────────────────────── + // B-3. setString() — 자릿수 초과 문자열 + validation=true + // TYPE_BYTEARRAY 분기는 자릿수 검증 자체가 없음 + // → validation 설정과 무관하게 예외 발생 안 함 + // ★ 이 동작은 Converter TYPE_BIGDECIMAL 분기 수정 후에도 동일해야 함 ★ + // ───────────────────────────────────────────────────────────────── + + @Test + @DisplayName("[B-3] BIG_VALUE 자릿수 초과 + validation=true → TYPE_BYTEARRAY이므로 예외 없음") + void testSetStringOverflowWithValidationEnabled() { + enableValidation(); + byte[] raw = VALID_RAW.getBytes(); + Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1"); + assertDoesNotThrow(() -> msg.setData(raw)); + + // 정수부 5자리(12345) > BIG_VALUE decimal=5 기준 제한(4자리) 초과 + // → TYPE_BIGDECIMAL이었다면 validation 오류, TYPE_BYTEARRAY이므로 통과 + assertDoesNotThrow( + () -> msg.setString("EAI_UBYTESTYPE_REQ1.BIG_VALUE[0]", "12345.123456"), + "TYPE_BYTEARRAY 필드는 validation 대상이 아니므로 자릿수 초과에도 예외가 발생하지 않아야 합니다"); + } + + // ───────────────────────────────────────────────────────────────── + // B-4. setString() — 자릿수 초과 문자열 + validation=false + // validation 설정과 무관하게 예외 없음 (B-3과 결과 동일) + // ───────────────────────────────────────────────────────────────── + + @Test + @DisplayName("[B-4] BIG_VALUE 자릿수 초과 + validation=false → 예외 없음") + void testSetStringOverflowWithValidationDisabled() { + // validation=false (기본값) + byte[] raw = VALID_RAW.getBytes(); + Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1"); + assertDoesNotThrow(() -> msg.setData(raw)); + + assertDoesNotThrow( + () -> msg.setString("EAI_UBYTESTYPE_REQ1.BIG_VALUE[0]", "12345.123456"), + "validation=false(기본값) 상태에서 TYPE_BYTEARRAY 필드는 자릿수 초과에 예외가 발생하지 않아야 합니다"); + } + + // ───────────────────────────────────────────────────────────────── + // 유틸 메서드 + // ───────────────────────────────────────────────────────────────── + + private void enableValidation() { + System.setProperty(SystemKeys.TRANSFROMER_VALIDATION, "true"); + SystemKeys.reload(); + } +} diff --git a/src/test/java/com/eactive/eai/transformer/XMLMessageValidationTest.java b/src/test/java/com/eactive/eai/transformer/XMLMessageValidationTest.java new file mode 100644 index 0000000..d3c2820 --- /dev/null +++ b/src/test/java/com/eactive/eai/transformer/XMLMessageValidationTest.java @@ -0,0 +1,253 @@ +package com.eactive.eai.transformer; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.jdbc.Sql; + +import com.eactive.eai.transformer.engine.TransformEngine; +import com.eactive.eai.transformer.function.jpa.TransformJPATestConfiguration; +import com.eactive.eai.transformer.message.Message; +import com.eactive.eai.transformer.message.MessageFactory; +import com.eactive.eai.transformer.message.MessageSetDataException; + +/** + * XMLMessage BIGDECIMAL validation 단위테스트 + * + * 테스트 대상 레이아웃: EAI_UXMLTYPE_REQ1 (XML 타입) + * - BIG_VALUE : TYPE_BIGDECIMAL(2118), length=10, decimal=5 → 정수부 최대 4자리 + * - BIG15_VALUE : TYPE_BIGDECIMAL(2118), length=15, decimal=0 → 정수부 최대 15자리 + * - STR_VALUE : TYPE_STRING(2101), length=20 + * + * 검증 흐름 + * setData(xmlString) → doSetData() → setValue(Element) → doSetValue() + * → TYPE_BIGDECIMAL 필드 + validation=true + 자릿수 초과 → InvalidAccessException + * → doSetData() catch → MessageSetDataException + * + * 시나리오 + * A) 정상 XML → 예외 없음 + * B) 정상 XML + validation=true → 예외 없음 + * C-1) BIG_VALUE 정수부 초과(5자리) + validation=true → MessageSetDataException + * C-2) BIG_VALUE 정수부 초과(5자리) + validation=false → 예외 없음 + * D-1) BIG15_VALUE 소수점 포함(decimal=0 필드) + validation=true → MessageSetDataException + * D-2) BIG15_VALUE 정수부 초과(16자리) + validation=true → MessageSetDataException + * E) BIG15_VALUE 정상 정수(≤15자리) + validation=true → 예외 없음 + */ +@DataJpaTest +@ContextConfiguration(classes = { TransformJPATestConfiguration.class }) +@Sql({ + "/com/eactive/eai/transformer/init_tseaitr06.sql", + "/com/eactive/eai/transformer/init_xml_validation.sql" +}) +@DisplayName("XMLMessage BIGDECIMAL Validation 테스트") +class XMLMessageValidationTest { + + @Autowired + TransformEngine engine; + + // BIG_VALUE: length=10, decimal=5 → 정수부 최대 4자리, 소수부 최대 5자리 + // BIG15_VALUE: length=15, decimal=0 → 정수부 최대 15자리, 소수점 없음 + private static final String VALID_XML = + "" + + "1234.12345" + + "123000" + + "HELLO" + + ""; + + @BeforeEach + void setUp() { + try { + if (!engine.isStarted()) { + engine.start(); + } + } catch (Exception e) { + assumeTrue(false, "TransformEngine 시작 실패: " + e.getMessage()); + } + } + + @AfterEach + void tearDown() { + System.setProperty(SystemKeys.TRANSFROMER_VALIDATION, "false"); + System.setProperty(SystemKeys.TRANSFROMER_SCIENTIFIC_NORMALIZE, "false"); + SystemKeys.reload(); + } + + // ───────────────────────────────────────────────────────────────── + // A. 정상 XML — validation 설정 무관 + // ───────────────────────────────────────────────────────────────── + + @Test + @DisplayName("[A] 정상 XML → 예외 없음") + void testValidXml() { + Message msg = MessageFactory.getFactory().getMessage("EAI_UXMLTYPE_REQ1"); + assertDoesNotThrow(() -> msg.setData(VALID_XML), + "정상 XML 파싱은 예외가 발생하지 않아야 합니다"); + } + + @Test + @DisplayName("[B] 정상 XML + validation=true → 예외 없음") + void testValidXml_withValidation() { + enableValidation(); + Message msg = MessageFactory.getFactory().getMessage("EAI_UXMLTYPE_REQ1"); + assertDoesNotThrow(() -> msg.setData(VALID_XML), + "정상값은 validation=true에서도 예외가 발생하지 않아야 합니다"); + } + + // ───────────────────────────────────────────────────────────────── + // C. BIG_VALUE 정수부 초과 — validation 설정에 따른 동작 분기 + // BIG_VALUE: length=10, decimal=5 → 정수부 최대 4자리 + // "12345.12345" → 정수부 5자리 초과 + // ───────────────────────────────────────────────────────────────── + + @Test + @DisplayName("[C-1] BIG_VALUE 정수부 초과(5자리) + validation=true → MessageSetDataException") + void testBigValueIntegerOverflow_withValidation() { + enableValidation(); + String xml = + "" + + "12345.12345" + + "123000" + + "HELLO" + + ""; + Message msg = MessageFactory.getFactory().getMessage("EAI_UXMLTYPE_REQ1"); + assertThrows(MessageSetDataException.class, () -> msg.setData(xml), + "정수부 자릿수 초과 시 validation=true이면 MessageSetDataException이 발생해야 합니다"); + } + + @Test + @DisplayName("[C-2] BIG_VALUE 정수부 초과(5자리) + validation=false → 예외 없음") + void testBigValueIntegerOverflow_withoutValidation() { + // validation=false (기본값) + String xml = + "" + + "12345.12345" + + "123000" + + "HELLO" + + ""; + Message msg = MessageFactory.getFactory().getMessage("EAI_UXMLTYPE_REQ1"); + assertDoesNotThrow(() -> msg.setData(xml), + "validation=false(기본값) 상태에서는 자릿수 초과에도 예외가 발생하지 않아야 합니다"); + } + + // ───────────────────────────────────────────────────────────────── + // D. BIG15_VALUE (decimal=0 필드) 오류 케이스 + // D-1) 소수점 포함값 — decimal=0은 정수 전용, 소수점 자체가 오류 + // D-2) 정수부 초과 — 16자리 > 15자리 제한 + // ───────────────────────────────────────────────────────────────── + + @Test + @DisplayName("[D-1] BIG15_VALUE decimal=0 필드에 소수점 포함값 + validation=true → MessageSetDataException") + void testBig15ValueWithDecimalPoint_withValidation() { + enableValidation(); + String xml = + "" + + "1234.12345" + + "123.456" + + "HELLO" + + ""; + Message msg = MessageFactory.getFactory().getMessage("EAI_UXMLTYPE_REQ1"); + assertThrows(MessageSetDataException.class, () -> msg.setData(xml), + "decimal=0 필드에 소수점 포함값 입력 시 validation=true이면 MessageSetDataException이 발생해야 합니다"); + } + + @Test + @DisplayName("[D-2] BIG15_VALUE 정수부 초과(16자리) + validation=true → MessageSetDataException") + void testBig15ValueIntegerOverflow_withValidation() { + enableValidation(); + String xml = + "" + + "1234.12345" + + "1234567890123456" + + "HELLO" + + ""; + Message msg = MessageFactory.getFactory().getMessage("EAI_UXMLTYPE_REQ1"); + assertThrows(MessageSetDataException.class, () -> msg.setData(xml), + "정수부 16자리(>15) 초과 시 validation=true이면 MessageSetDataException이 발생해야 합니다"); + } + + // ───────────────────────────────────────────────────────────────── + // E. BIG15_VALUE 정상 정수 (≤15자리) + validation=true → 예외 없음 + // ───────────────────────────────────────────────────────────────── + + @Test + @DisplayName("[E] BIG15_VALUE 정상 정수(≤15자리) + validation=true → 예외 없음") + void testBig15ValueValidInteger_withValidation() { + enableValidation(); + Message msg = MessageFactory.getFactory().getMessage("EAI_UXMLTYPE_REQ1"); + assertDoesNotThrow(() -> msg.setData(VALID_XML), + "15자리 이내 정수 입력 시 validation=true에서도 예외가 발생하지 않아야 합니다"); + } + + // ───────────────────────────────────────────────────────────────── + // F. 빈 요소 / 요소 없음 — 경계 케이스 + // F-1) 빈 요소() + validation=true → 예외 없음 + // dom4j는 빈 요소의 getData()를 null로 반환 + // → null instanceof String = false → 검증 건너뜀 → 요소 없음과 동일 동작 + // F-2) 빈 요소() + validation=false → 예외 없음 + // F-3) 요소 없음(BIG_VALUE 미포함 XML) + validation=true → 예외 없음 + // XML에 요소가 없으면 doSetValue() 순회 대상이 없어 검증 자체가 실행되지 않음 + // ───────────────────────────────────────────────────────────────── + + @Test + @DisplayName("[F-1] BIG_VALUE 빈 요소() + validation=true → 예외 없음 (dom4j null 반환)") + void testBigValueEmptyElement_withValidation() { + enableValidation(); + // dom4j는 빈 요소의 getData()를 null로 반환 → null instanceof String = false → 검증 건너뜀 + String xml = + "" + + "" + + "123000" + + "HELLO" + + ""; + Message msg = MessageFactory.getFactory().getMessage("EAI_UXMLTYPE_REQ1"); + assertDoesNotThrow(() -> msg.setData(xml), + "dom4j 빈 요소는 null로 처리되어 검증 대상이 아니므로 예외가 발생하지 않아야 합니다"); + } + + @Test + @DisplayName("[F-2] BIG_VALUE 빈 문자열 + validation=false → 예외 없음") + void testBigValueEmptyString_withoutValidation() { + // validation=false (기본값) + String xml = + "" + + "" + + "123000" + + "HELLO" + + ""; + Message msg = MessageFactory.getFactory().getMessage("EAI_UXMLTYPE_REQ1"); + assertDoesNotThrow(() -> msg.setData(xml), + "validation=false(기본값) 상태에서는 빈 문자열도 예외가 발생하지 않아야 합니다"); + } + + @Test + @DisplayName("[F-3] BIG_VALUE 요소 없음 + validation=true → 예외 없음") + void testBigValueMissingElement_withValidation() { + enableValidation(); + // BIG_VALUE 요소 자체를 XML에서 제외 + String xml = + "" + + "123000" + + "HELLO" + + ""; + Message msg = MessageFactory.getFactory().getMessage("EAI_UXMLTYPE_REQ1"); + assertDoesNotThrow(() -> msg.setData(xml), + "XML에 요소가 없으면 검증 대상이 아니므로 예외가 발생하지 않아야 합니다"); + } + + // ───────────────────────────────────────────────────────────────── + // 유틸 메서드 + // ───────────────────────────────────────────────────────────────── + + private void enableValidation() { + System.setProperty(SystemKeys.TRANSFROMER_VALIDATION, "true"); + SystemKeys.reload(); + } +} diff --git a/src/test/resources/com/eactive/eai/transformer/init_bytes_validation.sql b/src/test/resources/com/eactive/eai/transformer/init_bytes_validation.sql new file mode 100644 index 0000000..4d112c4 --- /dev/null +++ b/src/test/resources/com/eactive/eai/transformer/init_bytes_validation.sql @@ -0,0 +1,17 @@ +-- BytesMessage BIGDECIMAL validation 영향도 검증용 레이아웃 +-- EAI_UBYTESTYPE_REQ1 : BYTES 타입, 전문길이=35 (5+10+20) +-- - INT_VALUE : TYPE_BYTEARRAY(2113), length=5, decimal=0 → loutitemrefptrnidname='Integer' +-- - BIG_VALUE : TYPE_BYTEARRAY(2113), length=10, decimal=5 → loutitemrefptrnidname='BigDecimal' +-- - STR_VALUE : TYPE_BYTEARRAY(2113), length=20, decimal=0 → loutitemrefptrnidname='String' +-- +-- ※ BytesMessage 필드는 loutitemptrnidname=2113(TYPE_BYTEARRAY) 사용 +-- Converter.convert()의 TYPE_BIGDECIMAL(2118) 분기와 완전히 분리됨을 확인하기 위한 레이아웃 + +INSERT INTO tseaitr07 (loutname,loutptrnname,loutdesc,lastamndhms,eaibzwkdstcd,uapplname,sysintfacname,eaisevrdstcd,modfimgtstusdstcd,useyn,verinfo,author) VALUES + ('EAI_UBYTESTYPE_REQ1', 'BYTES', 'BYTES 타입 validation 영향도 검증', '2026052010000000', 'TST', ' ', ' ', 'E', ' ', '1', '00001', 'admin'); + +INSERT INTO TSEAITR08 (loutname,loutitemname,loutitemserno,loutitemidname,parntloutitemidname,loutitemdesc,loutitemnodeptrnidname,loutitemptrnidname,loutitempathname,loutitemlencnt,loutitemrefinfo,loutitemrefinfo2,loutitemoccurptrndstcd,loutitemmaxoccurnoitm,loutitemminoccurnoitm,loutitembascval,loutitemmskyn,loutdptcnt,decptlencnt,loutitemrefptrnidname,loutitemmasklength,multilang,loutitemmaskoffset) VALUES + ('EAI_UBYTESTYPE_REQ1', 'EAI_UBYTESTYPE_REQ1', 0, 9010, 0, 'BYTES 타입 validation 검증', 2201, 2100, NULL, 0, NULL, NULL, NULL, 0, 0, NULL, NULL, 1, 0, NULL, NULL, NULL, NULL), + ('EAI_UBYTESTYPE_REQ1', 'INT_VALUE', 1, 9011, 9010, 'INT 타입 필드 (BYTEARRAY)', 2203, 2113, NULL, 5, NULL, NULL, NULL, 1, 1, NULL, 'N', 2, 0, 'Integer', 0, NULL, NULL), + ('EAI_UBYTESTYPE_REQ1', 'BIG_VALUE', 2, 9012, 9010, 'BIGDECIMAL 필드 (BYTEARRAY) l=10 d=5', 2203, 2113, NULL, 10, NULL, NULL, NULL, 1, 1, NULL, 'N', 2, 5, 'BigDecimal', 0, NULL, NULL), + ('EAI_UBYTESTYPE_REQ1', 'STR_VALUE', 3, 9013, 9010, 'STRING 타입 필드 (BYTEARRAY)', 2203, 2113, NULL, 20, NULL, NULL, NULL, 1, 1, NULL, 'N', 2, 0, 'String', 0, NULL, NULL); diff --git a/src/test/resources/com/eactive/eai/transformer/init_xml_validation.sql b/src/test/resources/com/eactive/eai/transformer/init_xml_validation.sql new file mode 100644 index 0000000..51b59b2 --- /dev/null +++ b/src/test/resources/com/eactive/eai/transformer/init_xml_validation.sql @@ -0,0 +1,14 @@ +-- XMLMessage BIGDECIMAL validation 테스트용 레이아웃 +-- EAI_UXMLTYPE_REQ1 : XML 타입 +-- - BIG_VALUE : TYPE_BIGDECIMAL(2118), length=10, decimal=5 → 정수부 최대 4자리 +-- - BIG15_VALUE : TYPE_BIGDECIMAL(2118), length=15, decimal=0 → 정수부 최대 15자리, 소수점 없음 +-- - STR_VALUE : TYPE_STRING(2101), length=20, decimal=0 + +INSERT INTO tseaitr07 (loutname,loutptrnname,loutdesc,lastamndhms,eaibzwkdstcd,uapplname,sysintfacname,eaisevrdstcd,modfimgtstusdstcd,useyn,verinfo,author) VALUES + ('EAI_UXMLTYPE_REQ1', 'XML', 'XML 타입 BIGDECIMAL validation 테스트', '2026052010000000', 'TST', ' ', ' ', 'E', ' ', '1', '00001', 'admin'); + +INSERT INTO TSEAITR08 (loutname,loutitemname,loutitemserno,loutitemidname,parntloutitemidname,loutitemdesc,loutitemnodeptrnidname,loutitemptrnidname,loutitempathname,loutitemlencnt,loutitemrefinfo,loutitemrefinfo2,loutitemoccurptrndstcd,loutitemmaxoccurnoitm,loutitemminoccurnoitm,loutitembascval,loutitemmskyn,loutdptcnt,decptlencnt,loutitemrefptrnidname,loutitemmasklength,multilang,loutitemmaskoffset) VALUES + ('EAI_UXMLTYPE_REQ1', 'EAI_UXMLTYPE_REQ1', 0, 9020, 0, 'XML BIGDECIMAL validation 루트', 2201, 2100, NULL, 0, NULL, NULL, NULL, 0, 0, NULL, NULL, 1, 0, NULL, NULL, NULL, NULL), + ('EAI_UXMLTYPE_REQ1', 'BIG_VALUE', 1, 9021, 9020, 'BIGDECIMAL length=10 decimal=5', 2203, 2118, NULL, 10, NULL, NULL, NULL, 1, 1, NULL, 'N', 2, 5, 'BigDecimal', 0, NULL, NULL), + ('EAI_UXMLTYPE_REQ1', 'BIG15_VALUE', 2, 9022, 9020, 'BIGDECIMAL length=15 decimal=0', 2203, 2118, NULL, 15, NULL, NULL, NULL, 1, 1, NULL, 'N', 2, 0, 'BigDecimal', 0, NULL, NULL), + ('EAI_UXMLTYPE_REQ1', 'STR_VALUE', 3, 9023, 9020, 'STRING 타입 필드', 2203, 2101, NULL, 20, NULL, NULL, NULL, 1, 1, NULL, 'N', 2, 0, 'String', 0, NULL, NULL);