BytesMessage decimal 필드 숫자형 validation 추가 및 단위테스트 작성

- BytesMessage.setDataBytes(): decimal>0 필드에 비숫자 bytes 입력 시
  validation=true이면 MessageSetDataException 발생하도록 수정
- BytesMessageValidationTest: C섹션(C-1/C-2/C-3) 추가,
  tearDown을 clearProperty로 변경하여 SystemKeys 기본값(true) 올바르게 복원,
  B-4 DisplayName/메서드명을 setString이 decimal 검증 경로 밖임을 명시하도록 수정
This commit is contained in:
curry772
2026-05-20 15:08:59 +09:00
parent 858d72c4de
commit 4653d6a50b
2 changed files with 75 additions and 8 deletions
@@ -4,6 +4,7 @@ package com.eactive.eai.transformer.message;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.Stack; import java.util.Stack;
@@ -250,6 +251,18 @@ public class BytesMessage extends Message {
item.setValue(ByteUtil.cut(data, startPosition, item.getLength())); item.setValue(ByteUtil.cut(data, startPosition, item.getLength()));
startPosition += item.getLength(); startPosition += item.getLength();
remainSize -= item.getLength(); remainSize -= item.getLength();
if (SystemKeys.isValidationEnabled() && item.getDecimalPointLength() > 0) {
String strVal = new String((byte[]) item.getValue(), defaultCharset).trim();
try {
new BigDecimal(strVal);
} catch (NumberFormatException nfe) {
throw new MessageException(
String.format("decimal 필드 숫자형 오류: %s = [%s]",
item.getItemPath(), strVal));
}
}
} catch(MessageException me) {
throw me;
} catch(Exception e) { } catch(Exception e) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("전문레이아웃 매핑 중 데이터 사이즈 오류 ("+e.getMessage()+")"); sb.append("전문레이아웃 매핑 중 데이터 사이즈 오류 ("+e.getMessage()+")");
@@ -3,6 +3,7 @@ package com.eactive.eai.transformer;
import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.junit.jupiter.api.Assumptions.assumeTrue;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@@ -18,6 +19,7 @@ import com.eactive.eai.transformer.engine.TransformEngine;
import com.eactive.eai.transformer.function.jpa.TransformJPATestConfiguration; import com.eactive.eai.transformer.function.jpa.TransformJPATestConfiguration;
import com.eactive.eai.transformer.message.Message; import com.eactive.eai.transformer.message.Message;
import com.eactive.eai.transformer.message.MessageFactory; import com.eactive.eai.transformer.message.MessageFactory;
import com.eactive.eai.transformer.message.MessageSetDataException;
/** /**
* BytesMessage 필드 validation 영향도 단위테스트 * BytesMessage 필드 validation 영향도 단위테스트
@@ -71,8 +73,8 @@ class BytesMessageValidationTest {
@AfterEach @AfterEach
void tearDown() { void tearDown() {
System.setProperty(SystemKeys.TRANSFROMER_VALIDATION, "false"); System.clearProperty(SystemKeys.TRANSFROMER_VALIDATION);
System.setProperty(SystemKeys.TRANSFROMER_SCIENTIFIC_NORMALIZE, "false"); System.clearProperty(SystemKeys.TRANSFROMER_SCIENTIFIC_NORMALIZE);
SystemKeys.reload(); SystemKeys.reload();
} }
@@ -165,21 +167,73 @@ class BytesMessageValidationTest {
} }
// ───────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────
// B-4. setString() — 자릿수 초과 문자열 + validation=false // B-4. setString() — 자릿수 초과 문자열
// validation 설정과 무관하게 예외 없음 (B-3과 결과 동일) // setString 경로는 setDataBytes()를 통하지 않으므로
// validation 설정과 완전히 무관하게 예외 발생 안 함 (B-3과 결과 동일)
// ───────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────
@Test @Test
@DisplayName("[B-4] BIG_VALUE 자릿수 초과 + validation=false → 예외 없음") @DisplayName("[B-4] BIG_VALUE 자릿수 초과 setString → setString 경로는 decimal 검증 대상 아님")
void testSetStringOverflowWithValidationDisabled() { void testSetStringOverflowIsNotSubjectToDecimalValidation() {
// validation=false (기본값) // setString → setDataBytes() 미경유 → decimal 숫자형 검증 실행 안 됨
// validation 설정과 무관하게 예외 없음
byte[] raw = VALID_RAW.getBytes(); byte[] raw = VALID_RAW.getBytes();
Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1"); Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1");
assertDoesNotThrow(() -> msg.setData(raw)); assertDoesNotThrow(() -> msg.setData(raw));
assertDoesNotThrow( assertDoesNotThrow(
() -> msg.setString("EAI_UBYTESTYPE_REQ1.BIG_VALUE[0]", "12345.123456"), () -> msg.setString("EAI_UBYTESTYPE_REQ1.BIG_VALUE[0]", "12345.123456"),
"validation=false(기본값) 상태에서 TYPE_BYTEARRAY 필드는 자릿수 초과에 예외가 발생하지 않아야 합니다"); "TYPE_BYTEARRAY setString은 setDataBytes()를 통하지 않으므로 자릿수 초과에 예외가 발생하지 않아야 합니다");
}
// ─────────────────────────────────────────────────────────────────
// C. decimal > 0 필드 숫자 검증 — 파싱(setData) 시점
// BytesMessage는 raw bytes를 위치 기반으로 잘라 저장하므로,
// decimal > 0 인 숫자형 필드에 비숫자 bytes가 들어오는 경우를 검증
//
// INVALID_RAW: BIG_VALUE 위치(5~14)에 비숫자 "HELLO_WOLD" 삽입
// 전문 구조 동일: INT_VALUE(5) + BIG_VALUE(10) + STR_VALUE(20) = 35 bytes
//
// C-1) 비숫자 bytes + validation=true → MessageSetDataException
// C-2) 비숫자 bytes + validation=false → 예외 없음 (기존 동작 유지)
// C-3) 정상 숫자 bytes + validation=true → 예외 없음 ([A] 테스트와 동일 경로)
// ─────────────────────────────────────────────────────────────────
private static final String INVALID_RAW =
"00042" // INT_VALUE : 5자리 정수 (decimal=0, 검증 대상 아님)
+ "HELLO_WOLD" // BIG_VALUE : 비숫자 10자리 (decimal=5 → 숫자여야 함)
+ "HELLO WORLD "; // STR_VALUE : 20자리 문자열 (decimal=0, 검증 대상 아님)
@Test
@DisplayName("[C-1] decimal>0 필드에 비숫자 bytes + validation=true → MessageSetDataException")
void testDecimalFieldNonNumeric_withValidation() {
// validation 기본값이 true이므로 enableValidation() 호출 불필요
Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1");
assertThrows(MessageSetDataException.class,
() -> msg.setData(INVALID_RAW.getBytes()),
"decimal>0 필드에 비숫자 bytes 입력 시 validation=true이면 MessageSetDataException이 발생해야 합니다");
}
@Test
@DisplayName("[C-2] decimal>0 필드에 비숫자 bytes + validation=false → 예외 없음")
void testDecimalFieldNonNumeric_withoutValidation() {
// 기본값이 true이므로 명시적으로 비활성화
System.setProperty(SystemKeys.TRANSFROMER_VALIDATION, "false");
SystemKeys.reload();
Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1");
assertDoesNotThrow(
() -> msg.setData(INVALID_RAW.getBytes()),
"validation=false 상태에서는 비숫자 bytes도 예외가 발생하지 않아야 합니다");
}
@Test
@DisplayName("[C-3] decimal>0 필드에 정상 숫자 bytes + validation=true → 예외 없음")
void testDecimalFieldNumeric_withValidation() {
// validation 기본값이 true이므로 enableValidation() 호출 불필요
Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1");
assertDoesNotThrow(
() -> msg.setData(VALID_RAW.getBytes()),
"decimal>0 필드에 정상 숫자 bytes 입력 시 validation=true에서도 예외가 발생하지 않아야 합니다");
} }
// ───────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────