Files
flow-center-service/src/test/java/com/yunhai/flowcenter/engine/ExpressionEvaluatorTest.java
T
chenxing 92619508f0 feat(condition): CONDITION 判断方式二选一——EXPRESSION 内置 Aviator 表达式(沙箱)+ SERVICE 原口径;JudgeBody 补 submitterId
- ExpressionEvaluator:Aviator 5.4.3 沙箱(FEATURE_SET 只留 If、ALLOWED_CLASS_SET 空=禁类访问/反射)、编译缓存、
  顶层变量缺失抛错挂起(Aviator 缺失变量按 nil 且 nil 视作最小值,<= 兜底出口会静默放行——宁挂起不误放行)
- ConditionRouteService.decide 分流:EXPRESSION 按出边顺序取首个 true(逐边结果摘要落 raw_response),
  全 false/异常挂起沿用 SERVICE 纪律(route_log outlet=NULL + EXPRESSION 常量 + 告警 + 管理端重跑)
- DefValidator 发布期分层校验:EXPRESSION 出口缺条件/语法错/违禁表达式硬拦;表单变量放行(运行时核对);
  内置变量疑似拼错 warn;judgeMode 非法值拦
- /admin/router/eval 调试端点(同一求值核心,不落库不走流程)
- 单测 22 新增 + 全量 170 绿;E2E 18/18(发布/分支选路/挂起留痕/重跑/调试)见 local-debug/four-topics-a-e2e.py
2026-10-10 18:52:40 +08:00

109 lines
4.2 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.yunhai.flowcenter.engine;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* 表达式求值器单测(2026-10-10 四议题 Phase 4):沙箱纪律(禁类访问/反射/new)、
* 缺失变量补 null、nil 属性语义、非布尔不命中。发布校验/运行时/调试端点共用本核心。
*/
class ExpressionEvaluatorTest {
@Test
void basicComparison_trueAndFalse() {
Map<String, Object> env = new HashMap<>();
env.put("amount", 2000);
assertTrue((Boolean) ExpressionEvaluator.evaluate("amount > 1000", env));
assertFalse((Boolean) ExpressionEvaluator.evaluate("amount < 1000", env));
}
@Test
void missingTopLevelVariable_throws_typoFailsClosed() {
// 宁挂起不误放行:缺失变量直接抛错(nil 视作最小值,<= 兜底出口会静默放行,不能补 null)
Map<String, Object> env = new HashMap<>();
assertThrows(Exception.class, () -> ExpressionEvaluator.evaluate("missing > 1000", env));
assertThrows(Exception.class, () -> ExpressionEvaluator.evaluate("missing <= 1000", env));
}
@Test
void explicitNullValue_nilComparisons_documentedSemantics() {
// env 显式 null 仍走 nil 语义:== nil 可判空;<= 对 nil 为 true(Aviator 视 nil 为最小值)
Map<String, Object> env = new HashMap<>();
env.put("amount", null);
assertTrue((Boolean) ExpressionEvaluator.evaluate("amount == nil", env));
assertTrue((Boolean) ExpressionEvaluator.evaluate("amount <= 1000", env));
}
@Test
void nestedMapPropertyMissing_returnsNil() {
Map<String, Object> env = new HashMap<>();
env.put("form", new HashMap<String, Object>());
Object r = ExpressionEvaluator.evaluate("form.amount == nil", env);
assertTrue((Boolean) r);
}
@Test
void nonBooleanResult_notTreatedAsTrue() {
Map<String, Object> env = new HashMap<>();
env.put("amount", 2000);
Object r = ExpressionEvaluator.evaluate("amount + 1", env);
assertEquals(2001L, ((Number) r).longValue());
assertFalse(Boolean.TRUE.equals(r));
}
@Test
void stringFunction_allowed() {
Map<String, Object> env = new HashMap<>();
env.put("title", "请假申请");
assertTrue((Boolean) ExpressionEvaluator.evaluate("string.contains(title, '请假')", env));
}
@Test
void sandbox_rejectsNewInstance() {
assertThrows(Exception.class, ()
-> ExpressionEvaluator.evaluate("new java.util.Date()", new HashMap<>()));
}
@Test
void sandbox_rejectsStaticMethodAccess() {
assertThrows(Exception.class, ()
-> ExpressionEvaluator.evaluate("System.currentTimeMillis() > 0", new HashMap<>()));
}
@Test
void sandbox_rejectsChainedReflection() {
Map<String, Object> env = new HashMap<>();
env.put("title", "x");
assertThrows(Exception.class, ()
-> ExpressionEvaluator.evaluate("title.getClass().forName('java.lang.Runtime')", env));
}
@Test
void sandbox_bareClassReference_isInertVariableLookup() {
// 裸类名在属性语法糖下只是嵌套 map 查找(java→lang→Runtime),顶层变量 java 缺失被
// 求值前核对拦下——不构成类访问;真正的类访问(new/静态方法/链式反射)由上面三个用例证明被拒
assertThrows(IllegalStateException.class, ()
-> ExpressionEvaluator.evaluate("java.lang.Runtime", new HashMap<>()));
}
@Test
void builtinVariableStyle_comparisonAcrossNumberTypes() {
Map<String, Object> env = new HashMap<>();
env.put("submitterId", 990001);
assertTrue((Boolean) ExpressionEvaluator.evaluate("submitterId == 990001", env));
}
@Test
void syntaxError_thrownAtCompile() {
assertThrows(Exception.class, () -> ExpressionEvaluator.compile("amount >"));
}
}