refactor(接口测试): 后台转换JMX

This commit is contained in:
q4speed 2020-11-11 13:40:24 +08:00
parent bd577770a4
commit 5a3310a837
29 changed files with 1356 additions and 525 deletions

View File

@ -19,7 +19,7 @@
<java.version>1.8</java.version>
<jmeter.version>5.2.1</jmeter.version>
<nacos.version>1.1.3</nacos.version>
<dubbo.version>2.7.7</dubbo.version>
<dubbo.version>2.7.8</dubbo.version>
<graalvm.version>20.1.0</graalvm.version>
</properties>

View File

@ -1,5 +1,7 @@
package io.metersphere.api.controller;
import static io.metersphere.commons.utils.JsonPathUtils.getListJson;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.*;
@ -14,18 +16,15 @@ import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.request.QueryScheduleRequest;
import io.metersphere.dto.ScheduleDao;
import io.metersphere.service.CheckOwnerService;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import static io.metersphere.commons.utils.JsonPathUtils.getListJson;
import javax.annotation.Resource;
@RestController
@ -76,18 +75,19 @@ public class APITestController {
}
@PostMapping(value = "/create", consumes = {"multipart/form-data"})
public void create(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
apiTestService.create(request, file, bodyFiles);
public void create(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
apiTestService.create(request, bodyFiles);
}
@PostMapping(value = "/create/merge", consumes = {"multipart/form-data"})
public void mergeCreate(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "selectIds") List<String> selectIds) {
apiTestService.mergeCreate(request, file, selectIds);
public void mergeCreate(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "selectIds") List<String> selectIds) {
apiTestService.mergeCreate(request, selectIds);
}
@PostMapping(value = "/update", consumes = {"multipart/form-data"})
public void update(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
public void update(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
checkownerService.checkApiTestOwner(request.getId());
apiTestService.update(request, file, bodyFiles);
apiTestService.update(request, bodyFiles);
}
@PostMapping(value = "/copy")
@ -101,7 +101,6 @@ public class APITestController {
return apiTestService.get(testId);
}
@PostMapping("/delete")
public void delete(@RequestBody DeleteAPITestRequest request) {
String testId = request.getId();
@ -109,14 +108,19 @@ public class APITestController {
apiTestService.delete(testId);
}
@PostMapping(value = "/jmx")
public String getJMX(@RequestBody SaveAPITestRequest request) {
return apiTestService.getJMX(request);
}
@PostMapping(value = "/run")
public String run(@RequestBody SaveAPITestRequest request) {
return apiTestService.run(request);
}
@PostMapping(value = "/run/debug", consumes = {"multipart/form-data"})
public String runDebug(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
return apiTestService.runDebug(request, file, bodyFiles);
public String runDebug(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
return apiTestService.runDebug(request, bodyFiles);
}
@PostMapping(value = "/checkName")
@ -137,7 +141,7 @@ public class APITestController {
@PostMapping("/list/schedule/{goPage}/{pageSize}")
public List<ScheduleDao> listSchedule(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryScheduleRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
PageHelper.startPage(goPage, pageSize, true);
return apiTestService.listSchedule(request);
}

View File

@ -1,6 +1,7 @@
package io.metersphere.api.dto.scenario;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
@ -10,4 +11,20 @@ public class Body {
private String raw;
private String format;
private List<KeyValue> kvs;
private final static String KV = "KeyValue";
private final static String FORM_DATA = "Form Data";
private final static String RAW = "Raw";
public boolean isValid() {
if (this.isKV()) {
return kvs.stream().anyMatch(KeyValue::isValid);
} else {
return StringUtils.isNotBlank(raw);
}
}
public boolean isKV() {
return StringUtils.equals(type, KV);
}
}

View File

@ -0,0 +1,14 @@
package io.metersphere.api.dto.scenario;
import lombok.Data;
import java.util.List;
@Data
public class HttpConfig {
private String socket;
private String domain;
private String protocol = "https";
private int port;
private List<KeyValue> headers;
}

View File

@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario;
import io.metersphere.api.dto.scenario.request.BodyFile;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
@ -14,6 +15,7 @@ public class KeyValue {
private String description;
private String contentType;
private boolean enable;
private boolean encode = true;
public KeyValue() {
this.enable = true;
@ -31,4 +33,12 @@ public class KeyValue {
this.enable = true;
this.description = description;
}
public boolean isValid() {
return (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(value)) && !StringUtils.equalsIgnoreCase(type, "file");
}
public boolean isFile() {
return (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(value)) && StringUtils.equalsIgnoreCase(type, "file");
}
}

View File

@ -11,12 +11,12 @@ public class Scenario {
private String name;
private String url;
private String environmentId;
private Boolean enableCookieShare;
private boolean enableCookieShare;
private List<KeyValue> variables;
private List<KeyValue> headers;
private List<Request> requests;
private DubboConfig dubboConfig;
private TCPConfig tcpConfig;
private List<DatabaseConfig> databaseConfigs;
private Boolean enable;
private boolean enable = true;
}

View File

@ -6,12 +6,12 @@ import lombok.Data;
public class TCPConfig {
private String classname;
private String server;
private Integer port;
private Integer ctimeout;
private Integer timeout;
private Boolean reUseConnection;
private Boolean nodelay;
private Boolean closeConnection;
private String port;
private String ctimeout;
private String timeout;
private boolean reUseConnection;
private boolean nodelay;
private boolean closeConnection;
private String soLinger;
private String eolByte;
private String username;

View File

@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.assertions;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.StringUtils;
@EqualsAndHashCode(callSuper = true)
@Data
@ -11,4 +12,8 @@ public class AssertionDuration extends AssertionType {
public AssertionDuration() {
setType(AssertionType.DURATION);
}
public boolean isValid() {
return value > 0;
}
}

View File

@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.assertions;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.StringUtils;
@EqualsAndHashCode(callSuper = true)
@Data
@ -17,4 +18,8 @@ public class AssertionJSR223 extends AssertionType {
public AssertionJSR223() {
setType(AssertionType.JSR223);
}
public boolean isValid() {
return StringUtils.isNotBlank(script) && StringUtils.isNotBlank(language);
}
}

View File

@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.assertions;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.StringUtils;
@EqualsAndHashCode(callSuper = true)
@Data
@ -13,4 +14,8 @@ public class AssertionJsonPath extends AssertionType {
public AssertionJsonPath() {
setType(AssertionType.JSON_PATH);
}
public boolean isValid() {
return StringUtils.isNotBlank(expression);
}
}

View File

@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.assertions;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.StringUtils;
@EqualsAndHashCode(callSuper = true)
@Data
@ -9,9 +10,13 @@ public class AssertionRegex extends AssertionType {
private String subject;
private String expression;
private String description;
private Boolean assumeSuccess;
private boolean assumeSuccess;
public AssertionRegex() {
setType(AssertionType.REGEX);
}
public boolean isValid() {
return StringUtils.isNotBlank(subject) && StringUtils.isNotBlank(expression);
}
}

View File

@ -1,13 +1,56 @@
package io.metersphere.api.dto.scenario.controller;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
@Data
public class IfController {
private String type;
private String id;
private Boolean enable;
private boolean enable = true;
private String variable;
private String operator;
private String value;
public boolean isValid() {
if (StringUtils.contains(operator, "empty")) {
return StringUtils.isNotBlank(variable);
}
return StringUtils.isNotBlank(variable) && StringUtils.isNotBlank(operator) && StringUtils.isNotBlank(value);
}
public String getLabel() {
if (isValid()) {
String label = variable + " " + operator;
if (StringUtils.isNotBlank(value)) {
label += " " + this.value;
}
return label;
}
return "";
}
public String getCondition() {
String variable = "\"" + this.variable + "\"";
String operator = this.operator;
String value = "\"" + this.value + "\"";
if (StringUtils.contains(operator, "~")) {
value = "\".*" + this.value + ".*\"";
}
if (StringUtils.equals(operator, "is empty")) {
variable = "empty(" + variable + ")";
operator = "";
value = "";
}
if (StringUtils.equals(operator, "is not empty")) {
variable = "!empty(" + variable + ")";
operator = "";
value = "";
}
return "${__jexl3(" + variable + operator + value + ")}";
}
}

View File

@ -0,0 +1,13 @@
package io.metersphere.api.dto.scenario.environment;
import io.metersphere.api.dto.scenario.KeyValue;
import lombok.Data;
import java.util.List;
@Data
public class CommonConfig {
private List<KeyValue> variables;
private boolean enableHost;
private List<Host> hosts;
}

View File

@ -0,0 +1,16 @@
package io.metersphere.api.dto.scenario.environment;
import io.metersphere.api.dto.scenario.DatabaseConfig;
import io.metersphere.api.dto.scenario.HttpConfig;
import io.metersphere.api.dto.scenario.TCPConfig;
import lombok.Data;
import java.util.List;
@Data
public class EnvironmentConfig {
private CommonConfig commonConfig;
private HttpConfig httpConfig;
private List<DatabaseConfig> databaseConfigs;
private TCPConfig tcpConfig;
}

View File

@ -0,0 +1,12 @@
package io.metersphere.api.dto.scenario.environment;
import lombok.Data;
@Data
public class Host {
private String ip;
private String domain;
private String status;
private String annotation;
private String uuid;
}

View File

@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.extract;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.StringUtils;
@EqualsAndHashCode(callSuper = true)
@Data
@ -10,5 +11,9 @@ public class ExtractCommon extends ExtractType {
private String value; // value: ${variable}
private String expression;
private String description;
private Boolean multipleMatching;
private boolean multipleMatching;
public boolean isValid() {
return StringUtils.isNotBlank(variable) && StringUtils.isNotBlank(expression);
}
}

View File

@ -18,23 +18,23 @@ import java.util.List;
public class DubboRequest extends Request {
// type 必须放最前面以便能够转换正确的类
private String type = RequestType.DUBBO;
@JSONField(ordinal = 2)
@JSONField(ordinal = 52)
private String protocol;
@JsonProperty(value = "interface")
@JSONField(ordinal = 3, name = "interface")
@JSONField(ordinal = 53, name = "interface")
private String _interface;
@JSONField(ordinal = 4)
@JSONField(ordinal = 54)
private String method;
@JSONField(ordinal = 5)
@JSONField(ordinal = 55)
private ConfigCenter configCenter;
@JSONField(ordinal = 6)
@JSONField(ordinal = 56)
private RegistryCenter registryCenter;
@JSONField(ordinal = 7)
@JSONField(ordinal = 57)
private ConsumerAndService consumerAndService;
@JSONField(ordinal = 8)
@JSONField(ordinal = 58)
private List<KeyValue> args;
@JSONField(ordinal = 9)
@JSONField(ordinal = 59)
private List<KeyValue> attachmentArgs;
}

View File

@ -15,26 +15,24 @@ import java.util.List;
public class HttpRequest extends Request {
// type 必须放最前面以便能够转换正确的类
private String type = RequestType.HTTP;
@JSONField(ordinal = 2)
@JSONField(ordinal = 50)
private String url;
@JSONField(ordinal = 3)
@JSONField(ordinal = 51)
private String method;
@JSONField(ordinal = 4)
@JSONField(ordinal = 52)
private String path;
@JSONField(ordinal = 5)
private Boolean useEnvironment;
@JSONField(ordinal = 6)
@JSONField(ordinal = 53)
private List<KeyValue> parameters;
@JSONField(ordinal = 7)
@JSONField(ordinal = 54)
private List<KeyValue> headers;
@JSONField(ordinal = 8)
@JSONField(ordinal = 55)
private Body body;
@JSONField(ordinal = 14)
private Long connectTimeout;
@JSONField(ordinal = 15)
private Long responseTimeout;
@JSONField(ordinal = 16)
private Boolean followRedirects;
@JSONField(ordinal = 17)
private Boolean doMultipartPost;
@JSONField(ordinal = 56)
private String connectTimeout;
@JSONField(ordinal = 57)
private String responseTimeout;
@JSONField(ordinal = 58)
private boolean followRedirects;
@JSONField(ordinal = 59)
private boolean doMultipartPost;
}

View File

@ -22,22 +22,25 @@ import lombok.Data;
@JSONType(seeAlso = {HttpRequest.class, DubboRequest.class, SqlRequest.class, TCPRequest.class}, typeKey = "type")
@Data
public abstract class Request {
private String type;
@JSONField(ordinal = 1)
private String id;
@JSONField(ordinal = 2)
private String name;
@JSONField(ordinal = 3)
private Boolean enable;
private boolean enable = true;
@JSONField(ordinal = 4)
private Assertions assertions;
private boolean useEnvironment;
@JSONField(ordinal = 5)
private Extract extract;
private Assertions assertions;
@JSONField(ordinal = 6)
private JSR223PreProcessor jsr223PreProcessor;
private Extract extract;
@JSONField(ordinal = 7)
private JSR223PostProcessor jsr223PostProcessor;
private JSR223PreProcessor jsr223PreProcessor;
@JSONField(ordinal = 8)
private IfController controller;
private JSR223PostProcessor jsr223PostProcessor;
@JSONField(ordinal = 9)
private IfController controller;
@JSONField(ordinal = 10)
private ConstantTimer timer;
}

View File

@ -14,20 +14,16 @@ import java.util.List;
public class SqlRequest extends Request {
// type 必须放最前面以便能够转换正确的类
private String type = RequestType.SQL;
@JSONField(ordinal = 3)
@JSONField(ordinal = 50)
private String dataSource;
@JSONField(ordinal = 4)
@JSONField(ordinal = 51)
private String query;
@JSONField(ordinal = 5)
@JSONField(ordinal = 52)
private long queryTimeout;
@JSONField(ordinal = 6)
private Boolean useEnvironment;
@JSONField(ordinal = 7)
private Boolean followRedirects;
@JSONField(ordinal = 13)
@JSONField(ordinal = 53)
private String resultVariable;
@JSONField(ordinal = 14)
@JSONField(ordinal = 54)
private String variableNames;
@JSONField(ordinal = 15)
@JSONField(ordinal = 55)
private List<KeyValue> variables;
}

View File

@ -11,24 +11,22 @@ import lombok.EqualsAndHashCode;
public class TCPRequest extends Request {
// type 必须放最前面以便能够转换正确的类
private String type = RequestType.TCP;
@JSONField(ordinal = 50)
private Boolean useEnvironment;
@JSONField(ordinal = 51)
private String classname;
@JSONField(ordinal = 52)
private String server;
@JSONField(ordinal = 53)
private Integer port;
private String port;
@JSONField(ordinal = 54)
private Integer ctimeout;
private String ctimeout;
@JSONField(ordinal = 55)
private Integer timeout;
private String timeout;
@JSONField(ordinal = 56)
private Boolean reUseConnection;
private boolean reUseConnection;
@JSONField(ordinal = 57)
private Boolean nodelay;
private boolean nodelay;
@JSONField(ordinal = 58)
private Boolean closeConnection;
private boolean closeConnection;
@JSONField(ordinal = 59)
private String soLinger;
@JSONField(ordinal = 60)

View File

@ -6,6 +6,6 @@ import lombok.Data;
public class ConstantTimer {
private String type;
private String id;
private Boolean enable;
private boolean enable = true;
private String delay;
}

View File

@ -42,14 +42,13 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
private final List<SampleResult> queue = new ArrayList<>();
private APITestService apiTestService;
private APIReportService apiReportService;
private TestPlanTestCaseService testPlanTestCaseService;
private NoticeService noticeService;
private MailService mailService;
private DingTaskService dingTaskService;
private WxChatTaskService wxChatTaskService;
private SystemParameterService systemParameterService;
public String runMode = ApiRunMode.RUN.name();
@ -82,6 +81,18 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
if (mailService == null) {
LogUtil.error("mailService is required");
}
dingTaskService = CommonBeanFactory.getBean(DingTaskService.class);
if (dingTaskService == null) {
LogUtil.error("dingTaskService is required");
}
wxChatTaskService = CommonBeanFactory.getBean(WxChatTaskService.class);
if (wxChatTaskService == null) {
LogUtil.error("wxChatTaskService is required");
}
systemParameterService = CommonBeanFactory.getBean(SystemParameterService.class);
if (systemParameterService == null) {
LogUtil.error("systemParameterService is required");
}
super.setupTest(context);
}
@ -146,7 +157,6 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
queue.clear();
super.teardownTest(context);
TestPlanTestCaseService testPlanTestCaseService = CommonBeanFactory.getBean(TestPlanTestCaseService.class);
List<String> ids = testPlanTestCaseService.getTestPlanTestCaseIds(testResult.getTestId());
if (ids.size() > 0) {
try {
@ -167,12 +177,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
}
private static void sendTask(ApiTestReport report, TestResult testResult) {
NoticeService noticeService = CommonBeanFactory.getBean(NoticeService.class);
MailService mailService = CommonBeanFactory.getBean(MailService.class);
DingTaskService dingTaskService = CommonBeanFactory.getBean(DingTaskService.class);
WxChatTaskService wxChatTaskService = CommonBeanFactory.getBean(WxChatTaskService.class);
SystemParameterService systemParameterService = CommonBeanFactory.getBean(SystemParameterService.class);
private void sendTask(ApiTestReport report, TestResult testResult) {
if (StringUtils.equals(NoticeConstants.API, report.getTriggerMode()) || StringUtils.equals(NoticeConstants.SCHEDULE, report.getTriggerMode())) {
List<String> userIds = new ArrayList<>();
List<MessageDetail> taskList = new ArrayList<>();

View File

@ -0,0 +1,730 @@
package io.metersphere.api.jmeter;
import com.alibaba.fastjson.JSONObject;
import io.github.ningyu.jmeter.plugin.dubbo.gui.DubboSampleGui;
import io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample;
import io.github.ningyu.jmeter.plugin.dubbo.sample.MethodArgument;
import io.github.ningyu.jmeter.plugin.util.Constants;
import io.metersphere.api.dto.scenario.*;
import io.metersphere.api.dto.scenario.assertions.*;
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
import io.metersphere.api.dto.scenario.environment.Host;
import io.metersphere.api.dto.scenario.extract.*;
import io.metersphere.api.dto.scenario.request.*;
import io.metersphere.api.service.ApiTestEnvironmentService;
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
import io.metersphere.commons.utils.LogUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.protocol.HTTP;
import org.apache.jmeter.assertions.DurationAssertion;
import org.apache.jmeter.assertions.JSONPathAssertion;
import org.apache.jmeter.assertions.JSR223Assertion;
import org.apache.jmeter.assertions.ResponseAssertion;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.config.ConfigTestElement;
import org.apache.jmeter.control.IfController;
import org.apache.jmeter.control.LoopController;
import org.apache.jmeter.extractor.JSR223PostProcessor;
import org.apache.jmeter.extractor.RegexExtractor;
import org.apache.jmeter.extractor.XPath2Extractor;
import org.apache.jmeter.extractor.json.jsonpath.JSONPostProcessor;
import org.apache.jmeter.modifiers.JSR223PreProcessor;
import org.apache.jmeter.protocol.http.control.CookieManager;
import org.apache.jmeter.protocol.http.control.DNSCacheManager;
import org.apache.jmeter.protocol.http.control.Header;
import org.apache.jmeter.protocol.http.control.HeaderManager;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
import org.apache.jmeter.protocol.http.util.HTTPArgument;
import org.apache.jmeter.protocol.http.util.HTTPFileArg;
import org.apache.jmeter.protocol.jdbc.config.DataSourceElement;
import org.apache.jmeter.protocol.jdbc.sampler.JDBCSampler;
import org.apache.jmeter.protocol.tcp.sampler.TCPSampler;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestPlan;
import org.apache.jmeter.threads.ThreadGroup;
import org.apache.jmeter.timers.ConstantTimer;
import org.apache.jorphan.collections.HashTree;
import org.apache.jorphan.collections.ListedHashTree;
import org.springframework.stereotype.Component;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Resource;
@Component
public class JMXGenerator {
@Resource
private ApiTestEnvironmentService environmentService;
public HashTree parse(String testId, String testName, List<Scenario> scenarios) {
HashTree jmeterTestPlanHashTree = new ListedHashTree();
final HashTree testPlanHashTree = jmeterTestPlanHashTree.add(testPlan(testName));
scenarios.stream().filter(Scenario::isEnable).forEach(scenario -> {
final HashTree threadGroupHashTree = testPlanHashTree.add(threadGroup(scenario.getName()));
EnvironmentConfig config = getEnvironmentConfig(scenario.getEnvironmentId());
// 场景变量
if (CollectionUtils.isNotEmpty(scenario.getVariables())) {
threadGroupHashTree.add(arguments(scenario.getName() + " Variables", scenario.getVariables()));
}
// 场景请求头
if (CollectionUtils.isNotEmpty(scenario.getHeaders())) {
threadGroupHashTree.add(headerManager(scenario.getName() + " Headers", scenario.getHeaders()));
}
// 共享Cookie
if (scenario.isEnableCookieShare()) {
threadGroupHashTree.add(cookieManager(scenario));
}
// 场景JDBCDataSource
final Map<String, String> databaseConfigMap = new HashMap<>();
if (CollectionUtils.isNotEmpty(scenario.getDatabaseConfigs())) {
scenario.getDatabaseConfigs().forEach(databaseConfig -> {
threadGroupHashTree.add(jdbcDataSource(databaseConfig));
databaseConfigMap.put(databaseConfig.getId(), databaseConfig.getName());
});
}
// 场景TCP Config
threadGroupHashTree.add(tcpConfig(scenario.getName() + "TCP Config", scenario.getTcpConfig()));
// 请求
scenario.getRequests().stream().filter(Request::isEnable).forEach(request -> {
final HashTree samplerHashTree = new ListedHashTree();
Object sampler;
switch (request.getType()) {
case RequestType.TCP:
sampler = tcpSampler((TCPRequest) request);
// 引用环境的TCP Config
if (request.isUseEnvironment() && config != null) {
samplerHashTree.add(tcpConfig(request.getName() + "TCP Config", config.getTcpConfig()));
}
break;
case RequestType.DUBBO:
sampler = dubboSample((DubboRequest) request);
break;
case RequestType.SQL:
SqlRequest sqlRequest = (SqlRequest) request;
// 引用环境的JDBCDataSource
if (request.isUseEnvironment() && config != null) {
config.getDatabaseConfigs().forEach(databaseConfig -> {
if (!databaseConfigMap.containsValue(databaseConfig.getName())) {
samplerHashTree.add(jdbcDataSource(databaseConfig));
databaseConfigMap.put(databaseConfig.getId(), databaseConfig.getName());
}
});
}
samplerHashTree.add(arguments(sqlRequest.getName() + " Variables", sqlRequest.getVariables()));
sampler = jdbcSampler(sqlRequest, databaseConfigMap);
break;
default:
HttpRequest httpRequest = (HttpRequest) request;
sampler = httpSamplerProxy(httpRequest, config, testId);
// 请求头(包括引用环境里设置的请求头)
List<KeyValue> headers = httpRequest.getHeaders();
if (request.isUseEnvironment() && config != null) {
headers = merge(headers, config.getHttpConfig().getHeaders());
}
// 根据请求内容给请求头添加Content-Type
addContentType(headers, httpRequest.getBody());
samplerHashTree.add(headerManager(request.getName() + " Headers", headers));
break;
}
if (request.getController() != null && request.getController().isEnable() && request.getController().isValid()) {
threadGroupHashTree.add(ifController(request)).set(sampler, samplerHashTree);
} else {
threadGroupHashTree.set(sampler, samplerHashTree);
}
if (request.isUseEnvironment() && config != null && config.getCommonConfig() != null) {
addEnvironmentVariables(samplerHashTree, request, config);
addEnvironmentDNS(samplerHashTree, request, config);
}
addRequestAssertions(samplerHashTree, request);
addRequestExtractors(samplerHashTree, request);
addJSR223Processors(samplerHashTree, request);
if (request.getTimer() != null && request.getTimer().isEnable()) {
if (StringUtils.isNotBlank(request.getTimer().getDelay())) {
samplerHashTree.add(constantTimer(request));
}
}
});
});
return jmeterTestPlanHashTree;
}
private void addContentType(List<KeyValue> headers, Body body) {
if (!body.isKV() && StringUtils.isNotBlank(body.getFormat())) {
Map<String, String> map = new HashMap<>();
map.put("json", "application/json");
map.put("html", "text/html");
map.put("xml", "text/xml");
String contentType = map.get(body.getFormat());
boolean hasContentType = headers.stream().filter(KeyValue::isEnable).anyMatch(keyValue -> keyValue.getName().equals(HTTP.CONTENT_TYPE));
if (contentType != null && !hasContentType) {
headers.add(new KeyValue(HTTP.CONTENT_TYPE, contentType));
}
}
}
private void addEnvironmentVariables(HashTree samplerHashTree, Request request, EnvironmentConfig config) {
String name = request.getName() + "Environment Variables";
samplerHashTree.add(arguments(name, config.getCommonConfig().getVariables()));
}
private void addEnvironmentDNS(HashTree samplerHashTree, Request request, EnvironmentConfig config) {
if (config.getCommonConfig().isEnableHost() && CollectionUtils.isNotEmpty(config.getCommonConfig().getHosts())) {
String domain = config.getHttpConfig().getDomain().trim();
List<Host> hosts = new ArrayList<>();
config.getCommonConfig().getHosts().forEach(host -> {
if (StringUtils.isNotBlank(host.getDomain())) {
String hostDomain = host.getDomain().trim().replace("http://", "").replace("https://", "");
if (StringUtils.equals(hostDomain, domain)) {
host.setDomain(hostDomain); // 域名去掉协议
hosts.add(host);
}
}
});
samplerHashTree.add(dnsCacheManager(request.getName() + " DNSCacheManager", hosts));
}
}
private void addRequestAssertions(HashTree samplerHashTree, Request request) {
Assertions assertions = request.getAssertions();
if (CollectionUtils.isNotEmpty(assertions.getRegex())) {
assertions.getRegex().stream().filter(AssertionRegex::isValid).forEach(assertion ->
samplerHashTree.add(responseAssertion(assertion))
);
}
if (CollectionUtils.isNotEmpty(assertions.getJsonPath())) {
assertions.getJsonPath().stream().filter(AssertionJsonPath::isValid).forEach(assertion ->
samplerHashTree.add(jsonPathAssertion(assertion))
);
}
if (CollectionUtils.isNotEmpty(assertions.getJsr223())) {
assertions.getJsr223().stream().filter(AssertionJSR223::isValid).forEach(assertion ->
samplerHashTree.add(jsr223Assertion(assertion))
);
}
if (assertions.getDuration().isValid()) {
samplerHashTree.add(durationAssertion(assertions.getDuration()));
}
}
private void addRequestExtractors(HashTree samplerHashTree, Request request) {
Extract extract = request.getExtract();
if (CollectionUtils.isNotEmpty(extract.getRegex())) {
extract.getRegex().stream().filter(ExtractCommon::isValid).forEach(extractRegex ->
samplerHashTree.add(regexExtractor(extractRegex))
);
}
if (CollectionUtils.isNotEmpty(extract.getXpath())) {
extract.getXpath().stream().filter(ExtractCommon::isValid).forEach(extractXPath ->
samplerHashTree.add(xPath2Extractor(extractXPath))
);
}
if (CollectionUtils.isNotEmpty(extract.getJson())) {
extract.getJson().stream().filter(ExtractCommon::isValid).forEach(extractJSONPath ->
samplerHashTree.add(jsonPostProcessor(extractJSONPath))
);
}
}
private void addJSR223Processors(HashTree samplerHashTree, Request request) {
if (request.getJsr223PreProcessor() != null) {
if (StringUtils.isNotBlank(request.getJsr223PreProcessor().getScript())) {
samplerHashTree.add(jsr223PreProcessor(request));
}
}
if (request.getJsr223PostProcessor() != null) {
if (StringUtils.isNotBlank(request.getJsr223PostProcessor().getScript())) {
samplerHashTree.add(jsr223PostProcessor(request));
}
}
}
private EnvironmentConfig getEnvironmentConfig(String id) {
if (StringUtils.isBlank(id)) return null;
ApiTestEnvironmentWithBLOBs environment = environmentService.get(id);
if (environment != null) {
return JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class);
}
return null;
}
private TestPlan testPlan(String testName) {
TestPlan testPlan = new TestPlan(testName);
testPlan.setProperty(TestElement.TEST_CLASS, "TestPlan");
testPlan.setProperty(TestElement.GUI_CLASS, "TestPlanGui");
testPlan.setEnabled(true);
testPlan.setFunctionalMode(false);
testPlan.setSerialized(true);
testPlan.setTearDownOnShutdown(true);
testPlan.setUserDefinedVariables(new Arguments());
return testPlan;
}
private ThreadGroup threadGroup(String name) {
LoopController loopController = new LoopController();
loopController.setName("LoopController");
loopController.setProperty(TestElement.TEST_CLASS, "LoopController");
loopController.setProperty(TestElement.GUI_CLASS, "LoopControlPanel");
loopController.setEnabled(true);
loopController.setLoops(1);
ThreadGroup threadGroup = new ThreadGroup();
threadGroup.setNumThreads(1);
threadGroup.setRampUp(1);
threadGroup.setDelay(0);
threadGroup.setDuration(0);
threadGroup.setProperty(ThreadGroup.ON_SAMPLE_ERROR, ThreadGroup.ON_SAMPLE_ERROR_CONTINUE);
threadGroup.setScheduler(false);
threadGroup.setName(name);
threadGroup.setProperty(TestElement.TEST_CLASS, "ThreadGroup");
threadGroup.setProperty(TestElement.GUI_CLASS, "ThreadGroupGui");
threadGroup.setEnabled(true);
threadGroup.setSamplerController(loopController);
return threadGroup;
}
private Arguments arguments(String name, List<KeyValue> variables) {
Arguments arguments = new Arguments();
arguments.setEnabled(true);
arguments.setName(name);
arguments.setProperty(TestElement.TEST_CLASS, "Arguments");
arguments.setProperty(TestElement.GUI_CLASS, "ArgumentsPanel");
variables.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue ->
arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=")
);
return arguments;
}
private HeaderManager headerManager(String name, List<KeyValue> headers) {
HeaderManager headerManager = new HeaderManager();
headerManager.setEnabled(true);
headerManager.setName(name);
headerManager.setProperty(TestElement.TEST_CLASS, "HeaderManager");
headerManager.setProperty(TestElement.GUI_CLASS, "HeaderPanel");
headers.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue ->
headerManager.add(new Header(keyValue.getName(), keyValue.getValue()))
);
return headerManager;
}
private CookieManager cookieManager(Scenario scenario) {
CookieManager cookieManager = new CookieManager();
cookieManager.setEnabled(true);
cookieManager.setName(scenario.getName() + " Cookie");
cookieManager.setProperty(TestElement.TEST_CLASS, "CookieManager");
cookieManager.setProperty(TestElement.GUI_CLASS, "CookiePanel");
cookieManager.setClearEachIteration(false);
cookieManager.setControlledByThread(false);
return cookieManager;
}
private DataSourceElement jdbcDataSource(DatabaseConfig databaseConfig) {
DataSourceElement dataSourceElement = new DataSourceElement();
dataSourceElement.setEnabled(true);
dataSourceElement.setName(databaseConfig.getName() + " JDBCDataSource");
dataSourceElement.setProperty(TestElement.TEST_CLASS, "JDBCDataSource");
dataSourceElement.setProperty(TestElement.GUI_CLASS, "TestBeanGUI");
dataSourceElement.setAutocommit(true);
dataSourceElement.setKeepAlive(true);
dataSourceElement.setPreinit(true);
dataSourceElement.setDataSource(databaseConfig.getName());
dataSourceElement.setDbUrl(databaseConfig.getDbUrl());
dataSourceElement.setDriver(databaseConfig.getDriver());
dataSourceElement.setUsername(databaseConfig.getUsername());
dataSourceElement.setPassword(databaseConfig.getPassword());
dataSourceElement.setPoolMax(String.valueOf(databaseConfig.getPoolMax()));
dataSourceElement.setTimeout(String.valueOf(databaseConfig.getTimeout()));
dataSourceElement.setConnectionAge("5000");
dataSourceElement.setTrimInterval("60000");
dataSourceElement.setTransactionIsolation("DEFAULT");
return dataSourceElement;
}
private ConfigTestElement tcpConfig(String name, TCPConfig tcpConfig) {
ConfigTestElement configTestElement = new ConfigTestElement();
configTestElement.setEnabled(true);
configTestElement.setName(name);
configTestElement.setProperty(TestElement.TEST_CLASS, "ConfigTestElement");
configTestElement.setProperty(TestElement.GUI_CLASS, "TCPConfigGui");
configTestElement.setProperty(TCPSampler.CLASSNAME, tcpConfig.getClassname());
configTestElement.setProperty(TCPSampler.SERVER, tcpConfig.getServer());
configTestElement.setProperty(TCPSampler.PORT, tcpConfig.getPort());
configTestElement.setProperty(TCPSampler.TIMEOUT_CONNECT, tcpConfig.getCtimeout());
configTestElement.setProperty(TCPSampler.RE_USE_CONNECTION, tcpConfig.isReUseConnection());
configTestElement.setProperty(TCPSampler.NODELAY, tcpConfig.isNodelay());
configTestElement.setProperty(TCPSampler.CLOSE_CONNECTION, tcpConfig.isCloseConnection());
configTestElement.setProperty(TCPSampler.SO_LINGER, tcpConfig.getSoLinger());
configTestElement.setProperty(TCPSampler.EOL_BYTE, tcpConfig.getEolByte());
configTestElement.setProperty(TCPSampler.SO_LINGER, tcpConfig.getSoLinger());
configTestElement.setProperty(ConfigTestElement.USERNAME, tcpConfig.getUsername());
configTestElement.setProperty(ConfigTestElement.PASSWORD, tcpConfig.getPassword());
return configTestElement;
}
private HTTPSamplerProxy httpSamplerProxy(HttpRequest request, EnvironmentConfig config, String testId) {
HTTPSamplerProxy sampler = new HTTPSamplerProxy();
sampler.setEnabled(true);
sampler.setName(request.getName());
sampler.setProperty(TestElement.TEST_CLASS, "HTTPSamplerProxy");
sampler.setProperty(TestElement.GUI_CLASS, "HttpTestSampleGui");
sampler.setMethod(request.getMethod());
try {
if (request.isUseEnvironment()) {
sampler.setDomain(config.getHttpConfig().getDomain());
sampler.setPort(config.getHttpConfig().getPort());
sampler.setProtocol(config.getHttpConfig().getProtocol());
String url = config.getHttpConfig().getProtocol() + "://" + config.getHttpConfig().getSocket();
URL urlObject = new URL(url);
sampler.setDomain(config.getHttpConfig().getDomain());
String envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getPath();
if (StringUtils.isNotBlank(request.getPath())) {
envPath += request.getPath();
}
sampler.setPath(getPostQueryParameters(request, URLDecoder.decode(envPath, "UTF-8")));
} else {
String url = request.getUrl();
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "http://" + url;
}
URL urlObject = new URL(url);
sampler.setDomain(URLDecoder.decode(urlObject.getHost(), "UTF-8"));
sampler.setPort(urlObject.getPort());
sampler.setProtocol(urlObject.getProtocol());
sampler.setPath(getPostQueryParameters(request, URLDecoder.decode(urlObject.getPath(), "UTF-8")));
}
} catch (Exception e) {
LogUtil.error(e);
}
sampler.setConnectTimeout(request.getConnectTimeout());
sampler.setResponseTimeout(request.getResponseTimeout());
sampler.setFollowRedirects(request.isFollowRedirects());
sampler.setDoBrowserCompatibleMultipart(request.isDoMultipartPost());
// 请求参数
sampler.setArguments(httpArguments(request.getParameters()));
// 请求体
if (!StringUtils.equals(request.getMethod(), "GET")) {
List<KeyValue> body = new ArrayList<>();
if (request.getBody().isKV()) {
body = request.getBody().getKvs().stream().filter(KeyValue::isValid).collect(Collectors.toList());
sampler.setHTTPFiles(httpFileArgs(request, testId));
} else {
KeyValue keyValue = new KeyValue("", request.getBody().getRaw());
keyValue.setEnable(true);
keyValue.setEncode(false);
body.add(keyValue);
}
sampler.setPostBodyRaw(true);
sampler.setArguments(httpArguments(body));
}
return sampler;
}
private String getPostQueryParameters(HttpRequest request, String path) {
if (!StringUtils.equals(request.getMethod(), "GET")) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(path);
stringBuffer.append("?");
request.getParameters().stream().filter(KeyValue::isEnable).filter(KeyValue::isValid).forEach(keyValue ->
stringBuffer.append(keyValue.getName()).append("=").append(keyValue.getValue()).append("&")
);
return stringBuffer.substring(0, stringBuffer.length() - 1);
}
return "";
}
private Arguments httpArguments(List<KeyValue> list) {
Arguments arguments = new Arguments();
list.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue -> {
HTTPArgument httpArgument = new HTTPArgument(keyValue.getName(), keyValue.getValue());
httpArgument.setAlwaysEncoded(keyValue.isEncode());
httpArgument.setUseEquals(true);
httpArgument.setContentType(keyValue.getContentType());
arguments.addArgument(httpArgument);
}
);
return arguments;
}
private HTTPFileArg[] httpFileArgs(HttpRequest request, String testId) {
final String BODY_FILE_DIR = "/opt/metersphere/data/body";
List<HTTPFileArg> list = new ArrayList<>();
request.getBody().getKvs().stream().filter(KeyValue::isFile).filter(KeyValue::isEnable).forEach(keyValue -> {
if (keyValue.getFiles() != null) {
keyValue.getFiles().forEach(file -> {
String paramName = keyValue.getName();
String path = BODY_FILE_DIR + '/' + testId + '/' + file.getId() + '_' + file.getName();
String mimetype = keyValue.getContentType();
list.add(new HTTPFileArg(path, paramName, mimetype));
});
}
});
return list.toArray(new HTTPFileArg[0]);
}
private List<KeyValue> merge(List<KeyValue> list1, List<KeyValue> list2) {
Set<String> names = list1.stream().map(KeyValue::getName).collect(Collectors.toSet());
List<KeyValue> list = new ArrayList<>(list1);
list2.stream().filter(keyValue -> !names.contains(keyValue.getName())).forEach(list::add);
return list;
}
private TCPSampler tcpSampler(TCPRequest request) {
TCPSampler tcpSampler = new TCPSampler();
tcpSampler.setName(request.getName());
tcpSampler.setProperty(TestElement.TEST_CLASS, "TCPSampler");
tcpSampler.setProperty(TestElement.GUI_CLASS, "TCPSamplerGui");
tcpSampler.setClassname(request.getClassname());
tcpSampler.setServer(request.getServer());
tcpSampler.setPort(request.getPort());
tcpSampler.setConnectTimeout(request.getCtimeout());
tcpSampler.setProperty(TCPSampler.RE_USE_CONNECTION, request.isReUseConnection());
tcpSampler.setProperty(TCPSampler.NODELAY, request.isNodelay());
tcpSampler.setCloseConnection(String.valueOf(request.isCloseConnection()));
tcpSampler.setSoLinger(request.getSoLinger());
tcpSampler.setEolByte(request.getEolByte());
tcpSampler.setRequestData(request.getRequest());
tcpSampler.setProperty(ConfigTestElement.USERNAME, request.getUsername());
tcpSampler.setProperty(ConfigTestElement.PASSWORD, request.getPassword());
return tcpSampler;
}
private JDBCSampler jdbcSampler(SqlRequest request, Map<String, String> databaseConfigMap) {
JDBCSampler sampler = new JDBCSampler();
sampler.setName(request.getName());
sampler.setProperty(TestElement.TEST_CLASS, "JDBCSampler");
sampler.setProperty(TestElement.GUI_CLASS, "TestBeanGUI");
// request.getDataSource() 是ID需要转换为Name
sampler.setDataSource(databaseConfigMap.get(request.getDataSource()));
sampler.setQuery(request.getQuery());
sampler.setQueryTimeout(String.valueOf(request.getQueryTimeout()));
sampler.setResultVariable(request.getResultVariable());
sampler.setVariableNames(request.getVariableNames());
sampler.setResultSetHandler("Store as String");
sampler.setQueryType("Callable Statement");
return sampler;
}
private DubboSample dubboSample(DubboRequest request) {
DubboSample sampler = new DubboSample();
sampler.setName(request.getName());
sampler.setProperty(TestElement.TEST_CLASS, DubboSample.class.getName());
sampler.setProperty(TestElement.GUI_CLASS, DubboSampleGui.class.getName());
Constants.setConfigCenterProtocol(request.getConfigCenter().getProtocol(), sampler);
Constants.setConfigCenterGroup(request.getConfigCenter().getGroup(), sampler);
Constants.setConfigCenterNamespace(request.getConfigCenter().getNamespace(), sampler);
Constants.setConfigCenterUserName(request.getConfigCenter().getUsername(), sampler);
Constants.setConfigCenterPassword(request.getConfigCenter().getPassword(), sampler);
Constants.setConfigCenterAddress(request.getConfigCenter().getAddress(), sampler);
Constants.setConfigCenterTimeout(request.getConfigCenter().getTimeout(), sampler);
Constants.setRegistryProtocol(request.getRegistryCenter().getProtocol(), sampler);
Constants.setRegistryGroup(request.getRegistryCenter().getGroup(), sampler);
Constants.setRegistryUserName(request.getRegistryCenter().getUsername(), sampler);
Constants.setRegistryPassword(request.getRegistryCenter().getPassword(), sampler);
Constants.setRegistryTimeout(request.getRegistryCenter().getTimeout(), sampler);
Constants.setAddress(request.getRegistryCenter().getAddress(), sampler);
Constants.setTimeout(request.getConsumerAndService().getTimeout(), sampler);
Constants.setVersion(request.getConsumerAndService().getVersion(), sampler);
Constants.setGroup(request.getConsumerAndService().getGroup(), sampler);
Constants.setConnections(request.getConsumerAndService().getConnections(), sampler);
Constants.setLoadbalance(request.getConsumerAndService().getLoadBalance(), sampler);
Constants.setAsync(request.getConsumerAndService().getAsync(), sampler);
Constants.setCluster(request.getConsumerAndService().getCluster(), sampler);
Constants.setRpcProtocol(request.getProtocol(), sampler);
Constants.setInterfaceName(request.get_interface(), sampler);
Constants.setMethod(request.getMethod(), sampler);
List<MethodArgument> methodArgs = request.getArgs().stream().filter(KeyValue::isValid).filter(KeyValue::isEnable)
.map(keyValue -> new MethodArgument(keyValue.getName(), keyValue.getValue())).collect(Collectors.toList());
Constants.setMethodArgs(methodArgs, sampler);
List<MethodArgument> attachmentArgs = request.getAttachmentArgs().stream().filter(KeyValue::isValid).filter(KeyValue::isEnable)
.map(keyValue -> new MethodArgument(keyValue.getName(), keyValue.getValue())).collect(Collectors.toList());
Constants.setAttachmentArgs(attachmentArgs, sampler);
return sampler;
}
private DNSCacheManager dnsCacheManager(String name, List<Host> hosts) {
DNSCacheManager dnsCacheManager = new DNSCacheManager();
dnsCacheManager.setEnabled(true);
dnsCacheManager.setName(name);
dnsCacheManager.setProperty(TestElement.TEST_CLASS, "DNSCacheManager");
dnsCacheManager.setProperty(TestElement.GUI_CLASS, "DNSCachePanel");
dnsCacheManager.setCustomResolver(true);
hosts.forEach(host -> dnsCacheManager.addHost(host.getDomain(), host.getIp()));
return dnsCacheManager;
}
private ResponseAssertion responseAssertion(AssertionRegex assertionRegex) {
ResponseAssertion assertion = new ResponseAssertion();
assertion.setEnabled(true);
assertion.setName(assertionRegex.getDescription());
assertion.setProperty(TestElement.TEST_CLASS, "ResponseAssertion");
assertion.setProperty(TestElement.GUI_CLASS, "AssertionGui");
assertion.setAssumeSuccess(assertionRegex.isAssumeSuccess());
assertion.setToContainsType();
switch (assertionRegex.getSubject()) {
case "Response Code":
assertion.setTestFieldResponseCode();
break;
case "Response Headers":
assertion.setTestFieldResponseHeaders();
break;
case "Response Data":
assertion.setTestFieldResponseData();
break;
}
return assertion;
}
private JSONPathAssertion jsonPathAssertion(AssertionJsonPath assertionJsonPath) {
JSONPathAssertion assertion = new JSONPathAssertion();
assertion.setEnabled(true);
assertion.setName(assertionJsonPath.getDescription());
assertion.setProperty(TestElement.TEST_CLASS, "JSONPathAssertion");
assertion.setProperty(TestElement.GUI_CLASS, "JSONPathAssertionGui");
assertion.setJsonPath(assertionJsonPath.getExpression());
assertion.setExpectedValue(assertionJsonPath.getExpect());
assertion.setJsonValidationBool(true);
assertion.setExpectNull(false);
assertion.setInvert(false);
assertion.setIsRegex(true);
return assertion;
}
private DurationAssertion durationAssertion(AssertionDuration assertionDuration) {
DurationAssertion assertion = new DurationAssertion();
assertion.setEnabled(true);
assertion.setName("Response In Time: " + assertionDuration.getValue());
assertion.setProperty(TestElement.TEST_CLASS, "DurationAssertion");
assertion.setProperty(TestElement.GUI_CLASS, "DurationAssertionGui");
assertion.setAllowedDuration(assertionDuration.getValue());
return assertion;
}
private JSR223Assertion jsr223Assertion(AssertionJSR223 assertionJSR223) {
JSR223Assertion assertion = new JSR223Assertion();
assertion.setEnabled(true);
assertion.setName(assertionJSR223.getDesc());
assertion.setProperty(TestElement.TEST_CLASS, "JSR223Assertion");
assertion.setProperty(TestElement.GUI_CLASS, "TestBeanGUI");
assertion.setProperty("cacheKey", "true");
assertion.setProperty("scriptLanguage", assertionJSR223.getLanguage());
assertion.setProperty("script", assertionJSR223.getScript());
return assertion;
}
private RegexExtractor regexExtractor(ExtractRegex extractRegex) {
RegexExtractor extractor = new RegexExtractor();
extractor.setEnabled(true);
extractor.setName(extractRegex.getVariable() + " RegexExtractor");
extractor.setProperty(TestElement.TEST_CLASS, "RegexExtractor");
extractor.setProperty(TestElement.GUI_CLASS, "RegexExtractorGui");
extractor.setRefName(extractRegex.getVariable());
extractor.setRegex(extractRegex.getExpression());
extractor.setUseField(extractRegex.getUseHeaders());
if (extractRegex.isMultipleMatching()) {
extractor.setMatchNumber(-1);
}
extractor.setTemplate("$1$");
return extractor;
}
private JSONPostProcessor jsonPostProcessor(ExtractJSONPath extractJSONPath) {
JSONPostProcessor extractor = new JSONPostProcessor();
extractor.setEnabled(true);
extractor.setName(extractJSONPath.getVariable() + " JSONExtractor");
extractor.setProperty(TestElement.TEST_CLASS, "JSONPostProcessor");
extractor.setProperty(TestElement.GUI_CLASS, "JSONPostProcessorGui");
extractor.setRefNames(extractJSONPath.getVariable());
extractor.setJsonPathExpressions(extractJSONPath.getExpression());
if (extractJSONPath.isMultipleMatching()) {
extractor.setMatchNumbers("-1");
}
return extractor;
}
private XPath2Extractor xPath2Extractor(ExtractXPath extractXPath) {
XPath2Extractor extractor = new XPath2Extractor();
extractor.setEnabled(true);
extractor.setName(extractXPath.getVariable() + " XPath2Extractor");
extractor.setProperty(TestElement.TEST_CLASS, "XPath2Extractor");
extractor.setProperty(TestElement.GUI_CLASS, "XPath2ExtractorGui");
extractor.setRefName(extractXPath.getVariable());
extractor.setXPathQuery(extractXPath.getExpression());
if (extractXPath.isMultipleMatching()) {
extractor.setMatchNumber(-1);
}
return extractor;
}
private JSR223PreProcessor jsr223PreProcessor(Request request) {
JSR223PreProcessor processor = new JSR223PreProcessor();
processor.setEnabled(true);
processor.setName(request.getName());
processor.setProperty(TestElement.TEST_CLASS, "JSR223PreProcessor");
processor.setProperty(TestElement.GUI_CLASS, "TestBeanGUI");
processor.setProperty("cacheKey", "true");
processor.setProperty("scriptLanguage", request.getJsr223PreProcessor().getLanguage());
processor.setProperty("script", request.getJsr223PreProcessor().getScript());
return processor;
}
private JSR223PostProcessor jsr223PostProcessor(Request request) {
JSR223PostProcessor processor = new JSR223PostProcessor();
processor.setEnabled(true);
processor.setName(request.getName());
processor.setProperty(TestElement.TEST_CLASS, "JSR223PostProcessor");
processor.setProperty(TestElement.GUI_CLASS, "TestBeanGUI");
processor.setProperty("cacheKey", "true");
processor.setProperty("scriptLanguage", request.getJsr223PostProcessor().getLanguage());
processor.setProperty("script", request.getJsr223PostProcessor().getScript());
return processor;
}
private ConstantTimer constantTimer(Request request) {
ConstantTimer constantTimer = new ConstantTimer();
constantTimer.setEnabled(true);
constantTimer.setName(request.getTimer().getDelay() + " ms");
constantTimer.setProperty(TestElement.TEST_CLASS, "ConstantTimer");
constantTimer.setProperty(TestElement.GUI_CLASS, "ConstantTimerGui");
constantTimer.setDelay(request.getTimer().getDelay());
return constantTimer;
}
private IfController ifController(Request request) {
IfController ifController = new IfController();
ifController.setEnabled(true);
ifController.setName(request.getController().getLabel());
ifController.setCondition(request.getController().getCondition());
ifController.setProperty(TestElement.TEST_CLASS, "IfController");
ifController.setProperty(TestElement.GUI_CLASS, "IfControllerPanel");
ifController.setEvaluateAll(false);
ifController.setUseExpression(true);
return ifController;
}
}

View File

@ -1,5 +1,6 @@
package io.metersphere.api.jmeter;
import io.metersphere.api.dto.scenario.Scenario;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil;
@ -14,28 +15,38 @@ import org.apache.jorphan.collections.HashTree;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
@Service
public class JMeterService {
@Resource
private JmeterProperties jmeterProperties;
@Resource
private JMXGenerator jmxGenerator;
public void run(String testId, String debugReportId, InputStream is) {
@PostConstruct
public void init() {
String JMETER_HOME = getJmeterHome();
String JMETER_PROPERTIES = JMETER_HOME + "/bin/jmeter.properties";
JMeterUtils.loadJMeterProperties(JMETER_PROPERTIES);
JMeterUtils.setJMeterHome(JMETER_HOME);
JMeterUtils.setLocale(LocaleContextHolder.getLocale());
}
public HashTree getHashTree(String testId, String testName, List<Scenario> scenarios) {
return jmxGenerator.parse(testId, testName, scenarios);
}
public void run(String testId, String testName, List<Scenario> scenarios, String debugReportId) {
try {
Object scriptWrapper = SaveService.loadElement(is);
HashTree testPlan = getHashTree(scriptWrapper);
HashTree testPlan = getHashTree(testId, testName, scenarios);
JMeterVars.addJSR223PostProcessor(testPlan);
addBackendListener(testId, debugReportId, testPlan);
LocalRunner runner = new LocalRunner(testPlan);
@ -46,6 +57,17 @@ public class JMeterService {
}
}
public String getJMX(HashTree hashTree) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
SaveService.saveTree(hashTree, baos);
LogUtil.debug(baos.toString());
return baos.toString();
} catch (Exception e) {
LogUtil.warn("HashTree error, can't log jmx content");
}
return null;
}
public String getJmeterHome() {
String home = getClass().getResource("/").getPath() + "jmeter";
try {
@ -60,12 +82,6 @@ public class JMeterService {
}
}
private HashTree getHashTree(Object scriptWrapper) throws Exception {
Field field = scriptWrapper.getClass().getDeclaredField("testPlan");
field.setAccessible(true);
return (HashTree) field.get(scriptWrapper);
}
private void addBackendListener(String testId, String debugReportId, HashTree testPlan) {
BackendListener backendListener = new BackendListener();
backendListener.setName(testId);

View File

@ -8,7 +8,6 @@ import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.parse.ApiImportParser;
import io.metersphere.api.parse.ApiImportParserFactory;
import io.metersphere.api.parse.JmeterDocumentParser;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiTestFileMapper;
import io.metersphere.base.mapper.ApiTestMapper;
@ -23,9 +22,9 @@ import io.metersphere.controller.request.QueryScheduleRequest;
import io.metersphere.dto.ScheduleDao;
import io.metersphere.i18n.Translator;
import io.metersphere.job.sechedule.ApiTestJob;
import io.metersphere.notice.service.MailService;
import io.metersphere.notice.service.NoticeService;
import io.metersphere.service.*;
import io.metersphere.service.FileService;
import io.metersphere.service.QuotaService;
import io.metersphere.service.ScheduleService;
import io.metersphere.track.service.TestCaseService;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
@ -35,16 +34,15 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Resource;
@Service
@Transactional(rollbackFor = Exception.class)
public class APITestService {
@Resource
private UserService userService;
@Resource
private ApiTestMapper apiTestMapper;
@Resource
@ -61,10 +59,6 @@ public class APITestService {
private ScheduleService scheduleService;
@Resource
private TestCaseService testCaseService;
@Resource
private MailService mailService;
@Resource
private NoticeService noticeService;
private static final String BODY_FILE_DIR = "/opt/metersphere/data/body";
@ -82,33 +76,19 @@ public class APITestService {
return extApiTestMapper.listByIds(request.getIds());
}
public void create(SaveAPITestRequest request, MultipartFile file, List<MultipartFile> bodyFiles) {
public void create(SaveAPITestRequest request, List<MultipartFile> bodyFiles) {
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
ApiTest test = createTest(request, file);
ApiTest test = createTest(request);
createBodyFiles(test, bodyUploadIds, bodyFiles);
}
private ApiTest createTest(SaveAPITestRequest request, MultipartFile file) {
if (file == null) {
throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
}
checkQuota();
request.setBodyUploadIds(null);
ApiTest test = createTest(request);
saveFile(test.getId(), file);
return test;
}
public void update(SaveAPITestRequest request, MultipartFile file, List<MultipartFile> bodyFiles) {
if (file == null) {
throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
}
public void update(SaveAPITestRequest request, List<MultipartFile> bodyFiles) {
deleteFileByTestId(request.getId());
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
request.setBodyUploadIds(null);
ApiTest test = updateTest(request);
createBodyFiles(test, bodyUploadIds, bodyFiles);
saveFile(test.getId(), file);
}
private void createBodyFiles(ApiTest test, List<String> bodyUploadIds, List<MultipartFile> bodyFiles) {
@ -151,14 +131,6 @@ public class APITestService {
copy.setStatus(APITestStatus.Saved.name());
copy.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId());
apiTestMapper.insert(copy);
// copy test file
ApiTestFile apiTestFile = getFileByTestId(request.getId());
if (apiTestFile != null) {
FileMetadata fileMetadata = fileService.copyFile(apiTestFile.getFileId());
apiTestFile.setTestId(copy.getId());
apiTestFile.setFileId(fileMetadata.getId());
apiTestFileMapper.insert(apiTestFile);
}
copyBodyFiles(copy.getId(), request.getId());
}
@ -211,26 +183,13 @@ public class APITestService {
}
public String run(SaveAPITestRequest request) {
ApiTestFile file = getFileByTestId(request.getId());
if (file == null) {
MSException.throwException(Translator.get("file_cannot_be_null"));
}
byte[] bytes = fileService.loadFileAsBytes(file.getFileId());
// 解析 xml 处理 mock 数据
bytes = JmeterDocumentParser.parse(bytes);
InputStream is = new ByteArrayInputStream(bytes);
APITestResult apiTest = get(request.getId());
if (SessionUtils.getUser() == null) {
apiTest.setUserId(request.getUserId());
}
String reportId = apiReportService.create(apiTest, request.getTriggerMode());
/*if (request.getTriggerMode().equals("SCHEDULE")) {
List<Notice> notice = noticeService.queryNotice(request.getId());
mailService.sendHtml(reportId,notice,"api");
}*/
changeStatus(request.getId(), APITestStatus.Running);
jMeterService.run(request.getId(), null, is);
jMeterService.run(request.getId(), request.getName(), request.getScenarioDefinition(), null);
return reportId;
}
@ -271,6 +230,8 @@ public class APITestService {
}
private ApiTest createTest(SaveAPITestRequest request) {
checkQuota();
request.setBodyUploadIds(null);
checkNameExist(request);
final ApiTest test = new ApiTest();
test.setId(request.getId());
@ -285,14 +246,6 @@ public class APITestService {
return test;
}
private void saveFile(String testId, MultipartFile file) {
final FileMetadata fileMetadata = fileService.saveFile(file);
ApiTestFile apiTestFile = new ApiTestFile();
apiTestFile.setTestId(testId);
apiTestFile.setFileId(fileMetadata.getId());
apiTestFileMapper.insert(apiTestFile);
}
private void deleteFileByTestId(String testId) {
ApiTestFileExample ApiTestFileExample = new ApiTestFileExample();
ApiTestFileExample.createCriteria().andTestIdEqualTo(testId);
@ -305,18 +258,6 @@ public class APITestService {
}
}
private ApiTestFile getFileByTestId(String testId) {
ApiTestFileExample ApiTestFileExample = new ApiTestFileExample();
ApiTestFileExample.createCriteria().andTestIdEqualTo(testId);
final List<ApiTestFile> ApiTestFiles = apiTestFileMapper.selectByExample(ApiTestFileExample);
apiTestFileMapper.selectByExample(ApiTestFileExample);
if (!CollectionUtils.isEmpty(ApiTestFiles)) {
return ApiTestFiles.get(0);
} else {
return null;
}
}
public void updateSchedule(Schedule request) {
scheduleService.editSchedule(request);
addOrUpdateApiTestCronJob(request);
@ -409,10 +350,7 @@ public class APITestService {
return schedules;
}
public String runDebug(SaveAPITestRequest request, MultipartFile file, List<MultipartFile> bodyFiles) {
if (file == null) {
throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
}
public String runDebug(SaveAPITestRequest request, List<MultipartFile> bodyFiles) {
updateTest(request);
APITestResult apiTest = get(request.getId());
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
@ -423,17 +361,7 @@ public class APITestService {
}
String reportId = apiReportService.createDebugReport(apiTest);
InputStream is = null;
try {
byte[] bytes = file.getBytes();
// 解析 xml 处理 mock 数据
bytes = JmeterDocumentParser.parse(bytes);
is = new ByteArrayInputStream(bytes);
} catch (IOException e) {
LogUtil.error(e);
}
jMeterService.run(request.getId(), reportId, is);
jMeterService.run(request.getId(), request.getName(), request.getScenarioDefinition(), reportId);
return reportId;
}
@ -444,10 +372,12 @@ public class APITestService {
}
}
public void mergeCreate(SaveAPITestRequest request, MultipartFile file, List<String> selectIds) {
ApiTest test = createTest(request, file);
selectIds.forEach(sourceId -> {
copyBodyFiles(test.getId(), sourceId);
});
public void mergeCreate(SaveAPITestRequest request, List<String> selectIds) {
ApiTest test = createTest(request);
selectIds.forEach(sourceId -> copyBodyFiles(test.getId(), sourceId));
}
public String getJMX(SaveAPITestRequest request) {
return jMeterService.getJMX(jMeterService.getHashTree(request.getId(), request.getName(), request.getScenarioDefinition()));
}
}

View File

@ -27,10 +27,6 @@
{{ $t('load_test.save_and_run') }}
</el-button>
<!-- <el-button :disabled="isReadOnly" type="primary" plain v-if="isShowRun" @click="runTest">-->
<!-- {{$t('api_test.run')}}-->
<!-- </el-button>-->
<el-button :disabled="isReadOnly" type="warning" plain @click="cancel">{{ $t('commons.cancel') }}
</el-button>
@ -207,10 +203,7 @@
this.change = false;
let bodyFiles = this.getBodyUploadFiles();
let url = this.create ? "/api/create" : "/api/update";
let jmx = this.test.toJMX();
let blob = new Blob([jmx.xml], {type: "application/octet-stream"});
let file = new File([blob], jmx.name);
this.result = this.$fileUpload(url, file, bodyFiles, this.test, () => {
this.result = this.$fileUpload(url, null, bodyFiles, this.test, () => {
if (callback) callback();
this.create = false;
this.resetBodyFile();
@ -229,7 +222,8 @@
})
},
runTest() {
this.result = this.$post("/api/run", {id: this.test.id, triggerMode: 'MANUAL'}, (response) => {
this.test.triggerMode = 'MANUAL';
this.result = this.$post("/api/run", this.test, (response) => {
this.$success(this.$t('api_test.running'));
this.$router.push({
path: '/api/report/view/' + response.data
@ -306,20 +300,35 @@
cancel() {
this.$router.push('/api/test/list/all');
},
createPerformance() {
let validator = this.test.isValid();
if (!validator.isValid) {
this.$warning(this.$t(validator.info));
return;
}
this.result = this.$post("/api/jmx", this.test, response => {
let jmx = {
name: this.test.name + '.jmx',
xml: response.data
};
this.$store.commit('setTest', {
projectId: this.test.projectId,
name: this.test.name,
jmx: jmx
})
this.$router.push({
path: "/performance/test/create"
})
});
},
handleCommand(command) {
switch (command) {
case "report":
this.$refs.reportDialog.open();
break;
case "performance":
this.$store.commit('setTest', {
projectId: this.test.projectId,
name: this.test.name,
jmx: this.test.toJMX()
})
this.$router.push({
path: "/performance/test/create"
})
this.createPerformance();
break;
case "export":
downloadFile(this.test.name + ".json", this.test.export());
@ -376,10 +385,7 @@
return;
}
let jmx = runningTest.toJMX();
let blob = new Blob([jmx.xml], {type: "application/octet-stream"});
let file = new File([blob], jmx.name);
this.$fileUpload(url, file, bodyFiles, this.test, response => {
this.$fileUpload(url, null, bodyFiles, this.test, response => {
this.debugReportId = response.data;
this.resetBodyFile();
});

View File

@ -26,7 +26,6 @@
import MsApiScenarioConfig from "./components/ApiScenarioConfig";
import MsApiReportStatus from "../report/ApiReportStatus";
import MsApiReportDialog from "./ApiReportDialog";
import {getUUID} from "@/common/js/utils";
import {parseEnvironment} from "./model/EnvironmentModel";
@ -181,9 +180,6 @@
type: "application/json"
}));
let jmx = this.test.toJMX();
let blob = new Blob([jmx.xml], {type: "application/octet-stream"});
formData.append("file", new File([blob], jmx.name));
return {
method: 'POST',
url: url,

View File

@ -201,12 +201,12 @@ export class Test extends BaseConfig {
return {isValid: true};
}
toJMX() {
return {
name: this.name + '.jmx',
xml: new JMXGenerator(this).toXML()
};
}
// toJMX() {
// return {
// name: this.name + '.jmx',
// xml: new JMXGenerator(this).toXML()
// };
// }
}
export class Scenario extends BaseConfig {
@ -973,7 +973,7 @@ export class ConstantTimer extends Timer {
}
}
/** ------------------------------------------------------------------------ **/
/** ------------------------------------------------------------------------
const JMX_ASSERTION_CONDITION = {
MATCH: 1,
CONTAINS: 1 << 1,
@ -1188,7 +1188,7 @@ class JMXGenerator {
envArray = JSON.parse(environments);
}
envArray.forEach(item => {
if (item.enable != false && item.name && !keys.has(item.name)) {
if (item.enable !== false && item.name && !keys.has(item.name)) {
target.push(new KeyValue({name: item.name, value: item.value}));
}
})
@ -1373,8 +1373,7 @@ class JMXGenerator {
let hasContentType = false;
for (let index in request.headers) {
if (request.headers.hasOwnProperty(index)) {
if (request.headers[index].name === 'Content-Type' && request.headers[index].enable != false) {
request.headers.splice(index, 1);
if (request.headers[index].name === 'Content-Type' && request.headers[index].enable !== false) {
hasContentType = true;
break;
}
@ -1462,7 +1461,7 @@ class JMXGenerator {
getResponseAssertion(regex) {
let name = regex.description;
let type = JMX_ASSERTION_CONDITION.CONTAINS; // 固定用Match自己写正则
let type = JMX_ASSERTION_CONDITION.CONTAINS;
let value = regex.expression;
let assumeSuccess = regex.assumeSuccess;
switch (regex.subject) {
@ -1539,4 +1538,4 @@ class JMXGenerator {
}
}
**/