feat(接口测试): 场景增加逻辑控制器的解析

This commit is contained in:
wxg0103 2024-02-27 11:38:59 +08:00 committed by Craftsman
parent ee591fc936
commit 51c7327727
18 changed files with 802 additions and 25 deletions

View File

@ -2,6 +2,7 @@ package io.metersphere.api.constants;
/**
* 步骤的类型
*
* @Author: jianxing
* @CreateTime: 2024-01-10 11:24
*/
@ -21,6 +22,18 @@ public enum ApiScenarioStepType {
/**
* 场景
*/
API_SCENARIO
// todo 逻辑控制器等
API_SCENARIO,
/**
* 循环控制器
*/
LOOP_CONTROLLER,
/**
* 条件控制器
*/
IF_CONTROLLER,
/**
* 一次控制器
*/
ONCE_ONLY_CONTROLLER,
}

View File

@ -0,0 +1,5 @@
package io.metersphere.api.dto.request.controller;
public enum LoopType {
LOOP_COUNT, WHILE, FOREACH
}

View File

@ -0,0 +1,95 @@
package io.metersphere.api.dto.request.controller;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.StringUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Data
@EqualsAndHashCode(callSuper = true)
public class MsIfController extends AbstractMsTestElement {
/**
* 变量名称 ${variable} 长度255
*/
private String variable;
/**
* 操作符 == ,!=, < ,<=, >, >=, contains (=~),not contains (!~), is empty, is not empty
*/
private String condition;
/**
* ${value} 长度255
*/
private String value;
public boolean isValid() {
if (StringUtils.contains(condition, "is empty")) {
return StringUtils.isNotBlank(variable);
}
return StringUtils.isNotBlank(variable) && StringUtils.isNotBlank(condition) && StringUtils.isNotBlank(value);
}
public String getContentValue() {
try {
String content = this.variable;
String pattern = "\\$\\{([^}]*)\\}";
Pattern regex = Pattern.compile(pattern);
Matcher matcher = regex.matcher(content);
StringBuilder stringBuilder = new StringBuilder();
while (matcher.find()) {
stringBuilder.append(matcher.group(1)).append(",");
}
if (!stringBuilder.isEmpty()) {
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
}
if (StringUtils.isEmpty(stringBuilder.toString())) {
return this.variable;
}
return stringBuilder.toString();
} catch (Exception e) {
return null;
}
}
public String getConditionValue() {
String key = getContentValue();
String variable = (StringUtils.isEmpty(key) || key.equals(this.variable)) || key.startsWith("__")
? StringUtils.join("\"", this.variable, "\"")
: StringUtils.join("vars.get('", key, "')");
String operator = this.condition;
String value;
switch (operator) {
case "is empty":
variable = variable + "==" + "\"\\" + this.variable + "\"" + "|| empty(" + variable + ")";
operator = "";
value = "";
break;
case "is not empty":
variable = variable + "!=" + "\"\\" + this.variable + "\"" + "&& !empty(" + variable + ")";
operator = "";
value = "";
break;
case "<":
case ">":
case "<=":
case ">=":
value = this.value;
break;
case "=~":
case "!~":
value = "\"(\\n|.)*" + this.value + "(\\n|.)*\"";
break;
default:
value = "\"" + this.value + "\"";
break;
}
return "${__jexl3(" + variable + operator + value + ")}";
}
}

View File

@ -1,13 +1,40 @@
package io.metersphere.api.dto.request.controller;
import io.metersphere.plugin.api.annotation.PluginSubType;
import io.metersphere.plugin.api.dto.TestElementDTO;
import io.metersphere.api.dto.request.controller.loop.MsCountController;
import io.metersphere.api.dto.request.controller.loop.MsForEachController;
import io.metersphere.api.dto.request.controller.loop.MsWhileController;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.system.valid.EnumValue;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.UUID;
@Data
@EqualsAndHashCode(callSuper = true)
@PluginSubType("MsLoopController")
public class MsLoopController extends TestElementDTO {
private String loopType;
public class MsLoopController extends AbstractMsTestElement {
/**
* 循环类型
*
* @see LoopType
*/
@EnumValue(enumClass = LoopType.class)
private String loopType = LoopType.LOOP_COUNT.name();
/**
* 次数控制器
*/
private MsCountController msCountController;
/**
* ForEach控制器
*/
private MsForEachController forEachController;
/**
* While控制器
*/
private MsWhileController whileController;
private String currentTime = UUID.randomUUID().toString();
}

View File

@ -0,0 +1,11 @@
package io.metersphere.api.dto.request.controller;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class MsOnceOnlyController extends AbstractMsTestElement {
}

View File

@ -0,0 +1,5 @@
package io.metersphere.api.dto.request.controller;
public enum WhileConditionType {
CONDITION, SCRIPT
}

View File

@ -0,0 +1,15 @@
package io.metersphere.api.dto.request.controller.loop;
import lombok.Data;
@Data
public class MsCountController {
/**
* 循环次数
*/
private String loops;
/**
* 循环间隔
*/
private long loopTime = 0;
}

View File

@ -0,0 +1,20 @@
package io.metersphere.api.dto.request.controller.loop;
import lombok.Data;
@Data
public class MsForEachController {
/**
* 变量值 255
*/
private String variable;
/**
* 变量前缀 255
*/
private String value;
/**
* 循环间隔
*/
private long loopTime = 0;
}

View File

@ -0,0 +1,30 @@
package io.metersphere.api.dto.request.controller.loop;
import io.metersphere.api.dto.request.controller.WhileConditionType;
import io.metersphere.system.valid.EnumValue;
import lombok.Data;
@Data
public class MsWhileController {
/**
* 变量对象
*/
private MsWhileVariable msWhileVariable;
/**
* 脚本对象
*/
private MsWhileScript msWhileScript;
/**
* 超时时间
*/
private long timeout = 3000;
/**
* 条件类型
*
* @see io.metersphere.api.dto.request.controller.WhileConditionType
*/
@EnumValue(enumClass = WhileConditionType.class)
private String conditionType = WhileConditionType.CONDITION.name();
}

View File

@ -0,0 +1,14 @@
package io.metersphere.api.dto.request.controller.loop;
import lombok.Data;
@Data
public class MsWhileScript {
/**
* 脚本内容
*/
private String scriptValue;
}

View File

@ -0,0 +1,56 @@
package io.metersphere.api.dto.request.controller.loop;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
@Data
public class MsWhileVariable {
/**
* 变量 255
*/
private String variable;
/**
* 操作符 == ,!=, < ,<=, >, >=, contains (=~),not contains (!~), is empty, is not empty
*/
private String condition;
/**
* 255
*/
private String value;
public String getConditionValue() {
String variable = "\"" + this.getVariable() + "\"";
String operator = this.getCondition();
String value = null;
switch (operator) {
case "is empty":
variable = "(" + variable + "==" + "\"\\" + this.getVariable() + "\"" + "|| empty(" + variable + "))";
operator = "";
break;
case "is not empty":
variable = "(" + variable + "!=" + "\"\\" + this.getVariable() + "\"" + "&& !empty(" + variable + "))";
operator = "";
break;
case "<":
case ">":
case "<=":
case ">=":
if (StringUtils.isNumeric(this.getValue())) {
value = this.getValue();
} else {
value = "\"" + this.getValue() + "\"";
}
break;
case "=~":
case "!~":
value = "\"(\\n|.)*" + this.getVariable() + "(\\n|.)*\"";
break;
default:
value = "\"" + this.getValue() + "\"";
break;
}
return variable + operator + value;
}
}

View File

@ -0,0 +1,43 @@
package io.metersphere.api.parser.jmeter;
import io.metersphere.api.dto.request.controller.MsIfController;
import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter;
import io.metersphere.sdk.util.LogUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.control.IfController;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
public class MsIfControllerConverter extends AbstractJmeterElementConverter<MsIfController> {
@Override
public void toHashTree(HashTree tree, MsIfController element, ParameterConfig config) {
if (BooleanUtils.isFalse(element.getEnable())) {
LogUtils.info("MsIfController is disabled");
return;
}
//TODO 这里需要处理csv文件
HashTree groupTree = tree.add(ifController(element));
parseChild(groupTree, element, config);
}
private IfController ifController(MsIfController element) {
IfController ifController = new IfController();
ifController.setEnabled(element.getEnable());
ifController.setName(StringUtils.isNotBlank(element.getName()) ? element.getName() : "If Controller");
ifController.setProperty(TestElement.TEST_CLASS, IfController.class.getName());
ifController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("IfControllerPanel"));
ifController.setCondition(element.getConditionValue());
ifController.setEvaluateAll(false);
ifController.setUseExpression(true);
return ifController;
}
}

View File

@ -0,0 +1,207 @@
package io.metersphere.api.parser.jmeter;
import io.metersphere.api.dto.request.controller.LoopType;
import io.metersphere.api.dto.request.controller.MsLoopController;
import io.metersphere.api.dto.request.controller.WhileConditionType;
import io.metersphere.api.parser.jmeter.processor.ScriptFilter;
import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter;
import io.metersphere.project.constants.ScriptLanguageType;
import io.metersphere.sdk.util.LogUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.control.*;
import org.apache.jmeter.modifiers.JSR223PreProcessor;
import org.apache.jmeter.protocol.java.sampler.JSR223Sampler;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.timers.ConstantTimer;
import org.apache.jorphan.collections.HashTree;
public class MsLoopControllerConverter extends AbstractJmeterElementConverter<MsLoopController> {
@Override
public void toHashTree(HashTree tree, MsLoopController element, ParameterConfig config) {
if (BooleanUtils.isFalse(element.getEnable())) {
LogUtils.info("MsLoopController is disabled");
return;
}
final HashTree groupTree = controller(tree, element);
//TODO 这里需要处理csv文件 场景变量
if (groupTree == null) {
return;
}
// 循环间隔时长设置
ConstantTimer constantTimer = getConstantTimer(element);
if (constantTimer != null) {
groupTree.add(constantTimer);
}
parseChild(groupTree, element, config);
}
private HashTree controller(HashTree tree, MsLoopController element) {
if (StringUtils.equals(element.getLoopType(), LoopType.WHILE.name()) && element.getWhileController() != null) {
RunTime runTime = new RunTime();
runTime.setEnabled(true);
runTime.setProperty(TestElement.TEST_CLASS, RunTime.class.getName());
runTime.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("RunTimeGui"));
long timeout = element.getWhileController().getTimeout() / 1000;
if (timeout < 1) {
timeout = 1;
}
runTime.setRuntime(timeout);
String condition;
if (StringUtils.equals(WhileConditionType.CONDITION.name(), element.getWhileController().getConditionType())) {
condition = element.getWhileController().getMsWhileVariable().getConditionValue();
} else {
condition = element.getWhileController().getMsWhileScript().getScriptValue();
}
String ifCondition = "${__jexl3(" + condition + ")}";
String whileCondition = "${__jexl3(" + condition + " && \"${" + element.getCurrentTime() + "}\" !=\"stop\")}";
HashTree ifHashTree = tree.add(ifController(ifCondition, element.getEnable()));
addPreProc(ifHashTree, element);
HashTree hashTree = ifHashTree.add(initWhileController(element, whileCondition));
// 添加超时处理防止死循环
JSR223PreProcessor jsr223PreProcessor = new JSR223PreProcessor();
jsr223PreProcessor.setName("While 循环控制器超时处理脚本");
jsr223PreProcessor.setProperty(TestElement.TEST_CLASS, JSR223Sampler.class.getName());
jsr223PreProcessor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
jsr223PreProcessor.setProperty("scriptLanguage", ScriptLanguageType.BEANSHELL.name().toLowerCase());
ScriptFilter.verify(ScriptLanguageType.BEANSHELL.name().toLowerCase(), element.getName(), script(element));
jsr223PreProcessor.setProperty("script", script(element));
hashTree.add(jsr223PreProcessor);
return hashTree;
}
if (StringUtils.equals(element.getLoopType(), LoopType.FOREACH.name()) && element.getForEachController() != null) {
return tree.add(initForeachController(element));
}
if (StringUtils.equals(element.getLoopType(), LoopType.LOOP_COUNT.name()) && element.getMsCountController() != null) {
String ifCondition = StringUtils.join("${__jexl3(", element.getMsCountController().getLoops(), " > 0 ", ")}");
HashTree ifHashTree = tree.add(ifController(ifCondition, element.getEnable()));
return ifHashTree.add(initLoopController(element));
}
return null;
}
private static String script(MsLoopController element) {
String script = """
import java.util.*;
import java.text.SimpleDateFormat;
import org.apache.jmeter.threads.JMeterContextService;
// 循环控制器超时后结束循环
try{
String ms_current_timer = vars.get("%s");
long _nowTime = System.currentTimeMillis();
if(ms_current_timer == null ){
vars.put("%s",_nowTime.toString());
}
long time = Long.parseLong(vars.get("%s"));
if((_nowTime - time) > %s ){
vars.put("%s", "stop");
log.info( "结束循环");
}
}catch (Exception e){
log.info( e.getMessage());
vars.put("%s", "stop");
}
""";
return String.format(script, element.getCurrentTime(), element.getCurrentTime(), element.getCurrentTime(), element.getWhileController().getTimeout(), element.getCurrentTime(), element.getCurrentTime());
}
private void addPreProc(HashTree hashTree, MsLoopController element) {
JSR223Sampler sampler = new JSR223Sampler();
sampler.setName("MS_CLEAR_LOOPS_VAR_" + element.getCurrentTime());
sampler.setProperty(TestElement.TEST_CLASS, JSR223Sampler.class.getName());
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
sampler.setProperty("scriptLanguage", ScriptLanguageType.BEANSHELL.name().toLowerCase());
ScriptFilter.verify(ScriptLanguageType.BEANSHELL.name().toLowerCase(), element.getName(), script(element));
sampler.setProperty("script", "vars.put(\"" + element.getCurrentTime() + "\", null);");
hashTree.add(sampler);
}
private LoopController initLoopController(MsLoopController element) {
LoopController loopController = new LoopController();
loopController.setEnabled(element.getEnable());
loopController.setName(StringUtils.isNotBlank(element.getName()) ? element.getName() : "Loop Controller");
loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName());
loopController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("LoopControlPanel"));
loopController.setLoops(element.getMsCountController().getLoops());
if (StringUtils.isNotEmpty(element.getMsCountController().getLoops())) {
loopController.setContinueForever(true);
}
return loopController;
}
private IfController ifController(String condition, boolean enable) {
IfController ifController = new IfController();
ifController.setEnabled(enable);
ifController.setName("while ifController");
ifController.setProperty(TestElement.TEST_CLASS, IfController.class.getName());
ifController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("IfControllerPanel"));
ifController.setCondition(condition);
ifController.setEvaluateAll(false);
ifController.setUseExpression(true);
return ifController;
}
private WhileController initWhileController(MsLoopController element, String condition) {
WhileController controller = new WhileController();
controller.setEnabled(element.getEnable());
controller.setName(StringUtils.isNotBlank(element.getName()) ? element.getName() : "While Controller");
controller.setProperty(TestElement.TEST_CLASS, WhileController.class.getName());
controller.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("WhileControllerGui"));
controller.setCondition(condition);
return controller;
}
private ForeachController initForeachController(MsLoopController element) {
ForeachController controller = new ForeachController();
controller.setEnabled(element.getEnable());
controller.setName(StringUtils.isNotBlank(element.getName()) ? element.getName() : "Foreach Controller");
controller.setProperty(TestElement.TEST_CLASS, ForeachController.class.getName());
controller.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ForeachControlPanel"));
controller.setInputVal(element.getForEachController().getVariable());
controller.setReturnVal(element.getForEachController().getValue());
controller.setUseSeparator(true);
return controller;
}
private ConstantTimer getConstantTimer(MsLoopController element) {
ConstantTimer constantTimer = new ConstantTimer();
constantTimer.setEnabled(element.getEnable());
constantTimer.setProperty(TestElement.TEST_CLASS, ConstantTimer.class.getName());
constantTimer.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ConstantTimerGui"));
if (StringUtils.equals(element.getLoopType(), LoopType.WHILE.name()) && element.getWhileController() != null) {
return null;
}
if (StringUtils.equals(element.getLoopType(), LoopType.FOREACH.name()) && element.getForEachController() != null &&
element.getForEachController().getLoopTime() > 0) {
constantTimer.setProperty("ConstantTimer.delay", element.getForEachController().getLoopTime());
constantTimer.setDelay(element.getForEachController().getLoopTime() + StringUtils.EMPTY);
constantTimer.setName(element.getForEachController().getLoopTime() + " ms");
return constantTimer;
}
if (StringUtils.equals(element.getLoopType(), LoopType.LOOP_COUNT.name()) && element.getMsCountController() != null &&
element.getMsCountController().getLoopTime() > 0) {
constantTimer.setProperty("ConstantTimer.delay", element.getMsCountController().getLoopTime() + "");
constantTimer.setDelay(element.getMsCountController().getLoopTime() + StringUtils.EMPTY);
constantTimer.setName(element.getMsCountController().getLoopTime() + " ms");
return constantTimer;
}
return null;
}
}

View File

@ -0,0 +1,37 @@
package io.metersphere.api.parser.jmeter;
import io.metersphere.api.dto.request.controller.MsOnceOnlyController;
import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter;
import io.metersphere.sdk.util.LogUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.control.OnceOnlyController;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
public class MsOnceOnlyControllerConverter extends AbstractJmeterElementConverter<MsOnceOnlyController> {
@Override
public void toHashTree(HashTree tree, MsOnceOnlyController element, ParameterConfig config) {
if (BooleanUtils.isFalse(element.getEnable())) {
LogUtils.info("MsOnceOnlyController is disabled");
return;
}
//TODO 这里需要处理csv文件
HashTree groupTree = tree.add(onceOnlyController(element));
parseChild(groupTree, element, config);
}
private OnceOnlyController onceOnlyController(MsOnceOnlyController element) {
OnceOnlyController onceOnlyController = new OnceOnlyController();
onceOnlyController.setEnabled(element.getEnable());
onceOnlyController.setName(StringUtils.isNotBlank(element.getName()) ? element.getName() : "Once Only Controller");
onceOnlyController.setProperty(TestElement.TEST_CLASS, OnceOnlyController.class.getName());
onceOnlyController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("OnceOnlyControllerGui"));
return onceOnlyController;
}
}

View File

@ -11,6 +11,9 @@ import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.type.CollectionType;
import io.metersphere.api.dto.request.MsCommonElement;
import io.metersphere.api.dto.request.controller.MsIfController;
import io.metersphere.api.dto.request.controller.MsLoopController;
import io.metersphere.api.dto.request.controller.MsOnceOnlyController;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.sdk.exception.MSException;
@ -34,6 +37,9 @@ public class ApiDataUtils {
// 默认内置的子组件
namedTypes.add(new NamedType(MsHTTPElement.class, MsHTTPElement.class.getSimpleName()));
namedTypes.add(new NamedType(MsCommonElement.class, MsCommonElement.class.getSimpleName()));
namedTypes.add(new NamedType(MsIfController.class, MsIfController.class.getSimpleName()));
namedTypes.add(new NamedType(MsLoopController.class, MsLoopController.class.getSimpleName()));
namedTypes.add(new NamedType(MsOnceOnlyController.class, MsOnceOnlyController.class.getSimpleName()));
setObjectMapper(objectMapper);
namedTypes.forEach(objectMapper::registerSubtypes);

View File

@ -1,9 +1,6 @@
package io.metersphere.api.utils;
import io.metersphere.api.parser.jmeter.MsCommentScriptElementConverter;
import io.metersphere.api.parser.jmeter.MsCommonElementConverter;
import io.metersphere.api.parser.jmeter.MsHTTPElementConverter;
import io.metersphere.api.parser.jmeter.MsScenarioConverter;
import io.metersphere.api.parser.jmeter.*;
import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter;
import io.metersphere.plugin.api.spi.MsTestElement;
import io.metersphere.plugin.sdk.util.PluginLogUtils;
@ -32,6 +29,9 @@ public class JmeterElementConverterRegister {
register(MsCommonElementConverter.class);
register(MsCommentScriptElementConverter.class);
register(MsScenarioConverter.class);
register(MsIfControllerConverter.class);
register(MsLoopControllerConverter.class);
register(MsOnceOnlyControllerConverter.class);
}
/**

View File

@ -6,6 +6,8 @@ import io.metersphere.api.dto.ApiFile;
import io.metersphere.api.dto.assertion.MsAssertionConfig;
import io.metersphere.api.dto.debug.ModuleCreateRequest;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.request.controller.*;
import io.metersphere.api.dto.request.controller.loop.*;
import io.metersphere.api.dto.request.http.Header;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.dto.request.http.QueryParam;
@ -642,6 +644,7 @@ public class ApiScenarioControllerTests extends BaseTest {
/**
* 测试关联的文件更新
*
* @throws Exception
*/
public void testHandleFileAssociationUpgrade() throws Exception {
@ -665,7 +668,8 @@ public class ApiScenarioControllerTests extends BaseTest {
try {
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(new String(apiScenarioStepBlob.getContent()), AbstractMsTestElement.class);
apiFiles.addAll(apiCommonService.getApiFilesByFileId(fileId, msTestElement));
} catch (Exception e) {}
} catch (Exception e) {
}
}
return apiFiles;
}
@ -724,6 +728,7 @@ public class ApiScenarioControllerTests extends BaseTest {
partialScenarioStep.setRefType(ApiScenarioStepRefType.PARTIAL_REF.name());
partialScenarioStep.setChildren(getApiScenarioStepRequests());
steps.add(partialScenarioStep);
request.setId(addApiScenario.getId());
request.setSteps(steps);
request.setStepDetails(new HashMap<>());
@ -762,6 +767,207 @@ public class ApiScenarioControllerTests extends BaseTest {
requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE, DEBUG, request);
}
@Test
@Order(7)
public void testLoopController() throws Exception {
// @@请求成功
mockPost("/api/debug", "");
baseResourcePoolTestService.initProjectResourcePool();
// @@请求成功
ApiScenarioDebugRequest request = new ApiScenarioDebugRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
request.setScenarioConfig(new ScenarioConfig());
request.setEnvironmentId(envId);
request.setGrouped(false);
ScenarioStepConfig scenarioStepConfig = new ScenarioStepConfig();
scenarioStepConfig.setEnableScenarioEnv(true);
List<ApiScenarioStepRequest> steps = new ArrayList<>();
Map<String, Object> stepDetails = new HashMap<>();
MsIfController msIfController = ifController("test-if", true);
ApiScenarioStepRequest msIf = BeanUtils.copyBean(new ApiScenarioStepRequest(), msIfController);
msIf.setRefType(ApiScenarioStepRefType.DIRECT.name());
msIf.setProjectId(DEFAULT_PROJECT_ID);
msIf.setStepType(ApiScenarioStepType.IF_CONTROLLER.name());
msIf.setId(IDGenerator.nextStr());
steps.add(msIf);
stepDetails.put(msIf.getId(), JSON.parseObject(ApiDataUtils.toJSONString(msIfController)));
msIfController = ifController(null, true);
msIf = BeanUtils.copyBean(new ApiScenarioStepRequest(), msIfController);
msIf.setRefType(ApiScenarioStepRefType.DIRECT.name());
msIf.setProjectId(DEFAULT_PROJECT_ID);
msIf.setStepType(ApiScenarioStepType.IF_CONTROLLER.name());
msIf.setId(IDGenerator.nextStr());
steps.add(msIf);
stepDetails.put(msIf.getId(), JSON.parseObject(ApiDataUtils.toJSONString(msIfController)));
msIfController = ifController(null, false);
msIf = BeanUtils.copyBean(new ApiScenarioStepRequest(), msIfController);
msIf.setRefType(ApiScenarioStepRefType.DIRECT.name());
msIf.setProjectId(DEFAULT_PROJECT_ID);
msIf.setStepType(ApiScenarioStepType.IF_CONTROLLER.name());
msIf.setId(IDGenerator.nextStr());
steps.add(msIf);
stepDetails.put(msIf.getId(), JSON.parseObject(ApiDataUtils.toJSONString(msIfController)));
MsOnceOnlyController msOnceOnlyController = onceController("test-once-only", true);
ApiScenarioStepRequest onceOnlyController = BeanUtils.copyBean(new ApiScenarioStepRequest(), msOnceOnlyController);
onceOnlyController.setRefType(ApiScenarioStepRefType.DIRECT.name());
onceOnlyController.setProjectId(DEFAULT_PROJECT_ID);
onceOnlyController.setStepType(ApiScenarioStepType.ONCE_ONLY_CONTROLLER.name());
onceOnlyController.setId(IDGenerator.nextStr());
steps.add(onceOnlyController);
stepDetails.put(onceOnlyController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(msOnceOnlyController)));
msOnceOnlyController = onceController(null, true);
onceOnlyController = BeanUtils.copyBean(new ApiScenarioStepRequest(), msOnceOnlyController);
onceOnlyController.setRefType(ApiScenarioStepRefType.DIRECT.name());
onceOnlyController.setProjectId(DEFAULT_PROJECT_ID);
onceOnlyController.setStepType(ApiScenarioStepType.ONCE_ONLY_CONTROLLER.name());
onceOnlyController.setId(IDGenerator.nextStr());
steps.add(onceOnlyController);
stepDetails.put(onceOnlyController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(msOnceOnlyController)));
msOnceOnlyController = onceController(null, false);
onceOnlyController = BeanUtils.copyBean(new ApiScenarioStepRequest(), msOnceOnlyController);
onceOnlyController.setRefType(ApiScenarioStepRefType.DIRECT.name());
onceOnlyController.setProjectId(DEFAULT_PROJECT_ID);
onceOnlyController.setStepType(ApiScenarioStepType.ONCE_ONLY_CONTROLLER.name());
onceOnlyController.setId(IDGenerator.nextStr());
steps.add(onceOnlyController);
stepDetails.put(onceOnlyController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(msOnceOnlyController)));
MsLoopController loopCount = loopController("LOOP_COUNT", null, true);
ApiScenarioStepRequest loopController = BeanUtils.copyBean(new ApiScenarioStepRequest(), loopCount);
loopController.setRefType(ApiScenarioStepRefType.DIRECT.name());
loopController.setProjectId(DEFAULT_PROJECT_ID);
loopController.setId(IDGenerator.nextStr());
loopController.setStepType(ApiScenarioStepType.LOOP_CONTROLLER.name());
stepDetails.put(loopController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(loopCount)));
steps.add(loopController);
loopCount = loopController("LOOP_COUNT", null, false);
loopController = BeanUtils.copyBean(new ApiScenarioStepRequest(), loopCount);
loopController.setRefType(ApiScenarioStepRefType.DIRECT.name());
loopController.setProjectId(DEFAULT_PROJECT_ID);
loopController.setId(IDGenerator.nextStr());
loopController.setStepType(ApiScenarioStepType.LOOP_CONTROLLER.name());
stepDetails.put(loopController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(loopCount)));
steps.add(loopController);
MsLoopController aWhile = loopController("WHILE", "CONDITION", true);
loopController = BeanUtils.copyBean(new ApiScenarioStepRequest(), aWhile);
loopController.setRefType(ApiScenarioStepRefType.DIRECT.name());
loopController.setProjectId(DEFAULT_PROJECT_ID);
loopController.setId(IDGenerator.nextStr());
loopController.setStepType(ApiScenarioStepType.LOOP_CONTROLLER.name());
stepDetails.put(loopController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(aWhile)));
steps.add(loopController);
aWhile = loopController("WHILE", "SCRIPT", true);
loopController = BeanUtils.copyBean(new ApiScenarioStepRequest(), aWhile);
loopController.setRefType(ApiScenarioStepRefType.DIRECT.name());
loopController.setProjectId(DEFAULT_PROJECT_ID);
loopController.setId(IDGenerator.nextStr());
loopController.setStepType(ApiScenarioStepType.LOOP_CONTROLLER.name());
stepDetails.put(loopController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(aWhile)));
steps.add(loopController);
MsLoopController foreach = loopController("FOREACH", null, true);
loopController = BeanUtils.copyBean(new ApiScenarioStepRequest(), foreach);
loopController.setRefType(ApiScenarioStepRefType.DIRECT.name());
loopController.setProjectId(DEFAULT_PROJECT_ID);
loopController.setId(IDGenerator.nextStr());
loopController.setStepType(ApiScenarioStepType.LOOP_CONTROLLER.name());
stepDetails.put(loopController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(foreach)));
steps.add(loopController);
request.setId(addApiScenario.getId());
request.setSteps(steps);
request.setStepDetails(new HashMap<>());
request.setReportId(IDGenerator.nextStr());
request.setStepDetails(stepDetails);
this.requestPostWithOk(DEBUG, request);
}
//条件控制器
public MsIfController ifController(String name, boolean enable) {
//条件控制器
MsIfController msIfController = new MsIfController();
msIfController.setCondition("==");
msIfController.setName(StringUtils.isNotBlank(name) ? name : "条件控制器");
msIfController.setEnable(enable);
msIfController.setVariable("1");
msIfController.setValue("1");
msIfController.setStepId(IDGenerator.nextStr());
LinkedList<AbstractMsTestElement> msTestElements = new LinkedList<>();
//条件控制器下的步骤
MsHTTPElement msHTTPElement = MsHTTPElementTest.getMsHttpElement();
msTestElements.add(msHTTPElement);
msIfController.setChildren(msTestElements);
return msIfController;
}
//一次控制器
public MsOnceOnlyController onceController(String name, boolean enable) {
//一次控制器
MsOnceOnlyController onceOnlyController = new MsOnceOnlyController();
onceOnlyController.setName(StringUtils.isNotBlank(name) ? name : "一次控制器");
onceOnlyController.setStepId(IDGenerator.nextStr());
onceOnlyController.setEnable(enable);
LinkedList<AbstractMsTestElement> msTestElements = new LinkedList<>();
//一次控制器下的步骤
MsHTTPElement msHTTPElement = MsHTTPElementTest.getMsHttpElement();
msTestElements.add(msHTTPElement);
onceOnlyController.setChildren(msTestElements);
return onceOnlyController;
}
//循环控制器 loopController
public MsLoopController loopController(String loopType, String condition, boolean enable) {
//循环控制器
MsLoopController msLoopController = new MsLoopController();
msLoopController.setStepId(IDGenerator.nextStr());
switch (loopType) {
case "LOOP_COUNT" -> {
MsCountController msCountController = new MsCountController();
msLoopController.setName("次数循环控制器");
msCountController.setLoops("1");
msCountController.setLoopTime(300L);
msLoopController.setLoopType(LoopType.LOOP_COUNT.name());
msLoopController.setMsCountController(msCountController);
}
case "WHILE" -> {
MsWhileController whileController = new MsWhileController();
msLoopController.setLoopType(LoopType.WHILE.name());
msLoopController.setName("while 循环控制器");
if (StringUtils.isNotBlank(condition) && StringUtils.equals(condition, "CONDITION")) {
MsWhileVariable msWhileVariable = new MsWhileVariable();
msWhileVariable.setVariable("1");
msWhileVariable.setCondition("==");
msWhileVariable.setValue("1");
whileController.setMsWhileVariable(msWhileVariable);
} else {
whileController.setConditionType(WhileConditionType.SCRIPT.name());
MsWhileScript msWhileScript = new MsWhileScript();
msWhileScript.setScriptValue("test");
whileController.setMsWhileScript(msWhileScript);
}
whileController.setTimeout(300L);
msLoopController.setWhileController(whileController);
}
default -> {
MsForEachController forEachController = new MsForEachController();
msLoopController.setLoopType(LoopType.FOREACH.name());
forEachController.setVariable("1");
msLoopController.setName("foreach 循环控制器");
forEachController.setValue("1");
forEachController.setLoopTime(300L);
msLoopController.setForEachController(forEachController);
}
}
msLoopController.setEnable(enable);
LinkedList<AbstractMsTestElement> msTestElements = new LinkedList<>();
//循环控制器下的步骤
MsHTTPElement msHTTPElement = MsHTTPElementTest.getMsHttpElement();
msTestElements.add(msHTTPElement);
msLoopController.setChildren(msTestElements);
return msLoopController;
}
public Plugin addEnvTestPlugin() throws Exception {
PluginUpdateRequest request = new PluginUpdateRequest();
File jarFile = new File(

View File

@ -3,7 +3,6 @@ package io.metersphere.api.controller;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import io.metersphere.api.dto.request.controller.MsLoopController;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.dto.TestElementDTO;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.plugin.api.spi.MsTestElement;
import io.metersphere.system.base.BaseApiPluginTestService;
@ -38,18 +37,6 @@ public class PluginSubTypeTests {
ApiDataUtils.setResolver(namedTypes);
}
@Test
@Order(1)
public void newPluginSubTypeTest() throws Exception {
MsLoopController loopController = new MsLoopController();
loopController.setName("测试loopController");
String json = ApiDataUtils.toJSONString(loopController);
TestElementDTO testElementDTO = ApiDataUtils.parseObject(json, TestElementDTO.class);
Assertions.assertNotNull(testElementDTO);
}
@Test
@Order(2)
public void jdbcPluginSubTypeTest() throws Exception {