diff --git a/fizz-bootstrap/js/common.js b/fizz-bootstrap/js/common.js index 9e52cb2..cccb334 100644 --- a/fizz-bootstrap/js/common.js +++ b/fizz-bootstrap/js/common.js @@ -190,6 +190,36 @@ var common = { } var result = ctx[stepName]['result'] || {}; return field ? result[field] : result; + }, + + /** + * 获取步骤循环结果 + * @param {*} ctx 上下文 【必填】 + * @param {*} stepName 步骤名【必填】 + */ + getStepCircle: function (ctx, stepName){ + if(!ctx || !stepName || !ctx[stepName]){ + return null; + } + // 返回循环结果数组 + return ctx[stepName]['circle']; + }, + + /** + * 获取请求的循环结果 + * @param {*} ctx 上下文 【必填】 + * @param {*} stepName 步骤名【必填】 + */ + getRequestCircle: function (ctx, stepName, requestName){ + if(!ctx || !stepName || !requestName){ + return null; + } + if(!ctx[stepName] || !ctx[stepName]['requests'] || !ctx[stepName]['requests'][requestName] || + !ctx[stepName]['requests'][requestName]['request']){ + return null; + } + // 返回循环结果数组 + return ctx[stepName]['requests'][requestName]['circle']; } /* *********** step request end ************ */ diff --git a/fizz-bootstrap/js/context.js b/fizz-bootstrap/js/context.js index 0a31511..42cadf7 100644 --- a/fizz-bootstrap/js/context.js +++ b/fizz-bootstrap/js/context.js @@ -46,7 +46,34 @@ var context = { response: { headers: {}, body: {} - } + }, + // 请求循环组件当前循环对象 + item: null, + // 请求循环组件当前循环对象的下标 + index: null, + // 请求循环的结果 + circle: [{ + // 循环对象 + item: null, + // 循环对象的下标 + index: null, + // 请求相关参数 + request:{ + url: "", + method: "GET/POST", + headers: {}, + body: {} + }, + // 根据转换规则转换后的接口响应 + response: { + headers: {}, + body: {} + } + }], + // 条件组件的执行结果 + conditionResults: [ + {'我的条件1': true} + ] }, // 接口2 request2: { @@ -59,13 +86,52 @@ var context = { response: { headers: {}, body: {} - } + }, + // 请求循环组件当前循环对象 + item: null, + // 请求循环组件当前循环对象的下标 + index: null, + // 请求循环的结果 + circle: [{ + // 循环对象 + item: null, + // 循环对象的下标 + index: null, + // 请求相关参数 + request:{ + url: "", + method: "GET/POST", + headers: {}, + body: {} + }, + // 根据转换规则转换后的接口响应 + response: { + headers: {}, + body: {} + } + }], + // 条件组件的执行结果 + conditionResults: [ + {'我的条件1': true} + ] } //... }, // 步骤结果 - result: {} - + result: {}, + // 步骤循环组件当前循环对象 + item: null, + // 步骤循环组件当前循环对象的下标 + index: null, + // 步骤循环的结果 + circle:[{ + // 循环对象 + item: null, + // 循环对象的下标 + index: null, + // 步骤结果 + result: {} + }] } } \ No newline at end of file diff --git a/fizz-bootstrap/src/main/resources/js/common.js b/fizz-bootstrap/src/main/resources/js/common.js index 9e52cb2..cccb334 100644 --- a/fizz-bootstrap/src/main/resources/js/common.js +++ b/fizz-bootstrap/src/main/resources/js/common.js @@ -190,6 +190,36 @@ var common = { } var result = ctx[stepName]['result'] || {}; return field ? result[field] : result; + }, + + /** + * 获取步骤循环结果 + * @param {*} ctx 上下文 【必填】 + * @param {*} stepName 步骤名【必填】 + */ + getStepCircle: function (ctx, stepName){ + if(!ctx || !stepName || !ctx[stepName]){ + return null; + } + // 返回循环结果数组 + return ctx[stepName]['circle']; + }, + + /** + * 获取请求的循环结果 + * @param {*} ctx 上下文 【必填】 + * @param {*} stepName 步骤名【必填】 + */ + getRequestCircle: function (ctx, stepName, requestName){ + if(!ctx || !stepName || !requestName){ + return null; + } + if(!ctx[stepName] || !ctx[stepName]['requests'] || !ctx[stepName]['requests'][requestName] || + !ctx[stepName]['requests'][requestName]['request']){ + return null; + } + // 返回循环结果数组 + return ctx[stepName]['requests'][requestName]['circle']; } /* *********** step request end ************ */ diff --git a/fizz-core/src/main/java/we/fizz/Pipeline.java b/fizz-core/src/main/java/we/fizz/Pipeline.java index db99759..0b2caf0 100644 --- a/fizz-core/src/main/java/we/fizz/Pipeline.java +++ b/fizz-core/src/main/java/we/fizz/Pipeline.java @@ -119,12 +119,12 @@ public class Pipeline { }else { LinkedList opSteps = (LinkedList) steps.clone(); Step step1 = opSteps.removeFirst(); - Mono> result = runStep(step1, null).expand(response -> { - if (opSteps.isEmpty() || response.isStop()) { + Mono> result = runStep(step1, null).expand(lastStepResponse -> { + if (opSteps.isEmpty() || lastStepResponse.isStop()) { return Mono.empty(); } Step step = opSteps.pop(); - return runStep(step, response); + return runStep(step, lastStepResponse); }).flatMap(response -> Flux.just(response)).collectList(); return result.flatMap(clientResponse -> { return handleOutput(input); @@ -132,7 +132,9 @@ public class Pipeline { } } - private Mono runStep(Step step, StepResponse response){ + private Mono runStep(Step step, StepResponse lastStepResponse){ + StepResponse stepResponse = new StepResponse(step, null, new HashMap>()); + stepContext.put(step.getName(), stepResponse); List components = step.getComponents(); if (components != null && components.size() > 0) { StepContextPosition stepCtxPos = new StepContextPosition(step.getName()); diff --git a/fizz-core/src/main/java/we/fizz/Step.java b/fizz-core/src/main/java/we/fizz/Step.java index 2a7e9e0..5ccaafa 100644 --- a/fizz-core/src/main/java/we/fizz/Step.java +++ b/fizz-core/src/main/java/we/fizz/Step.java @@ -114,8 +114,7 @@ public class Step { public void beforeRun(StepContext stepContext2, StepResponse response ) { stepContext = stepContext2; lastStepResponse = response; - StepResponse stepResponse = new StepResponse(this, null, new HashMap>()); - stepContext.put(name, stepResponse); + StepResponse stepResponse = (StepResponse) stepContext.get(this.name); Map configs = this.getRequestConfigs(); for(String configName :configs.keySet()) { InputConfig inputConfig = configs.get(configName); diff --git a/fizz-core/src/main/java/we/fizz/StepContext.java b/fizz-core/src/main/java/we/fizz/StepContext.java index 6e6bf84..974cf92 100644 --- a/fizz-core/src/main/java/we/fizz/StepContext.java +++ b/fizz-core/src/main/java/we/fizz/StepContext.java @@ -801,6 +801,48 @@ public class StepContext extends ConcurrentHashMap { circle.add(circleResult); } + /** + * 设置判断条件组件结果
+ * Set result of condition components
+ * + * @param stepName step name + * @param requestName request name + * @param conditionDesc condition description, such as:
+ * circle[0]-execCondition:my condition 1
+ * circle[1]-breakCondition:my condition 2
+ * condition:my condition 3
+ * @param rs result of condition component + */ + public void addConditionResult(String stepName, String requestName, String conditionDesc, boolean rs) { + if (requestName == null) { + StepResponse stepResponse = (StepResponse) this.get(stepName); + if (stepResponse == null) { + return; + } + List> results = (List>) stepResponse.getConditionResults(); + if (results == null) { + results = new ArrayList<>(); + stepResponse.setConditionResults(results); + } + Map result = new HashMap<>(); + result.put(conditionDesc, rs); + results.add(result); + } else { + Map request = getStepRequest(stepName, requestName); + if (request == null) { + return; + } + List> results = (List>) request.get("conditionResults"); + if (results == null) { + results = new ArrayList<>(); + request.put("conditionResults", results); + } + Map result = new HashMap<>(); + result.put(conditionDesc, rs); + results.add(result); + } + } + /** * 获取请求的循环对象
* Returns the current circle item of request
@@ -808,12 +850,12 @@ public class StepContext extends ConcurrentHashMap { * @param stepName * @param requestName */ - public List> getRequestCircleItem(String stepName, String requestName) { + public Object getRequestCircleItem(String stepName, String requestName) { Map request = getStepRequest(stepName, requestName); if (request == null) { return null; } - return (List>) request.get("circle"); + return request.get("item"); } /** @@ -823,12 +865,12 @@ public class StepContext extends ConcurrentHashMap { * @param stepName * @param requestName */ - public Object getRequestCircle(String stepName, String requestName) { + public List> getRequestCircle(String stepName, String requestName) { Map request = getStepRequest(stepName, requestName); if (request == null) { return null; } - return request.get("item"); + return (List>) request.get("circle"); } private Object deepCopy(Object obj) { diff --git a/fizz-core/src/main/java/we/fizz/StepResponse.java b/fizz-core/src/main/java/we/fizz/StepResponse.java index 01dc264..31caf1c 100644 --- a/fizz-core/src/main/java/we/fizz/StepResponse.java +++ b/fizz-core/src/main/java/we/fizz/StepResponse.java @@ -35,6 +35,8 @@ public class StepResponse { private Integer index; // circle results private List> circle; + // result of condition components + private List> conditionResults; public StepResponse(Step aStep, HashMap item, Map> requests) { setStepName(aStep.getName()); @@ -96,5 +98,11 @@ public class StepResponse { public void setIndex(Integer index) { this.index = index; } + public List> getConditionResults() { + return conditionResults; + } + public void setConditionResults(List> conditionResults) { + this.conditionResults = conditionResults; + } } diff --git a/fizz-core/src/main/java/we/fizz/component/ComponentHelper.java b/fizz-core/src/main/java/we/fizz/component/ComponentHelper.java index 72f1eb6..1a03875 100644 --- a/fizz-core/src/main/java/we/fizz/component/ComponentHelper.java +++ b/fizz-core/src/main/java/we/fizz/component/ComponentHelper.java @@ -98,60 +98,20 @@ public class ComponentHelper { if (conditions != null && conditions.size() > 0) { ONode ctxNode = toONode(stepContext); for (Condition c : conditions) { - if (!c.exec(ctxNode)) { - return null; + boolean rs = c.exec(ctxNode); + stepContext.addConditionResult(stepCtxPos.getStepName(), stepCtxPos.getRequestName(), c.getDesc(), + rs); + if (!rs) { + return Mono.empty(); } } } if (circle != null) { return circle.exec(stepContext, stepCtxPos, f); + } else { + return f.apply(stepContext, stepCtxPos); } -// // conditions before circle component -// List conditions1 = new ArrayList<>(); -// // conditions after circle component -// List conditions2 = new ArrayList<>(); -// Circle circle = null; -// for (IComponent component : components) { -// if (ComponentTypeEnum.CIRCLE == component.getType()) { -// circle = (Circle) component; -// } -// if (circle == null && ComponentTypeEnum.CONDITION == component.getType()) { -// conditions1.add((Condition) component); -// } -// if (circle != null && ComponentTypeEnum.CONDITION == component.getType()) { -// conditions2.add((Condition) component); -// } -// } -// -// if (conditions1 != null && conditions1.size() > 0) { -// ONode ctxNode = toONode(stepContext); -// for (Condition c : conditions1) { -// if (!c.exec(ctxNode)) { -// return null; -// } -// } -// } -// -// if (circle != null) { -// return circle.exec(stepContext, (ctx) -> { -// boolean canRun = true; -// if (conditions2 != null && conditions2.size() > 0) { -// ONode ctxNode = toONode(ctx); -// for (Condition c : conditions2) { -// if (!c.exec(ctxNode)) { -// canRun = false; -// } -// } -// } -// if (canRun) { -// return f.apply(ctx); -// } else { -// return Mono.empty(); -// } -// -// }); -// } } return Mono.empty(); } diff --git a/fizz-core/src/main/java/we/fizz/component/circle/Circle.java b/fizz-core/src/main/java/we/fizz/component/circle/Circle.java index 85e3e3b..db1be11 100644 --- a/fizz-core/src/main/java/we/fizz/component/circle/Circle.java +++ b/fizz-core/src/main/java/we/fizz/component/circle/Circle.java @@ -21,11 +21,8 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.function.BiFunction; -import java.util.function.Function; import org.noear.snack.ONode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import lombok.Data; import reactor.core.publisher.Flux; @@ -39,7 +36,6 @@ import we.fizz.component.ValueTypeEnum; import we.fizz.component.condition.Condition; import we.fizz.exception.FizzRuntimeException; import we.fizz.input.PathMapping; -import we.fizz.input.RPCInput; /** * Circle component @@ -102,7 +98,7 @@ public class Circle implements IComponent { /** * Reference value of dataSource */ - private List refValue; + private Object refValue; private boolean refReadFlag; @@ -113,8 +109,12 @@ public class Circle implements IComponent { if (dataSource == null) { return fixedValue; } - if (dataSource instanceof Integer || dataSource instanceof Long) { - fixedValue = Integer.valueOf(dataSource.toString()); + if (dataSource instanceof Integer || dataSource instanceof Long || dataSource instanceof String) { + try { + fixedValue = Integer.valueOf(dataSource.toString()); + } catch (Exception e) { + throw new FizzRuntimeException("invalid data source, fixed data source must be a positive integer"); + } if (fixedValue.intValue() < 1) { throw new FizzRuntimeException("invalid data source, fixed data source must be a positive integer"); } @@ -125,7 +125,7 @@ public class Circle implements IComponent { } @SuppressWarnings("unchecked") - private List getRefValue(ONode ctxNode) { + private Object getRefValue(ONode ctxNode) { if (refReadFlag) { return refValue; } @@ -136,8 +136,24 @@ public class Circle implements IComponent { if (value instanceof Collection) { refValue = (List) value; return refValue; + } else if (value instanceof Integer || value instanceof Long || value instanceof String) { + try { + Integer times = Integer.valueOf(value.toString()); + if (times.intValue() < 1) { + throw new FizzRuntimeException( + "invalid data source, data source must be a positive integer or an array"); + } + refValue = times; + } catch (FizzRuntimeException e) { + throw e; + } catch (Exception e) { + throw new FizzRuntimeException( + "invalid data source, data source must be a positive integer or an array"); + } + return refValue; } else { - throw new FizzRuntimeException("invalid data source, referenced data source must be a array"); + throw new FizzRuntimeException( + "invalid data source, referenced data source must be a positive integer or an array"); } } @@ -146,32 +162,49 @@ public class Circle implements IComponent { * * @return */ + @SuppressWarnings("unchecked") public CircleItem next(ONode ctxNode) { if (ValueTypeEnum.FIXED.equals(dataSourceType)) { Integer total = this.getFixedValue(ctxNode); if (index == null) { index = 0; - currentItem = index; + currentItem = index + 1; return new CircleItem(currentItem, index); } else if (index.intValue() < total.intValue() - 1) { index = index + 1; - currentItem = index; + currentItem = index + 1; return new CircleItem(currentItem, index); } else { return null; } } else if (ValueTypeEnum.REF.equals(dataSourceType)) { - List list = this.getRefValue(ctxNode); - if (index == null) { - index = 0; - currentItem = list.get(index); - return new CircleItem(currentItem, index); - } else if (index.intValue() < list.size() - 1) { - index = index + 1; - currentItem = list.get(index); - return new CircleItem(currentItem, index); - } else { - return null; + Object refValue = this.getRefValue(ctxNode); + if (refValue instanceof Collection) { + List list = (List) refValue; + if (index == null) { + index = 0; + currentItem = list.get(index); + return new CircleItem(currentItem, index); + } else if (index.intValue() < list.size() - 1) { + index = index + 1; + currentItem = list.get(index); + return new CircleItem(currentItem, index); + } else { + return null; + } + } else if (refValue instanceof Integer) { + Integer total = (Integer) refValue; + if (index == null) { + index = 0; + currentItem = index + 1; + return new CircleItem(currentItem, index); + } else if (index.intValue() < total.intValue() - 1) { + index = index + 1; + currentItem = index + 1; + return new CircleItem(currentItem, index); + } else { + return null; + } } } return null; @@ -183,11 +216,15 @@ public class Circle implements IComponent { * @param ctxNode * @return */ - public boolean canExec(ONode ctxNode) { + public boolean canExec(int index, ONode ctxNode, StepContext stepContext, + StepContextPosition stepCtxPos) { if (this.execConditions != null && this.execConditions.size() > 0) { try { - for (Condition condition : execConditions) { - if (!condition.exec(ctxNode)) { + for (Condition c : execConditions) { + boolean rs = c.exec(ctxNode); + stepContext.addConditionResult(stepCtxPos.getStepName(), stepCtxPos.getRequestName(), + "circle[" + index + "]-execCondition:" + c.getDesc(), rs); + if (!rs) { return false; } } @@ -204,11 +241,15 @@ public class Circle implements IComponent { * @param ctxNode * @return */ - public boolean breakCircle(ONode ctxNode) { + public boolean breakCircle(int index, ONode ctxNode, StepContext stepContext, + StepContextPosition stepCtxPos) { if (this.breakConditions != null && this.breakConditions.size() > 0) { try { - for (Condition condition : breakConditions) { - if (condition.exec(ctxNode)) { + for (Condition c : breakConditions) { + boolean rs = c.exec(ctxNode); + stepContext.addConditionResult(stepCtxPos.getStepName(), stepCtxPos.getRequestName(), + "circle[" + index + "]-breakCondition:" + c.getDesc(), rs); + if (rs) { return true; } } @@ -219,14 +260,14 @@ public class Circle implements IComponent { return false; } - @SuppressWarnings("unchecked") + @SuppressWarnings({ "unchecked", "rawtypes" }) public Mono exec(StepContext stepContext, StepContextPosition stepCtxPos, BiFunction f) { - ONode ctxNode = ComponentHelper.toONode(stepContext); - CircleItem nextItem = this.next(ctxNode); + ONode ctxNode1 = ComponentHelper.toONode(stepContext); + CircleItem nextItem = this.next(ctxNode1); if (nextItem != null) { - return Mono.just(new CircleItemResult(nextItem, null)).expand(circleItemResult -> { - // put nextItem to step context and ctxNode for further JSON path mapping + return Mono.just(new CircleItemResult(ctxNode1, nextItem, null)).expand(circleItemResult -> { + // put nextItem to step context CircleItem cItem = circleItemResult.nextItem; if (stepCtxPos.getRequestName() != null) { stepContext.setRequestCircleItem(stepCtxPos.getStepName(), stepCtxPos.getRequestName(), @@ -234,23 +275,26 @@ public class Circle implements IComponent { } else { stepContext.setStepCircleItem(stepCtxPos.getStepName(), cItem.getItem(), cItem.getIndex()); } + ONode ctxNode = circleItemResult.ctxNode; PathMapping.setByPath(ctxNode, stepCtxPos.getPath() + ".item", cItem.getItem(), true); PathMapping.setByPath(ctxNode, stepCtxPos.getPath() + ".index", cItem.getIndex(), true); - - if (!this.canExec(ctxNode)) { - return Mono.just(new CircleItemResult(this.next(ctxNode), null)); - } - if (this.breakCircle(ctxNode)) { - return Mono.empty(); + + if (!this.canExec(cItem.getIndex(), ctxNode, stepContext, stepCtxPos)) { + return Mono.just(new CircleItemResult(ctxNode, this.next(ctxNode), null)); } return f.apply(stepContext, stepCtxPos).flatMap(r -> { - CircleItem nextItem2 = this.next(ctxNode); + ONode ctxNode2 = ComponentHelper.toONode(stepContext); + if (this.breakCircle(cItem.getIndex(), ctxNode, stepContext, stepCtxPos)) { + return Mono.empty(); + } + CircleItem nextItem2 = this.next(ctxNode2); if (nextItem2 == null) { return Mono.empty(); } - return Mono.just(new CircleItemResult(nextItem2, r)); + return Mono.just(new CircleItemResult(ctxNode2, nextItem2, r)); }); - }).flatMap(circleItemResult -> Flux.just(circleItemResult)).collectList().flatMap(list -> { + }).flatMap(circleItemResult -> Flux.just(circleItemResult)).collectList().flatMap(r -> { + List list = (List) r; if (list != null && list.size() > 0) { Collections.reverse(list); for (int i = 0; i < list.size(); i++) { @@ -268,10 +312,12 @@ public class Circle implements IComponent { @Data class CircleItemResult { + private ONode ctxNode; private CircleItem nextItem; private Object result; - public CircleItemResult(CircleItem nextItem, Object result) { + public CircleItemResult(ONode ctxNode, CircleItem nextItem, Object result) { + this.ctxNode = ctxNode; this.nextItem = nextItem; this.result = result; } diff --git a/fizz-core/src/main/java/we/fizz/component/condition/Condition.java b/fizz-core/src/main/java/we/fizz/component/condition/Condition.java index a7e62b7..036749c 100644 --- a/fizz-core/src/main/java/we/fizz/component/condition/Condition.java +++ b/fizz-core/src/main/java/we/fizz/component/condition/Condition.java @@ -124,6 +124,7 @@ public class Condition implements IComponent { case CONTAINS: if (v1 == null) { rs = false; + break; } if (v1 instanceof Collection && !(v2 instanceof Collection)) { Collection coll1 = (Collection) v1; @@ -143,6 +144,7 @@ public class Condition implements IComponent { case NOT_CONTAIN: if (v1 == null) { rs = true; + break; } if (v1 instanceof Collection && !(v2 instanceof Collection)) { Collection coll1 = (Collection) v1; @@ -162,6 +164,7 @@ public class Condition implements IComponent { case CONTAINS_ANY: if (v1 == null || v2 == null) { rs = false; + break; } if (v1 instanceof Collection && v2 instanceof Collection) { Collection coll1 = (Collection) v1; @@ -235,7 +238,7 @@ public class Condition implements IComponent { } private Object cast(RefDataTypeEnum type, Object val) { - if (type != null) { + if (type != null && val != null) { switch (type) { case INT: val = Integer.valueOf(val.toString()); diff --git a/fizz-core/src/test/java/we/fizz/component/CircleTests.java b/fizz-core/src/test/java/we/fizz/component/CircleTests.java index 4e2ee52..ff09c68 100644 --- a/fizz-core/src/test/java/we/fizz/component/CircleTests.java +++ b/fizz-core/src/test/java/we/fizz/component/CircleTests.java @@ -25,6 +25,7 @@ import java.util.List; import org.junit.jupiter.api.Test; import org.noear.snack.ONode; +import we.fizz.StepContext; import we.fizz.component.circle.Circle; import we.fizz.component.circle.CircleItem; import we.fizz.component.condition.Condition; @@ -41,7 +42,6 @@ class CircleTests { void contextLoads() { } - @SuppressWarnings("rawtypes") @Test void testNextFixedDataSource() { @@ -50,14 +50,14 @@ class CircleTests { // FIXED data source Circle c = new Circle(null, ValueTypeEnum.FIXED, 3, null, null); CircleItem circleItem = c.next(ctxNode); - assertEquals(0, (Integer) circleItem.getItem()); - - circleItem = c.next(ctxNode); assertEquals(1, (Integer) circleItem.getItem()); circleItem = c.next(ctxNode); assertEquals(2, (Integer) circleItem.getItem()); + circleItem = c.next(ctxNode); + assertEquals(3, (Integer) circleItem.getItem()); + circleItem = c.next(ctxNode); assertEquals(null, circleItem); @@ -115,7 +115,8 @@ class CircleTests { CircleItem circleItem = circle.next(ctxNode); PathMapping.setByPath(ctxNode, "item", circleItem.getItem(), true); PathMapping.setByPath(ctxNode, "index", circleItem.getIndex(), true); - boolean rs = circle.canExec(ctxNode); + boolean rs = circle.canExec(circleItem.getIndex(), ctxNode, new StepContext(), + new StepContextPosition("step1", null)); assertEquals(i, circleItem.getIndex()); if (i < 3) { assertEquals(true, rs); @@ -127,7 +128,7 @@ class CircleTests { } } - + @Test void testBreakCondition() { ONode ctxNode = ONode.load(new HashMap()); @@ -154,7 +155,9 @@ class CircleTests { CircleItem circleItem = circle.next(ctxNode); PathMapping.setByPath(ctxNode, "item", circleItem.getItem(), true); PathMapping.setByPath(ctxNode, "index", circleItem.getIndex(), true); - boolean rs = circle.breakCircle(ctxNode); + + boolean rs = circle.breakCircle(circleItem.getIndex(), ctxNode, new StepContext(), + new StepContextPosition("step1", null)); assertEquals(i, circleItem.getIndex()); if (i < 3) { assertEquals(false, rs);