diff --git a/JSON_VALIDATION_TEST_PLAN.md b/JSON_VALIDATION_TEST_PLAN.md new file mode 100644 index 0000000..7e3b6d1 --- /dev/null +++ b/JSON_VALIDATION_TEST_PLAN.md @@ -0,0 +1,132 @@ +# JSONMessage Validation 단위테스트 작업 내역 + +## 브랜치 +`feature/json-validation-test` + +## 커밋 +`ae629d5` — JSONMessage validation 단위테스트 추가 및 SystemKeys 버그 수정 +`(최신)` — doSetValue() 자릿수 검사 setData() 시점으로 이동 및 테스트 수정 + +--- + +## 1. 목표 + +`JSONMessage`의 두 가지 validation 오류 시나리오를 단위테스트로 검증한다. + +| 시나리오 | 발생 시점 | 결과 | +|---|---|---| +| 숫자형 필드에 타입 불일치 문자열 입력 | `setData()` | ✅ 정상 동작 확인 | +| 숫자형 필드의 자릿수 초과 | `setData()` | ✅ `doSetValue()` 개선으로 setData() 시점으로 통일 | + +--- + +## 2. 수정 파일 + +### 2-1. `SystemKeys.java` — 버그 수정 + +`isValidationEnabled()`가 `validationEnabled` 필드를 무시하고 `false`를 하드코딩으로 반환하는 버그. +`reload()`는 `transformer.validation` 시스템 프로퍼티를 읽어 필드에 저장하지만 반환값에 반영되지 않았다. + +```java +// 수정 전 +public static boolean isValidationEnabled() { return false; } + +// 수정 후 +public static boolean isValidationEnabled() { return validationEnabled; } +``` + +### 2-2. `init_json_validation.sql` — 신규 + +`JSONMessageValidationTest` 전용 테스트 데이터. 공유 init 파일(`init_tseaitr07/08.sql`)을 수정하지 않기 위해 별도 파일로 분리. + +- `tseaitr07`: `EAI_UJSONTYPE_REQ1` 레이아웃 헤더 (타입: JSON) +- `tseaitr08`: 레이아웃 아이템 5건 + +| 필드명 | 타입코드 | length | decimal | 정수부 최대 | +|---|---|---|---|---| +| INT_VALUE | 2102 (INT) | 5 | 0 | — | +| LONG_VALUE | 2103 (LONG) | 10 | 0 | — | +| BIG_VALUE | 2118 (BIGDECIMAL) | 10 | 5 | 4자리 | +| BIG15_VALUE | 2118 (BIGDECIMAL) | 15 | 0 | 15자리 | + +> 정수부 최대 = `length - decimal - 1` + +### 2-3. `JSONMessageValidationTest.java` — 신규 + +`@DataJpaTest` + H2 인메모리 DB 기반 JUnit 5 테스트. 테스트 7개 전체 통과. + +기존 방식(`AllTests.setParams()` + `TransformEngine.getInstance()`)은 Spring 컨텍스트 없이 실행 시 NPE가 발생했다. `@DataJpaTest` + `@ContextConfiguration(TransformJPATestConfiguration.class)` + `@Autowired TransformEngine`으로 교체해 해결. + +### 2-4. `JSONMessage.java` — `doSetValue()` 수정 + +`doSetValue()`의 `TYPE_BIGDECIMAL` case에 자릿수 검사 추가. 기존에는 자릿수 초과가 `getData()` 시점에 감지되어 타입 불일치와 발생 시점이 달랐다. + +```java +// 수정 전 +case Types.TYPE_BIGDECIMAL : + setBigDecimal( itemPath, new BigDecimal(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; +``` + +``` +@DataJpaTest +@ContextConfiguration(classes = { TransformJPATestConfiguration.class }) +@Sql({ + "/com/eactive/eai/transformer/init_tseaitr06.sql", // 레이아웃 타입 코드 + "/com/eactive/eai/transformer/init_json_validation.sql" // EAI_UJSONTYPE_REQ1 레이아웃 +}) +``` + +--- + +## 3. 테스트 케이스 + +### 타입 불일치 — `setData()` 시점, validation 플래그 무관 + +| 테스트 메서드 | 입력 | 기대 결과 | +|---|---|---| +| `testIntFieldTypeError` | `"INT_VALUE":"abc"` | `MessageSetDataException` (cause: `NumberFormatException`) | +| `testLongFieldTypeError` | `"LONG_VALUE":"not_a_number"` | `MessageSetDataException` (cause: `NumberFormatException`) | +| `testBigDecimalFieldTypeError` | `"BIG_VALUE":"invalid_decimal"` | `MessageSetDataException` (cause: `NumberFormatException`) | + +### 자릿수 초과 — `setData()` 시점, `transformer.validation=true` 필요 + +| 테스트 메서드 | 입력 | 기대 결과 | +|---|---|---| +| `testBigDecimalIntegerPartOverflow` | `"BIG_VALUE":12345.12345` (정수부 5자리) | `MessageSetDataException` ("Invalid decimal point format") | +| `testBigDecimalFractionalPartOverflow` | `"BIG_VALUE":1234.123456` (소수부 6자리) | `MessageSetDataException` ("Invalid decimal point format") | +| `testBigDecimalIntegerOnlyOverflow` | `"BIG15_VALUE":1234567890123456` (16자리) | `MessageSetDataException` ("Invalid decimal point format") | +| `testBigDecimalWithinBounds` | `"BIG_VALUE":1234.12345` (정수부 4자리, 소수부 5자리) | 정상 처리, 결과 not null | + +--- + +## 4. 예외 전파 경로 + +``` +[타입 불일치 — setData() 시점] +setData(json) + └─ doSetData() + └─ doSetValue() + └─ new Integer/Long/BigDecimal(value) ← NumberFormatException + └─ setValue() wraps → InvalidAccessException + └─ doSetData() wraps → MessageSetDataException + +[자릿수 초과 — setData() 시점, validation=true] +setData(json) + └─ doSetData() + └─ doSetValue() + └─ isValid() false → throw InvalidAccessException + └─ setValue() wraps → InvalidAccessException + └─ doSetData() wraps → MessageSetDataException +``` 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 1559313..3dc4138 100644 --- a/src/main/java/com/eactive/eai/transformer/message/JSONMessage.java +++ b/src/main/java/com/eactive/eai/transformer/message/JSONMessage.java @@ -265,6 +265,12 @@ public class JSONMessage extends Message { 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 : diff --git a/src/test/java/com/eactive/eai/transformer/JSONMessageValidationTest.java b/src/test/java/com/eactive/eai/transformer/JSONMessageValidationTest.java index 5e6c908..5b0358a 100644 --- a/src/test/java/com/eactive/eai/transformer/JSONMessageValidationTest.java +++ b/src/test/java/com/eactive/eai/transformer/JSONMessageValidationTest.java @@ -16,7 +16,6 @@ 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.InvalidDataTypeException; import com.eactive.eai.transformer.message.Message; import com.eactive.eai.transformer.message.MessageFactory; import com.eactive.eai.transformer.message.MessageSetDataException; @@ -32,7 +31,7 @@ import com.eactive.eai.transformer.message.MessageSetDataException; * * 검증 시나리오 * 1) 타입 불일치 — 숫자형 필드에 비숫자 문자열 입력 시 setData() 에서 즉시 오류 - * 2) 길이 초과 — BigDecimal 정수부/소수부 자릿수 초과 시 getData() 에서 오류 + * 2) 길이 초과 — BigDecimal 정수부/소수부 자릿수 초과 시 setData() 에서 즉시 오류 * (SystemKeys.TRANSFROMER_VALIDATION=true 필요) */ @DataJpaTest @@ -111,59 +110,56 @@ class JSONMessageValidationTest { } // ───────────────────────────────────────────────────────────────── - // 2. 숫자형 길이 초과 테스트 — getData() 시점에 오류 발생 + // 2. 숫자형 길이 초과 테스트 — setData() 시점에 오류 발생 // transformer.validation=true 설정 필요 // ───────────────────────────────────────────────────────────────── @Test - @DisplayName("BIG_VALUE 정수부 초과(5자리 > 4자리 제한) → InvalidDataTypeException") - void testBigDecimalIntegerPartOverflow() throws Exception { + @DisplayName("BIG_VALUE 정수부 초과(5자리 > 4자리 제한) → MessageSetDataException") + void testBigDecimalIntegerPartOverflow() { enableValidation(); // 정수부 5자리(12345) → 제한(4자리) 초과 String data = "{\"EAI_UJSONTYPE_REQ1\":{\"BIG_VALUE\":12345.12345}}"; Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1"); - msg.setData(data); - InvalidDataTypeException ex = assertThrows( - InvalidDataTypeException.class, - () -> msg.getData() + 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자리 제한) → InvalidDataTypeException") - void testBigDecimalFractionalPartOverflow() throws Exception { + @DisplayName("BIG_VALUE 소수부 초과(6자리 > 5자리 제한) → MessageSetDataException") + void testBigDecimalFractionalPartOverflow() { enableValidation(); // 소수부 6자리(123456) → 제한(5자리) 초과 String data = "{\"EAI_UJSONTYPE_REQ1\":{\"BIG_VALUE\":1234.123456}}"; Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1"); - msg.setData(data); - InvalidDataTypeException ex = assertThrows( - InvalidDataTypeException.class, - () -> msg.getData() + 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자리 제한) → InvalidDataTypeException") - void testBigDecimalIntegerOnlyOverflow() throws Exception { + @DisplayName("BIG15_VALUE 정수부 초과(16자리 > 15자리 제한) → MessageSetDataException") + void testBigDecimalIntegerOnlyOverflow() { enableValidation(); // 정수부 16자리 → 제한(15자리) 초과 String data = "{\"EAI_UJSONTYPE_REQ1\":{\"BIG15_VALUE\":1234567890123456}}"; Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1"); - msg.setData(data); - InvalidDataTypeException ex = assertThrows( - InvalidDataTypeException.class, - () -> msg.getData() + MessageSetDataException ex = assertThrows( + MessageSetDataException.class, + () -> msg.setData(data) ); assertTrue(containsMessage(ex, "Invalid decimal point format"), "'Invalid decimal point format' 메시지가 포함되어야 합니다");