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 3dc4138..f7da5a6 100644 --- a/src/main/java/com/eactive/eai/transformer/message/JSONMessage.java +++ b/src/main/java/com/eactive/eai/transformer/message/JSONMessage.java @@ -166,10 +166,12 @@ public class JSONMessage extends Message { throw new InvalidAccessException("The number of repetitions is different from the number of repetitions of the actual data. , " + item.getName(), item.getItemPath()); } } // validation + } catch(InvalidAccessException e) { + throw e; } catch(Exception e) { throw new InvalidAccessException("setValue error", getItemPath(), null, e); } - } + } protected void doSetValue(JsonNode node, HashMap validationTargets, String parentPath) { Item item = null; @@ -255,40 +257,49 @@ public class JSONMessage extends Message { } else if(value == null || value.length() == 0) { setObject( itemPath, value ); } else { - int itemType = item.getDataTypeId(); - switch(itemType) { - case Types.TYPE_INT : - case Types.TYPE_SHORT : - setInt( itemPath, new Integer(value) ); - break; - case Types.TYPE_LONG : - setLong( itemPath, new Long(value) ); - break; - case Types.TYPE_BIGDECIMAL : - if (SystemKeys.isValidationEnabled() && !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()), - itemPath); - } - setBigDecimal( itemPath, new BigDecimal(value) ); - break; - case Types.TYPE_DOUBLE : - setDouble( itemPath, new Double(value) ); - break; - case Types.TYPE_FLOAT : - setFloat( itemPath, new Float(value) ); - break; - case Types.TYPE_BOOLEAN : - setObject( itemPath, new Boolean(value) ); - break; - case Types.TYPE_STRING : - setString(itemPath, value); - break; - default : - setString(itemPath, value); - break; - } + int itemType = item.getDataTypeId(); + try { + switch(itemType) { + case Types.TYPE_INT : + case Types.TYPE_SHORT : + setInt( itemPath, new Integer(value) ); + break; + case Types.TYPE_LONG : + setLong( itemPath, new Long(value) ); + break; + case Types.TYPE_BIGDECIMAL : + if (SystemKeys.isValidationEnabled() && !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()), + itemPath); + } + setBigDecimal( itemPath, new BigDecimal(value) ); + break; + case Types.TYPE_DOUBLE : + setDouble( itemPath, new Double(value) ); + break; + case Types.TYPE_FLOAT : + setFloat( itemPath, new Float(value) ); + break; + case Types.TYPE_BOOLEAN : + setObject( itemPath, new Boolean(value) ); + break; + case Types.TYPE_STRING : + setString(itemPath, value); + break; + default : + setString(itemPath, value); + break; + } + } catch (InvalidAccessException e) { + throw e; + } catch (Exception e) { + throw new InvalidAccessException( + String.format("Type mismatch: value='%s', field='%s', dataType=%s", + value, itemPath, Types.getType(itemType)), + itemPath, null, e); + } } } } diff --git a/src/test/java/com/eactive/eai/transformer/JSONMessageValidationTest.java b/src/test/java/com/eactive/eai/transformer/JSONMessageValidationTest.java index 5b0358a..ccd9334 100644 --- a/src/test/java/com/eactive/eai/transformer/JSONMessageValidationTest.java +++ b/src/test/java/com/eactive/eai/transformer/JSONMessageValidationTest.java @@ -28,11 +28,16 @@ import com.eactive.eai.transformer.message.MessageSetDataException; * - LONG_VALUE : LONG, length=10, decimal=0 * - BIG_VALUE : BIGDECIMAL, length=10, decimal=5 → 정수부 최대 4자리 * - BIG15_VALUE : BIGDECIMAL, length=15, decimal=0 → 정수부 최대 15자리 + * - STR_VALUE : STRING, length=20 * * 검증 시나리오 - * 1) 타입 불일치 — 숫자형 필드에 비숫자 문자열 입력 시 setData() 에서 즉시 오류 - * 2) 길이 초과 — BigDecimal 정수부/소수부 자릿수 초과 시 setData() 에서 즉시 오류 - * (SystemKeys.TRANSFROMER_VALIDATION=true 필요) + * 1-a) 타입 불일치(오류) — 숫자형 필드에 비숫자 문자열 입력 시 setData() 에서 즉시 오류 + * 1-b) 타입 불일치(정상) — 숫자형 필드에 숫자 문자열 입력 시 파싱 가능하면 정상 처리 + * 2-a) Java 타입 범위 초과 — INT/LONG 최대값 초과 시 NumberFormatException (validation 설정 무관) + * 2-b) BigDecimal 자릿수 초과 (숫자 리터럴) — 정수부/소수부 자릿수 초과 시 즉시 오류 + * 2-c) BigDecimal 자릿수 초과 (문자열 형태) — JSON 문자열로 전달된 경우도 동일하게 검증 + * (2-b, 2-c 모두 SystemKeys.TRANSFROMER_VALIDATION=true 필요) + * 3) 역방향 타입 — 문자열 필드에 JSON 숫자 입력 시 asText()로 자동 변환 → 오류 없음 */ @DataJpaTest @ContextConfiguration(classes = { TransformJPATestConfiguration.class }) @@ -110,8 +115,82 @@ class JSONMessageValidationTest { } // ───────────────────────────────────────────────────────────────── - // 2. 숫자형 길이 초과 테스트 — setData() 시점에 오류 발생 - // transformer.validation=true 설정 필요 + // 1-b. 숫자형 필드에 숫자 문자열 입력 → 정상 처리 + // JSON 문자열 노드라도 파싱 가능한 숫자면 오류 없이 처리됨 + // ───────────────────────────────────────────────────────────────── + + @Test + @DisplayName("INT 타입 필드에 숫자 문자열 입력 → 오류 없이 정상 처리") + void testIntFieldWithNumericString() throws Exception { + String data = "{\"EAI_UJSONTYPE_REQ1\":{\"INT_VALUE\":\"42\"}}"; + Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1"); + msg.setData(data); + + String result = (String) msg.getData(); + assertNotNull(result, "결과값이 null이 아니어야 합니다"); + assertTrue(result.contains("\"INT_VALUE\":42"), "INT_VALUE가 숫자 42로 출력되어야 합니다"); + } + + @Test + @DisplayName("LONG 타입 필드에 숫자 문자열 입력 → 오류 없이 정상 처리") + void testLongFieldWithNumericString() throws Exception { + String data = "{\"EAI_UJSONTYPE_REQ1\":{\"LONG_VALUE\":\"9876543210\"}}"; + Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1"); + msg.setData(data); + + String result = (String) msg.getData(); + assertNotNull(result, "결과값이 null이 아니어야 합니다"); + assertTrue(result.contains("\"LONG_VALUE\":9876543210"), "LONG_VALUE가 숫자 9876543210으로 출력되어야 합니다"); + } + + @Test + @DisplayName("BIGDECIMAL 타입 필드에 숫자 문자열 입력 → 오류 없이 정상 처리") + void testBigDecimalFieldWithNumericString() throws Exception { + String data = "{\"EAI_UJSONTYPE_REQ1\":{\"BIG_VALUE\":\"1234.12345\"}}"; + Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1"); + msg.setData(data); + + String result = (String) msg.getData(); + assertNotNull(result, "결과값이 null이 아니어야 합니다"); + assertTrue(result.contains("\"BIG_VALUE\":1234.12345"), "BIG_VALUE가 숫자 1234.12345로 출력되어야 합니다"); + } + + // ───────────────────────────────────────────────────────────────── + // 2-a. INT/LONG Java 타입 범위 초과 — validation 설정 무관하게 NumberFormatException + // ───────────────────────────────────────────────────────────────── + + @Test + @DisplayName("INT 타입 필드에 Integer 최대값 초과 입력 → NumberFormatException") + void testIntFieldJavaTypeOverflow() { + // Integer.MAX_VALUE = 2147483647, 초과값 → new Integer() 파싱 실패 + String data = "{\"EAI_UJSONTYPE_REQ1\":{\"INT_VALUE\":\"2147483648\"}}"; + Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1"); + + MessageSetDataException ex = assertThrows( + MessageSetDataException.class, + () -> msg.setData(data) + ); + assertTrue(containsCause(ex, NumberFormatException.class), + "원인이 NumberFormatException이어야 합니다"); + } + + @Test + @DisplayName("LONG 타입 필드에 Long 최대값 초과 입력 → NumberFormatException") + void testLongFieldJavaTypeOverflow() { + // Long.MAX_VALUE = 9223372036854775807, 초과값 → new Long() 파싱 실패 + String data = "{\"EAI_UJSONTYPE_REQ1\":{\"LONG_VALUE\":\"9223372036854775808\"}}"; + Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1"); + + MessageSetDataException ex = assertThrows( + MessageSetDataException.class, + () -> msg.setData(data) + ); + assertTrue(containsCause(ex, NumberFormatException.class), + "원인이 NumberFormatException이어야 합니다"); + } + + // ───────────────────────────────────────────────────────────────── + // 2-b. BigDecimal 자릿수 초과 (JSON 숫자 리터럴) — transformer.validation=true 필요 // ───────────────────────────────────────────────────────────────── @Test @@ -180,6 +259,102 @@ class JSONMessageValidationTest { assertTrue(result.contains("BIG_VALUE"), "BIG_VALUE가 결과에 포함되어야 합니다"); } + // ───────────────────────────────────────────────────────────────── + // 2-c. BigDecimal 자릿수 초과 (JSON 문자열 형태) — transformer.validation=true 필요 + // ───────────────────────────────────────────────────────────────── + + @Test + @DisplayName("BIG_VALUE 정수부 초과 문자열 입력(5자리 > 4자리 제한) → MessageSetDataException") + void testBigDecimalIntegerPartOverflowFromString() { + enableValidation(); + + // JSON 문자열 "12345.12345" → 정수부 5자리 초과 + String data = "{\"EAI_UJSONTYPE_REQ1\":{\"BIG_VALUE\":\"12345.12345\"}}"; + Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1"); + + MessageSetDataException ex = assertThrows( + MessageSetDataException.class, + () -> msg.setData(data) + ); + assertTrue(containsMessage(ex, "Invalid decimal point format"), + "'Invalid decimal point format' 메시지가 포함되어야 합니다"); + } + + @Test + @DisplayName("BIG_VALUE 소수부 초과 문자열 입력(6자리 > 5자리 제한) → MessageSetDataException") + void testBigDecimalFractionalPartOverflowFromString() { + enableValidation(); + + // JSON 문자열 "1234.123456" → 소수부 6자리 초과 + String data = "{\"EAI_UJSONTYPE_REQ1\":{\"BIG_VALUE\":\"1234.123456\"}}"; + Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1"); + + MessageSetDataException ex = assertThrows( + MessageSetDataException.class, + () -> msg.setData(data) + ); + assertTrue(containsMessage(ex, "Invalid decimal point format"), + "'Invalid decimal point format' 메시지가 포함되어야 합니다"); + } + + @Test + @DisplayName("BIG15_VALUE 정수부 초과 문자열 입력(16자리 > 15자리 제한) → MessageSetDataException") + void testBigDecimalIntegerOnlyOverflowFromString() { + enableValidation(); + + // JSON 문자열 "1234567890123456" → 정수부 16자리 초과 + String data = "{\"EAI_UJSONTYPE_REQ1\":{\"BIG15_VALUE\":\"1234567890123456\"}}"; + Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1"); + + MessageSetDataException ex = assertThrows( + MessageSetDataException.class, + () -> msg.setData(data) + ); + assertTrue(containsMessage(ex, "Invalid decimal point format"), + "'Invalid decimal point format' 메시지가 포함되어야 합니다"); + } + + // ───────────────────────────────────────────────────────────────── + // 3. 역방향 타입 테스트 — 문자열 필드에 숫자 입력 → 정상 처리 + // JSON 숫자 노드는 node.asText()로 문자열 변환되어 오류 없이 처리됨 + // ───────────────────────────────────────────────────────────────── + + @Test + @DisplayName("STRING 타입 필드에 JSON 정수 입력 → 오류 없이 정상 처리") + void testStringFieldWithIntegerValue() throws Exception { + String data = "{\"EAI_UJSONTYPE_REQ1\":{\"STR_VALUE\":123}}"; + Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1"); + msg.setData(data); + + String result = (String) msg.getData(); + assertNotNull(result, "결과값이 null이 아니어야 합니다"); + assertTrue(result.contains("STR_VALUE"), "STR_VALUE가 결과에 포함되어야 합니다"); + } + + @Test + @DisplayName("STRING 타입 필드에 JSON 실수 입력 → 오류 없이 정상 처리") + void testStringFieldWithDecimalValue() throws Exception { + String data = "{\"EAI_UJSONTYPE_REQ1\":{\"STR_VALUE\":12.34}}"; + Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1"); + msg.setData(data); + + String result = (String) msg.getData(); + assertNotNull(result, "결과값이 null이 아니어야 합니다"); + assertTrue(result.contains("STR_VALUE"), "STR_VALUE가 결과에 포함되어야 합니다"); + } + + @Test + @DisplayName("STRING 타입 필드에 JSON 큰 정수 입력 → 오류 없이 정상 처리") + void testStringFieldWithLargeIntegerValue() throws Exception { + String data = "{\"EAI_UJSONTYPE_REQ1\":{\"STR_VALUE\":9876543210}}"; + Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1"); + msg.setData(data); + + String result = (String) msg.getData(); + assertNotNull(result, "결과값이 null이 아니어야 합니다"); + assertTrue(result.contains("STR_VALUE"), "STR_VALUE가 결과에 포함되어야 합니다"); + } + // ───────────────────────────────────────────────────────────────── // 유틸 메서드 // ───────────────────────────────────────────────────────────────── diff --git a/src/test/resources/com/eactive/eai/transformer/init_json_validation.sql b/src/test/resources/com/eactive/eai/transformer/init_json_validation.sql index 35334e4..fd75362 100644 --- a/src/test/resources/com/eactive/eai/transformer/init_json_validation.sql +++ b/src/test/resources/com/eactive/eai/transformer/init_json_validation.sql @@ -6,4 +6,5 @@ INSERT INTO TSEAITR08 (loutname,loutitemname,loutitemserno,loutitemidname,parntl ('EAI_UJSONTYPE_REQ1', 'INT_VALUE', 1, 9001, 9000, 'INT 타입 필드', 2203, 2102, NULL, 5, NULL, NULL, NULL, 1, 1, NULL, 'N', 2, 0, 'Integer', 0, NULL, NULL), ('EAI_UJSONTYPE_REQ1', 'LONG_VALUE', 2, 9002, 9000, 'LONG 타입 필드', 2203, 2103, NULL, 10, NULL, NULL, NULL, 1, 1, NULL, 'N', 2, 0, 'Long', 0, NULL, NULL), ('EAI_UJSONTYPE_REQ1', 'BIG_VALUE', 3, 9003, 9000, 'BIGDECIMAL length=10 decimal=5', 2203, 2118, NULL, 10, NULL, NULL, NULL, 1, 1, NULL, 'N', 2, 5, 'BigDecimal', 0, NULL, NULL), - ('EAI_UJSONTYPE_REQ1', 'BIG15_VALUE', 4, 9004, 9000, 'BIGDECIMAL length=15 decimal=0', 2203, 2118, NULL, 15, NULL, NULL, NULL, 1, 1, NULL, 'N', 2, 0, 'BigDecimal', 0, NULL, NULL); + ('EAI_UJSONTYPE_REQ1', 'BIG15_VALUE', 4, 9004, 9000, 'BIGDECIMAL length=15 decimal=0', 2203, 2118, NULL, 15, NULL, NULL, NULL, 1, 1, NULL, 'N', 2, 0, 'BigDecimal', 0, NULL, NULL), + ('EAI_UJSONTYPE_REQ1', 'STR_VALUE', 5, 9005, 9000, 'STRING 타입 필드', 2203, 2101, NULL, 20, NULL, NULL, NULL, 1, 1, NULL, 'N', 2, 0, 'String', 0, NULL, NULL);