Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cd5cb4c0b5 | |||
| a1d822c2d7 | |||
| 05e887f7f1 | |||
| 4653d6a50b | |||
| 858d72c4de | |||
| 9860f31d83 | |||
| ac71355e05 | |||
| e0ea4a4f5d | |||
| 82c7c2bc8c | |||
| d469fac2d7 | |||
| 2d3f659012 | |||
| 17f6df5068 |
@@ -231,3 +231,6 @@ gradle-app.setting
|
|||||||
# End of https://www.gitignore.io/api/java,macos,gradle,intellij
|
# End of https://www.gitignore.io/api/java,macos,gradle,intellij
|
||||||
/EAISIMWeb/
|
/EAISIMWeb/
|
||||||
/.apt_generated_tests/
|
/.apt_generated_tests/
|
||||||
|
|
||||||
|
# 작업내역 문서
|
||||||
|
작업내역_*.md
|
||||||
|
|||||||
@@ -1,132 +0,0 @@
|
|||||||
# 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
|
|
||||||
```
|
|
||||||
@@ -7,6 +7,7 @@ public class SystemKeys {
|
|||||||
public static final String TRANSFROMER_STRING_TRIMEND = "transformer.string.trimend";
|
public static final String TRANSFROMER_STRING_TRIMEND = "transformer.string.trimend";
|
||||||
public static final String TRANSFROMER_VALIDATION = "transformer.validation";
|
public static final String TRANSFROMER_VALIDATION = "transformer.validation";
|
||||||
public static final String TRANSFROMER_DECIMALPOINT_ADD = "transformer.decimalpoint.add";
|
public static final String TRANSFROMER_DECIMALPOINT_ADD = "transformer.decimalpoint.add";
|
||||||
|
public static final String TRANSFROMER_SCIENTIFIC_NORMALIZE = "transformer.scientific.normalize";
|
||||||
|
|
||||||
public static final String TRANSFROMER_LAYOUT_CACHE_INIT_ONSTARTUP = "transformer.layout.cache.init.onstartup";
|
public static final String TRANSFROMER_LAYOUT_CACHE_INIT_ONSTARTUP = "transformer.layout.cache.init.onstartup";
|
||||||
public static final String TRANSFROMER_LAYOUT_CACHE_SIZE = "transformer.layout.cache.size";
|
public static final String TRANSFROMER_LAYOUT_CACHE_SIZE = "transformer.layout.cache.size";
|
||||||
@@ -30,6 +31,7 @@ public class SystemKeys {
|
|||||||
private static boolean stringTrimEnd = true;
|
private static boolean stringTrimEnd = true;
|
||||||
private static boolean validationEnabled = true;
|
private static boolean validationEnabled = true;
|
||||||
private static boolean addDecimalpoint = false;
|
private static boolean addDecimalpoint = false;
|
||||||
|
private static boolean scientificNormalize = true;
|
||||||
|
|
||||||
private static boolean layoutCacheOnStartup = false;
|
private static boolean layoutCacheOnStartup = false;
|
||||||
|
|
||||||
@@ -64,7 +66,14 @@ public class SystemKeys {
|
|||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
addDecimalpoint = false;
|
addDecimalpoint = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sBoolean = System.getProperty(TRANSFROMER_SCIENTIFIC_NORMALIZE, "false");
|
||||||
|
try {
|
||||||
|
scientificNormalize = Boolean.valueOf(sBoolean);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
scientificNormalize = false;
|
||||||
|
}
|
||||||
|
|
||||||
sBoolean = System.getProperty(TRANSFROMER_LAYOUT_CACHE_INIT_ONSTARTUP, "false");
|
sBoolean = System.getProperty(TRANSFROMER_LAYOUT_CACHE_INIT_ONSTARTUP, "false");
|
||||||
try {
|
try {
|
||||||
layoutCacheOnStartup = Boolean.valueOf(sBoolean);
|
layoutCacheOnStartup = Boolean.valueOf(sBoolean);
|
||||||
@@ -93,6 +102,10 @@ public class SystemKeys {
|
|||||||
return addDecimalpoint;
|
return addDecimalpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isScientificNormalizeEnabled() {
|
||||||
|
return scientificNormalize;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isLayoutCacheOnStartup() {
|
public static boolean isLayoutCacheOnStartup() {
|
||||||
return layoutCacheOnStartup;
|
return layoutCacheOnStartup;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ public class Parser implements Serializable {
|
|||||||
getJep().setAllowAssignment(true);
|
getJep().setAllowAssignment(true);
|
||||||
getJep().setAllowUndeclared(true);
|
getJep().setAllowUndeclared(true);
|
||||||
|
|
||||||
|
addBooleanConstants();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
addGenericFunctions();
|
addGenericFunctions();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -89,6 +91,13 @@ public class Parser implements Serializable {
|
|||||||
|
|
||||||
public void initSymbolTable() {
|
public void initSymbolTable() {
|
||||||
getJep().initSymTab();
|
getJep().initSymTab();
|
||||||
|
addBooleanConstants();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** JEP 기본 상수에 없는 true/false 를 Boolean 타입으로 등록한다. */
|
||||||
|
private void addBooleanConstants() {
|
||||||
|
getJep().addVariable("true", Boolean.TRUE);
|
||||||
|
getJep().addVariable("false", Boolean.FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -144,8 +153,9 @@ public class Parser implements Serializable {
|
|||||||
private void addFunction(String name, PostfixMathCommand command) {
|
private void addFunction(String name, PostfixMathCommand command) {
|
||||||
if(name == null || command == null) {
|
if(name == null || command == null) {
|
||||||
logger.warn("addFunction ("+name+","+command+") skip null");
|
logger.warn("addFunction ("+name+","+command+") skip null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
getJep().addFunction(name, command); // 원본 대소문자 (camelCase 표현식 지원)
|
||||||
getJep().addFunction(name.toUpperCase(), command);
|
getJep().addFunction(name.toUpperCase(), command);
|
||||||
getJep().addFunction(name.toLowerCase(), command);
|
getJep().addFunction(name.toLowerCase(), command);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.eactive.eai.transformer.function.generic;
|
||||||
|
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import org.nfunk.jep.ParseException;
|
||||||
|
import org.nfunk.jep.function.PostfixMathCommand;
|
||||||
|
|
||||||
|
public class ConcatN extends PostfixMathCommand {
|
||||||
|
public ConcatN() {
|
||||||
|
numberOfParameters = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void run(Stack inStack) throws ParseException {
|
||||||
|
checkStack(inStack);
|
||||||
|
|
||||||
|
int n = curNumberOfParameters;
|
||||||
|
if (n < 1) {
|
||||||
|
throw new ParseException("concatN() requires at least 1 parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] params = new String[n];
|
||||||
|
for (int i = n - 1; i >= 0; i--) {
|
||||||
|
Object p = inStack.pop();
|
||||||
|
if (!(p instanceof String)) {
|
||||||
|
throw new ParseException("Invalid parameter type: parameter " + (i + 1) + " is not a string");
|
||||||
|
}
|
||||||
|
params[i] = (String) p;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (String s : params) {
|
||||||
|
sb.append(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
inStack.push(sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.eactive.eai.transformer.function.generic;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import org.nfunk.jep.ParseException;
|
||||||
|
import org.nfunk.jep.function.PostfixMathCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JEP 커스텀 함수 - 날짜/시간 형식 변환
|
||||||
|
*
|
||||||
|
* <p>사용법: {@code convertDateTime(source, fromFormat, toFormat)}
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code source} - 변환할 날짜 문자열 (예: "20240508")</li>
|
||||||
|
* <li>{@code fromFormat} - 입력 형식 패턴 (예: "yyyyMMdd")</li>
|
||||||
|
* <li>{@code toFormat} - 출력 형식 패턴 (예: "yyyy-MM-dd")</li>
|
||||||
|
* </ul>
|
||||||
|
* 반환값: toFormat으로 재포맷된 날짜 문자열
|
||||||
|
*/
|
||||||
|
public class ConvertDateTime extends PostfixMathCommand {
|
||||||
|
public ConvertDateTime() {
|
||||||
|
numberOfParameters = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 스택에서 (source, fromFormat, toFormat) 순으로 꺼내 날짜 형식을 변환한다.
|
||||||
|
*
|
||||||
|
* @param inStack JEP 후위 연산 스택
|
||||||
|
* @throws ParseException 파라미터 타입 오류, 날짜 파싱 실패, 잘못된 형식 패턴
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void run(Stack inStack) throws ParseException {
|
||||||
|
checkStack(inStack);
|
||||||
|
|
||||||
|
Object p3 = inStack.pop();
|
||||||
|
Object p2 = inStack.pop();
|
||||||
|
Object p1 = inStack.pop();
|
||||||
|
|
||||||
|
if (!(p1 instanceof String)) {
|
||||||
|
throw new ParseException("convertDateTime(): source must be a string");
|
||||||
|
}
|
||||||
|
if (!(p2 instanceof String)) {
|
||||||
|
throw new ParseException("convertDateTime(): fromFormat must be a string");
|
||||||
|
}
|
||||||
|
if (!(p3 instanceof String)) {
|
||||||
|
throw new ParseException("convertDateTime(): toFormat must be a string");
|
||||||
|
}
|
||||||
|
|
||||||
|
String source = (String) p1;
|
||||||
|
String fromFormat = (String) p2;
|
||||||
|
String toFormat = (String) p3;
|
||||||
|
|
||||||
|
try {
|
||||||
|
SimpleDateFormat sdfFrom = new SimpleDateFormat(fromFormat);
|
||||||
|
sdfFrom.setLenient(false);
|
||||||
|
Date date = sdfFrom.parse(source);
|
||||||
|
inStack.push(new SimpleDateFormat(toFormat).format(date));
|
||||||
|
} catch (java.text.ParseException e) {
|
||||||
|
throw new ParseException(
|
||||||
|
"convertDateTime(): failed to parse '" + source + "' with format '" + fromFormat + "': " + e.getMessage());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new ParseException(
|
||||||
|
"convertDateTime(): invalid format pattern: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.eactive.eai.transformer.function.generic;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import org.nfunk.jep.ParseException;
|
||||||
|
import org.nfunk.jep.function.PostfixMathCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JEP 커스텀 함수 - 현재 일시 반환
|
||||||
|
*
|
||||||
|
* <p>사용법: {@code getCurrent(format)}
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code format} - SimpleDateFormat 패턴 (예: "yyyyMMddHHmmss")</li>
|
||||||
|
* </ul>
|
||||||
|
* 반환값: 지정 형식으로 포맷된 현재 일시 문자열
|
||||||
|
*/
|
||||||
|
public class GetCurrent extends PostfixMathCommand {
|
||||||
|
public GetCurrent() {
|
||||||
|
numberOfParameters = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 스택에서 format 패턴을 꺼내 현재 일시를 포맷하여 반환한다.
|
||||||
|
*
|
||||||
|
* @param inStack JEP 후위 연산 스택
|
||||||
|
* @throws ParseException 파라미터 타입 오류, 잘못된 형식 패턴
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void run(Stack inStack) throws ParseException {
|
||||||
|
checkStack(inStack);
|
||||||
|
|
||||||
|
Object p1 = inStack.pop();
|
||||||
|
|
||||||
|
if (!(p1 instanceof String)) {
|
||||||
|
throw new ParseException("getCurrent(): format must be a string");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
inStack.push(new SimpleDateFormat((String) p1).format(new Date()));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new ParseException("getCurrent(): invalid date format '" + p1 + "': " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package com.eactive.eai.transformer.function.generic;
|
||||||
|
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import org.nfunk.jep.ParseException;
|
||||||
|
import org.nfunk.jep.function.PostfixMathCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JEP 커스텀 함수 - 삼항 조건 평가
|
||||||
|
*
|
||||||
|
* <p>사용법: {@code getTrinomial(target, compare, operator, trueValue, falseValue)}
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code target} - 비교 대상 값</li>
|
||||||
|
* <li>{@code compare} - 비교 기준 값</li>
|
||||||
|
* <li>{@code operator} - 비교 연산자 문자열 (=, <, >, <=, >=)</li>
|
||||||
|
* <li>{@code trueValue} - 조건이 참일 때 반환할 값</li>
|
||||||
|
* <li>{@code falseValue} - 조건이 거짓일 때 반환할 값</li>
|
||||||
|
* </ul>
|
||||||
|
* 타입별 비교: Number는 수치 비교, Boolean은 '=' 만 지원, 그 외는 문자열 사전순 비교
|
||||||
|
*/
|
||||||
|
public class GetTrinomial extends PostfixMathCommand {
|
||||||
|
public GetTrinomial() {
|
||||||
|
numberOfParameters = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 스택에서 (target, compare, operator, trueValue, falseValue) 순으로 꺼내 조건을 평가한다.
|
||||||
|
*
|
||||||
|
* @param inStack JEP 후위 연산 스택
|
||||||
|
* @throws ParseException operator 타입 오류, 지원하지 않는 연산자, boolean에 '=' 외 연산자 사용
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void run(Stack inStack) throws ParseException {
|
||||||
|
checkStack(inStack);
|
||||||
|
|
||||||
|
Object falseValue = inStack.pop();
|
||||||
|
Object trueValue = inStack.pop();
|
||||||
|
Object operator = inStack.pop();
|
||||||
|
Object compare = inStack.pop();
|
||||||
|
Object target = inStack.pop();
|
||||||
|
|
||||||
|
if (!(operator instanceof String)) {
|
||||||
|
throw new ParseException("getTrinomial(): operator must be a string (=, <, >, <=, >=)");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean result = compare(target, compare, ((String) operator).trim());
|
||||||
|
inStack.push(result ? trueValue : falseValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* target과 compare를 op 연산자로 비교한다.
|
||||||
|
* Number → 수치 비교, Boolean → 동등 비교, 그 외 → 문자열 사전순 비교
|
||||||
|
*/
|
||||||
|
private boolean compare(Object target, Object compare, String op) throws ParseException {
|
||||||
|
if (target instanceof Number && compare instanceof Number) {
|
||||||
|
double t = ((Number) target).doubleValue();
|
||||||
|
double c = ((Number) compare).doubleValue();
|
||||||
|
switch (op) {
|
||||||
|
case "=": return t == c;
|
||||||
|
case "<": return t < c;
|
||||||
|
case ">": return t > c;
|
||||||
|
case "<=": return t <= c;
|
||||||
|
case ">=": return t >= c;
|
||||||
|
default: throw new ParseException("getTrinomial(): unsupported operator: " + op);
|
||||||
|
}
|
||||||
|
} else if (target instanceof Boolean && compare instanceof Boolean) {
|
||||||
|
if (!"=".equals(op)) {
|
||||||
|
throw new ParseException("getTrinomial(): boolean comparison only supports '=' operator");
|
||||||
|
}
|
||||||
|
return target.equals(compare);
|
||||||
|
} else {
|
||||||
|
String t = target != null ? target.toString() : "";
|
||||||
|
String c = compare != null ? compare.toString() : "";
|
||||||
|
int cmp = t.compareTo(c);
|
||||||
|
switch (op) {
|
||||||
|
case "=": return cmp == 0;
|
||||||
|
case "<": return cmp < 0;
|
||||||
|
case ">": return cmp > 0;
|
||||||
|
case "<=": return cmp <= 0;
|
||||||
|
case ">=": return cmp >= 0;
|
||||||
|
default: throw new ParseException("getTrinomial(): unsupported operator: " + op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.eactive.eai.transformer.function.generic;
|
||||||
|
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import org.nfunk.jep.ParseException;
|
||||||
|
import org.nfunk.jep.function.PostfixMathCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JEP 커스텀 함수 - 문자열 전체 치환
|
||||||
|
*
|
||||||
|
* <p>사용법: {@code replaceAll(source, from, to)}
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code source} - 원본 문자열</li>
|
||||||
|
* <li>{@code from} - 치환 대상 문자열 (정규식 아님, 리터럴 매칭)</li>
|
||||||
|
* <li>{@code to} - 치환 결과 문자열</li>
|
||||||
|
* </ul>
|
||||||
|
* 반환값: source 내 from과 일치하는 모든 부분을 to로 치환한 문자열
|
||||||
|
*
|
||||||
|
* <p>주의: JEP 내장 함수 {@code str()}와 충돌하므로, 변수명으로 {@code str}을 사용하지 말 것.
|
||||||
|
*/
|
||||||
|
public class ReplaceAll extends PostfixMathCommand {
|
||||||
|
public ReplaceAll() {
|
||||||
|
numberOfParameters = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 스택에서 (source, from, to) 순으로 꺼내 전체 치환 후 결과를 반환한다.
|
||||||
|
*
|
||||||
|
* @param inStack JEP 후위 연산 스택
|
||||||
|
* @throws ParseException 파라미터가 String 타입이 아닌 경우
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void run(Stack inStack) throws ParseException {
|
||||||
|
checkStack(inStack);
|
||||||
|
|
||||||
|
Object p3 = inStack.pop();
|
||||||
|
Object p2 = inStack.pop();
|
||||||
|
Object p1 = inStack.pop();
|
||||||
|
|
||||||
|
if (!(p1 instanceof String)) {
|
||||||
|
throw new ParseException("replaceAll(): first parameter (source string) must be a string");
|
||||||
|
}
|
||||||
|
if (!(p2 instanceof String)) {
|
||||||
|
throw new ParseException("replaceAll(): second parameter (from string) must be a string");
|
||||||
|
}
|
||||||
|
if (!(p3 instanceof String)) {
|
||||||
|
throw new ParseException("replaceAll(): third parameter (to string) must be a string");
|
||||||
|
}
|
||||||
|
|
||||||
|
inStack.push(((String) p1).replace((String) p2, (String) p3));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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) {
|
public static String addPoint(String dpNum, int tLen , int dLen) {
|
||||||
if (dpNum == null || dpNum.trim().length() ==0 ){
|
if (dpNum == null || dpNum.trim().length() ==0 ){
|
||||||
return "0";
|
return "0";
|
||||||
|
|||||||
@@ -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()+")");
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import java.util.Stack;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import com.eactive.eai.transformer.SystemKeys;
|
import com.eactive.eai.transformer.SystemKeys;
|
||||||
|
import com.eactive.eai.transformer.layout.Converter;
|
||||||
|
import com.eactive.eai.transformer.layout.Group;
|
||||||
import com.eactive.eai.transformer.layout.InvalidNodeException;
|
import com.eactive.eai.transformer.layout.InvalidNodeException;
|
||||||
import com.eactive.eai.transformer.layout.Item;
|
import com.eactive.eai.transformer.layout.Item;
|
||||||
import com.eactive.eai.transformer.layout.Types;
|
import com.eactive.eai.transformer.layout.Types;
|
||||||
@@ -17,6 +19,7 @@ import com.eactive.eai.transformer.util.Logger;
|
|||||||
import com.fasterxml.jackson.core.JsonFactory;
|
import com.fasterxml.jackson.core.JsonFactory;
|
||||||
import com.fasterxml.jackson.core.JsonGenerator;
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.core.json.JsonReadFeature;
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
@@ -36,22 +39,24 @@ public class JSONMessage extends Message {
|
|||||||
protected JsonNode data;
|
protected JsonNode data;
|
||||||
protected ObjectMapper mapper = null;
|
protected ObjectMapper mapper = null;
|
||||||
protected JsonFactory factory = null;
|
protected JsonFactory factory = null;
|
||||||
protected boolean bStaled = false;
|
protected boolean bStaled = true;
|
||||||
|
|
||||||
public JSONMessage() {
|
public JSONMessage() {
|
||||||
super();
|
super();
|
||||||
mapper = new ObjectMapper();
|
mapper = new ObjectMapper();
|
||||||
// if necessary, add proper options -> use system property config ?
|
// if necessary, add proper options -> use system property config ?
|
||||||
mapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
|
mapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
|
||||||
mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
|
mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
|
||||||
mapper.setNodeFactory(JsonNodeFactory.withExactBigDecimals(true));
|
mapper.setNodeFactory(JsonNodeFactory.withExactBigDecimals(true));
|
||||||
|
|
||||||
factory = mapper.getFactory();
|
factory = mapper.getFactory();
|
||||||
|
factory.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createObject() {
|
public void createObject() {
|
||||||
data = mapper.createObjectNode();
|
// data = mapper.createObjectNode();
|
||||||
|
data = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -97,59 +102,28 @@ public class JSONMessage extends Message {
|
|||||||
throw new InvalidDataTypeException("Not able to generate JSON messages with data. data type = " + data.getClass().getName(), data);
|
throw new InvalidDataTypeException("Not able to generate JSON messages with data. data type = " + data.getClass().getName(), data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.data == null) {
|
ObjectNode node = (ObjectNode) this.data.get(getName());
|
||||||
logger.error("Parsed data is null");
|
if(node == null) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//ObjectNode node = (ObjectNode) this.data.get(getName());
|
|
||||||
/*
|
|
||||||
if(node == null) {
|
|
||||||
// if layoutname not appended
|
// if layoutname not appended
|
||||||
setValue(this.data, getName());
|
setValue(this.data, getName());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
setValue(node, getName());
|
setValue(node, getName());
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
ObjectNode node = null;
|
|
||||||
|
|
||||||
// 변경된 부분: NullNode 검사 추가
|
|
||||||
if (this.data.has(getName()) && !this.data.get(getName()).isNull()) {
|
|
||||||
JsonNode nameNode = this.data.get(getName());
|
|
||||||
if (nameNode.isObject()) {
|
|
||||||
node = (ObjectNode) nameNode;
|
|
||||||
|
|
||||||
// 배열 데이터 처리 개선
|
|
||||||
processArrayNodes(this.data);
|
|
||||||
|
|
||||||
// 값 설정
|
|
||||||
setValue(node, getName());
|
|
||||||
} else {
|
|
||||||
logger.warn("Node with name '" + getName() + "' is not an object, skipping setValue operation");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// layoutname이 추가되지 않은 경우
|
|
||||||
setValue(this.data, getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new MessageSetDataException(e.getMessage(), this, data, e);
|
throw new MessageSetDataException(e.getMessage(), this, data, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(JsonNode node, String itempPath) throws InvalidAccessException {
|
public void setValue(JsonNode node, String itempPath) throws InvalidAccessException {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// this.value = Converter.convert(value, getDataTypeId());
|
// this.value = Converter.convert(value, getDataTypeId());
|
||||||
/**
|
/**
|
||||||
* occRef 설정을 위해
|
* occRef 설정을 위해
|
||||||
*/
|
*/
|
||||||
|
|
||||||
HashMap validationTargets = new HashMap();
|
HashMap validationTargets = new HashMap();
|
||||||
|
|
||||||
doSetValue(node, validationTargets, itempPath);
|
doSetValue(node, validationTargets, itempPath);
|
||||||
|
|
||||||
// validation
|
// validation
|
||||||
@@ -166,25 +140,38 @@ 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());
|
throw new InvalidAccessException("The number of repetitions is different from the number of repetitions of the actual data. , " + item.getName(), item.getItemPath());
|
||||||
}
|
}
|
||||||
} // validation
|
} // validation
|
||||||
|
} catch(InvalidAccessException e) {
|
||||||
|
throw e;
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
throw new InvalidAccessException("setValue error", getItemPath(), null, e);
|
throw new InvalidAccessException("setValue error", getItemPath(), null, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void doSetValue(JsonNode node, HashMap validationTargets, String parentPath) {
|
protected void doSetValue(JsonNode node, HashMap validationTargets, String parentPath) {
|
||||||
Item item = null;
|
Item item = null;
|
||||||
|
if (node.isNull()) {
|
||||||
|
// null이면 타겟 형식에서 Grid인지 확인 해 봄
|
||||||
|
String itemPath = parentPath + "[-1]";
|
||||||
|
item = this.getItem(itemPath);
|
||||||
|
if (item != null && item instanceof Group && StringUtils.equalsAny(item.getOccType(), "*", "+")) {
|
||||||
|
item = getOrCreateItem(denomalizeXPath(parentPath));
|
||||||
|
if (item != null) {
|
||||||
|
item.setIndex(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if( node.isObject()) {
|
if( node.isObject()) {
|
||||||
Iterator<String> fieldNames = node.fieldNames();
|
Iterator<String> fieldNames = node.fieldNames();
|
||||||
if(StringUtils.isNotEmpty(parentPath)) {
|
if(StringUtils.isNotEmpty(parentPath)) {
|
||||||
item = getOrCreateItem(denomalizeXPath(parentPath));
|
item = getOrCreateItem(denomalizeXPath(parentPath));
|
||||||
|
|
||||||
if(SystemKeys.isValidationEnabled() && StringUtils.isNotEmpty(item.getOccRef())) {
|
if(SystemKeys.isValidationEnabled() && StringUtils.isNotEmpty(item.getOccRef())) {
|
||||||
validationTargets.put(item.getParent().getItemPath() + Types.FIELD_SEPARATOR + item.getOccRef(), item);
|
validationTargets.put(item.getParent().getItemPath() + Types.FIELD_SEPARATOR + item.getOccRef(), item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while(fieldNames.hasNext()) {
|
while(fieldNames.hasNext()) {
|
||||||
String fieldName = fieldNames.next();
|
String fieldName = fieldNames.next();
|
||||||
|
|
||||||
JsonNode fieldValue = node.get(fieldName);
|
JsonNode fieldValue = node.get(fieldName);
|
||||||
try {
|
try {
|
||||||
String path = null;
|
String path = null;
|
||||||
@@ -203,92 +190,85 @@ public class JSONMessage extends Message {
|
|||||||
}
|
}
|
||||||
else if( node.isArray() ) {
|
else if( node.isArray() ) {
|
||||||
ArrayNode arrayNode = (ArrayNode) node;
|
ArrayNode arrayNode = (ArrayNode) node;
|
||||||
|
if(arrayNode.size() == 0 && StringUtils.isNotEmpty(parentPath)) {
|
||||||
// 배열이 문자열 값만 포함하는지 확인
|
try {
|
||||||
boolean isSimpleArray = true;
|
// 아직 clone 되지 않았으면 생성 안함.
|
||||||
for (int i = 0; i < arrayNode.size(); i++) {
|
item = this.getItem(denomalizeXPath(parentPath));
|
||||||
if (!arrayNode.get(i).isValueNode()) {
|
if (item != null) {
|
||||||
isSimpleArray = false;
|
// clone 후에는 -1로
|
||||||
break;
|
item = getOrCreateItem(denomalizeXPath(parentPath));
|
||||||
|
if (item != null) {
|
||||||
|
item.setIndex(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InvalidNodeException ie) {
|
||||||
|
// continue silently
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(int i = 0; i < arrayNode.size(); i++) {
|
||||||
|
JsonNode arrayItem = arrayNode.get(i);
|
||||||
|
if(StringUtils.isNotEmpty(parentPath)) {
|
||||||
|
try {
|
||||||
|
item = getOrCreateItem(denomalizeXPath(parentPath + "[" + i + "]"));
|
||||||
|
} catch(InvalidNodeException ie) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
doSetValue(arrayItem, validationTargets, parentPath + "[" + i + "]");
|
||||||
|
}
|
||||||
if (isSimpleArray) {
|
|
||||||
// 문자열 배열은 직접 값 설정
|
|
||||||
try {
|
|
||||||
setString(parentPath, arrayNode.toString());
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("Error setting simple array: " + parentPath, e);
|
|
||||||
// 개별 요소 설정
|
|
||||||
for (int i = 0; i < arrayNode.size(); i++) {
|
|
||||||
try {
|
|
||||||
String itemPath = parentPath + "[" + i + "]";
|
|
||||||
setString(itemPath, arrayNode.get(i).asText());
|
|
||||||
} catch (Exception ex) {
|
|
||||||
logger.error("Error setting array item: " + parentPath + "[" + i + "]", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 복잡한 배열 구조 처리
|
|
||||||
for (int i = 0; i < arrayNode.size(); i++) {
|
|
||||||
JsonNode arrayItem = arrayNode.get(i);
|
|
||||||
|
|
||||||
if (StringUtils.isNotEmpty(parentPath)) {
|
|
||||||
try {
|
|
||||||
item = getOrCreateItem(denomalizeXPath(parentPath + "[" + i + "]"));
|
|
||||||
} catch (InvalidNodeException ie) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doSetValue(arrayItem, validationTargets, parentPath + "[" + i + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String itemPath = parentPath;
|
String itemPath = parentPath;
|
||||||
item = getOrCreateItem(denomalizeXPath(itemPath));
|
item = getOrCreateItem(denomalizeXPath(itemPath));
|
||||||
String value = node.asText();
|
String value = node.asText();
|
||||||
|
if (node.isNull()) return;
|
||||||
if(node == null || node.isNull()) {
|
if(value == null || value.length() == 0) {
|
||||||
setObject(itemPath, null); // 실제 null 값 설정
|
|
||||||
} else if(value == null || value.length() == 0) {
|
|
||||||
setObject( itemPath, value );
|
setObject( itemPath, value );
|
||||||
} else {
|
}
|
||||||
int itemType = item.getDataTypeId();
|
else {
|
||||||
switch(itemType) {
|
int itemType = item.getDataTypeId();
|
||||||
case Types.TYPE_INT :
|
try {
|
||||||
case Types.TYPE_SHORT :
|
switch(itemType) {
|
||||||
setInt( itemPath, new Integer(value) );
|
case Types.TYPE_INT :
|
||||||
break;
|
case Types.TYPE_SHORT :
|
||||||
case Types.TYPE_LONG :
|
setInt( itemPath, new Integer(value) );
|
||||||
setLong( itemPath, new Long(value) );
|
break;
|
||||||
break;
|
case Types.TYPE_LONG :
|
||||||
case Types.TYPE_BIGDECIMAL :
|
setLong( itemPath, new Long(value) );
|
||||||
if (SystemKeys.isValidationEnabled() && !isValid(value, item.getLength(), item.getDecimalPointLength())) {
|
break;
|
||||||
throw new InvalidAccessException(
|
case Types.TYPE_BIGDECIMAL :
|
||||||
String.format("Invalid decimal point format Error value=%s,length=%d,pointLength=%d",
|
if (SystemKeys.isValidationEnabled() && !Converter.isValid(value, item.getLength(), item.getDecimalPointLength())) {
|
||||||
value, item.getLength(), item.getDecimalPointLength()),
|
throw new InvalidAccessException(
|
||||||
itemPath);
|
String.format("Invalid decimal point format Error value=%s,length=%d,pointLength=%d",
|
||||||
}
|
value, item.getLength(), item.getDecimalPointLength()),
|
||||||
setBigDecimal( itemPath, new BigDecimal(value) );
|
itemPath);
|
||||||
break;
|
}
|
||||||
case Types.TYPE_DOUBLE :
|
setBigDecimal( itemPath, new BigDecimal(value) );
|
||||||
setDouble( itemPath, new Double(value) );
|
break;
|
||||||
break;
|
case Types.TYPE_DOUBLE :
|
||||||
case Types.TYPE_FLOAT :
|
setDouble( itemPath, new Double(value) );
|
||||||
setFloat( itemPath, new Float(value) );
|
break;
|
||||||
break;
|
case Types.TYPE_FLOAT :
|
||||||
case Types.TYPE_BOOLEAN :
|
setFloat( itemPath, new Float(value) );
|
||||||
setObject( itemPath, new Boolean(value) );
|
break;
|
||||||
break;
|
case Types.TYPE_BOOLEAN :
|
||||||
case Types.TYPE_STRING :
|
setObject( itemPath, new Boolean(value) );
|
||||||
setString(itemPath, value);
|
break;
|
||||||
break;
|
case Types.TYPE_STRING :
|
||||||
default :
|
setString(itemPath, value);
|
||||||
setString(itemPath, value);
|
break;
|
||||||
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -358,9 +338,8 @@ public class JSONMessage extends Message {
|
|||||||
if( parent instanceof ObjectNode ) {
|
if( parent instanceof ObjectNode ) {
|
||||||
obj = (ObjectNode)parent;
|
obj = (ObjectNode)parent;
|
||||||
|
|
||||||
String val = getStringValue(item.getValue(),item);
|
String val = getStringValue(item.getValue(),item);
|
||||||
int itemType = item.getDataTypeId();
|
int itemType = item.getDataTypeId();
|
||||||
|
|
||||||
if (isMask && "Y".equals(item.getMask()) && (Types.TYPE_STRING == itemType )){
|
if (isMask && "Y".equals(item.getMask()) && (Types.TYPE_STRING == itemType )){
|
||||||
val = maskString2(val, item.getMaskLength(), item.getMaskOffset());
|
val = maskString2(val, item.getMaskLength(), item.getMaskOffset());
|
||||||
item.setValue(val);
|
item.setValue(val);
|
||||||
@@ -373,8 +352,9 @@ public class JSONMessage extends Message {
|
|||||||
arr = mapper.createArrayNode();
|
arr = mapper.createArrayNode();
|
||||||
setJsonArrayValue(arr, item);
|
setJsonArrayValue(arr, item);
|
||||||
obj.set(prefix+item.getName(), arr);
|
obj.set(prefix+item.getName(), arr);
|
||||||
}else{
|
}else{
|
||||||
arr.add(value);
|
// arr.add(value);
|
||||||
|
setJsonArrayValue(arr, item); //IBK에서 사용.
|
||||||
}
|
}
|
||||||
|
|
||||||
} else { //FIELD
|
} else { //FIELD
|
||||||
@@ -406,7 +386,7 @@ public class JSONMessage extends Message {
|
|||||||
logger.error("not support parent type JSONArray");
|
logger.error("not support parent type JSONArray");
|
||||||
throw new Exception("not support parent type JSONArray");
|
throw new Exception("not support parent type JSONArray");
|
||||||
}
|
}
|
||||||
if (item.getChildCount() > 0){
|
if (item.getChildCount() > 0 ){
|
||||||
stack.push(current);
|
stack.push(current);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -431,38 +411,11 @@ public class JSONMessage extends Message {
|
|||||||
private void setJsonNodeValue(ObjectNode node, String fieldName, Item item) {
|
private void setJsonNodeValue(ObjectNode node, String fieldName, Item item) {
|
||||||
int itemType = item.getDataTypeId();
|
int itemType = item.getDataTypeId();
|
||||||
Object value = item.getValue();
|
Object value = item.getValue();
|
||||||
|
|
||||||
if(value == null) {
|
if(value == null) {
|
||||||
node.putNull(fieldName);
|
node.putNull(fieldName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(value instanceof JsonNode) {
|
|
||||||
node.set(fieldName, (JsonNode) value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// New code to check if string is JSON array
|
|
||||||
if (itemType == Types.TYPE_STRING && value instanceof String) {
|
|
||||||
String strValue = (String) value;
|
|
||||||
if ((strValue.startsWith("[") && strValue.endsWith("]")) ||
|
|
||||||
(strValue.startsWith("{") && strValue.endsWith("}"))) {
|
|
||||||
try {
|
|
||||||
// Try to parse as JSON
|
|
||||||
JsonNode jsonNode = mapper.readTree(strValue);
|
|
||||||
node.set(fieldName, jsonNode);
|
|
||||||
return;
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Not valid JSON, continue with normal processing
|
|
||||||
logger.debug("Failed to parse as JSON, using as string: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(value instanceof byte[])) {
|
|
||||||
logger.info("value : " + value.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(itemType) {
|
switch(itemType) {
|
||||||
case Types.TYPE_INT :
|
case Types.TYPE_INT :
|
||||||
case Types.TYPE_SHORT :
|
case Types.TYPE_SHORT :
|
||||||
@@ -472,8 +425,9 @@ public class JSONMessage extends Message {
|
|||||||
node.put(fieldName, (long) value);
|
node.put(fieldName, (long) value);
|
||||||
break;
|
break;
|
||||||
case Types.TYPE_BIGDECIMAL :
|
case Types.TYPE_BIGDECIMAL :
|
||||||
BigDecimal bd = (BigDecimal)value;
|
// BigDecimal bd = (BigDecimal)value;
|
||||||
node.put(fieldName, bd);
|
// node.put(fieldName, bd.floatValue());
|
||||||
|
node.put(fieldName, (BigDecimal)value);
|
||||||
break;
|
break;
|
||||||
case Types.TYPE_DOUBLE :
|
case Types.TYPE_DOUBLE :
|
||||||
node.put(fieldName, (double) value);
|
node.put(fieldName, (double) value);
|
||||||
@@ -485,12 +439,7 @@ public class JSONMessage extends Message {
|
|||||||
node.put(fieldName, (boolean) value);
|
node.put(fieldName, (boolean) value);
|
||||||
break;
|
break;
|
||||||
case Types.TYPE_STRING :
|
case Types.TYPE_STRING :
|
||||||
|
|
||||||
node.put(fieldName, (String) value);
|
node.put(fieldName, (String) value);
|
||||||
if (value instanceof JsonNode) {
|
|
||||||
logger.info("value : " + (String) value );
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default :
|
default :
|
||||||
node.put(fieldName, (String) value);
|
node.put(fieldName, (String) value);
|
||||||
@@ -512,13 +461,13 @@ public class JSONMessage extends Message {
|
|||||||
arrayNode.add( (long) value);
|
arrayNode.add( (long) value);
|
||||||
break;
|
break;
|
||||||
case Types.TYPE_BIGDECIMAL :
|
case Types.TYPE_BIGDECIMAL :
|
||||||
arrayNode.add( (float) value);
|
arrayNode.add( (BigDecimal)value);
|
||||||
break;
|
break;
|
||||||
case Types.TYPE_DOUBLE :
|
case Types.TYPE_DOUBLE :
|
||||||
arrayNode.add( (double) value);
|
arrayNode.add( (double) value);
|
||||||
break;
|
break;
|
||||||
case Types.TYPE_FLOAT :
|
case Types.TYPE_FLOAT :
|
||||||
arrayNode.add( (float) value);
|
arrayNode.add( (float) value);
|
||||||
break;
|
break;
|
||||||
case Types.TYPE_BOOLEAN :
|
case Types.TYPE_BOOLEAN :
|
||||||
arrayNode.add( (boolean) value);
|
arrayNode.add( (boolean) value);
|
||||||
@@ -527,7 +476,7 @@ public class JSONMessage extends Message {
|
|||||||
arrayNode.add( (String) value);
|
arrayNode.add( (String) value);
|
||||||
break;
|
break;
|
||||||
default :
|
default :
|
||||||
arrayNode.add( (String) value);
|
arrayNode.add( (String) value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,8 +520,7 @@ public class JSONMessage extends Message {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public Object getData() {
|
public Object getData() {
|
||||||
|
if (bStaled || this.data == null) {
|
||||||
if (bStaled) {
|
|
||||||
try {
|
try {
|
||||||
this.data = getJSONData();
|
this.data = getJSONData();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
@@ -592,7 +540,6 @@ public class JSONMessage extends Message {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if(this.data == null) return null;
|
if(this.data == null) return null;
|
||||||
|
|
||||||
if(isPretty) return data.toPrettyString();
|
if(isPretty) return data.toPrettyString();
|
||||||
else return data.toString();
|
else return data.toString();
|
||||||
}
|
}
|
||||||
@@ -651,7 +598,7 @@ public class JSONMessage extends Message {
|
|||||||
|
|
||||||
private String getFormat(String value, int length, int pointLength, boolean isValidation) throws Exception{
|
private String getFormat(String value, int length, int pointLength, boolean isValidation) throws Exception{
|
||||||
if(isValidation) {
|
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"
|
throw new Exception(String.format("Invalid decimal point format Error value=%s,length=%d,pointLength=%d"
|
||||||
, value,length,pointLength)
|
, value,length,pointLength)
|
||||||
);
|
);
|
||||||
@@ -714,7 +661,7 @@ public class JSONMessage extends Message {
|
|||||||
if (data.length != 1){ //소수점이 들어와서 오류
|
if (data.length != 1){ //소수점이 들어와서 오류
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (data[0].length() > length){ //정수부 오류
|
if (data[0].length() > length - pointLength - 1){ //정수부 오류
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -753,71 +700,12 @@ public class JSONMessage extends Message {
|
|||||||
private String getHeader() {
|
private String getHeader() {
|
||||||
return getName() + " (" + MESSAGE_NAME + ")";
|
return getName() + " (" + MESSAGE_NAME + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 배열 처리 메소드
|
|
||||||
private void processArrayNodes(JsonNode rootNode) {
|
|
||||||
Iterator<String> fieldNames = rootNode.fieldNames();
|
|
||||||
while (fieldNames.hasNext()) {
|
|
||||||
String fieldName = fieldNames.next();
|
|
||||||
JsonNode node = rootNode.get(fieldName);
|
|
||||||
|
|
||||||
// data_part와 같은 객체 노드 처리
|
|
||||||
if (node.isObject()) {
|
|
||||||
processArrayNodes(node);
|
|
||||||
}
|
|
||||||
// 배열 노드 처리
|
|
||||||
else if (node.isArray()) {
|
|
||||||
ArrayNode arrayNode = (ArrayNode) node;
|
|
||||||
|
|
||||||
// 배열 노드 처리
|
|
||||||
if (arrayNode.size() > 0) {
|
|
||||||
// 배열 데이터 설정
|
|
||||||
try {
|
|
||||||
|
|
||||||
if(this.data instanceof ObjectNode) {
|
|
||||||
ObjectNode objNode = (ObjectNode) this.data;
|
|
||||||
objNode.set(fieldName, arrayNode);
|
|
||||||
} else if (rootNode instanceof ObjectNode) {
|
|
||||||
((ObjectNode) rootNode).set(fieldName, arrayNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i=0; i < arrayNode.size(); i++) {
|
|
||||||
JsonNode itemNode = arrayNode.get(i);
|
|
||||||
|
|
||||||
String itemPath = fieldName + "[" + i + "]";
|
|
||||||
|
|
||||||
if(itemNode.isObject()) {
|
|
||||||
processArrayNodes(itemNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("Error setting array data for field: " + fieldName, e);
|
|
||||||
// 배열 요소를 개별적으로 처리
|
|
||||||
for (int i = 0; i < arrayNode.size(); i++) {
|
|
||||||
try {
|
|
||||||
String itemPath = fieldName + "[" + i + "]";
|
|
||||||
JsonNode itemNode = arrayNode.get(i);
|
|
||||||
if (itemNode.isTextual()) {
|
|
||||||
setString(itemPath, itemNode.asText());
|
|
||||||
} else {
|
|
||||||
setString(itemPath, itemNode.toString());
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
logger.error("Error setting array item [" + i + "] for field: " + fieldName, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString(){
|
public String toString(){
|
||||||
// if staled -> reorganize data
|
// if staled -> reorganize data
|
||||||
String jsonString = null;
|
String jsonString = null;
|
||||||
try {
|
try {
|
||||||
jsonString = (String)getData();
|
jsonString = toLogString();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("[JSONMessage] " + e.getMessage(), e);
|
logger.error("[JSONMessage] " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.dom4j.Namespace;
|
|||||||
import org.dom4j.QName;
|
import org.dom4j.QName;
|
||||||
|
|
||||||
import com.eactive.eai.transformer.SystemKeys;
|
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.InvalidNodeException;
|
||||||
import com.eactive.eai.transformer.layout.Item;
|
import com.eactive.eai.transformer.layout.Item;
|
||||||
import com.eactive.eai.transformer.layout.Layout;
|
import com.eactive.eai.transformer.layout.Layout;
|
||||||
@@ -198,8 +199,17 @@ public class XMLMessage extends Message {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (item.isField()) {
|
else if (item.isField()) {
|
||||||
Object data = child.getData();
|
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 {
|
try {
|
||||||
if (data instanceof String){
|
if (data instanceof String){
|
||||||
setString(childPath, (String) data);
|
setString(childPath, (String) data);
|
||||||
|
|||||||
@@ -0,0 +1,247 @@
|
|||||||
|
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.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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.clearProperty(SystemKeys.TRANSFROMER_VALIDATION);
|
||||||
|
System.clearProperty(SystemKeys.TRANSFROMER_SCIENTIFIC_NORMALIZE);
|
||||||
|
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() — 자릿수 초과 문자열
|
||||||
|
// setString 경로는 setDataBytes()를 통하지 않으므로
|
||||||
|
// validation 설정과 완전히 무관하게 예외 발생 안 함 (B-3과 결과 동일)
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("[B-4] BIG_VALUE 자릿수 초과 setString → setString 경로는 decimal 검증 대상 아님")
|
||||||
|
void testSetStringOverflowIsNotSubjectToDecimalValidation() {
|
||||||
|
// setString → setDataBytes() 미경유 → decimal 숫자형 검증 실행 안 됨
|
||||||
|
// validation 설정과 무관하게 예외 없음
|
||||||
|
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"),
|
||||||
|
"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에서도 예외가 발생하지 않아야 합니다");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// 유틸 메서드
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void enableValidation() {
|
||||||
|
System.setProperty(SystemKeys.TRANSFROMER_VALIDATION, "true");
|
||||||
|
SystemKeys.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
package com.eactive.eai.transformer;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.nfunk.jep.JEP;
|
||||||
|
|
||||||
|
import com.eactive.eai.transformer.function.generic.ConcatN;
|
||||||
|
|
||||||
|
@DisplayName("ConcatN 가변 파라미터 함수 테스트")
|
||||||
|
class ConcatNFunctionTest {
|
||||||
|
|
||||||
|
private JEP jep;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
jep = new JEP();
|
||||||
|
jep.addStandardFunctions();
|
||||||
|
jep.addStandardConstants();
|
||||||
|
jep.setAllowUndeclared(true);
|
||||||
|
jep.addFunction("concatN", new ConcatN());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String eval(String expr) throws Exception {
|
||||||
|
jep.parseExpression(expr);
|
||||||
|
assertFalse(jep.hasError(), "파싱 오류: " + jep.getErrorInfo());
|
||||||
|
Object result = jep.getValueAsObject();
|
||||||
|
assertFalse(jep.hasError(), "평가 오류: " + jep.getErrorInfo());
|
||||||
|
return (String) result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// 파라미터 수 변화 테스트
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("파라미터 1개 — 입력값 그대로 반환")
|
||||||
|
void testOneParam() throws Exception {
|
||||||
|
jep.addVariable("a", "Hello");
|
||||||
|
|
||||||
|
String result = eval("concatN(a)");
|
||||||
|
|
||||||
|
assertEquals("Hello", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("파라미터 2개 — 기존 Concat 동작과 동일")
|
||||||
|
void testTwoParams() throws Exception {
|
||||||
|
jep.addVariable("a", "Hello");
|
||||||
|
jep.addVariable("b", "World");
|
||||||
|
|
||||||
|
String result = eval("concatN(a, b)");
|
||||||
|
|
||||||
|
assertEquals("HelloWorld", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("파라미터 3개")
|
||||||
|
void testThreeParams() throws Exception {
|
||||||
|
jep.addVariable("a", "2024");
|
||||||
|
jep.addVariable("sep", "-");
|
||||||
|
jep.addVariable("b", "01");
|
||||||
|
|
||||||
|
String result = eval("concatN(a, sep, b)");
|
||||||
|
|
||||||
|
assertEquals("2024-01", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("파라미터 5개 — 날짜 + 시간 조합")
|
||||||
|
void testFiveParams() throws Exception {
|
||||||
|
jep.addVariable("year", "2024");
|
||||||
|
jep.addVariable("dash", "-");
|
||||||
|
jep.addVariable("month", "05");
|
||||||
|
jep.addVariable("dash2", "-");
|
||||||
|
jep.addVariable("day", "08");
|
||||||
|
|
||||||
|
String result = eval("concatN(year, dash, month, dash2, day)");
|
||||||
|
|
||||||
|
assertEquals("2024-05-08", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("파라미터 7개 — 날짜 + 공백 + 시간 조합")
|
||||||
|
void testSevenParams() throws Exception {
|
||||||
|
jep.addVariable("year", "2024");
|
||||||
|
jep.addVariable("dash", "-");
|
||||||
|
jep.addVariable("month", "05");
|
||||||
|
jep.addVariable("day", "08");
|
||||||
|
jep.addVariable("space", " ");
|
||||||
|
jep.addVariable("hour", "14");
|
||||||
|
jep.addVariable("minute", "30");
|
||||||
|
|
||||||
|
String result = eval("concatN(year, dash, month, dash, day, space, hour)");
|
||||||
|
|
||||||
|
assertEquals("2024-05-08 14", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// 변수 재할당 후 동일 표현식 재평가 — 파라미터 수 유지, 값만 변경
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("동일 표현식(3개), 변수 값만 변경하여 반복 평가")
|
||||||
|
void testReuseExpressionWithDifferentValues() throws Exception {
|
||||||
|
jep.parseExpression("concatN(code, dash, seq)");
|
||||||
|
assertFalse(jep.hasError());
|
||||||
|
|
||||||
|
jep.addVariable("code", "KOR");
|
||||||
|
jep.addVariable("dash", "-");
|
||||||
|
jep.addVariable("seq", "001");
|
||||||
|
assertEquals("KOR-001", (String) jep.getValueAsObject());
|
||||||
|
|
||||||
|
jep.addVariable("code", "USA");
|
||||||
|
jep.addVariable("seq", "999");
|
||||||
|
assertEquals("USA-999", (String) jep.getValueAsObject());
|
||||||
|
|
||||||
|
jep.addVariable("code", "JPN");
|
||||||
|
jep.addVariable("seq", "042");
|
||||||
|
assertEquals("JPN-042", (String) jep.getValueAsObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// 리터럴 / 혼합 사용
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("리터럴 문자열만 5개 사용")
|
||||||
|
void testLiteralsOnly() throws Exception {
|
||||||
|
String result = eval("concatN(\"A\", \"B\", \"C\", \"D\", \"E\")");
|
||||||
|
|
||||||
|
assertEquals("ABCDE", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("변수와 리터럴 혼합 — 괄호로 감싸기")
|
||||||
|
void testMixedVariableAndLiteral() throws Exception {
|
||||||
|
jep.addVariable("code", "KOR");
|
||||||
|
jep.addVariable("seq", "001");
|
||||||
|
|
||||||
|
String result = eval("concatN(\"[\", code, \"-\", seq, \"]\")");
|
||||||
|
|
||||||
|
assertEquals("[KOR-001]", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// 오류 케이스
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("비문자열(숫자) 파라미터 포함 → 오류 발생")
|
||||||
|
void testNonStringParamCausesError() {
|
||||||
|
// JEP 는 정수 리터럴을 Double 로 처리하므로 평가 시 오류가 발생해야 함
|
||||||
|
jep.parseExpression("concatN(\"A\", 123, \"C\")");
|
||||||
|
if (!jep.hasError()) {
|
||||||
|
jep.getValueAsObject();
|
||||||
|
assertTrue(jep.hasError(), "비문자열 파라미터가 포함된 경우 오류가 발생해야 합니다");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package com.eactive.eai.transformer;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import com.eactive.eai.transformer.engine.Parser;
|
||||||
|
import com.eactive.eai.transformer.function.generic.ConcatN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ParserTest 스타일로 Parser 를 통해 concatN 함수를 검증한다.
|
||||||
|
*
|
||||||
|
* - 표현식은 parse() 한 번 → 변수값만 교체하며 반복 평가 (JEP 재사용 패턴)
|
||||||
|
* - 변수 : parser.addVariable() 로 바인딩
|
||||||
|
* - 상수 : 표현식 안에 "문자열" 리터럴로 직접 기입
|
||||||
|
*/
|
||||||
|
@DisplayName("ConcatN - Parser 변수/상수 혼합 테스트")
|
||||||
|
class ConcatNParserTest {
|
||||||
|
|
||||||
|
private Parser parser;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
parser = new Parser();
|
||||||
|
// DB 등록 전 테스트이므로 직접 등록
|
||||||
|
parser.getJep().addFunction("concatn", new ConcatN());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// 1. 변수만 사용
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("변수 2개 — parse 1회, 값 교체 반복 평가")
|
||||||
|
void testTwoVariables() throws Exception {
|
||||||
|
parser.parse("concatn(a, b)");
|
||||||
|
|
||||||
|
parser.addVariable("a", "Hello");
|
||||||
|
parser.addVariable("b", "World");
|
||||||
|
assertEquals("HelloWorld", parser.getValueAsObject());
|
||||||
|
|
||||||
|
parser.addVariable("a", "foo");
|
||||||
|
parser.addVariable("b", "bar");
|
||||||
|
assertEquals("foobar", parser.getValueAsObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("변수 3개")
|
||||||
|
void testThreeVariables() throws Exception {
|
||||||
|
parser.parse("concatn(v1, v2, v3)");
|
||||||
|
|
||||||
|
parser.addVariable("v1", "AAA");
|
||||||
|
parser.addVariable("v2", "BBB");
|
||||||
|
parser.addVariable("v3", "CCC");
|
||||||
|
assertEquals("AAABBBCCC", parser.getValueAsObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("변수 5개")
|
||||||
|
void testFiveVariables() throws Exception {
|
||||||
|
parser.parse("concatn(y, m, d, h, mi)");
|
||||||
|
|
||||||
|
parser.addVariable("y", "2024");
|
||||||
|
parser.addVariable("m", "05");
|
||||||
|
parser.addVariable("d", "08");
|
||||||
|
parser.addVariable("h", "14");
|
||||||
|
parser.addVariable("mi", "30");
|
||||||
|
assertEquals("20240508" + "14" + "30", parser.getValueAsObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// 2. 변수 + 상수(리터럴) 혼합
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("날짜 구분자 '-' 상수 삽입 — 변수 교체 반복")
|
||||||
|
void testDateWithSeparator() throws Exception {
|
||||||
|
parser.parse("concatn(year, \"-\", month, \"-\", day)");
|
||||||
|
|
||||||
|
parser.addVariable("year", "2024");
|
||||||
|
parser.addVariable("month", "01");
|
||||||
|
parser.addVariable("day", "15");
|
||||||
|
assertEquals("2024-01-15", parser.getValueAsObject());
|
||||||
|
|
||||||
|
parser.addVariable("year", "2025");
|
||||||
|
parser.addVariable("month", "12");
|
||||||
|
parser.addVariable("day", "31");
|
||||||
|
assertEquals("2025-12-31", parser.getValueAsObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("접두·접미 상수로 감싸기")
|
||||||
|
void testWrapWithConstants() throws Exception {
|
||||||
|
parser.parse("concatn(\"[\", code, \"-\", seq, \"]\")");
|
||||||
|
|
||||||
|
parser.addVariable("code", "KOR");
|
||||||
|
parser.addVariable("seq", "001");
|
||||||
|
assertEquals("[KOR-001]", parser.getValueAsObject());
|
||||||
|
|
||||||
|
parser.addVariable("code", "USA");
|
||||||
|
parser.addVariable("seq", "999");
|
||||||
|
assertEquals("[USA-999]", parser.getValueAsObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("앞에 상수 접두어 + 변수 1개")
|
||||||
|
void testConstantPrefix() throws Exception {
|
||||||
|
parser.parse("concatn(\"REQ-\", txId)");
|
||||||
|
|
||||||
|
parser.addVariable("txId", "20240508001");
|
||||||
|
assertEquals("REQ-20240508001", parser.getValueAsObject());
|
||||||
|
|
||||||
|
parser.addVariable("txId", "20240508002");
|
||||||
|
assertEquals("REQ-20240508002", parser.getValueAsObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("변수 사이에 공백 상수 삽입")
|
||||||
|
void testSpaceSeparator() throws Exception {
|
||||||
|
parser.parse("concatn(firstName, \" \", lastName)");
|
||||||
|
|
||||||
|
parser.addVariable("firstName", "Gil-dong");
|
||||||
|
parser.addVariable("lastName", "Hong");
|
||||||
|
assertEquals("Gil-dong Hong", parser.getValueAsObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// 3. 성능 측정 (ParserTest 스타일)
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("100만 회 반복 평가 — parse 1회 재사용 성능")
|
||||||
|
void testPerformance() throws Exception {
|
||||||
|
String expr = "concatn(code, \"-\", seq)";
|
||||||
|
parser.parse(expr);
|
||||||
|
|
||||||
|
Object result = null;
|
||||||
|
long t = System.currentTimeMillis();
|
||||||
|
for (int i = 0; i < 1_000_000; i++) {
|
||||||
|
parser.addVariable("code", "KOR");
|
||||||
|
parser.addVariable("seq", String.valueOf(i));
|
||||||
|
result = parser.getValueAsObject();
|
||||||
|
}
|
||||||
|
long elapsed = System.currentTimeMillis() - t;
|
||||||
|
|
||||||
|
System.out.println(String.format("%s = %s %dms", expr, result, elapsed));
|
||||||
|
|
||||||
|
assertEquals("KOR-999999", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package com.eactive.eai.transformer;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.nfunk.jep.JEP;
|
||||||
|
|
||||||
|
import com.eactive.eai.transformer.function.generic.ConvertDateTime;
|
||||||
|
|
||||||
|
@DisplayName("ConvertDateTime 날짜 변환 함수 테스트")
|
||||||
|
class ConvertDateTimeFunctionTest {
|
||||||
|
|
||||||
|
private JEP jep;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
jep = new JEP();
|
||||||
|
jep.addStandardFunctions();
|
||||||
|
jep.addStandardConstants();
|
||||||
|
jep.setAllowUndeclared(true);
|
||||||
|
jep.addFunction("convertDateTime", new ConvertDateTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String eval(String expr) {
|
||||||
|
jep.parseExpression(expr);
|
||||||
|
assertFalse(jep.hasError(), "파싱 오류: " + jep.getErrorInfo());
|
||||||
|
Object result = jep.getValueAsObject();
|
||||||
|
assertFalse(jep.hasError(), "평가 오류: " + jep.getErrorInfo());
|
||||||
|
return (String) result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("yyyyMMdd → yyyy-MM-dd 변환")
|
||||||
|
void testCompactToHyphen() {
|
||||||
|
jep.addVariable("src", "20240508");
|
||||||
|
assertEquals("2024-05-08", eval("convertDateTime(src, \"yyyyMMdd\", \"yyyy-MM-dd\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("yyyyMMddHHmmss → yyyy-MM-dd'T'HH:mm:ss 변환 (ISO 8601)")
|
||||||
|
void testToIso8601() {
|
||||||
|
jep.addVariable("src", "20240508143022");
|
||||||
|
assertEquals("2024-05-08T14:30:22",
|
||||||
|
eval("convertDateTime(src, \"yyyyMMddHHmmss\", \"yyyy-MM-dd'T'HH:mm:ss\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("yyyy-MM-dd'T'HH:mm:ss → yyyyMMddHHmmss 변환")
|
||||||
|
void testFromIso8601ToCompact() {
|
||||||
|
jep.addVariable("src", "2024-05-08T14:30:22");
|
||||||
|
assertEquals("20240508143022",
|
||||||
|
eval("convertDateTime(src, \"yyyy-MM-dd'T'HH:mm:ss\", \"yyyyMMddHHmmss\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("yyyyMMddHHmmssSSS → yyyy-MM-dd HH:mm:ss.SSS 변환 (밀리초 포함)")
|
||||||
|
void testWithMilliseconds() {
|
||||||
|
jep.addVariable("src", "20240508143022123");
|
||||||
|
assertEquals("2024-05-08 14:30:22.123",
|
||||||
|
eval("convertDateTime(src, \"yyyyMMddHHmmssSSS\", \"yyyy-MM-dd HH:mm:ss.SSS\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("날짜 포맷만 변환 (시간 없음)")
|
||||||
|
void testDateOnly() {
|
||||||
|
jep.addVariable("src", "2024/05/08");
|
||||||
|
assertEquals("20240508",
|
||||||
|
eval("convertDateTime(src, \"yyyy/MM/dd\", \"yyyyMMdd\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("잘못된 날짜 → 오류 발생")
|
||||||
|
void testInvalidDate() {
|
||||||
|
jep.addVariable("src", "99999999");
|
||||||
|
jep.parseExpression("convertDateTime(src, \"yyyyMMdd\", \"yyyy-MM-dd\")");
|
||||||
|
if (!jep.hasError()) {
|
||||||
|
jep.getValueAsObject();
|
||||||
|
assertTrue(jep.hasError(), "잘못된 날짜는 오류가 발생해야 합니다");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package com.eactive.eai.transformer;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.nfunk.jep.JEP;
|
||||||
|
|
||||||
|
import com.eactive.eai.transformer.function.generic.GetCurrent;
|
||||||
|
|
||||||
|
@DisplayName("GetCurrent 현재시간 반환 함수 테스트")
|
||||||
|
class GetCurrentFunctionTest {
|
||||||
|
|
||||||
|
private JEP jep;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
jep = new JEP();
|
||||||
|
jep.addStandardFunctions();
|
||||||
|
jep.addStandardConstants();
|
||||||
|
jep.setAllowUndeclared(true);
|
||||||
|
jep.addFunction("getCurrent", new GetCurrent());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String eval(String expr) {
|
||||||
|
jep.parseExpression(expr);
|
||||||
|
assertFalse(jep.hasError(), "파싱 오류: " + jep.getErrorInfo());
|
||||||
|
Object result = jep.getValueAsObject();
|
||||||
|
assertFalse(jep.hasError(), "평가 오류: " + jep.getErrorInfo());
|
||||||
|
return (String) result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("yyyyMMdd 형식 — 오늘 날짜와 일치")
|
||||||
|
void testDateFormat() {
|
||||||
|
String expected = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
||||||
|
String result = eval("getCurrent(\"yyyyMMdd\")");
|
||||||
|
assertEquals(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("yyyy-MM-dd 형식 — 오늘 날짜와 일치")
|
||||||
|
void testHyphenDateFormat() {
|
||||||
|
String expected = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
|
||||||
|
String result = eval("getCurrent(\"yyyy-MM-dd\")");
|
||||||
|
assertEquals(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("yyyyMMddHHmmss 형식 — 결과가 14자리")
|
||||||
|
void testDateTimeFormat() {
|
||||||
|
String result = eval("getCurrent(\"yyyyMMddHHmmss\")");
|
||||||
|
assertNotNull(result);
|
||||||
|
assertTrue(result.matches("\\d{14}"), "결과가 14자리 숫자여야 합니다: " + result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("yyyy-MM-dd'T'HH:mm:ss ISO 8601 형식")
|
||||||
|
void testIso8601Format() {
|
||||||
|
String result = eval("getCurrent(\"yyyy-MM-dd'T'HH:mm:ss\")");
|
||||||
|
assertNotNull(result);
|
||||||
|
assertTrue(result.matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}"),
|
||||||
|
"ISO 8601 형식이어야 합니다: " + result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("yyyyMMddHHmmssSSS 형식 — 결과가 17자리")
|
||||||
|
void testDateTimeMillisFormat() {
|
||||||
|
String result = eval("getCurrent(\"yyyyMMddHHmmssSSS\")");
|
||||||
|
assertNotNull(result);
|
||||||
|
assertTrue(result.matches("\\d{17}"), "결과가 17자리 숫자여야 합니다: " + result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("잘못된 포맷 → 오류 발생")
|
||||||
|
void testInvalidFormat() {
|
||||||
|
jep.parseExpression("getCurrent(\"invalid!!!###\")");
|
||||||
|
if (!jep.hasError()) {
|
||||||
|
jep.getValueAsObject();
|
||||||
|
assertTrue(jep.hasError(), "잘못된 포맷은 오류가 발생해야 합니다");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertEquals(String expected, String result) {
|
||||||
|
org.junit.jupiter.api.Assertions.assertEquals(expected, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
package com.eactive.eai.transformer;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.nfunk.jep.JEP;
|
||||||
|
|
||||||
|
import com.eactive.eai.transformer.function.generic.GetTrinomial;
|
||||||
|
|
||||||
|
@DisplayName("GetTrinomial 삼항연산자 함수 테스트")
|
||||||
|
class GetTrinomialFunctionTest {
|
||||||
|
|
||||||
|
private JEP jep;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
jep = new JEP();
|
||||||
|
jep.addStandardFunctions();
|
||||||
|
jep.addStandardConstants();
|
||||||
|
jep.setAllowUndeclared(true);
|
||||||
|
|
||||||
|
jep.addVariable("true", Boolean.TRUE);
|
||||||
|
jep.addVariable("false", Boolean.FALSE);
|
||||||
|
|
||||||
|
jep.addFunction("getTrinomial", new GetTrinomial());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object eval(String expr) {
|
||||||
|
jep.parseExpression(expr);
|
||||||
|
assertFalse(jep.hasError(), "파싱 오류: " + jep.getErrorInfo());
|
||||||
|
Object result = jep.getValueAsObject();
|
||||||
|
assertFalse(jep.hasError(), "평가 오류: " + jep.getErrorInfo());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// 숫자형 비교
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("숫자 = 비교 — 참")
|
||||||
|
void testNumberEqualTrue() {
|
||||||
|
jep.addVariable("a", 10.0);
|
||||||
|
assertEquals("같음", eval("getTrinomial(a, 10, \"=\", \"같음\", \"다름\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("숫자 = 비교 — 거짓")
|
||||||
|
void testNumberEqualFalse() {
|
||||||
|
jep.addVariable("a", 5.0);
|
||||||
|
assertEquals("다름", eval("getTrinomial(a, 10, \"=\", \"같음\", \"다름\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("숫자 < 비교 — 참")
|
||||||
|
void testNumberLessThanTrue() {
|
||||||
|
jep.addVariable("score", 70.0);
|
||||||
|
assertEquals("미달", eval("getTrinomial(score, 80, \"<\", \"미달\", \"통과\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("숫자 > 비교 — 참")
|
||||||
|
void testNumberGreaterThanTrue() {
|
||||||
|
jep.addVariable("score", 90.0);
|
||||||
|
assertEquals("우수", eval("getTrinomial(score, 80, \">\", \"우수\", \"보통\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("숫자 <= 비교 — 경계값")
|
||||||
|
void testNumberLessOrEqual() {
|
||||||
|
jep.addVariable("v", 80.0);
|
||||||
|
assertEquals("이하", eval("getTrinomial(v, 80, \"<=\", \"이하\", \"초과\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("숫자 >= 비교 — 경계값")
|
||||||
|
void testNumberGreaterOrEqual() {
|
||||||
|
jep.addVariable("v", 80.0);
|
||||||
|
assertEquals("이상", eval("getTrinomial(v, 80, \">=\", \"이상\", \"미만\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// 문자열 비교
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("문자열 = 비교 — 참")
|
||||||
|
void testStringEqualTrue() {
|
||||||
|
jep.addVariable("status", "Y");
|
||||||
|
assertEquals("활성", eval("getTrinomial(status, \"Y\", \"=\", \"활성\", \"비활성\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("문자열 = 비교 — 거짓")
|
||||||
|
void testStringEqualFalse() {
|
||||||
|
jep.addVariable("status", "N");
|
||||||
|
assertEquals("비활성", eval("getTrinomial(status, \"Y\", \"=\", \"활성\", \"비활성\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("문자열 < 비교 — 사전순")
|
||||||
|
void testStringLessThan() {
|
||||||
|
jep.addVariable("s", "apple");
|
||||||
|
assertEquals("앞", eval("getTrinomial(s, \"banana\", \"<\", \"앞\", \"뒤\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// 결과값 타입 — 숫자 반환
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("결과값이 숫자형")
|
||||||
|
void testNumericReturnValue() {
|
||||||
|
jep.addVariable("flag", "Y");
|
||||||
|
Object result = eval("getTrinomial(flag, \"Y\", \"=\", 1, 0)");
|
||||||
|
assertTrue(result instanceof Number);
|
||||||
|
assertEquals(1.0, ((Number) result).doubleValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// boolean 비교
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("boolean = 비교 — 참 (JEP는 boolean 리터럴 미지원, 변수로 바인딩)")
|
||||||
|
void testBooleanEqual() {
|
||||||
|
jep.addVariable("b", Boolean.TRUE);
|
||||||
|
jep.addVariable("bTrue", Boolean.TRUE);
|
||||||
|
assertEquals("참", eval("getTrinomial(b, bTrue, \"=\", \"참\", \"거짓\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("boolean = 비교 — 거짓")
|
||||||
|
void testBooleanEqualFalse() {
|
||||||
|
jep.addVariable("b", Boolean.FALSE);
|
||||||
|
jep.addVariable("bTrue", Boolean.TRUE);
|
||||||
|
assertEquals("거짓", eval("getTrinomial(b, bTrue, \"=\", \"참\", \"거짓\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// 실전 Transformer 표현식 패턴
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("실전 패턴 — true/false 결과값 (JEP 상수 1.0/0.0 반환 검증)")
|
||||||
|
void testBooleanLiteralReturnValue() {
|
||||||
|
// JEP addStandardConstants()가 true=1.0, false=0.0 으로 등록하는지 확인
|
||||||
|
// → 등록되어 있다면 변수 바인딩 없이 숫자로 반환됨
|
||||||
|
// → 등록 안 됐다면 파싱 오류 또는 undeclared 변수(NaN)로 처리됨
|
||||||
|
jep.addVariable("intVal", 10.0);
|
||||||
|
Object result = eval("getTrinomial(intVal, 10, \"=\", true, false)");
|
||||||
|
assertTrue(result instanceof Number,
|
||||||
|
"true/false 결과값이 Number 타입이어야 함 (실제: " + result.getClass().getSimpleName() + ")");
|
||||||
|
assertEquals(1.0, ((Number) result).doubleValue(), 0.0,
|
||||||
|
"조건 참일 때 true(=1.0) 반환 기대");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("실전 패턴 — dot 경로 변수(AGW_…SRCS.INT_VALUE) + boolean 결과값")
|
||||||
|
void testRealWorldExpression() {
|
||||||
|
// Transformer 실사용 표현식:
|
||||||
|
// getTrinomial(AGW_TESTREQ10031S1_SRCS.INT_VALUE, 10, "=", true, false)
|
||||||
|
//
|
||||||
|
// JEP에서 dot(.)은 element-access 연산자로 파싱됨
|
||||||
|
// → 파싱 성공 여부와 변수 바인딩 방법을 확인하기 위한 테스트
|
||||||
|
String expr = "getTrinomial(AGW_TESTREQ10031S1_SRCS.INT_VALUE, 10, \"=\", true, false)";
|
||||||
|
|
||||||
|
// [1] 파싱만 먼저 검증
|
||||||
|
jep.parseExpression(expr);
|
||||||
|
assertFalse(jep.hasError(), "파싱 오류: " + jep.getErrorInfo());
|
||||||
|
|
||||||
|
// [2] 파싱 후 JEP 심볼 테이블에 등록된 변수명 확인
|
||||||
|
// dot-expression 이므로 "AGW_TESTREQ10031S1_SRCS" 가 심볼로 잡힐 것으로 예상
|
||||||
|
java.util.Set<String> symbols = jep.getSymbolTable().keySet();
|
||||||
|
assertTrue(
|
||||||
|
symbols.contains("AGW_TESTREQ10031S1_SRCS") || symbols.contains("AGW_TESTREQ10031S1_SRCS.INT_VALUE"),
|
||||||
|
"심볼 테이블에 소스 변수가 포함되어야 함. 실제 심볼: " + symbols
|
||||||
|
);
|
||||||
|
|
||||||
|
// [3] 심볼에 값 바인딩 후 평가
|
||||||
|
// dot-expression(AGW_…SRCS.INT_VALUE)은 JEP vector element 접근이므로
|
||||||
|
// 스칼라 바인딩 시 평가 오류 발생 가능 → 오류 메시지로 대응 방법 확인
|
||||||
|
jep.addVariable("AGW_TESTREQ10031S1_SRCS", 10.0);
|
||||||
|
|
||||||
|
Object result = jep.getValueAsObject();
|
||||||
|
if (jep.hasError()) {
|
||||||
|
// dot element-access 실패 시: Transformer는 심볼 전체 경로를 변수명으로 등록해야 함
|
||||||
|
// → Parser.addVariable("AGW_TESTREQ10031S1_SRCS.INT_VALUE", value) 방식 필요
|
||||||
|
jep.addVariable("AGW_TESTREQ10031S1_SRCS.INT_VALUE", 10.0);
|
||||||
|
jep.parseExpression(expr);
|
||||||
|
assertFalse(jep.hasError(), "재파싱 오류: " + jep.getErrorInfo());
|
||||||
|
result = jep.getValueAsObject();
|
||||||
|
assertFalse(jep.hasError(), "평가 오류: " + jep.getErrorInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(result instanceof Number,
|
||||||
|
"결과값이 Number 타입이어야 함 (실제: " + (result == null ? "null" : result.getClass().getSimpleName()) + ")");
|
||||||
|
assertEquals(1.0, ((Number) result).doubleValue(), 0.0,
|
||||||
|
"INT_VALUE=10, 비교값=10, 연산자=등호 → 참(true=1.0) 반환 기대");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,11 +28,16 @@ import com.eactive.eai.transformer.message.MessageSetDataException;
|
|||||||
* - LONG_VALUE : LONG, length=10, decimal=0
|
* - LONG_VALUE : LONG, length=10, decimal=0
|
||||||
* - BIG_VALUE : BIGDECIMAL, length=10, decimal=5 → 정수부 최대 4자리
|
* - BIG_VALUE : BIGDECIMAL, length=10, decimal=5 → 정수부 최대 4자리
|
||||||
* - BIG15_VALUE : BIGDECIMAL, length=15, decimal=0 → 정수부 최대 15자리
|
* - BIG15_VALUE : BIGDECIMAL, length=15, decimal=0 → 정수부 최대 15자리
|
||||||
|
* - STR_VALUE : STRING, length=20
|
||||||
*
|
*
|
||||||
* 검증 시나리오
|
* 검증 시나리오
|
||||||
* 1) 타입 불일치 — 숫자형 필드에 비숫자 문자열 입력 시 setData() 에서 즉시 오류
|
* 1-a) 타입 불일치(오류) — 숫자형 필드에 비숫자 문자열 입력 시 setData() 에서 즉시 오류
|
||||||
* 2) 길이 초과 — BigDecimal 정수부/소수부 자릿수 초과 시 setData() 에서 즉시 오류
|
* 1-b) 타입 불일치(정상) — 숫자형 필드에 숫자 문자열 입력 시 파싱 가능하면 정상 처리
|
||||||
* (SystemKeys.TRANSFROMER_VALIDATION=true 필요)
|
* 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
|
@DataJpaTest
|
||||||
@ContextConfiguration(classes = { TransformJPATestConfiguration.class })
|
@ContextConfiguration(classes = { TransformJPATestConfiguration.class })
|
||||||
@@ -60,6 +65,7 @@ class JSONMessageValidationTest {
|
|||||||
@AfterEach
|
@AfterEach
|
||||||
void tearDown() {
|
void tearDown() {
|
||||||
System.setProperty(SystemKeys.TRANSFROMER_VALIDATION, "false");
|
System.setProperty(SystemKeys.TRANSFROMER_VALIDATION, "false");
|
||||||
|
System.setProperty(SystemKeys.TRANSFROMER_SCIENTIFIC_NORMALIZE, "false");
|
||||||
SystemKeys.reload();
|
SystemKeys.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,8 +116,82 @@ class JSONMessageValidationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────
|
||||||
// 2. 숫자형 길이 초과 테스트 — setData() 시점에 오류 발생
|
// 1-b. 숫자형 필드에 숫자 문자열 입력 → 정상 처리
|
||||||
// transformer.validation=true 설정 필요
|
// 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
|
@Test
|
||||||
@@ -180,6 +260,177 @@ class JSONMessageValidationTest {
|
|||||||
assertTrue(result.contains("BIG_VALUE"), "BIG_VALUE가 결과에 포함되어야 합니다");
|
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가 결과에 포함되어야 합니다");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// 4. 지수 표현(Scientific Notation) 정규화 테스트
|
||||||
|
// transformer.scientific.normalize=true 일 때만 적용
|
||||||
|
// 4-a) 정규화 ON + validation ON
|
||||||
|
// 4-b) 정규화 OFF → 기존 동작(false positive / false negative) 확인
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("[정규화 ON] 지수형 정수부 초과(1.23e5=123000 > 4자리 제한) → MessageSetDataException")
|
||||||
|
void testScientificNotationIntegerOverflow_withNormalize() {
|
||||||
|
enableValidation();
|
||||||
|
enableScientificNormalize();
|
||||||
|
|
||||||
|
// 1.23e5 = 123000, 정수부 6자리 > 4자리 제한
|
||||||
|
String data = "{\"EAI_UJSONTYPE_REQ1\":{\"BIG_VALUE\":1.23e5}}";
|
||||||
|
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("[정규화 ON] 지수형 문자열 정수부 초과(\"1.23e5\" > 4자리 제한) → MessageSetDataException")
|
||||||
|
void testScientificNotationStringOverflow_withNormalize() {
|
||||||
|
enableValidation();
|
||||||
|
enableScientificNormalize();
|
||||||
|
|
||||||
|
String data = "{\"EAI_UJSONTYPE_REQ1\":{\"BIG_VALUE\":\"1.23e5\"}}";
|
||||||
|
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("[정규화 ON] 지수형 BIG15_VALUE 정상 범위(1.23e5=123000 ≤ 15자리) → 정상 처리")
|
||||||
|
void testScientificNotationBig15Valid_withNormalize() throws Exception {
|
||||||
|
enableValidation();
|
||||||
|
enableScientificNormalize();
|
||||||
|
|
||||||
|
// 1.23e5 = 123000, 6자리 ≤ 15자리 제한 → 정상
|
||||||
|
String data = "{\"EAI_UJSONTYPE_REQ1\":{\"BIG15_VALUE\":1.23e5}}";
|
||||||
|
Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1");
|
||||||
|
msg.setData(data);
|
||||||
|
|
||||||
|
String result = (String) msg.getData();
|
||||||
|
assertNotNull(result, "결과값이 null이 아니어야 합니다");
|
||||||
|
assertTrue(result.contains("BIG15_VALUE"), "BIG15_VALUE가 결과에 포함되어야 합니다");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("[정규화 OFF] 지수형 BIG15_VALUE 정상값(1.23e5)이 false negative로 거부됨 → MessageSetDataException")
|
||||||
|
void testScientificNotationBig15Valid_withoutNormalize() {
|
||||||
|
enableValidation();
|
||||||
|
disableScientificNormalize();
|
||||||
|
|
||||||
|
// 1.23e5 = 123000, 유효값(6자리 ≤ 15자리)이지만 정규화 없이는 "1.23E+5".split(".")=[2개] → 거부
|
||||||
|
String data = "{\"EAI_UJSONTYPE_REQ1\":{\"BIG15_VALUE\":1.23e5}}";
|
||||||
|
Message msg = MessageFactory.getFactory().getMessage("EAI_UJSONTYPE_REQ1");
|
||||||
|
|
||||||
|
assertThrows(
|
||||||
|
MessageSetDataException.class,
|
||||||
|
() -> msg.setData(data),
|
||||||
|
"정규화 OFF 시 false negative로 예외가 발생해야 합니다"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────
|
||||||
// 유틸 메서드
|
// 유틸 메서드
|
||||||
// ─────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────
|
||||||
@@ -189,6 +440,16 @@ class JSONMessageValidationTest {
|
|||||||
SystemKeys.reload();
|
SystemKeys.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void enableScientificNormalize() {
|
||||||
|
System.setProperty(SystemKeys.TRANSFROMER_SCIENTIFIC_NORMALIZE, "true");
|
||||||
|
SystemKeys.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disableScientificNormalize() {
|
||||||
|
System.setProperty(SystemKeys.TRANSFROMER_SCIENTIFIC_NORMALIZE, "false");
|
||||||
|
SystemKeys.reload();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean containsCause(Throwable e, Class<?> causeClass) {
|
private boolean containsCause(Throwable e, Class<?> causeClass) {
|
||||||
Throwable t = e;
|
Throwable t = e;
|
||||||
while (t != null) {
|
while (t != null) {
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package com.eactive.eai.transformer;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.nfunk.jep.JEP;
|
||||||
|
|
||||||
|
import com.eactive.eai.transformer.function.generic.ReplaceAll;
|
||||||
|
|
||||||
|
@DisplayName("ReplaceAll 문자열 치환 함수 테스트")
|
||||||
|
class ReplaceAllFunctionTest {
|
||||||
|
|
||||||
|
private JEP jep;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
jep = new JEP();
|
||||||
|
jep.addStandardFunctions();
|
||||||
|
jep.addStandardConstants();
|
||||||
|
jep.setAllowUndeclared(true);
|
||||||
|
jep.addFunction("replaceAll", new ReplaceAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String eval(String expr) {
|
||||||
|
jep.parseExpression(expr);
|
||||||
|
assertFalse(jep.hasError(), "파싱 오류: " + jep.getErrorInfo());
|
||||||
|
Object result = jep.getValueAsObject();
|
||||||
|
assertFalse(jep.hasError(), "평가 오류: " + jep.getErrorInfo());
|
||||||
|
return (String) result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("단일 치환 — 홍길동 → 홍길순")
|
||||||
|
void testSingleReplace() {
|
||||||
|
jep.addVariable("name", "홍길동");
|
||||||
|
assertEquals("홍길순", eval("replaceAll(name, \"동\", \"순\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("다중 치환 — 모든 매칭 문자열 변경")
|
||||||
|
void testMultipleOccurrences() {
|
||||||
|
// "aababab" 안의 'a' 4개가 모두 'X'로 치환 → "XXbXbXb"
|
||||||
|
jep.addVariable("src", "aababab");
|
||||||
|
assertEquals("XXbXbXb", eval("replaceAll(src, \"a\", \"X\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("치환 대상 없음 — 원본 그대로 반환")
|
||||||
|
void testNoMatch() {
|
||||||
|
jep.addVariable("src", "hello");
|
||||||
|
assertEquals("hello", eval("replaceAll(src, \"z\", \"X\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("치환 후 문자열이 빈 문자열 (JEP는 \"\" 리터럴 미지원 → 변수 바인딩)")
|
||||||
|
void testReplaceWithEmpty() {
|
||||||
|
jep.addVariable("src", "abc123def");
|
||||||
|
assertEquals("abcdef", eval("replaceAll(src, \"123\", \"\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("날짜 구분자 변환 — '-' → '/'")
|
||||||
|
void testDateSeparator() {
|
||||||
|
jep.addVariable("date", "2024-05-08");
|
||||||
|
assertEquals("2024/05/08", eval("replaceAll(date, \"-\", \"/\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("리터럴만 사용")
|
||||||
|
void testLiteralsOnly() {
|
||||||
|
assertEquals("world world",
|
||||||
|
eval("replaceAll(\"hello hello\", \"hello\", \"world\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("비문자열 파라미터 → 오류 발생")
|
||||||
|
void testNonStringParamCausesError() {
|
||||||
|
jep.parseExpression("replaceAll(123, \"1\", \"X\")");
|
||||||
|
if (!jep.hasError()) {
|
||||||
|
jep.getValueAsObject();
|
||||||
|
assertTrue(jep.hasError(), "숫자 파라미터는 오류가 발생해야 합니다");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 =
|
||||||
|
"<EAI_UXMLTYPE_REQ1>" +
|
||||||
|
"<BIG_VALUE>1234.12345</BIG_VALUE>" +
|
||||||
|
"<BIG15_VALUE>123000</BIG15_VALUE>" +
|
||||||
|
"<STR_VALUE>HELLO</STR_VALUE>" +
|
||||||
|
"</EAI_UXMLTYPE_REQ1>";
|
||||||
|
|
||||||
|
@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 =
|
||||||
|
"<EAI_UXMLTYPE_REQ1>" +
|
||||||
|
"<BIG_VALUE>12345.12345</BIG_VALUE>" +
|
||||||
|
"<BIG15_VALUE>123000</BIG15_VALUE>" +
|
||||||
|
"<STR_VALUE>HELLO</STR_VALUE>" +
|
||||||
|
"</EAI_UXMLTYPE_REQ1>";
|
||||||
|
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 =
|
||||||
|
"<EAI_UXMLTYPE_REQ1>" +
|
||||||
|
"<BIG_VALUE>12345.12345</BIG_VALUE>" +
|
||||||
|
"<BIG15_VALUE>123000</BIG15_VALUE>" +
|
||||||
|
"<STR_VALUE>HELLO</STR_VALUE>" +
|
||||||
|
"</EAI_UXMLTYPE_REQ1>";
|
||||||
|
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 =
|
||||||
|
"<EAI_UXMLTYPE_REQ1>" +
|
||||||
|
"<BIG_VALUE>1234.12345</BIG_VALUE>" +
|
||||||
|
"<BIG15_VALUE>123.456</BIG15_VALUE>" +
|
||||||
|
"<STR_VALUE>HELLO</STR_VALUE>" +
|
||||||
|
"</EAI_UXMLTYPE_REQ1>";
|
||||||
|
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 =
|
||||||
|
"<EAI_UXMLTYPE_REQ1>" +
|
||||||
|
"<BIG_VALUE>1234.12345</BIG_VALUE>" +
|
||||||
|
"<BIG15_VALUE>1234567890123456</BIG15_VALUE>" +
|
||||||
|
"<STR_VALUE>HELLO</STR_VALUE>" +
|
||||||
|
"</EAI_UXMLTYPE_REQ1>";
|
||||||
|
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) 빈 요소(<BIG_VALUE/>) + validation=true → 예외 없음
|
||||||
|
// dom4j는 빈 요소의 getData()를 null로 반환
|
||||||
|
// → null instanceof String = false → 검증 건너뜀 → 요소 없음과 동일 동작
|
||||||
|
// F-2) 빈 요소(<BIG_VALUE/>) + validation=false → 예외 없음
|
||||||
|
// F-3) 요소 없음(BIG_VALUE 미포함 XML) + validation=true → 예외 없음
|
||||||
|
// XML에 요소가 없으면 doSetValue() 순회 대상이 없어 검증 자체가 실행되지 않음
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("[F-1] BIG_VALUE 빈 요소(<BIG_VALUE/>) + validation=true → 예외 없음 (dom4j null 반환)")
|
||||||
|
void testBigValueEmptyElement_withValidation() {
|
||||||
|
enableValidation();
|
||||||
|
// dom4j는 빈 요소의 getData()를 null로 반환 → null instanceof String = false → 검증 건너뜀
|
||||||
|
String xml =
|
||||||
|
"<EAI_UXMLTYPE_REQ1>" +
|
||||||
|
"<BIG_VALUE></BIG_VALUE>" +
|
||||||
|
"<BIG15_VALUE>123000</BIG15_VALUE>" +
|
||||||
|
"<STR_VALUE>HELLO</STR_VALUE>" +
|
||||||
|
"</EAI_UXMLTYPE_REQ1>";
|
||||||
|
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 =
|
||||||
|
"<EAI_UXMLTYPE_REQ1>" +
|
||||||
|
"<BIG_VALUE></BIG_VALUE>" +
|
||||||
|
"<BIG15_VALUE>123000</BIG15_VALUE>" +
|
||||||
|
"<STR_VALUE>HELLO</STR_VALUE>" +
|
||||||
|
"</EAI_UXMLTYPE_REQ1>";
|
||||||
|
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 =
|
||||||
|
"<EAI_UXMLTYPE_REQ1>" +
|
||||||
|
"<BIG15_VALUE>123000</BIG15_VALUE>" +
|
||||||
|
"<STR_VALUE>HELLO</STR_VALUE>" +
|
||||||
|
"</EAI_UXMLTYPE_REQ1>";
|
||||||
|
Message msg = MessageFactory.getFactory().getMessage("EAI_UXMLTYPE_REQ1");
|
||||||
|
assertDoesNotThrow(() -> msg.setData(xml),
|
||||||
|
"XML에 요소가 없으면 검증 대상이 아니므로 예외가 발생하지 않아야 합니다");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// 유틸 메서드
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void enableValidation() {
|
||||||
|
System.setProperty(SystemKeys.TRANSFROMER_VALIDATION, "true");
|
||||||
|
SystemKeys.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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', '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', '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', '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);
|
||||||
|
|||||||
@@ -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);
|
||||||
Reference in New Issue
Block a user