feat(接口测试): 解析断言相关执行脚本

This commit is contained in:
AgAngle 2024-01-03 15:33:03 +08:00 committed by Craftsman
parent f193b9f242
commit ee4d3a7d49
49 changed files with 1120 additions and 202 deletions

View File

@ -12,11 +12,11 @@ public enum MsAssertionCondition {
/**
* 包含
*/
INCLUDE,
CONTAINS,
/**
* 不包含
*/
NOT_INCLUDE,
NOT_CONTAINS,
/**
* 等于
*/

View File

@ -10,11 +10,12 @@ import lombok.Data;
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "assertionType")
@JsonSubTypes({
@JsonSubTypes.Type(value = ResponseCodeAssertion.class),
@JsonSubTypes.Type(value = ResponseHeaderAssertion.class),
@JsonSubTypes.Type(value = ResponseBodyAssertion.class),
@JsonSubTypes.Type(value = ResponseTimeAssertion.class),
@JsonSubTypes.Type(value = ScriptAssertion.class),
@JsonSubTypes.Type(value = MsResponseCodeAssertion.class),
@JsonSubTypes.Type(value = MsResponseHeaderAssertion.class),
@JsonSubTypes.Type(value = MsResponseBodyAssertion.class),
@JsonSubTypes.Type(value = MsResponseTimeAssertion.class),
@JsonSubTypes.Type(value = MsScriptAssertion.class),
@JsonSubTypes.Type(value = MsVariableAssertion.class),
})
public abstract class MsAssertion {
/**

View File

@ -1,23 +0,0 @@
package io.metersphere.api.dto.request.assertion;
/**
* body断言中的断言类型
*/
public enum MsBodyAssertionType {
/**
* 正则断言
*/
REGEX,
/**
* XPath断言
*/
XPATH,
/**
* JSONPath断言
*/
JSON_PATH,
/**
* 文档断言
*/
DOCUMENT
}

View File

@ -0,0 +1,89 @@
package io.metersphere.api.dto.request.assertion;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.metersphere.api.dto.request.assertion.body.MsDocumentAssertion;
import io.metersphere.api.dto.request.assertion.body.MsJSONPathAssertion;
import io.metersphere.api.dto.request.assertion.body.MsRegexAssertion;
import io.metersphere.api.dto.request.assertion.body.MsXPathAssertion;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* 请求体断言
*
* @Author: jianxing
* @CreateTime: 2023-11-22 15:33
*/
@Data
@JsonTypeName("RESPONSE_BODY")
public class MsResponseBodyAssertion extends MsAssertion {
/**
* 断言类型
* 根据断言类型选择对应的断言
* 这里跟前端数据结构有差异
* 后端从设计层面支持多种断言前端只支持一种
* 同时切换可以同时持久化两种类型
*
* @see MsBodyAssertionType
*/
private String assertionBodyType;
/**
* jsonPath断言
*/
private MsJSONPathAssertion jsonPathAssertion;
/**
* xpath断言
*/
private MsXPathAssertion xpathAssertion;
/**
* 文档断言
*/
private MsDocumentAssertion documentAssertion;
/**
* 正则断言
*/
private MsRegexAssertion regexAssertion;
private static Map<MsBodyAssertionType, Class> bodyAssertionClassMap = new HashMap<>();
static {
bodyAssertionClassMap.put(MsBodyAssertionType.JSON_PATH, MsJSONPathAssertion.class);
bodyAssertionClassMap.put(MsBodyAssertionType.DOCUMENT, MsDocumentAssertion.class);
bodyAssertionClassMap.put(MsBodyAssertionType.REGEX, MsRegexAssertion.class);
bodyAssertionClassMap.put(MsBodyAssertionType.XPATH, MsXPathAssertion.class);
}
public Class getBodyAssertionClassByType() {
return bodyAssertionClassMap.get(MsBodyAssertionType.valueOf(assertionBodyType));
}
public Object getBodyAssertionDataByType() {
Map<MsBodyAssertionType, Object> boadyAssertionMap = new HashMap<>();
boadyAssertionMap.put(MsBodyAssertionType.JSON_PATH, jsonPathAssertion);
boadyAssertionMap.put(MsBodyAssertionType.DOCUMENT, documentAssertion);
boadyAssertionMap.put(MsBodyAssertionType.REGEX, regexAssertion);
boadyAssertionMap.put(MsBodyAssertionType.XPATH, xpathAssertion);
return boadyAssertionMap.get(MsBodyAssertionType.valueOf(assertionBodyType));
}
public enum MsBodyAssertionType {
/**
* 正则断言
*/
REGEX,
/**
* XPath断言
*/
XPATH,
/**
* JSONPath断言
*/
JSON_PATH,
/**
* 文档断言
*/
DOCUMENT
}
}

View File

@ -9,17 +9,17 @@ import lombok.Data;
*/
@Data
@JsonTypeName("RESPONSE_CODE")
public class ResponseCodeAssertion extends MsAssertion {
public class MsResponseCodeAssertion extends MsAssertion {
/**
* 匹配条件
* 不校验即忽略状态
* 选择其他条件时也忽略状态
* 不校验可搭配其他校验使用
* 值为 MsAssertionCondition
* @see io.metersphere.sdk.constants.MsAssertionCondition
*/
private String condition;
/**
* 匹配值
*/
private String value;
private String expectedValue;
}

View File

@ -12,7 +12,7 @@ import java.util.List;
*/
@Data
@JsonTypeName("RESPONSE_HEADER")
public class ResponseHeaderAssertion extends MsAssertion {
public class MsResponseHeaderAssertion extends MsAssertion {
private List<ResponseHeaderAssertionItem> assertions;
@ -35,6 +35,6 @@ public class ResponseHeaderAssertion extends MsAssertion {
/**
* 匹配值
*/
private String value;
private String expectedValue;
}
}

View File

@ -10,10 +10,10 @@ import lombok.Data;
*/
@Data
@JsonTypeName("RESPONSE_TIME")
public class ResponseTimeAssertion extends MsAssertion {
public class MsResponseTimeAssertion extends MsAssertion {
/**
* 最大响应时间
* 响应时间在xx毫秒内
*/
private Long maxResponseTime;
private Long expectedValue;
}

View File

@ -0,0 +1,39 @@
package io.metersphere.api.dto.request.assertion;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.metersphere.api.dto.request.http.KeyValueParam;
import io.metersphere.api.dto.request.processors.ScriptProcessor;
import lombok.Data;
import java.util.List;
/**
* 变量断言
* @Author: jianxing
* @CreateTime: 2023-11-22 15:33
*/
@Data
@JsonTypeName("SCRIPT")
public class MsScriptAssertion extends MsAssertion {
/**
* 脚本内容
*/
private String script;
/**
* 脚本语言
* @see ScriptProcessor.ScriptLanguageType
*/
private String scriptLanguage;
/**
* 是否启用公共脚本
*/
private Boolean enableCommonScript;
/**
* 脚本ID
*/
private String scriptId;
/**
* 公共脚本入参
*/
private List<KeyValueParam> params;
}

View File

@ -12,9 +12,9 @@ import java.util.List;
*/
@Data
@JsonTypeName("VARIABLE")
public class VariableAssertion {
public class MsVariableAssertion extends MsAssertion {
private List<VariableAssertionItem> variableAssertionItem;
private List<VariableAssertionItem> variableAssertionItems;
@Data
public static class VariableAssertionItem {
/**
@ -24,7 +24,7 @@ public class VariableAssertion {
/**
* 变量名
*/
private String name;
private String variableName;
/**
* 匹配条件
* 值为 MsAssertionCondition
@ -33,6 +33,6 @@ public class VariableAssertion {
/**
* 匹配值
*/
private String value;
private String expectedValue;
}
}

View File

@ -1,43 +0,0 @@
package io.metersphere.api.dto.request.assertion;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.metersphere.api.dto.request.assertion.body.DocumentAssertion;
import io.metersphere.api.dto.request.assertion.body.JSONPathAssertion;
import io.metersphere.api.dto.request.assertion.body.RegexAssertion;
import io.metersphere.api.dto.request.assertion.body.XPathAssertion;
import lombok.Data;
/**
* 请求体断言
* @Author: jianxing
* @CreateTime: 2023-11-22 15:33
*/
@Data
@JsonTypeName("RESPONSE_BODY")
public class ResponseBodyAssertion extends MsAssertion {
/**
* 断言类型
* 根据断言类型选择对应的断言
* 这里跟前端数据结构有差异
* 后端从设计层面支持多种断言前端只支持一种
* 同时切换可以同时持久化两种类型
* 值为 MsBodyAssertionType
*/
private String assertionType;
/**
* jsonPath断言
*/
private JSONPathAssertion jsonPathAssertion;
/**
* xpath断言
*/
private XPathAssertion xpathAssertion;
/**
* 文档断言
*/
private DocumentAssertion documentAssertion;
/**
* 正则断言
*/
private RegexAssertion regexAssertion;
}

View File

@ -1,31 +0,0 @@
package io.metersphere.api.dto.request.assertion;
import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data;
/**
* 变量断言
* @Author: jianxing
* @CreateTime: 2023-11-22 15:33
*/
@Data
@JsonTypeName("SCRIPT")
public class ScriptAssertion extends MsAssertion {
/**
* 脚本描述
*/
private String description;
/**
* 脚本内容
*/
private String content;
/**
* 是否使用功能脚本
* 公共脚本和手动录入脚本只能二选一
*/
private Boolean enableCommonScript;
/**
* 引用公共脚本的ID
*/
private String commonScriptId;
}

View File

@ -1,17 +0,0 @@
package io.metersphere.api.dto.request.assertion.body;
import lombok.Data;
/**
*
* JSONPath断言
* @Author: jianxing
* @CreateTime: 2023-11-23 14:04
*/
@Data
public class JSONPathAssertionItem extends BodyAssertionItem {
private String expression;
private String condition;
private String value;
}

View File

@ -8,6 +8,6 @@ import lombok.Data;
* @CreateTime: 2023-11-23 14:25
*/
@Data
public abstract class BodyAssertionItem {
public abstract class MsBodyAssertionItem {
private Boolean enable = true;
}

View File

@ -8,7 +8,7 @@ import lombok.Data;
* @CreateTime: 2023-11-23 14:19
*/
@Data
public class DocumentAssertion extends BodyAssertionItem {
public class MsDocumentAssertion extends MsBodyAssertionItem {
/**
* 跟随定义的apiId
* 传空为不跟随接口定义
@ -21,14 +21,20 @@ public class DocumentAssertion extends BodyAssertionItem {
* 这里跟前端数据结构有差异
* 后端从设计层面支持多种文档格式前端只支持一种
* 同时切换可以同时持久化两种格式
* @see DocumentType
*/
private String documentType;
/**
* json格式的文档断言
*/
private DocumentAssertionElement jsonAssertion;
private MsDocumentAssertionElement jsonAssertion;
/**
* xml格式的文档断言
*/
private DocumentAssertionElement xmlAssertion;
private MsDocumentAssertionElement xmlAssertion;
public enum DocumentType {
JSON,
XML;
}
}

View File

@ -9,7 +9,7 @@ import java.util.List;
* @CreateTime: 2023-11-23 11:43
*/
@Data
public class DocumentAssertionElement {
public class MsDocumentAssertionElement {
private String id;
/**
* 参数名
@ -43,5 +43,5 @@ public class DocumentAssertionElement {
/**
* 子对象
*/
private List<DocumentAssertionElement> children;
private List<MsDocumentAssertionElement> children;
}

View File

@ -11,11 +11,11 @@ import java.util.List;
* @CreateTime: 2023-11-23 14:04
*/
@Data
public class JSONPathAssertion {
public class MsJSONPathAssertion {
/**
* 断言列表
*/
private List<JSONPathAssertionItem> assertions;
private List<MsJSONPathAssertionItem> assertions;
}

View File

@ -0,0 +1,23 @@
package io.metersphere.api.dto.request.assertion.body;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
/**
*
* JSONPath断言
* @Author: jianxing
* @CreateTime: 2023-11-23 14:04
*/
@Data
public class MsJSONPathAssertionItem extends MsBodyAssertionItem {
private String expression;
private String condition;
private String expectedValue;
public boolean isValid() {
return StringUtils.isNotBlank(expression) && StringUtils.isNotBlank(condition) && StringUtils.isNotBlank(expectedValue);
}
}

View File

@ -10,9 +10,9 @@ import java.util.List;
* @CreateTime: 2023-11-23 14:03
*/
@Data
public class RegexAssertion {
public class MsRegexAssertion {
/**
* 断言列表
*/
private List<RegexAssertionItem> assertions;
private List<MsRegexAssertionItem> assertions;
}

View File

@ -0,0 +1,18 @@
package io.metersphere.api.dto.request.assertion.body;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
/**
* 正则断言
* @Author: jianxing
* @CreateTime: 2023-11-23 14:03
*/
@Data
public class MsRegexAssertionItem extends MsBodyAssertionItem {
private String expression;
public boolean isValid() {
return StringUtils.isNotBlank(expression);
}
}

View File

@ -11,14 +11,25 @@ import java.util.List;
* @CreateTime: 2023-11-23 14:18
*/
@Data
public class XPathAssertion {
public class MsXPathAssertion {
/**
* 响应内容格式
* xml 或者 html
*/
private String format;
private String responseFormat;
/**
* xpath断言
*/
private List<XPathAssertionItem> assertions;
private List<MsXPathAssertionItem> assertions;
public enum ResponseFormat {
/**
* XML
*/
XML,
/**
* HTML
*/
HTML
}
}

View File

@ -0,0 +1,20 @@
package io.metersphere.api.dto.request.assertion.body;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
/**
* XPath断言
* @Author: jianxing
* @CreateTime: 2023-11-23 14:18
*/
@Data
public class MsXPathAssertionItem extends MsBodyAssertionItem {
private String expression;
private String expectedValue;
public boolean isValid() {
return StringUtils.isNotBlank(expression) && StringUtils.isNotBlank(expectedValue);
}
}

View File

@ -1,13 +0,0 @@
package io.metersphere.api.dto.request.assertion.body;
import lombok.Data;
/**
* 正则断言
* @Author: jianxing
* @CreateTime: 2023-11-23 14:03
*/
@Data
public class RegexAssertionItem extends BodyAssertionItem {
private String expression;
}

View File

@ -1,15 +0,0 @@
package io.metersphere.api.dto.request.assertion.body;
import lombok.Data;
/**
* XPath断言
* @Author: jianxing
* @CreateTime: 2023-11-23 14:18
*/
@Data
public class XPathAssertionItem extends BodyAssertionItem {
private String expression;
private String condition;
private String value;
}

View File

@ -25,7 +25,7 @@ public class ScriptProcessor extends MsProcessor {
/**
* 是否启用公共脚本
*/
private Boolean enableCommonScript;
private Boolean enableCommonScript = false;
/**
* 脚本ID
*/

View File

@ -25,6 +25,10 @@ public abstract class MsExtract {
* 表达式
*/
private String expression;
/**
* 是否启用
*/
private Boolean enable = true;
public boolean isValid() {
return StringUtils.isNotBlank(variableName) && StringUtils.isNotBlank(expression);

View File

@ -2,10 +2,16 @@ package io.metersphere.api.parser.jmeter;
import io.metersphere.api.dto.request.MsCommonElement;
import io.metersphere.api.dto.request.assertion.MsAssertion;
import io.metersphere.api.dto.request.assertion.MsAssertionConfig;
import io.metersphere.api.dto.request.assertion.MsResponseCodeAssertion;
import io.metersphere.api.dto.request.processors.MsProcessorConfig;
import io.metersphere.api.parser.jmeter.processor.MsProcessorConverterFactory;
import io.metersphere.api.parser.jmeter.processor.assertion.AssertionConverterFactory;
import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter;
import io.metersphere.sdk.constants.MsAssertionCondition;
import org.apache.commons.lang3.StringUtils;
import org.apache.jorphan.collections.HashTree;
/**
@ -26,6 +32,29 @@ public class MsCommonElementConverter extends AbstractJmeterElementConverter<MsC
handlePreProcessor(tree, element.getPreProcessorConfig(), config);
// 解析后置处理器
handlePostProcessor(tree, element.getPostProcessorConfig(), config);
// 处理断言
handleAssertion(tree, element.getAssertionConfig(), config);
}
private void handleAssertion(HashTree tree, MsAssertionConfig assertionConfig, ParameterConfig config) {
// todo 开关默认开启关闭则运行该接口时不执行全局前置
// assertionConfig.getEnableGlobal();
if (assertionConfig == null || assertionConfig.getAssertions() == null) {
return;
}
boolean isIgnoreStatus = false;
for (MsAssertion assertion : assertionConfig.getAssertions()) {
if (assertion instanceof MsResponseCodeAssertion) {
MsResponseCodeAssertion responseCodeAssertion = (MsResponseCodeAssertion) assertion;
// 如果状态码断言添加了不校验状态码则所有断言忽略状态码
if (StringUtils.equals(responseCodeAssertion.getCondition(), MsAssertionCondition.UNCHECK.name())) {
isIgnoreStatus = true;
}
}
}
boolean finalIsIgnoreStatus = isIgnoreStatus;
assertionConfig.getAssertions()
.forEach(assertion -> AssertionConverterFactory.getConverter(assertion.getClass()).parse(tree, assertion, config, finalIsIgnoreStatus));
}
private void handlePreProcessor(HashTree tree, MsProcessorConfig preProcessorConfig, ParameterConfig config) {

View File

@ -18,6 +18,8 @@ import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
import org.springframework.http.HttpMethod;
import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.HTTP_TEST_SAMPLE_GUI;
/**
* @Author: jianxing
* @CreateTime: 2023-10-27 10:07
@ -38,7 +40,7 @@ public class MsHTTPElementConverter extends AbstractJmeterElementConverter<MsHTT
HTTPSamplerProxy sampler = new HTTPSamplerProxy();
sampler.setName(msHTTPElement.getName());
sampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName());
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("HttpTestSampleGui"));
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(HTTP_TEST_SAMPLE_GUI));
sampler.setMethod(msHTTPElement.getMethod());
// todo 根据环境设置
@ -60,8 +62,7 @@ public class MsHTTPElementConverter extends AbstractJmeterElementConverter<MsHTT
Body body = msHTTPElement.getBody();
// 请求体处理
if (body != null) {
Class bodyClass = body.getBodyClassByType();
MsBodyConverter converter = MsBodyConverterFactory.getConverter(bodyClass);
MsBodyConverter converter = MsBodyConverterFactory.getConverter(body.getBodyClassByType());
// 这里get请求不处理 form-date www-form-urlencoded 类型的参数
// 否则会被 jmeter 作为 query 参数

View File

@ -11,4 +11,9 @@ public class JmeterAlias {
public static final String JSON_POST_PROCESSOR_GUI = "JSONPostProcessorGui";
public static final String X_PATH_EXTRACTOR_GUI = "XPathExtractorGui";
public static final String X_PATH2_EXTRACTOR_GUI = "XPath2ExtractorGui";
public static final String ASSERTION_GUI = "AssertionGui";
public static final String DURATION_ASSERTION_GUI = "DurationAssertionGui";
public static final String HTTP_TEST_SAMPLE_GUI = "HttpTestSampleGui";
public static final String XPATH_ASSERTION_GUI = "XPathAssertionGui";
public static final String X_PATH_2_ASSERTION_GUI = "XPath2AssertionGui";
}

View File

@ -7,6 +7,6 @@ package io.metersphere.api.parser.jmeter.constants;
public class JmeterProperty {
public static final String SCRIPT = "script";
public static final String CACHE_KEY = "cacheKey";
public static final String BEAN_SAMPLER_QUERY = "BeanShellSampler.query";
public static final String SCRIPT_LANGUAGE = "scriptLanguage";
public final static String ASS_OPTION = "ASS_OPTION";
}

View File

@ -21,8 +21,11 @@ public class ExtractPostProcessorConverter extends MsProcessorConverter<ExtractP
processor.getExtractors()
.stream()
.filter(MsExtract::isValid)
.forEach(extract ->
ExtractConverterFactory.getConverter(extract.getClass())
.parse(hashTree, extract, config));
.forEach(extract -> {
// 单调提取器的 enable 跟随整体的 enable
extract.setEnable(processor.getEnable());
ExtractConverterFactory.getConverter(extract.getClass())
.parse(hashTree, extract, config);
});
}
}

View File

@ -16,7 +16,7 @@ public abstract class ScriptProcessorConverter extends MsProcessorConverter<Scri
public static final String ENV_VARIABLE_EXPRESSION = "${__metersphere_env_id}";
public static final String MS_RUNNING_ENV_PREFIX = "MS.ENV.";
protected void parse(TestElement testElement, ScriptProcessor scriptProcessor) {
public static void parse(TestElement testElement, ScriptProcessor scriptProcessor) {
// 脚本安全校验
ScriptFilter.verify(scriptProcessor.getScriptLanguage(), scriptProcessor.getName(), scriptProcessor.getScript());

View File

@ -0,0 +1,65 @@
package io.metersphere.api.parser.jmeter.processor.assertion;
import io.metersphere.api.dto.request.assertion.MsAssertion;
import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.sdk.constants.MsAssertionCondition;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.assertions.ResponseAssertion;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.ASSERTION_GUI;
/**
* @Author: jianxing
* @CreateTime: 2023-12-27 10:32
*/
public abstract class AssertionConverter<T extends MsAssertion> {
/**
* 解析对应的提取器
* @param hashTree
* @param extract
* @param config
*/
public abstract void parse(HashTree hashTree, T extract, ParameterConfig config, boolean isIgnoreStatus);
protected boolean needParse(MsAssertion msAssertion, ParameterConfig config) {
// 如果组件是启用的或者设置了解析禁用的组件则返回 true
if (BooleanUtils.isTrue(msAssertion.getEnable()) || config.getParseDisabledElement()) {
return true;
}
return false;
}
public static ResponseAssertion createResponseAssertion() {
ResponseAssertion assertion = new ResponseAssertion();
assertion.setProperty(TestElement.TEST_CLASS, ResponseAssertion.class.getName());
assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(ASSERTION_GUI));
return assertion;
}
protected String generateRegexExpression(String condition, String text) {
Map<MsAssertionCondition, Function<String, String>> regexgenerateMap = new HashMap<>();
// 只支持以下条件
regexgenerateMap.put(MsAssertionCondition.CONTAINS, value -> StringUtils.join(".*", value, ".*"));
regexgenerateMap.put(MsAssertionCondition.NOT_CONTAINS, value -> StringUtils.join("(?s)^((?!", value, ").)*$"));
regexgenerateMap.put(MsAssertionCondition.END_WITH, value -> StringUtils.join(value, "$"));
regexgenerateMap.put(MsAssertionCondition.START_WITH, value -> StringUtils.join("^", value));
regexgenerateMap.put(MsAssertionCondition.EQUALS, value -> StringUtils.join("^", value, "$"));
regexgenerateMap.put(MsAssertionCondition.NOT_EQUALS, value -> StringUtils.join("^(?!", value, "$).*$"));
regexgenerateMap.put(MsAssertionCondition.EMPTY, value -> StringUtils.join("^$", value));
regexgenerateMap.put(MsAssertionCondition.NOT_EMPTY, value -> StringUtils.join("^(?!^$).*$", value));
regexgenerateMap.put(MsAssertionCondition.REGEX, value -> value);
MsAssertionCondition msAssertionCondition = MsAssertionCondition.valueOf(condition);
if (msAssertionCondition != null && regexgenerateMap.get(msAssertionCondition) != null) {
return regexgenerateMap.get(msAssertionCondition).apply(text);
}
return text;
}
}

View File

@ -0,0 +1,27 @@
package io.metersphere.api.parser.jmeter.processor.assertion;
import io.metersphere.api.dto.request.assertion.*;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: jianxing
* @CreateTime: 2023-12-27 10:31
*/
public class AssertionConverterFactory {
private static Map<Class, AssertionConverter> converterMap = new HashMap<>();
static {
converterMap.put(MsResponseCodeAssertion.class, new ResponseCodeAssertionConverter());
converterMap.put(MsResponseHeaderAssertion.class, new ResponseHeaderAssertionConverter());
converterMap.put(MsResponseBodyAssertion.class, new ResponseBodyAssertionConverter());
converterMap.put(MsResponseTimeAssertion.class, new ResponseTimeAssertionConverter());
converterMap.put(MsScriptAssertion.class, new ScriptAssertionConverter());
converterMap.put(MsVariableAssertion.class, new VariableAssertionConverter());
}
public static AssertionConverter getConverter(Class processorClass) {
return converterMap.get(processorClass);
}
}

View File

@ -0,0 +1,22 @@
package io.metersphere.api.parser.jmeter.processor.assertion;
import io.metersphere.api.dto.request.assertion.MsResponseBodyAssertion;
import io.metersphere.api.parser.jmeter.processor.assertion.body.ResponseBodyTypeAssertionConverter;
import io.metersphere.api.parser.jmeter.processor.assertion.body.ResponseBodyTypeAssertionFactory;
import io.metersphere.plugin.api.dto.ParameterConfig;
import org.apache.jorphan.collections.HashTree;
/**
* @Author: jianxing
* @CreateTime: 2023-12-27 21:01
*/
public class ResponseBodyAssertionConverter extends AssertionConverter<MsResponseBodyAssertion> {
@Override
public void parse(HashTree hashTree, MsResponseBodyAssertion msAssertion, ParameterConfig config, boolean isIgnoreStatus) {
if (!needParse(msAssertion, config)) {
return;
}
ResponseBodyTypeAssertionConverter converter = ResponseBodyTypeAssertionFactory.getConverter(msAssertion.getBodyAssertionClassByType());
converter.parse(hashTree, msAssertion.getBodyAssertionDataByType(), config, isIgnoreStatus, msAssertion.getEnable());
}
}

View File

@ -0,0 +1,48 @@
package io.metersphere.api.parser.jmeter.processor.assertion;
import io.metersphere.api.dto.request.assertion.MsResponseCodeAssertion;
import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.sdk.constants.MsAssertionCondition;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.assertions.ResponseAssertion;
import org.apache.jorphan.collections.HashTree;
/**
* @Author: jianxing
* @CreateTime: 2023-12-27 21:01
*/
public class ResponseCodeAssertionConverter extends AssertionConverter<MsResponseCodeAssertion> {
@Override
public void parse(HashTree hashTree, MsResponseCodeAssertion msAssertion, ParameterConfig config, boolean isIgnoreStatus) {
if (!needParse(msAssertion, config) || !isValid(msAssertion)) {
return;
}
hashTree.add(parse2ResponseAssertion(msAssertion, isIgnoreStatus));
}
public boolean isValid(MsResponseCodeAssertion msAssertion) {
return StringUtils.isNotBlank(msAssertion.getExpectedValue()) && StringUtils.isNotBlank(msAssertion.getCondition());
}
private ResponseAssertion parse2ResponseAssertion(MsResponseCodeAssertion msAssertion, boolean isIgnoreStatus) {
ResponseAssertion assertion = createResponseAssertion();
String expectedValue = msAssertion.getExpectedValue();
assertion.setEnabled(msAssertion.getEnable());
assertion.setAssumeSuccess(false);
assertion.setAssumeSuccess(isIgnoreStatus);
assertion.setEnabled(msAssertion.getEnable());
String condition = msAssertion.getCondition();
assertion.setName(String.format("Response code %s %s", condition.toLowerCase().replace("_", ""), expectedValue));
MsAssertionCondition msAssertionCondition = MsAssertionCondition.valueOf(condition);
if (msAssertionCondition!= null) {
assertion.addTestString(generateRegexExpression(condition, expectedValue));
} else {
assertion.addTestString(expectedValue);
}
assertion.setToContainsType();
assertion.setTestFieldResponseCode();
return assertion;
}
}

View File

@ -0,0 +1,61 @@
package io.metersphere.api.parser.jmeter.processor.assertion;
import io.metersphere.api.dto.request.assertion.MsResponseHeaderAssertion;
import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.sdk.constants.MsAssertionCondition;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.assertions.ResponseAssertion;
import org.apache.jorphan.collections.HashTree;
/**
* @Author: jianxing
* @CreateTime: 2023-12-27 21:01
*/
public class ResponseHeaderAssertionConverter extends AssertionConverter<MsResponseHeaderAssertion> {
@Override
public void parse(HashTree hashTree, MsResponseHeaderAssertion msAssertion, ParameterConfig config, boolean isIgnoreStatus) {
if (!needParse(msAssertion, config)) {
return;
}
Boolean globalEnable = msAssertion.getEnable();
msAssertion.getAssertions()
.stream()
.filter(this::isHeaderAssertionValid)
.forEach(headerAssertionItem -> {
ResponseAssertion responseAssertion = parse2ResponseAssertion(headerAssertionItem, globalEnable);
responseAssertion.setAssumeSuccess(isIgnoreStatus);
hashTree.add(responseAssertion);
});
}
public boolean isHeaderAssertionValid(MsResponseHeaderAssertion.ResponseHeaderAssertionItem headerAssertionItem) {
return StringUtils.isNotBlank(headerAssertionItem.getHeader())
&& StringUtils.isNotBlank(headerAssertionItem.getCondition())
&& StringUtils.isNotBlank(headerAssertionItem.getExpectedValue());
}
private ResponseAssertion parse2ResponseAssertion(MsResponseHeaderAssertion.ResponseHeaderAssertionItem msAssertion,
Boolean globalEnable) {
ResponseAssertion assertion = createResponseAssertion();
assertion.setEnabled(msAssertion.getEnable());
if (BooleanUtils.isFalse(globalEnable)) {
// 如果整体禁用则禁用
assertion.setEnabled(false);
}
String expectedValue = msAssertion.getExpectedValue();
String condition = msAssertion.getCondition();
assertion.setName(String.format("Response header %s %s", condition.toLowerCase().replace("_", ""), expectedValue));
MsAssertionCondition msAssertionCondition = MsAssertionCondition.valueOf(condition);
if (msAssertionCondition!= null) {
assertion.addTestString(generateRegexExpression(condition, expectedValue));
} else {
assertion.addTestString(expectedValue);
}
assertion.setToContainsType();
assertion.setTestFieldResponseHeaders();
return assertion;
}
}

View File

@ -0,0 +1,42 @@
package io.metersphere.api.parser.jmeter.processor.assertion;
import io.metersphere.api.dto.request.assertion.MsResponseTimeAssertion;
import io.metersphere.plugin.api.dto.ParameterConfig;
import org.apache.jmeter.assertions.DurationAssertion;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.DURATION_ASSERTION_GUI;
/**
* @Author: jianxing
* @CreateTime: 2023-12-27 21:01
*/
public class ResponseTimeAssertionConverter extends AssertionConverter<MsResponseTimeAssertion> {
@Override
public void parse(HashTree hashTree, MsResponseTimeAssertion msAssertion, ParameterConfig config, boolean isIgnoreStatus) {
if (!needParse(msAssertion, config) || !isValid(msAssertion)) {
return;
}
hashTree.add(parse2DurationAssertion(msAssertion));
}
private DurationAssertion parse2DurationAssertion(MsResponseTimeAssertion msAssertion) {
DurationAssertion assertion = new DurationAssertion();
assertion.setEnabled(msAssertion.getEnable());
assertion.setName("Response In Time: " + msAssertion.getExpectedValue());
assertion.setProperty(TestElement.TEST_CLASS, DurationAssertion.class.getName());
assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(DURATION_ASSERTION_GUI));
assertion.setAllowedDuration(msAssertion.getExpectedValue());
return assertion;
}
public boolean isValid(MsResponseTimeAssertion msAssertion) {
return msAssertion.getExpectedValue() != null && msAssertion.getExpectedValue() > 0;
}
}

View File

@ -0,0 +1,32 @@
package io.metersphere.api.parser.jmeter.processor.assertion;
import io.metersphere.api.dto.request.assertion.MsScriptAssertion;
import io.metersphere.api.dto.request.processors.ScriptProcessor;
import io.metersphere.api.parser.jmeter.processor.ScriptProcessorConverter;
import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.sdk.util.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.assertions.JSR223Assertion;
import org.apache.jorphan.collections.HashTree;
/**
* @Author: jianxing
* @CreateTime: 2023-12-27 21:01
*/
public class ScriptAssertionConverter extends AssertionConverter<MsScriptAssertion> {
@Override
public void parse(HashTree hashTree, MsScriptAssertion msAssertion, ParameterConfig config, boolean isIgnoreStatus) {
if (!needParse(msAssertion, config) || !isValid(msAssertion)) {
return;
}
JSR223Assertion jsr223Assertion = new JSR223Assertion();
ScriptProcessorConverter.parse(jsr223Assertion, BeanUtils.copyBean(new ScriptProcessor(), msAssertion));
hashTree.add(jsr223Assertion);
}
public boolean isValid(MsScriptAssertion msAssertion) {
// todo 公共脚本库
return StringUtils.isNotBlank(msAssertion.getScript());
}
}

View File

@ -0,0 +1,210 @@
package io.metersphere.api.parser.jmeter.processor.assertion;
import io.metersphere.api.dto.request.assertion.MsVariableAssertion;
import io.metersphere.api.dto.request.processors.ScriptProcessor;
import io.metersphere.api.parser.jmeter.processor.ScriptProcessorConverter;
import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.sdk.constants.MsAssertionCondition;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.assertions.JSR223Assertion;
import org.apache.jorphan.collections.HashTree;
import java.util.HashMap;
/**
* @Author: jianxing
* @CreateTime: 2023-12-27 21:01
*/
public class VariableAssertionConverter extends AssertionConverter<MsVariableAssertion> {
@Override
public void parse(HashTree hashTree, MsVariableAssertion msAssertion, ParameterConfig config, boolean isIgnoreStatus) {
if (!needParse(msAssertion, config)) {
return;
}
Boolean globalEnable = msAssertion.getEnable();
msAssertion.getVariableAssertionItems()
.stream()
.filter(this::isValid)
.forEach(variableAssertionItem -> {
if (needParse(variableAssertionItem, config)) {
JSR223Assertion jsr223Assertion = parse2JSR233Assertion(variableAssertionItem);
jsr223Assertion.setEnabled(variableAssertionItem.getEnable());
if (BooleanUtils.isFalse(globalEnable)) {
// 如果整体禁用则禁用
jsr223Assertion.setEnabled(false);
}
hashTree.add(jsr223Assertion);
}
});
}
protected boolean needParse(MsVariableAssertion.VariableAssertionItem variableAssertionItem, ParameterConfig config) {
// 如果组件是启用的或者设置了解析禁用的组件则返回 true
if (BooleanUtils.isTrue(variableAssertionItem.getEnable()) || config.getParseDisabledElement()) {
return true;
}
return false;
}
private static JSR223Assertion parse2JSR233Assertion(MsVariableAssertion.VariableAssertionItem variableAssertionItem) {
ScriptProcessor scriptProcessor = new ScriptProcessor();
scriptProcessor.setScript(parse2BeanshellJSR233Script(variableAssertionItem));
String variableName = variableAssertionItem.getVariableName();
String condition = variableAssertionItem.getCondition();
String expectedValue = variableAssertionItem.getExpectedValue();
String name = String.format("Variable '%s' expect %s %s", variableName, condition.toLowerCase().replace("_", ""), expectedValue);
scriptProcessor.setName(name);
scriptProcessor.setScriptLanguage(ScriptProcessor.ScriptLanguageType.BEANSHELL_JSR233.getValue());
JSR223Assertion jsr223Assertion = new JSR223Assertion();
ScriptProcessorConverter.parse(jsr223Assertion, scriptProcessor);
return jsr223Assertion;
}
public boolean isValid(MsVariableAssertion.VariableAssertionItem variableAssertionItem) {
return StringUtils.isNotBlank(variableAssertionItem.getVariableName()) && StringUtils.isNotBlank(variableAssertionItem.getExpectedValue())
&& StringUtils.isNotBlank(variableAssertionItem.getCondition());
}
private static String parse2BeanshellJSR233Script(MsVariableAssertion.VariableAssertionItem variableAssertionItem) {
HashMap<String, String> handleMap = new HashMap<>();
String script = String.format(
"""
variableValue = vars.get("%s");
expectation = "%s";
flag = true;
""", variableAssertionItem.getVariableName(), variableAssertionItem.getExpectedValue());
handleMap.put(MsAssertionCondition.EQUALS.name(),
"""
result = expectation.equals(variableValue);"
msg = "value == " + expectation;
""");
handleMap.put(MsAssertionCondition.NOT_EQUALS.name(),
"""
result = !expectation.equals(variableValue);
msg = "value != " + expectation;
""");
handleMap.put(MsAssertionCondition.CONTAINS.name(),
"""
result = expectation.contains(variableValue);
msg = "value contains " + expectation;
""");
handleMap.put(MsAssertionCondition.NOT_CONTAINS.name(),
"""
result = !expectation.contains(variableValue);
msg = "value not contains " + expectation;
""");
handleMap.put(MsAssertionCondition.GT.name(),
"""
number = Integer.parseInt(expectation);
result = number > expectation;
msg = "value > " + expectation;
""");
handleMap.put(MsAssertionCondition.GT_OR_EQUALS.name(),
"""
number = Integer.parseInt(expectation);
result = number >= expectation;
msg = "value >= " + expectation;
""");
handleMap.put(MsAssertionCondition.LT.name(),
"""
number = Integer.parseInt(expectation);
result = number < expectation;
msg = "value < " + expectation;
""");
handleMap.put(MsAssertionCondition.LT_OR_EQUALS.name(),
"""
number = Integer.parseInt(expectation);
result = number <= expectation;
msg = "value <= " + expectation;
""");
handleMap.put(MsAssertionCondition.START_WITH.name(),
"""
result = expectation.startsWith(variableValue);
msg = "value start with " + expectation;
""");
handleMap.put(MsAssertionCondition.END_WITH.name(),
"""
result = expectation.endWith(variableValue);
msg = "value end with " + expectation;
""");
handleMap.put(MsAssertionCondition.LENGTH_EQUALS.name(),
"""
number = Integer.parseInt(expectation);
result = variableValue.length() == number;
msg = "value length == " + expectation;
""");
handleMap.put(MsAssertionCondition.LENGTH_GT.name(),
"""
number = Integer.parseInt(expectation);
result = variableValue.length() > number;
msg = "value length > " + expectation;
""");
handleMap.put(MsAssertionCondition.LENGTH_GT.name(),
"""
number = Integer.parseInt(expectation);
result = variableValue.length() >= number;
msg = "value length >= " + expectation;
""");
handleMap.put(MsAssertionCondition.LENGTH_LT.name(),
"""
number = Integer.parseInt(expectation);
result = variableValue.length() < number;
msg = "value length < " + expectation;
""");
handleMap.put(MsAssertionCondition.LENGTH_LT_OR_EQUALS.name(),
"""
number = Integer.parseInt(expectation);
result = variableValue.length() <= number;
msg = "value length <= " + expectation;
""");
handleMap.put(MsAssertionCondition.EMPTY.name(),
"""
result = variableValue == void || variableValue.length() == 0;
msg = "value is empty";
flag = false;
""");
handleMap.put(MsAssertionCondition.NOT_EMPTY.name(),
"""
result = variableValue != void && variableValue.length() > 0;
msg = "value is not empty";
flag = false;
""");
String condition = variableAssertionItem.getCondition();
String handleScript = handleMap.get(condition);
if (StringUtils.isNotBlank(handleScript)) {
script += handleMap.get(MsAssertionCondition.EQUALS.name());
}
script += """
if (!result) {
if (flag) {
msg = "assertion [" + msg + "]: false;";
}
AssertionResult.setFailureMessage(msg);
AssertionResult.setFailure(true);
}
""";
return script;
}
}

View File

@ -0,0 +1,28 @@
package io.metersphere.api.parser.jmeter.processor.assertion.body;
import io.metersphere.api.dto.request.assertion.body.MsDocumentAssertion;
import io.metersphere.plugin.api.dto.ParameterConfig;
import org.apache.jorphan.collections.HashTree;
/**
* @Author: jianxing
* @CreateTime: 2024-01-03 10:05
*/
public class DocumentAssertionConverter extends ResponseBodyTypeAssertionConverter<MsDocumentAssertion> {
@Override
public void parse(HashTree hashTree, MsDocumentAssertion msAssertion, ParameterConfig config, boolean isIgnoreStatus, boolean globalEnable) {
if (msAssertion == null) {
return;
}
// todo 定义好 jsonschema 再补充
// String documentType = msAssertion.getDocumentType();
// MsDocumentAssertionElement documentAssertionElement;
// if (StringUtils.equals(documentType, MsDocumentAssertion.DocumentType.XML.name())) {
// documentAssertionElement = msAssertion.getXmlAssertion();
// } else {
// documentAssertionElement = msAssertion.getJsonAssertion();
// }
}
}

View File

@ -0,0 +1,51 @@
package io.metersphere.api.parser.jmeter.processor.assertion.body;
import io.metersphere.api.dto.request.assertion.body.MsJSONPathAssertion;
import io.metersphere.api.dto.request.assertion.body.MsJSONPathAssertionItem;
import io.metersphere.assertions.JSONPathAssertion;
import io.metersphere.plugin.api.dto.ParameterConfig;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
/**
* @Author: jianxing
* @CreateTime: 2024-01-03 10:05
*/
public class JSONPathAssertionConverter extends ResponseBodyTypeAssertionConverter<MsJSONPathAssertion> {
@Override
public void parse(HashTree hashTree, MsJSONPathAssertion msAssertion, ParameterConfig config, boolean isIgnoreStatus, boolean globalEnable) {
if (msAssertion == null || msAssertion.getAssertions() == null) {
return;
}
msAssertion.getAssertions().stream()
.filter(MsJSONPathAssertionItem::isValid)
.forEach(msJSONPathAssertionItem -> {
if (needParse(msJSONPathAssertionItem, config)) {
hashTree.add(parse2JSONPathAssertion(msJSONPathAssertionItem, globalEnable));
}
});
}
private JSONPathAssertion parse2JSONPathAssertion(MsJSONPathAssertionItem msAssertion, Boolean globalEnable) {
// 使用定制的 JSONPath 断言组件
JSONPathAssertion assertion = new JSONPathAssertion();
assertion.setEnabled(msAssertion.getEnable());
String condition = msAssertion.getCondition();
String expression = msAssertion.getExpression();
String expectedValue = msAssertion.getExpectedValue();
assertion.setName(String.format("Response date JSONPath expect %s %s %s", expression, condition.toLowerCase().replace("_", ""), expectedValue));
if (BooleanUtils.isFalse(globalEnable)) {
// 如果整体禁用则禁用
assertion.setEnabled(false);
}
assertion.setProperty(TestElement.TEST_CLASS, JSONPathAssertion.class.getName());
assertion.setProperty(TestElement.GUI_CLASS, "io.metersphere.assertions.gui.JSONPathAssertionGui");
assertion.setJsonValidationBool(true);
assertion.setCondition(condition);
assertion.setJsonPath(expression);
assertion.setExpectedValue(expectedValue);
return assertion;
}
}

View File

@ -0,0 +1,46 @@
package io.metersphere.api.parser.jmeter.processor.assertion.body;
import io.metersphere.api.dto.request.assertion.body.MsRegexAssertion;
import io.metersphere.api.dto.request.assertion.body.MsRegexAssertionItem;
import io.metersphere.api.parser.jmeter.processor.assertion.AssertionConverter;
import io.metersphere.plugin.api.dto.ParameterConfig;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.jmeter.assertions.ResponseAssertion;
import org.apache.jorphan.collections.HashTree;
/**
* @Author: jianxing
* @CreateTime: 2024-01-03 10:05
*/
public class RegexAssertionConverter extends ResponseBodyTypeAssertionConverter<MsRegexAssertion> {
@Override
public void parse(HashTree hashTree, MsRegexAssertion msAssertion, ParameterConfig config, boolean isIgnoreStatus, boolean globalEnable) {
if (msAssertion == null || msAssertion.getAssertions() == null) {
return;
}
msAssertion.getAssertions().stream()
.filter(MsRegexAssertionItem::isValid)
.forEach(regexAssertionItem -> {
if (needParse(regexAssertionItem, config)) {
ResponseAssertion responseAssertion = parse2RegexResponseAssertion(regexAssertionItem, globalEnable);
responseAssertion.setAssumeSuccess(isIgnoreStatus);
hashTree.add(responseAssertion);
}
});
}
private ResponseAssertion parse2RegexResponseAssertion(MsRegexAssertionItem msAssertion, Boolean globalEnable) {
ResponseAssertion assertion = AssertionConverter.createResponseAssertion();
assertion.setEnabled(msAssertion.getEnable());
assertion.setName("Response date expect regex " + msAssertion.getExpression());
assertion.addTestString(msAssertion.getExpression());
assertion.setTestFieldResponseData();
if (BooleanUtils.isFalse(globalEnable)) {
// 如果整体禁用则禁用
assertion.setEnabled(false);
}
assertion.setToContainsType();
return assertion;
}
}

View File

@ -0,0 +1,29 @@
package io.metersphere.api.parser.jmeter.processor.assertion.body;
import io.metersphere.api.dto.request.assertion.body.MsBodyAssertionItem;
import io.metersphere.plugin.api.dto.ParameterConfig;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.jorphan.collections.HashTree;
/**
* @Author: jianxing
* @CreateTime: 2024-01-02 21:40
*/
public abstract class ResponseBodyTypeAssertionConverter <T> {
/**
* 解析对应的提取器
*
* @param hashTree
* @param extract
* @param config
*/
public abstract void parse(HashTree hashTree, T extract, ParameterConfig config, boolean isIgnoreStatus, boolean globalEnable);
protected boolean needParse(MsBodyAssertionItem msAssertion, ParameterConfig config) {
// 如果组件是启用的或者设置了解析禁用的组件则返回 true
if (BooleanUtils.isTrue(msAssertion.getEnable()) || config.getParseDisabledElement()) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,29 @@
package io.metersphere.api.parser.jmeter.processor.assertion.body;
import io.metersphere.api.dto.request.assertion.body.MsDocumentAssertion;
import io.metersphere.api.dto.request.assertion.body.MsJSONPathAssertion;
import io.metersphere.api.dto.request.assertion.body.MsRegexAssertion;
import io.metersphere.api.dto.request.assertion.body.MsXPathAssertion;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: jianxing
* @CreateTime: 2024-01-03 09:42
*/
public class ResponseBodyTypeAssertionFactory {
private static Map<Class, ResponseBodyTypeAssertionConverter> converterMap = new HashMap<>();
static {
converterMap.put(MsJSONPathAssertion.class, new JSONPathAssertionConverter());
converterMap.put(MsXPathAssertion.class, new XPathAssertionConverter());
converterMap.put(MsDocumentAssertion.class, new DocumentAssertionConverter());
converterMap.put(MsRegexAssertion.class, new RegexAssertionConverter());
}
public static ResponseBodyTypeAssertionConverter getConverter(Class processorClass) {
return converterMap.get(processorClass);
}
}

View File

@ -0,0 +1,75 @@
package io.metersphere.api.parser.jmeter.processor.assertion.body;
import io.metersphere.api.dto.request.assertion.body.MsXPathAssertion;
import io.metersphere.api.dto.request.assertion.body.MsXPathAssertionItem;
import io.metersphere.api.dto.request.processors.extract.XPathExtract;
import io.metersphere.plugin.api.dto.ParameterConfig;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.assertions.XPath2Assertion;
import org.apache.jmeter.assertions.XPathAssertion;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.XPATH_ASSERTION_GUI;
import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.X_PATH_2_ASSERTION_GUI;
/**
* @Author: jianxing
* @CreateTime: 2024-01-03 10:05
*/
public class XPathAssertionConverter extends ResponseBodyTypeAssertionConverter<MsXPathAssertion> {
@Override
public void parse(HashTree hashTree, MsXPathAssertion msAssertion, ParameterConfig config, boolean isIgnoreStatus, boolean globalEnable) {
if (msAssertion == null || msAssertion.getAssertions() == null) {
return;
}
String responseFormat = msAssertion.getResponseFormat();
msAssertion.getAssertions().stream()
.filter(MsXPathAssertionItem::isValid)
.forEach(msXPathAssertionItem -> {
if (needParse(msXPathAssertionItem, config)) {
if (StringUtils.equals(responseFormat, XPathExtract.ResponseFormat.HTML.name())) {
XPathAssertion xPathAssertion = parse2XPathAssertion(msXPathAssertionItem, globalEnable);
hashTree.add(xPathAssertion);
} else {
XPath2Assertion xPath2Assertion = parse2XPath2Assertion(msXPathAssertionItem, globalEnable);
hashTree.add(xPath2Assertion);
}
}
});
}
private XPathAssertion parse2XPathAssertion(MsXPathAssertionItem msAssertion, Boolean globalEnable) {
XPathAssertion assertion = new XPathAssertion();
assertion.setEnabled(msAssertion.getEnable());
assertion.setTolerant(true);
assertion.setValidating(false);
assertion.setName("Response date expect xpath " + msAssertion.getExpression());
assertion.setProperty(TestElement.TEST_CLASS, XPathAssertion.class.getName());
assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(XPATH_ASSERTION_GUI));
assertion.setXPathString(msAssertion.getExpression());
assertion.setNegated(false);
if (BooleanUtils.isFalse(globalEnable)) {
// 如果整体禁用则禁用
assertion.setEnabled(false);
}
return assertion;
}
private XPath2Assertion parse2XPath2Assertion(MsXPathAssertionItem msAssertion, Boolean globalEnable) {
XPath2Assertion assertion = new XPath2Assertion();
assertion.setEnabled(msAssertion.getEnable());
assertion.setName("Response date expect xpath " + msAssertion.getExpression());
assertion.setProperty(TestElement.TEST_CLASS, XPath2Assertion.class.getName());
assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(X_PATH_2_ASSERTION_GUI));
assertion.setXPathString(msAssertion.getExpression());
assertion.setNegated(false);
if (BooleanUtils.isFalse(globalEnable)) {
// 如果整体禁用则禁用
assertion.setEnabled(false);
}
return assertion;
}
}

View File

@ -25,6 +25,7 @@ public class JSONPathExtractConverter extends ExtractConverter<JSONPathExtract>
extractor.setJsonPathExpressions(msExtract.getExpression());
// 处理匹配多条等匹配规则
extractor.setMatchNumbers(parseResultMatchingRule(msExtract).toString());
extractor.setEnabled(msExtract.getEnable());
hashTree.add(extractor);
}
}

View File

@ -24,6 +24,7 @@ public class RegexExtractConverter extends ExtractConverter<RegexExtract> {
extractor.setRefName(msExtract.getVariableName());
extractor.setRegex(msExtract.getExpression());
extractor.setUseField(msExtract.getExtractScope());
extractor.setEnabled(msExtract.getEnable());
// 处理匹配多条等匹配规则
extractor.setMatchNumber(parseResultMatchingRule(msExtract));

View File

@ -35,6 +35,7 @@ public class XPathExtractConverter extends ExtractConverter<XPathExtract> {
extractor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(X_PATH_EXTRACTOR_GUI));
extractor.setRefName(msExtract.getVariableName());
extractor.setXPathQuery(msExtract.getExpression());
extractor.setEnabled(msExtract.getEnable());
// 处理匹配多条等匹配规则
extractor.setMatchNumber(parseResultMatchingRule(msExtract));
return extractor;
@ -47,6 +48,7 @@ public class XPathExtractConverter extends ExtractConverter<XPathExtract> {
extractor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(X_PATH2_EXTRACTOR_GUI));
extractor.setRefName(msExtract.getVariableName());
extractor.setXPathQuery(msExtract.getExpression());
extractor.setEnabled(msExtract.getEnable());
// 处理匹配多条等匹配规则
extractor.setMatchNumber(parseResultMatchingRule(msExtract));
return extractor;

View File

@ -244,46 +244,88 @@ public class MsHTTPElementTest {
String json = ApiDataUtils.toJSONString(msHTTPElement);
Assertions.assertNotNull(json);
Assertions.assertEquals(ApiDataUtils.parseObject(json, AbstractMsTestElement.class), msHTTPElement);
// 测试脚本解析
ParameterConfig parameterConfig = new ParameterConfig();
parameterConfig.setReportId("reportId");
TestElementParser defaultParser = TestElementParserFactory.getDefaultParser();
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(json, AbstractMsTestElement.class);
defaultParser.parse(msTestElement, parameterConfig);
}
public static List<MsAssertion> getGeneralAssertions() {
List<MsAssertion> assertions = new ArrayList<>();
ResponseCodeAssertion responseCodeAssertion = new ResponseCodeAssertion();
MsResponseCodeAssertion responseCodeAssertion = new MsResponseCodeAssertion();
responseCodeAssertion.setCondition(MsAssertionCondition.EMPTY.name());
responseCodeAssertion.setValue("value");
responseCodeAssertion.setExpectedValue("value");
responseCodeAssertion.setName("name");
assertions.add(responseCodeAssertion);
ResponseHeaderAssertion responseHeaderAssertion = new ResponseHeaderAssertion();
ResponseHeaderAssertion.ResponseHeaderAssertionItem responseHeaderAssertionItem = new ResponseHeaderAssertion.ResponseHeaderAssertionItem();
MsResponseHeaderAssertion responseHeaderAssertion = new MsResponseHeaderAssertion();
MsResponseHeaderAssertion.ResponseHeaderAssertionItem responseHeaderAssertionItem = new MsResponseHeaderAssertion.ResponseHeaderAssertionItem();
responseHeaderAssertionItem.setHeader("header");
responseHeaderAssertionItem.setValue("value");
responseHeaderAssertionItem.setExpectedValue("value");
responseHeaderAssertionItem.setCondition(MsAssertionCondition.EMPTY.name());
responseHeaderAssertion.setAssertions(List.of(responseHeaderAssertionItem));
assertions.add(responseHeaderAssertion);
ResponseBodyAssertion responseBodyAssertion = new ResponseBodyAssertion();
responseBodyAssertion.setAssertionType(MsBodyAssertionType.JSON_PATH.name());
RegexAssertion regexAssertion = new RegexAssertion();
regexAssertion.setAssertions(List.of(new RegexAssertionItem()));
responseBodyAssertion.setRegexAssertion(regexAssertion);
responseBodyAssertion.setDocumentAssertion(new DocumentAssertion());
responseBodyAssertion.setJsonPathAssertion(new JSONPathAssertion());
responseBodyAssertion.setXpathAssertion(new XPathAssertion());
assertions.add(responseBodyAssertion);
MsResponseBodyAssertion regexResponseBodyAssertion = new MsResponseBodyAssertion();
regexResponseBodyAssertion.setAssertionBodyType(MsResponseBodyAssertion.MsBodyAssertionType.REGEX.name());
MsRegexAssertion regexAssertion = new MsRegexAssertion();
ResponseTimeAssertion responseTimeAssertion = new ResponseTimeAssertion();
responseTimeAssertion.setMaxResponseTime(1000L);
MsRegexAssertionItem msRegexAssertionItem = new MsRegexAssertionItem();
msRegexAssertionItem.setExpression("^test");
regexAssertion.setAssertions(List.of(msRegexAssertionItem));
regexResponseBodyAssertion.setRegexAssertion(regexAssertion);
assertions.add(regexResponseBodyAssertion);
MsResponseBodyAssertion documentResponseBodyAssertion = new MsResponseBodyAssertion();
documentResponseBodyAssertion.setAssertionBodyType(MsResponseBodyAssertion.MsBodyAssertionType.DOCUMENT.name());
MsDocumentAssertion msDocumentAssertion = new MsDocumentAssertion();
documentResponseBodyAssertion.setDocumentAssertion(msDocumentAssertion);
assertions.add(documentResponseBodyAssertion);
MsResponseBodyAssertion jsonPathResponseBodyAssertion = new MsResponseBodyAssertion();
jsonPathResponseBodyAssertion.setAssertionBodyType(MsResponseBodyAssertion.MsBodyAssertionType.JSON_PATH.name());
MsJSONPathAssertion msJSONPathAssertion = new MsJSONPathAssertion();
MsJSONPathAssertionItem msJSONPathAssertionItem = new MsJSONPathAssertionItem();
msJSONPathAssertionItem.setExpression("^test");
msJSONPathAssertionItem.setCondition(MsAssertionCondition.REGEX.name());
msJSONPathAssertionItem.setExpectedValue("expectedValue");
msJSONPathAssertion.setAssertions(List.of(msJSONPathAssertionItem));
jsonPathResponseBodyAssertion.setJsonPathAssertion(msJSONPathAssertion);
assertions.add(jsonPathResponseBodyAssertion);
MsResponseBodyAssertion xpathPathResponseBodyAssertion = new MsResponseBodyAssertion();
xpathPathResponseBodyAssertion.setAssertionBodyType(MsResponseBodyAssertion.MsBodyAssertionType.XPATH.name());
MsXPathAssertion xPathPathAssertion = new MsXPathAssertion();
xPathPathAssertion.setResponseFormat(MsXPathAssertion.ResponseFormat.XML.name());
MsXPathAssertionItem xPathAssertionItem = new MsXPathAssertionItem();
xPathAssertionItem.setExpression("^test");
xPathAssertionItem.setExpectedValue("expectedValue");
xPathPathAssertion.setAssertions(List.of(xPathAssertionItem));
xpathPathResponseBodyAssertion.setXpathAssertion(xPathPathAssertion);
assertions.add(xpathPathResponseBodyAssertion);
MsResponseTimeAssertion responseTimeAssertion = new MsResponseTimeAssertion();
responseTimeAssertion.setExpectedValue(1000L);
responseTimeAssertion.setEnable(true);
responseTimeAssertion.setName("aa");
assertions.add(responseTimeAssertion);
ScriptAssertion scriptAssertion = new ScriptAssertion();
scriptAssertion.setCommonScriptId("1111");
scriptAssertion.setContent("1111");
scriptAssertion.setDescription("1111");
MsScriptAssertion scriptAssertion = new MsScriptAssertion();
scriptAssertion.setScriptId("1111");
scriptAssertion.setScript("1111");
scriptAssertion.setName("1111");
assertions.add(scriptAssertion);
MsVariableAssertion msVariableAssertion = new MsVariableAssertion();
MsVariableAssertion.VariableAssertionItem variableAssertionItem = new MsVariableAssertion.VariableAssertionItem();
variableAssertionItem.setCondition(MsAssertionCondition.GT.name());
variableAssertionItem.setExpectedValue("ev");
variableAssertionItem.setVariableName("vn");
msVariableAssertion.setVariableAssertionItems(List.of(variableAssertionItem));
assertions.add(msVariableAssertion);
return assertions;
}