ConcatN 가변 파라미터 문자열 연결 함수 추가 및 단위테스트 작성
- ConcatN: numberOfParameters=-1로 파라미터 수 제한 없이 문자열 연결 - 기존 Concat(2개 고정)을 대체 가능하며 하위 호환성 유지 - ConcatNFunctionTest: JEP 직접 사용, 파라미터 수 변화 검증 - ConcatNParserTest: Parser 클래스 경유, 변수/상수 혼합 표현식 및 성능 검증 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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,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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user