Merge branch 'v1.8'
This commit is contained in:
commit
2113db181b
|
@ -139,7 +139,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springdoc</groupId>
|
<groupId>org.springdoc</groupId>
|
||||||
<artifactId>springdoc-openapi-ui</artifactId>
|
<artifactId>springdoc-openapi-ui</artifactId>
|
||||||
<version>1.2.32</version>
|
<version>1.5.6</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- jmeter -->
|
<!-- jmeter -->
|
||||||
|
@ -280,7 +280,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.swagger.parser.v3</groupId>
|
<groupId>io.swagger.parser.v3</groupId>
|
||||||
<artifactId>swagger-parser</artifactId>
|
<artifactId>swagger-parser</artifactId>
|
||||||
<version>2.0.22</version>
|
<version>2.0.24</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- ApacheJMeter_core scope 是 runtime,这里去掉scope -->
|
<!-- ApacheJMeter_core scope 是 runtime,这里去掉scope -->
|
||||||
|
|
|
@ -12,8 +12,7 @@ import io.metersphere.api.dto.datacount.response.TaskInfoResult;
|
||||||
import io.metersphere.api.dto.definition.RunDefinitionRequest;
|
import io.metersphere.api.dto.definition.RunDefinitionRequest;
|
||||||
import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter;
|
import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter;
|
||||||
import io.metersphere.api.service.*;
|
import io.metersphere.api.service.*;
|
||||||
import io.metersphere.base.domain.ApiTest;
|
import io.metersphere.base.domain.*;
|
||||||
import io.metersphere.base.domain.Schedule;
|
|
||||||
import io.metersphere.commons.constants.RoleConstants;
|
import io.metersphere.commons.constants.RoleConstants;
|
||||||
import io.metersphere.commons.constants.ScheduleGroup;
|
import io.metersphere.commons.constants.ScheduleGroup;
|
||||||
import io.metersphere.commons.utils.CronUtils;
|
import io.metersphere.commons.utils.CronUtils;
|
||||||
|
@ -30,13 +29,15 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.jorphan.collections.HashTree;
|
import org.apache.jorphan.collections.HashTree;
|
||||||
import org.apache.shiro.authz.annotation.Logical;
|
import org.apache.shiro.authz.annotation.Logical;
|
||||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||||
import org.python.core.AstList;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static io.metersphere.commons.utils.JsonPathUtils.getListJson;
|
import static io.metersphere.commons.utils.JsonPathUtils.getListJson;
|
||||||
|
|
||||||
|
@ -251,9 +252,7 @@ public class APITestController {
|
||||||
|
|
||||||
@GetMapping("/testSceneInfoCount/{projectId}")
|
@GetMapping("/testSceneInfoCount/{projectId}")
|
||||||
public ApiDataCountDTO testSceneInfoCount(@PathVariable String projectId) {
|
public ApiDataCountDTO testSceneInfoCount(@PathVariable String projectId) {
|
||||||
|
|
||||||
ApiDataCountDTO apiCountResult = new ApiDataCountDTO();
|
ApiDataCountDTO apiCountResult = new ApiDataCountDTO();
|
||||||
|
|
||||||
long scenarioCountNumber = apiAutomationService.countScenarioByProjectID(projectId);
|
long scenarioCountNumber = apiAutomationService.countScenarioByProjectID(projectId);
|
||||||
apiCountResult.setAllApiDataCountNumber(scenarioCountNumber);
|
apiCountResult.setAllApiDataCountNumber(scenarioCountNumber);
|
||||||
|
|
||||||
|
@ -272,17 +271,46 @@ public class APITestController {
|
||||||
//未执行、未通过、已通过
|
//未执行、未通过、已通过
|
||||||
List<ApiDataCountResult> countResultByRunResult = apiAutomationService.countRunResultByProjectID(projectId);
|
List<ApiDataCountResult> countResultByRunResult = apiAutomationService.countRunResultByProjectID(projectId);
|
||||||
apiCountResult.countRunResult(countResultByRunResult);
|
apiCountResult.countRunResult(countResultByRunResult);
|
||||||
|
|
||||||
long allCount = apiCountResult.getUnexecuteCount() + apiCountResult.getExecutionPassCount() + apiCountResult.getExecutionFailedCount();
|
long allCount = apiCountResult.getUnexecuteCount() + apiCountResult.getExecutionPassCount() + apiCountResult.getExecutionFailedCount();
|
||||||
|
|
||||||
if (allCount != 0) {
|
|
||||||
float coverageRageNumber = (float) apiCountResult.getExecutionPassCount() * 100 / allCount;
|
|
||||||
DecimalFormat df = new DecimalFormat("0.0");
|
DecimalFormat df = new DecimalFormat("0.0");
|
||||||
|
if (allCount != 0) {
|
||||||
|
//通过率
|
||||||
|
float coverageRageNumber = (float) apiCountResult.getExecutionPassCount() * 100 / allCount;
|
||||||
apiCountResult.setPassRage(df.format(coverageRageNumber) + "%");
|
apiCountResult.setPassRage(df.format(coverageRageNumber) + "%");
|
||||||
}
|
}
|
||||||
|
|
||||||
return apiCountResult;
|
return apiCountResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/countInterfaceCoverage/{projectId}")
|
||||||
|
public String countInterfaceCoverage(@PathVariable String projectId) {
|
||||||
|
String returnStr = "100%";
|
||||||
|
/**
|
||||||
|
* 接口覆盖率
|
||||||
|
* 复制的接口定义/复制或引用的单接口用例/ 添加的自定义请求 url 路径与现有的接口定义一致的请求
|
||||||
|
*/
|
||||||
|
long startTime1 = System.currentTimeMillis();
|
||||||
|
List<ApiScenarioWithBLOBs> allScenarioInfoList = apiAutomationService.selectIdAndScenarioByProjectId(projectId);
|
||||||
|
long startTime2 = System.currentTimeMillis();
|
||||||
|
System.out.println("Search data time : " + (startTime2 - startTime1));
|
||||||
|
startTime1 = System.currentTimeMillis();
|
||||||
|
List<ApiDefinition> allEffectiveApiIdList = apiDefinitionService.selectEffectiveIdByProjectId(projectId);
|
||||||
|
startTime2 = System.currentTimeMillis();
|
||||||
|
System.out.println("Search data time (api info) : " + (startTime2 - startTime1));
|
||||||
|
List<ApiTestCase> allEffectiveApiCaseList = apiTestCaseService.selectEffectiveTestCaseByProjectId(projectId);
|
||||||
|
long startTime3 = System.currentTimeMillis();
|
||||||
|
System.out.println("Search data time (case info): " + (startTime3 - startTime2));
|
||||||
|
|
||||||
|
try {
|
||||||
|
startTime1 = System.currentTimeMillis();
|
||||||
|
float intetfaceCoverageRageNumber = apiAutomationService.countInterfaceCoverage(allScenarioInfoList, allEffectiveApiIdList, allEffectiveApiCaseList);
|
||||||
|
startTime2 = System.currentTimeMillis();
|
||||||
|
System.out.println("Count data time : " + (startTime2 - startTime1));
|
||||||
|
DecimalFormat df = new DecimalFormat("0.0");
|
||||||
|
returnStr = df.format(intetfaceCoverageRageNumber) + "%";
|
||||||
|
}catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return returnStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/scheduleTaskInfoCount/{projectId}")
|
@GetMapping("/scheduleTaskInfoCount/{projectId}")
|
||||||
|
@ -346,11 +374,11 @@ public class APITestController {
|
||||||
@GetMapping("/runningTask/{projectID}/{callFrom}")
|
@GetMapping("/runningTask/{projectID}/{callFrom}")
|
||||||
public List<TaskInfoResult> runningTask(@PathVariable String projectID, @PathVariable String callFrom) {
|
public List<TaskInfoResult> runningTask(@PathVariable String projectID, @PathVariable String callFrom) {
|
||||||
List<String> typeFilter = new ArrayList<>();
|
List<String> typeFilter = new ArrayList<>();
|
||||||
if(StringUtils.equals(callFrom, "api_test")) { // 接口测试首页显示的运行中定时任务,只要这3种,不需要 性能测试、api_test(旧版)
|
if (StringUtils.equals(callFrom, "api_test")) { // 接口测试首页显示的运行中定时任务,只要这3种,不需要 性能测试、api_test(旧版)
|
||||||
typeFilter.add(ScheduleGroup.API_SCENARIO_TEST.name());
|
typeFilter.add(ScheduleGroup.API_SCENARIO_TEST.name());
|
||||||
typeFilter.add(ScheduleGroup.SWAGGER_IMPORT.name());
|
typeFilter.add(ScheduleGroup.SWAGGER_IMPORT.name());
|
||||||
typeFilter.add(ScheduleGroup.TEST_PLAN_TEST.name());
|
typeFilter.add(ScheduleGroup.TEST_PLAN_TEST.name());
|
||||||
} else if(StringUtils.equals(callFrom, "track_home")) { // 测试跟踪首页只显示测试计划的定时任务
|
} else if (StringUtils.equals(callFrom, "track_home")) { // 测试跟踪首页只显示测试计划的定时任务
|
||||||
typeFilter.add(ScheduleGroup.TEST_PLAN_TEST.name());
|
typeFilter.add(ScheduleGroup.TEST_PLAN_TEST.name());
|
||||||
}
|
}
|
||||||
List<TaskInfoResult> resultList = scheduleService.findRunningTaskInfoByProjectID(projectID, typeFilter);
|
List<TaskInfoResult> resultList = scheduleService.findRunningTaskInfoByProjectID(projectID, typeFilter);
|
||||||
|
@ -386,7 +414,7 @@ public class APITestController {
|
||||||
String testName = runRequest.getName();
|
String testName = runRequest.getName();
|
||||||
|
|
||||||
//将jmx处理封装为通用方法
|
//将jmx处理封装为通用方法
|
||||||
JmxInfoDTO dto = apiTestService.updateJmxString(jmxString,testName,false);
|
JmxInfoDTO dto = apiTestService.updateJmxString(jmxString, testName, false);
|
||||||
dto.setName(runRequest.getName() + ".jmx");
|
dto.setName(runRequest.getName() + ".jmx");
|
||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,11 @@ public class ApiDataCountDTO {
|
||||||
*/
|
*/
|
||||||
private String successRage = " 0%";
|
private String successRage = " 0%";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接口覆盖率
|
||||||
|
*/
|
||||||
|
private String interfaceCoverage = " 0%";
|
||||||
|
|
||||||
public ApiDataCountDTO(){}
|
public ApiDataCountDTO(){}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -320,6 +320,9 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object parseSchema(Schema schema, Set<String> refSet, Map<String, Schema> infoMap) {
|
private Object parseSchema(Schema schema, Set<String> refSet, Map<String, Schema> infoMap) {
|
||||||
|
if (schema == null) {
|
||||||
|
return new JSONObject();
|
||||||
|
}
|
||||||
infoMap.put(schema.getName(), schema);
|
infoMap.put(schema.getName(), schema);
|
||||||
if (StringUtils.isNotBlank(schema.get$ref())) {
|
if (StringUtils.isNotBlank(schema.get$ref())) {
|
||||||
if (refSet.contains(schema.get$ref())) {
|
if (refSet.contains(schema.get$ref())) {
|
||||||
|
|
|
@ -160,14 +160,16 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
||||||
sampler.setProtocol(urlObject.getProtocol());
|
sampler.setProtocol(urlObject.getProtocol());
|
||||||
sampler.setPath(urlObject.getPath());
|
sampler.setPath(urlObject.getPath());
|
||||||
} else {
|
} else {
|
||||||
String configStr = config.getConfig().get(this.getProjectId()).getHttpConfig().getSocket();
|
|
||||||
sampler.setDomain(configStr);
|
|
||||||
if (config.getConfig().get(this.getProjectId()).getHttpConfig().getPort() > 0) {
|
|
||||||
sampler.setDomain(config.getConfig().get(this.getProjectId()).getHttpConfig().getDomain());
|
sampler.setDomain(config.getConfig().get(this.getProjectId()).getHttpConfig().getDomain());
|
||||||
|
url = config.getConfig().get(this.getProjectId()).getHttpConfig().getProtocol() + "://" + config.getConfig().get(this.getProjectId()).getHttpConfig().getSocket();
|
||||||
|
URL urlObject = new URL(url);
|
||||||
|
String envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getPath();
|
||||||
|
if (StringUtils.isNotBlank(this.getPath())) {
|
||||||
|
envPath += this.getPath();
|
||||||
}
|
}
|
||||||
sampler.setPort(config.getConfig().get(this.getProjectId()).getHttpConfig().getPort());
|
sampler.setPort(config.getConfig().get(this.getProjectId()).getHttpConfig().getPort());
|
||||||
sampler.setProtocol(config.getConfig().get(this.getProjectId()).getHttpConfig().getProtocol());
|
sampler.setProtocol(config.getConfig().get(this.getProjectId()).getHttpConfig().getProtocol());
|
||||||
sampler.setPath(this.getPath());
|
sampler.setPath(envPath);
|
||||||
}
|
}
|
||||||
String envPath = sampler.getPath();
|
String envPath = sampler.getPath();
|
||||||
if (CollectionUtils.isNotEmpty(this.getRest()) && this.isRest()) {
|
if (CollectionUtils.isNotEmpty(this.getRest()) && this.isRest()) {
|
||||||
|
@ -187,7 +189,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String url = this.getUrl();
|
String url = this.getUrl();
|
||||||
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
if (StringUtils.isNotEmpty(url) && !url.startsWith("http://") && !url.startsWith("https://")) {
|
||||||
url = "http://" + url;
|
url = "http://" + url;
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
|
if (StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package io.metersphere.api.service;
|
package io.metersphere.api.service;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.alibaba.fastjson.JSONPath;
|
import com.alibaba.fastjson.JSONPath;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
@ -29,6 +30,7 @@ import io.metersphere.commons.utils.*;
|
||||||
import io.metersphere.controller.request.ScheduleRequest;
|
import io.metersphere.controller.request.ScheduleRequest;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import io.metersphere.job.sechedule.ApiScenarioTestJob;
|
import io.metersphere.job.sechedule.ApiScenarioTestJob;
|
||||||
|
import io.metersphere.job.sechedule.SwaggerUrlImportJob;
|
||||||
import io.metersphere.job.sechedule.TestPlanTestJob;
|
import io.metersphere.job.sechedule.TestPlanTestJob;
|
||||||
import io.metersphere.service.ScheduleService;
|
import io.metersphere.service.ScheduleService;
|
||||||
import io.metersphere.track.dto.TestPlanDTO;
|
import io.metersphere.track.dto.TestPlanDTO;
|
||||||
|
@ -230,6 +232,7 @@ public class ApiAutomationService {
|
||||||
scenario.setPrincipal(request.getPrincipal());
|
scenario.setPrincipal(request.getPrincipal());
|
||||||
scenario.setStepTotal(request.getStepTotal());
|
scenario.setStepTotal(request.getStepTotal());
|
||||||
scenario.setUpdateTime(System.currentTimeMillis());
|
scenario.setUpdateTime(System.currentTimeMillis());
|
||||||
|
scenario.setDescription(request.getDescription());
|
||||||
scenario.setScenarioDefinition(JSON.toJSONString(request.getScenarioDefinition()));
|
scenario.setScenarioDefinition(JSON.toJSONString(request.getScenarioDefinition()));
|
||||||
if (StringUtils.isNotEmpty(request.getStatus())) {
|
if (StringUtils.isNotEmpty(request.getStatus())) {
|
||||||
scenario.setStatus(request.getStatus());
|
scenario.setStatus(request.getStatus());
|
||||||
|
@ -710,6 +713,10 @@ public class ApiAutomationService {
|
||||||
return extApiScenarioMapper.countByProjectID(projectId);
|
return extApiScenarioMapper.countByProjectID(projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ApiScenarioWithBLOBs> selectIdAndScenarioByProjectId(String projectId) {
|
||||||
|
return extApiScenarioMapper.selectIdAndScenarioByProjectId(projectId);
|
||||||
|
}
|
||||||
|
|
||||||
public long countScenarioByProjectIDAndCreatInThisWeek(String projectId) {
|
public long countScenarioByProjectIDAndCreatInThisWeek(String projectId) {
|
||||||
Map<String, Date> startAndEndDateInWeek = DateUtils.getWeedFirstTimeAndLastTime(new Date());
|
Map<String, Date> startAndEndDateInWeek = DateUtils.getWeedFirstTimeAndLastTime(new Date());
|
||||||
Date firstTime = startAndEndDateInWeek.get("firstTime");
|
Date firstTime = startAndEndDateInWeek.get("firstTime");
|
||||||
|
@ -811,7 +818,9 @@ public class ApiAutomationService {
|
||||||
if (StringUtils.equals(request.getGroup(), ScheduleGroup.TEST_PLAN_TEST.name())) {
|
if (StringUtils.equals(request.getGroup(), ScheduleGroup.TEST_PLAN_TEST.name())) {
|
||||||
scheduleService.addOrUpdateCronJob(
|
scheduleService.addOrUpdateCronJob(
|
||||||
request, TestPlanTestJob.getJobKey(request.getResourceId()), TestPlanTestJob.getTriggerKey(request.getResourceId()), TestPlanTestJob.class);
|
request, TestPlanTestJob.getJobKey(request.getResourceId()), TestPlanTestJob.getTriggerKey(request.getResourceId()), TestPlanTestJob.class);
|
||||||
} else {
|
}else if(StringUtils.equals(request.getGroup(), ScheduleGroup.SWAGGER_IMPORT.name())){
|
||||||
|
scheduleService.addOrUpdateCronJob(request, SwaggerUrlImportJob.getJobKey(request.getResourceId()), SwaggerUrlImportJob.getTriggerKey(request.getResourceId()), SwaggerUrlImportJob.class);
|
||||||
|
} else{
|
||||||
scheduleService.addOrUpdateCronJob(
|
scheduleService.addOrUpdateCronJob(
|
||||||
request, ApiScenarioTestJob.getJobKey(request.getResourceId()), ApiScenarioTestJob.getTriggerKey(request.getResourceId()), ApiScenarioTestJob.class);
|
request, ApiScenarioTestJob.getJobKey(request.getResourceId()), ApiScenarioTestJob.getTriggerKey(request.getResourceId()), ApiScenarioTestJob.class);
|
||||||
}
|
}
|
||||||
|
@ -1031,4 +1040,131 @@ public class ApiAutomationService {
|
||||||
|
|
||||||
this.deleteBatch(request.getIds());
|
this.deleteBatch(request.getIds());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计接口覆盖率
|
||||||
|
* 1.场景中复制的接口
|
||||||
|
* 2.场景中引用/复制的案例
|
||||||
|
* 3.场景中的自定义路径与接口定义中的匹配
|
||||||
|
*
|
||||||
|
* @param allScenarioInfoList 场景集合(id / scenario大字段 必须有数据)
|
||||||
|
* @param allEffectiveApiList 接口集合(id / path 必须有数据)
|
||||||
|
* @param allEffectiveApiCaseList 案例集合(id /api_definition_id 必须有数据)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public float countInterfaceCoverage(List<ApiScenarioWithBLOBs> allScenarioInfoList, List<ApiDefinition> allEffectiveApiList, List<ApiTestCase> allEffectiveApiCaseList) {
|
||||||
|
// float coverageRageNumber = (float) apiCountResult.getExecutionPassCount() * 100 / allCount;
|
||||||
|
float intetfaceCoverage = 0;
|
||||||
|
if (allEffectiveApiList == null || allEffectiveApiList.isEmpty()) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 前置工作:
|
||||||
|
* 1。将接口集合转化数据结构: map<url,List<id>> urlMap 用来做3的筛选
|
||||||
|
* 2。将案例集合转化数据结构:map<testCase.id,List<testCase.apiId>> caseIdMap 用来做2的筛选
|
||||||
|
* 3。将接口集合转化数据结构: List<id> allApiIdList 用来做1的筛选
|
||||||
|
* 4。自定义List<api.id> coveragedIdList 已覆盖的id集合。 最终计算公式是 coveragedIdList/allApiIdList
|
||||||
|
*
|
||||||
|
* 解析allScenarioList的scenarioDefinition字段。
|
||||||
|
* 1。提取每个步骤的url。 在 urlMap筛选
|
||||||
|
* 2。提取每个步骤的id. 在caseIdMap 和 allApiIdList。
|
||||||
|
*/
|
||||||
|
Map<String, List<String>> urlMap = new HashMap<>();
|
||||||
|
List<String> allApiIdList = new ArrayList<>();
|
||||||
|
Map<String,List<String>> caseIdMap = new HashMap<>();
|
||||||
|
for (ApiDefinition model : allEffectiveApiList) {
|
||||||
|
String url = model.getPath();
|
||||||
|
String id = model.getId();
|
||||||
|
allApiIdList.add(id);
|
||||||
|
if (urlMap.containsKey(url)) {
|
||||||
|
urlMap.get(url).add(id);
|
||||||
|
} else {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
list.add(id);
|
||||||
|
urlMap.put(url, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (ApiTestCase model : allEffectiveApiCaseList){
|
||||||
|
String caseId = model.getId();
|
||||||
|
String apiId = model.getApiDefinitionId();
|
||||||
|
if (urlMap.containsKey(caseId)) {
|
||||||
|
urlMap.get(caseId).add(apiId);
|
||||||
|
} else {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
list.add(apiId);
|
||||||
|
urlMap.put(caseId, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allApiIdList.isEmpty()) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> urlList = new ArrayList<>();
|
||||||
|
List<String> idList= new ArrayList<>();
|
||||||
|
|
||||||
|
for (ApiScenarioWithBLOBs model : allScenarioInfoList) {
|
||||||
|
String scenarioDefiniton = model.getScenarioDefinition();
|
||||||
|
this.addUrlAndIdToList(scenarioDefiniton,urlList,idList);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> containsApiIdList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (String url : urlList) {
|
||||||
|
List<String> apiIdList = urlMap.get(url);
|
||||||
|
if(apiIdList!=null ){
|
||||||
|
for (String api : apiIdList) {
|
||||||
|
if(!containsApiIdList.contains(api)){
|
||||||
|
containsApiIdList.add(api);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String id : idList) {
|
||||||
|
List<String> apiIdList = caseIdMap.get(id);
|
||||||
|
if(apiIdList!=null ){
|
||||||
|
for (String api : apiIdList) {
|
||||||
|
if(!containsApiIdList.contains(api)){
|
||||||
|
containsApiIdList.add(api);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allApiIdList.contains(id)){
|
||||||
|
if(!containsApiIdList.contains(id)){
|
||||||
|
containsApiIdList.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float coverageRageNumber = (float) containsApiIdList.size() * 100 / allApiIdList.size();
|
||||||
|
return coverageRageNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addUrlAndIdToList(String scenarioDefiniton, List<String> urlList, List<String> idList) {
|
||||||
|
try {
|
||||||
|
JSONObject scenarioObj = JSONObject.parseObject(scenarioDefiniton);
|
||||||
|
if(scenarioObj.containsKey("hashTree")){
|
||||||
|
JSONArray hashArr = scenarioObj.getJSONArray("hashTree");
|
||||||
|
for (int i = 0; i < hashArr.size(); i++) {
|
||||||
|
JSONObject elementObj = hashArr.getJSONObject(i);
|
||||||
|
if(elementObj.containsKey("id")){
|
||||||
|
String id = elementObj.getString("id");
|
||||||
|
idList.add(id);
|
||||||
|
}
|
||||||
|
if(elementObj.containsKey("url")){
|
||||||
|
String url = elementObj.getString("url");
|
||||||
|
urlList.add(url);
|
||||||
|
}
|
||||||
|
if(elementObj.containsKey("path")){
|
||||||
|
String path = elementObj.getString("path");
|
||||||
|
urlList.add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch (Exception e){
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -912,4 +912,8 @@ public class ApiDefinitionService {
|
||||||
}
|
}
|
||||||
return apiExportResult;
|
return apiExportResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ApiDefinition> selectEffectiveIdByProjectId(String projectId) {
|
||||||
|
return extApiDefinitionMapper.selectEffectiveIdByProjectId(projectId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -648,4 +648,8 @@ public class ApiTestCaseService {
|
||||||
public ApiDefinitionExecResult getInfo(String id){
|
public ApiDefinitionExecResult getInfo(String id){
|
||||||
return apiDefinitionExecResultMapper.selectByPrimaryKey(id);
|
return apiDefinitionExecResultMapper.selectByPrimaryKey(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ApiTestCase> selectEffectiveTestCaseByProjectId(String projectId) {
|
||||||
|
return extApiTestCaseMapper.selectEffectiveTestCaseByProjectId(projectId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,4 +38,6 @@ public interface ExtApiDefinitionMapper {
|
||||||
List<ApiDefinitionResult> listRelevance(@Param("request")ApiDefinitionRequest request);
|
List<ApiDefinitionResult> listRelevance(@Param("request")ApiDefinitionRequest request);
|
||||||
List<ApiDefinitionResult> listRelevanceReview(@Param("request")ApiDefinitionRequest request);
|
List<ApiDefinitionResult> listRelevanceReview(@Param("request")ApiDefinitionRequest request);
|
||||||
List<String> selectIds(@Param("request") BaseQueryRequest query);
|
List<String> selectIds(@Param("request") BaseQueryRequest query);
|
||||||
|
|
||||||
|
List<ApiDefinition> selectEffectiveIdByProjectId(String projectId);
|
||||||
}
|
}
|
|
@ -465,6 +465,11 @@
|
||||||
<include refid="queryWhereCondition"/>
|
<include refid="queryWhereCondition"/>
|
||||||
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.orders"/>
|
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.orders"/>
|
||||||
</select>
|
</select>
|
||||||
|
<select id="selectEffectiveIdByProjectId" resultType="io.metersphere.base.domain.ApiDefinition">
|
||||||
|
select id,path
|
||||||
|
from api_definition
|
||||||
|
WHERE project_id = #{0} AND status != 'Trash'
|
||||||
|
</select>
|
||||||
|
|
||||||
<sql id="queryWhereCondition">
|
<sql id="queryWhereCondition">
|
||||||
<where>
|
<where>
|
||||||
|
|
|
@ -6,7 +6,6 @@ import io.metersphere.api.dto.datacount.ApiDataCountResult;
|
||||||
import io.metersphere.base.domain.ApiScenario;
|
import io.metersphere.base.domain.ApiScenario;
|
||||||
import io.metersphere.base.domain.ApiScenarioExample;
|
import io.metersphere.base.domain.ApiScenarioExample;
|
||||||
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
|
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
|
||||||
import io.metersphere.controller.request.BaseQueryRequest;
|
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -28,6 +27,8 @@ public interface ExtApiScenarioMapper {
|
||||||
|
|
||||||
long countByProjectID(String projectId);
|
long countByProjectID(String projectId);
|
||||||
|
|
||||||
|
List<ApiScenarioWithBLOBs> selectIdAndScenarioByProjectId(String projectId);
|
||||||
|
|
||||||
long countByProjectIDAndCreatInThisWeek(@Param("projectId") String projectId, @Param("firstDayTimestamp") long firstDayTimestamp, @Param("lastDayTimestamp") long lastDayTimestamp);
|
long countByProjectIDAndCreatInThisWeek(@Param("projectId") String projectId, @Param("firstDayTimestamp") long firstDayTimestamp, @Param("lastDayTimestamp") long lastDayTimestamp);
|
||||||
|
|
||||||
List<ApiDataCountResult> countRunResultByProjectID(String projectId);
|
List<ApiDataCountResult> countRunResultByProjectID(String projectId);
|
||||||
|
|
|
@ -304,6 +304,9 @@
|
||||||
<select id="countByProjectID" resultType="java.lang.Long">
|
<select id="countByProjectID" resultType="java.lang.Long">
|
||||||
SELECT COUNT(id) AS countNumber FROM api_scenario WHERE project_id = #{0} AND status != 'Trash'
|
SELECT COUNT(id) AS countNumber FROM api_scenario WHERE project_id = #{0} AND status != 'Trash'
|
||||||
</select>
|
</select>
|
||||||
|
<select id="selectIdAndScenarioByProjectId" resultType="io.metersphere.base.domain.ApiScenarioWithBLOBs">
|
||||||
|
SELECT id,scenario_definition FROM api_scenario WHERE project_id = #{0} AND status != 'Trash'
|
||||||
|
</select>
|
||||||
<select id="countByProjectIDAndCreatInThisWeek" resultType="java.lang.Long">
|
<select id="countByProjectIDAndCreatInThisWeek" resultType="java.lang.Long">
|
||||||
SELECT count(id) AS countNumber FROM api_scenario
|
SELECT count(id) AS countNumber FROM api_scenario
|
||||||
WHERE project_id = #{projectId} AND status != 'Trash'
|
WHERE project_id = #{projectId} AND status != 'Trash'
|
||||||
|
|
|
@ -31,4 +31,6 @@ public interface ExtApiTestCaseMapper {
|
||||||
ApiTestCase getNextNum(@Param("definitionId") String definitionId);
|
ApiTestCase getNextNum(@Param("definitionId") String definitionId);
|
||||||
|
|
||||||
ApiTestCaseInfo selectApiCaseInfoByPrimaryKey(String id);
|
ApiTestCaseInfo selectApiCaseInfoByPrimaryKey(String id);
|
||||||
|
|
||||||
|
List<ApiTestCase> selectEffectiveTestCaseByProjectId(String projectId);
|
||||||
}
|
}
|
|
@ -394,5 +394,8 @@
|
||||||
<select id="listPorjectAllCaseName" resultType="java.lang.String">
|
<select id="listPorjectAllCaseName" resultType="java.lang.String">
|
||||||
select name from api_test_case where project_id = #{projectId}
|
select name from api_test_case where project_id = #{projectId}
|
||||||
</select>
|
</select>
|
||||||
|
<select id="selectEffectiveTestCaseByProjectId" resultType="io.metersphere.base.domain.ApiTestCase">
|
||||||
|
select id,api_definition_id from api_test_case where project_id = #{projectId}
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
|
@ -11,6 +11,7 @@ public class KafkaProperties {
|
||||||
public static final String KAFKA_PREFIX = "kafka";
|
public static final String KAFKA_PREFIX = "kafka";
|
||||||
|
|
||||||
private String acks = "0"; // 不要设置all
|
private String acks = "0"; // 不要设置all
|
||||||
|
private String expectedDelayEndTime = "30000"; // 30s
|
||||||
private String topic;
|
private String topic;
|
||||||
private String fields;
|
private String fields;
|
||||||
private String timestamp;
|
private String timestamp;
|
||||||
|
|
|
@ -67,6 +67,16 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//增加字数校验,每一层不能超过100字
|
||||||
|
for (int i = 0; i < nodes.length; i++) {
|
||||||
|
String nodeStr = nodes[i];
|
||||||
|
if(StringUtils.isNotEmpty(nodeStr)){
|
||||||
|
if(nodeStr.trim().length()>100){
|
||||||
|
stringBuilder.append(Translator.get("module") + Translator.get("test_track.length_less_than") + "100:"+nodeStr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (StringUtils.equals(data.getType(), TestCaseConstants.Type.Functional.getValue()) && StringUtils.equals(data.getMethod(), TestCaseConstants.Method.Auto.getValue())) {
|
// if (StringUtils.equals(data.getType(), TestCaseConstants.Type.Functional.getValue()) && StringUtils.equals(data.getMethod(), TestCaseConstants.Method.Auto.getValue())) {
|
||||||
|
|
|
@ -25,8 +25,9 @@ public class JmeterFileController {
|
||||||
|
|
||||||
@GetMapping("download")
|
@GetMapping("download")
|
||||||
public ResponseEntity<byte[]> downloadJmeterFiles(@RequestParam("testId") String testId, @RequestParam("resourceId") String resourceId,
|
public ResponseEntity<byte[]> downloadJmeterFiles(@RequestParam("testId") String testId, @RequestParam("resourceId") String resourceId,
|
||||||
@RequestParam("ratio") double ratio, @RequestParam("startTime") long startTime,
|
@RequestParam("ratio") double ratio,
|
||||||
@RequestParam("reportId") String reportId, @RequestParam("resourceIndex") int resourceIndex) {
|
@RequestParam("reportId") String reportId, @RequestParam("resourceIndex") int resourceIndex) {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
byte[] bytes = jmeterFileService.downloadZip(testId, resourceId, ratio, startTime, reportId, resourceIndex);
|
byte[] bytes = jmeterFileService.downloadZip(testId, resourceId, ratio, startTime, reportId, resourceIndex);
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
||||||
|
|
|
@ -29,6 +29,7 @@ public class JmeterDocumentParser implements DocumentParser {
|
||||||
private final static String HASH_TREE_ELEMENT = "hashTree";
|
private final static String HASH_TREE_ELEMENT = "hashTree";
|
||||||
private final static String TEST_PLAN = "TestPlan";
|
private final static String TEST_PLAN = "TestPlan";
|
||||||
private final static String STRING_PROP = "stringProp";
|
private final static String STRING_PROP = "stringProp";
|
||||||
|
private final static String BOOL_PROP = "boolProp";
|
||||||
private final static String COLLECTION_PROP = "collectionProp";
|
private final static String COLLECTION_PROP = "collectionProp";
|
||||||
private final static String CONCURRENCY_THREAD_GROUP = "com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup";
|
private final static String CONCURRENCY_THREAD_GROUP = "com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup";
|
||||||
private final static String VARIABLE_THROUGHPUT_TIMER = "kg.apc.jmeter.timers.VariableThroughputTimer";
|
private final static String VARIABLE_THROUGHPUT_TIMER = "kg.apc.jmeter.timers.VariableThroughputTimer";
|
||||||
|
@ -94,6 +95,7 @@ public class JmeterDocumentParser implements DocumentParser {
|
||||||
processCheckoutDnsCacheManager(ele);
|
processCheckoutDnsCacheManager(ele);
|
||||||
processCheckoutArguments(ele);
|
processCheckoutArguments(ele);
|
||||||
processCheckoutResponseAssertion(ele);
|
processCheckoutResponseAssertion(ele);
|
||||||
|
processCheckoutSerializeThreadgroups(ele);
|
||||||
} else if (nodeNameEquals(ele, CONCURRENCY_THREAD_GROUP)) {
|
} else if (nodeNameEquals(ele, CONCURRENCY_THREAD_GROUP)) {
|
||||||
processThreadGroupName(ele);
|
processThreadGroupName(ele);
|
||||||
processCheckoutTimer(ele);
|
processCheckoutTimer(ele);
|
||||||
|
@ -141,6 +143,21 @@ public class JmeterDocumentParser implements DocumentParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processCheckoutSerializeThreadgroups(Element element) {
|
||||||
|
NodeList childNodes = element.getChildNodes();
|
||||||
|
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||||
|
Node item = childNodes.item(i);
|
||||||
|
if (nodeNameEquals(item, BOOL_PROP)) {
|
||||||
|
String serializeName = ((Element) item).getAttribute("name");
|
||||||
|
if (StringUtils.equals(serializeName, "TestPlan.serialize_threadgroups")) {
|
||||||
|
// 保存线程组是否是顺序执行
|
||||||
|
context.addProperty("serialize_threadgroups", item.getTextContent());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void processArgumentFiles(Element element) {
|
private void processArgumentFiles(Element element) {
|
||||||
NodeList childNodes = element.getChildNodes();
|
NodeList childNodes = element.getChildNodes();
|
||||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||||
|
@ -479,7 +496,7 @@ public class JmeterDocumentParser implements DocumentParser {
|
||||||
item.appendChild(elementProp);
|
item.appendChild(elementProp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item instanceof Element && nodeNameEquals(item, "boolProp")
|
if (item instanceof Element && nodeNameEquals(item, BOOL_PROP)
|
||||||
&& org.apache.commons.lang3.StringUtils.equals(((Element) item).getAttribute("name"), "DNSCacheManager.isCustomResolver")) {
|
&& org.apache.commons.lang3.StringUtils.equals(((Element) item).getAttribute("name"), "DNSCacheManager.isCustomResolver")) {
|
||||||
item.getFirstChild().setNodeValue("true");
|
item.getFirstChild().setNodeValue("true");
|
||||||
}
|
}
|
||||||
|
@ -511,7 +528,7 @@ public class JmeterDocumentParser implements DocumentParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Element createBoolProp(Document document, String name, boolean value) {
|
private Element createBoolProp(Document document, String name, boolean value) {
|
||||||
Element boolProp = document.createElement("boolProp");
|
Element boolProp = document.createElement(BOOL_PROP);
|
||||||
boolProp.setAttribute("name", name);
|
boolProp.setAttribute("name", name);
|
||||||
boolProp.appendChild(document.createTextNode(String.valueOf(value)));
|
boolProp.appendChild(document.createTextNode(String.valueOf(value)));
|
||||||
return boolProp;
|
return boolProp;
|
||||||
|
@ -569,6 +586,8 @@ public class JmeterDocumentParser implements DocumentParser {
|
||||||
collectionProp.appendChild(createKafkaProp(document, "test.name", context.getTestName()));
|
collectionProp.appendChild(createKafkaProp(document, "test.name", context.getTestName()));
|
||||||
collectionProp.appendChild(createKafkaProp(document, "test.startTime", context.getStartTime().toString()));
|
collectionProp.appendChild(createKafkaProp(document, "test.startTime", context.getStartTime().toString()));
|
||||||
collectionProp.appendChild(createKafkaProp(document, "test.reportId", context.getReportId()));
|
collectionProp.appendChild(createKafkaProp(document, "test.reportId", context.getReportId()));
|
||||||
|
collectionProp.appendChild(createKafkaProp(document, "test.expectedEndTime", (String) context.getProperty("expectedEndTime")));
|
||||||
|
collectionProp.appendChild(createKafkaProp(document, "test.expectedDelayEndTime", kafkaProperties.getExpectedDelayEndTime())); // 30s
|
||||||
|
|
||||||
elementProp.appendChild(collectionProp);
|
elementProp.appendChild(collectionProp);
|
||||||
// set elementProp
|
// set elementProp
|
||||||
|
@ -710,6 +729,8 @@ public class JmeterDocumentParser implements DocumentParser {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// 处理预计结束时间
|
||||||
|
processExpectedEndTime(duration);
|
||||||
|
|
||||||
threadGroup.setAttribute("enabled", enabled);
|
threadGroup.setAttribute("enabled", enabled);
|
||||||
if (BooleanUtils.toBoolean(deleted)) {
|
if (BooleanUtils.toBoolean(deleted)) {
|
||||||
|
@ -815,6 +836,8 @@ public class JmeterDocumentParser implements DocumentParser {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// 处理预计结束时间
|
||||||
|
processExpectedEndTime(hold);
|
||||||
|
|
||||||
threadGroup.setAttribute("enabled", enabled);
|
threadGroup.setAttribute("enabled", enabled);
|
||||||
if (BooleanUtils.toBoolean(deleted)) {
|
if (BooleanUtils.toBoolean(deleted)) {
|
||||||
|
@ -835,6 +858,27 @@ public class JmeterDocumentParser implements DocumentParser {
|
||||||
threadGroup.appendChild(createStringProp(document, "Unit", "S"));
|
threadGroup.appendChild(createStringProp(document, "Unit", "S"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processExpectedEndTime(String duration) {
|
||||||
|
long startTime = context.getStartTime();
|
||||||
|
Long d = Long.parseLong(duration);
|
||||||
|
Object serialize = context.getProperty("TestPlan.serialize_threadgroups");
|
||||||
|
String expectedEndTime = (String) context.getProperty("expectedEndTime");
|
||||||
|
if (StringUtils.isBlank(expectedEndTime)) {
|
||||||
|
expectedEndTime = startTime + "";
|
||||||
|
}
|
||||||
|
long endTime = Long.parseLong(expectedEndTime);
|
||||||
|
|
||||||
|
if (BooleanUtils.toBoolean((String) serialize)) {
|
||||||
|
// 顺序执行线程组
|
||||||
|
context.addProperty("expectedEndTime", String.valueOf(endTime + d * 1000));
|
||||||
|
} else {
|
||||||
|
// 同时执行线程组
|
||||||
|
if (endTime < startTime + d * 1000) {
|
||||||
|
context.addProperty("expectedEndTime", String.valueOf(startTime + d * 1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void processIterationThreadGroup(Element threadGroup) {
|
private void processIterationThreadGroup(Element threadGroup) {
|
||||||
// 检查 threadgroup 后面的hashtree是否为空
|
// 检查 threadgroup 后面的hashtree是否为空
|
||||||
Node hashTree = threadGroup.getNextSibling();
|
Node hashTree = threadGroup.getNextSibling();
|
||||||
|
@ -903,8 +947,11 @@ public class JmeterDocumentParser implements DocumentParser {
|
||||||
threadGroup.appendChild(createStringProp(document, "ThreadGroup.duration", "10"));
|
threadGroup.appendChild(createStringProp(document, "ThreadGroup.duration", "10"));
|
||||||
threadGroup.appendChild(createStringProp(document, "ThreadGroup.delay", ""));
|
threadGroup.appendChild(createStringProp(document, "ThreadGroup.delay", ""));
|
||||||
threadGroup.appendChild(createBoolProp(document, "ThreadGroup.same_user_on_next_iteration", true));
|
threadGroup.appendChild(createBoolProp(document, "ThreadGroup.same_user_on_next_iteration", true));
|
||||||
}
|
|
||||||
|
|
||||||
|
// 处理预计结束时间, (按照迭代次数 * 线程数)s
|
||||||
|
String duration = String.valueOf(Long.parseLong(loops) * Long.parseLong(threads));
|
||||||
|
processExpectedEndTime(duration);
|
||||||
|
}
|
||||||
|
|
||||||
private void processCheckoutTimer(Element element) {
|
private void processCheckoutTimer(Element element) {
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -91,8 +91,8 @@ public class TestPlanController {
|
||||||
|
|
||||||
@PostMapping("/edit")
|
@PostMapping("/edit")
|
||||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||||
public void editTestPlan(@RequestBody TestPlanDTO testPlanDTO) {
|
public String editTestPlan(@RequestBody TestPlanDTO testPlanDTO) {
|
||||||
testPlanService.editTestPlan(testPlanDTO, true);
|
return testPlanService.editTestPlan(testPlanDTO, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/edit/status/{planId}")
|
@PostMapping("/edit/status/{planId}")
|
||||||
|
|
|
@ -176,7 +176,7 @@ public class TestPlanService {
|
||||||
return Optional.ofNullable(testPlanMapper.selectByPrimaryKey(testPlanId)).orElse(new TestPlan());
|
return Optional.ofNullable(testPlanMapper.selectByPrimaryKey(testPlanId)).orElse(new TestPlan());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int editTestPlan(TestPlanDTO testPlan, Boolean isSendMessage) {
|
public String editTestPlan(TestPlanDTO testPlan, Boolean isSendMessage) {
|
||||||
checkTestPlanExist(testPlan);
|
checkTestPlanExist(testPlan);
|
||||||
TestPlan res = testPlanMapper.selectByPrimaryKey(testPlan.getId()); // 先查一次库
|
TestPlan res = testPlanMapper.selectByPrimaryKey(testPlan.getId()); // 先查一次库
|
||||||
testPlan.setUpdateTime(System.currentTimeMillis());
|
testPlan.setUpdateTime(System.currentTimeMillis());
|
||||||
|
@ -230,7 +230,7 @@ public class TestPlanService {
|
||||||
.build();
|
.build();
|
||||||
noticeSendService.send(NoticeConstants.TaskType.TEST_PLAN_TASK, noticeModel);
|
noticeSendService.send(NoticeConstants.TaskType.TEST_PLAN_TASK, noticeModel);
|
||||||
}
|
}
|
||||||
return i;
|
return testPlan.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
//计划内容
|
//计划内容
|
||||||
|
|
|
@ -392,8 +392,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
runTest(data) {
|
runTest(data) {
|
||||||
this.setTabTitle(data);
|
|
||||||
this.handleTabsEdit(this.$t("commons.api"), "TEST", data);
|
this.handleTabsEdit(this.$t("commons.api"), "TEST", data);
|
||||||
|
this.setTabTitle(data);
|
||||||
},
|
},
|
||||||
saveApi(data) {
|
saveApi(data) {
|
||||||
this.setTabTitle(data);
|
this.setTabTitle(data);
|
||||||
|
|
|
@ -58,22 +58,26 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
run() {
|
run() {
|
||||||
let testPlan = new TestPlan();
|
let projectId = this.$store.state.projectId;
|
||||||
let threadGroup = new ThreadGroup();
|
|
||||||
threadGroup.hashTree = [];
|
|
||||||
testPlan.hashTree = [threadGroup];
|
|
||||||
this.runData.forEach(item => {
|
|
||||||
threadGroup.hashTree.push(item);
|
|
||||||
})
|
|
||||||
|
|
||||||
let projectId = "";
|
|
||||||
// 如果envMap不存在,是单接口调用
|
// 如果envMap不存在,是单接口调用
|
||||||
if (!this.envMap || this.envMap.size === 0) {
|
if (!this.envMap || this.envMap.size === 0) {
|
||||||
projectId = this.$store.state.projectId;
|
projectId = this.$store.state.projectId;
|
||||||
} else {
|
} else {
|
||||||
// 场景步骤下接口调用
|
// 场景步骤下接口调用
|
||||||
|
if(this.runData.projectId){
|
||||||
projectId = this.runData.projectId;
|
projectId = this.runData.projectId;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let testPlan = new TestPlan();
|
||||||
|
let threadGroup = new ThreadGroup();
|
||||||
|
threadGroup.hashTree = [];
|
||||||
|
testPlan.hashTree = [threadGroup];
|
||||||
|
this.runData.forEach(item => {
|
||||||
|
item.projectId = projectId;
|
||||||
|
threadGroup.hashTree.push(item);
|
||||||
|
})
|
||||||
|
|
||||||
let reqObj = {id: this.reportId, testElement: testPlan, type: this.type,projectId: projectId, environmentMap: strMapToObj(this.envMap)};
|
let reqObj = {id: this.reportId, testElement: testPlan, type: this.type,projectId: projectId, environmentMap: strMapToObj(this.envMap)};
|
||||||
let bodyFiles = getBodyUploadFiles(reqObj, this.runData);
|
let bodyFiles = getBodyUploadFiles(reqObj, this.runData);
|
||||||
let url = "";
|
let url = "";
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<i class="icon el-icon-arrow-right" :class="{'is-active': apiCase.active}" @click="active(apiCase)"/>
|
<i class="icon el-icon-arrow-right" :class="{'is-active': apiCase.active}" @click="active(apiCase)"/>
|
||||||
<el-input v-if="!apiCase.id || isShowInput" size="small" v-model="apiCase.name" :name="index" :key="index"
|
<el-input v-if="!apiCase.id || isShowInput" size="small" v-model="apiCase.name" :name="index" :key="index"
|
||||||
class="ms-api-header-select" style="width: 180px"
|
class="ms-api-header-select" style="width: 180px"
|
||||||
@blur="saveTestCase(apiCase)" :placeholder="$t('commons.input_name')" ref="nameEdit"/>
|
@blur="saveTestCase(apiCase,true)" :placeholder="$t('commons.input_name')" ref="nameEdit"/>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ apiCase.id ? apiCase.name : '' }}
|
{{ apiCase.id ? apiCase.name : '' }}
|
||||||
<i class="el-icon-edit" style="cursor:pointer" @click="showInput(apiCase)" v-tester/>
|
<i class="el-icon-edit" style="cursor:pointer" @click="showInput(apiCase)" v-tester/>
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
|
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<div class="tag-item" @click.stop>
|
<div class="tag-item" @click.stop>
|
||||||
<ms-input-tag :currentScenario="apiCase" ref="tag" @keyup.enter.native="saveTestCase(apiCase)"/>
|
<ms-input-tag :currentScenario="apiCase" ref="tag" @keyup.enter.native="saveTestCase(apiCase,true)"/>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
|
@ -291,7 +291,7 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
saveCase(row) {
|
saveCase(row,hideAlert) {
|
||||||
let tmp = JSON.parse(JSON.stringify(row));
|
let tmp = JSON.parse(JSON.stringify(row));
|
||||||
this.isShowInput = false;
|
this.isShowInput = false;
|
||||||
if (this.validate(tmp)) {
|
if (this.validate(tmp)) {
|
||||||
|
@ -328,16 +328,18 @@
|
||||||
row.createTime = data.createTime;
|
row.createTime = data.createTime;
|
||||||
row.updateTime = data.updateTime;
|
row.updateTime = data.updateTime;
|
||||||
if (!row.message) {
|
if (!row.message) {
|
||||||
|
if(!hideAlert){
|
||||||
this.$success(this.$t('commons.save_success'));
|
this.$success(this.$t('commons.save_success'));
|
||||||
this.$emit('refresh');
|
this.$emit('refresh');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
saveTestCase(row) {
|
saveTestCase(row,hideAlert) {
|
||||||
if (this.api.saved) {
|
if (this.api.saved) {
|
||||||
this.addModule(row);
|
this.addModule(row);
|
||||||
} else {
|
} else {
|
||||||
this.saveCase(row);
|
this.saveCase(row,hideAlert);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
showInput(row) {
|
showInput(row) {
|
||||||
|
|
|
@ -152,6 +152,7 @@
|
||||||
saveApiAndCase(api) {
|
saveApiAndCase(api) {
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
this.api = api;
|
this.api = api;
|
||||||
|
this.currentApi = api;
|
||||||
this.addCase();
|
this.addCase();
|
||||||
},
|
},
|
||||||
setEnvironment(environment) {
|
setEnvironment(environment) {
|
||||||
|
@ -305,6 +306,8 @@
|
||||||
this.$warning(this.$t('api_test.environment.select_environment'));
|
this.$warning(this.$t('api_test.environment.select_environment'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.envMap = new Map();
|
||||||
|
this.envMap.set(this.$store.state.projectId,this.environment);
|
||||||
this.runData = [];
|
this.runData = [];
|
||||||
this.batchLoadingIds = [];
|
this.batchLoadingIds = [];
|
||||||
this.selectdCases = [];
|
this.selectdCases = [];
|
||||||
|
|
|
@ -248,6 +248,7 @@ import {Api_List} from "@/business/components/common/model/JsonData";
|
||||||
import HeaderCustom from "@/business/components/common/head/HeaderCustom";
|
import HeaderCustom from "@/business/components/common/head/HeaderCustom";
|
||||||
import HeaderLabelOperate from "@/business/components/common/head/HeaderLabelOperate";
|
import HeaderLabelOperate from "@/business/components/common/head/HeaderLabelOperate";
|
||||||
import {Body} from "@/business/components/api/definition/model/ApiTestModel";
|
import {Body} from "@/business/components/api/definition/model/ApiTestModel";
|
||||||
|
import {buildNodePath} from "@/business/components/api/definition/model/NodeTree";
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -768,13 +769,21 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
buildApiPath(apis) {
|
buildApiPath(apis) {
|
||||||
apis.forEach((api) => {
|
try {
|
||||||
|
let options = [];
|
||||||
this.moduleOptions.forEach(item => {
|
this.moduleOptions.forEach(item => {
|
||||||
|
buildNodePath(item, {path: ''}, options);
|
||||||
|
});
|
||||||
|
apis.forEach((api) => {
|
||||||
|
options.forEach((item) => {
|
||||||
if (api.moduleId === item.id) {
|
if (api.moduleId === item.id) {
|
||||||
api.modulePath = item.path;
|
api.modulePath = item.path;
|
||||||
}
|
}
|
||||||
|
})
|
||||||
});
|
});
|
||||||
});
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
sort(column) {
|
sort(column) {
|
||||||
// 每次只对一个字段排序
|
// 每次只对一个字段排序
|
||||||
|
|
|
@ -133,16 +133,11 @@
|
||||||
this.data.forEach(node => {
|
this.data.forEach(node => {
|
||||||
buildTree(node, {path: ''});
|
buildTree(node, {path: ''});
|
||||||
});
|
});
|
||||||
|
this.$emit('setModuleOptions', this.data);
|
||||||
this.$emit('setNodeTree', this.data);
|
this.$emit('setNodeTree', this.data);
|
||||||
if (this.$refs.nodeTree) {
|
if (this.$refs.nodeTree) {
|
||||||
this.$refs.nodeTree.filter(this.condition.filterText);
|
this.$refs.nodeTree.filter(this.condition.filterText);
|
||||||
}
|
}
|
||||||
let moduleOptions = [];
|
|
||||||
this.data.forEach(node => {
|
|
||||||
buildNodePath(node, {path: ''}, moduleOptions);
|
|
||||||
});
|
|
||||||
this.moduleOptions = moduleOptions;
|
|
||||||
this.$emit('setModuleOptions', moduleOptions);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
<ms-test-case-info-card @redirectPage="redirectPage" :test-case-count-data="testCaseCountData"/>
|
<ms-test-case-info-card @redirectPage="redirectPage" :test-case-count-data="testCaseCountData"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<ms-scene-info-card @redirectPage="redirectPage" :scene-count-data="sceneCountData"/>
|
<ms-scene-info-card @redirectPage="redirectPage" :scene-count-data="sceneCountData" :interface-coverage="interfaceCoverage"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<ms-schedule-task-info-card :schedule-task-count-data="scheduleTaskCountData"/>
|
<ms-schedule-task-info-card :schedule-task-count-data="scheduleTaskCountData"/>
|
||||||
|
@ -82,6 +82,7 @@ export default {
|
||||||
sceneCountData: {},
|
sceneCountData: {},
|
||||||
testCaseCountData: {},
|
testCaseCountData: {},
|
||||||
scheduleTaskCountData: {},
|
scheduleTaskCountData: {},
|
||||||
|
interfaceCoverage: "waitting...",
|
||||||
tipsType: "1",
|
tipsType: "1",
|
||||||
result: {},
|
result: {},
|
||||||
}
|
}
|
||||||
|
@ -109,6 +110,10 @@ export default {
|
||||||
this.sceneCountData = response.data;
|
this.sceneCountData = response.data;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.$get("/api/countInterfaceCoverage/" + selectProjectId, response => {
|
||||||
|
this.interfaceCoverage = response.data;
|
||||||
|
});
|
||||||
|
|
||||||
this.$get("/api/testCaseInfoCount/" + selectProjectId, response => {
|
this.$get("/api/testCaseInfoCount/" + selectProjectId, response => {
|
||||||
this.testCaseCountData = response.data;
|
this.testCaseCountData = response.data;
|
||||||
});
|
});
|
||||||
|
|
|
@ -52,16 +52,29 @@
|
||||||
<el-main style="padding: 5px;margin-top: 10px">
|
<el-main style="padding: 5px;margin-top: 10px">
|
||||||
<el-container>
|
<el-container>
|
||||||
<el-aside width="60%" class="count-number-show" style="margin-bottom: 0px;margin-top: 0px">
|
<el-aside width="60%" class="count-number-show" style="margin-bottom: 0px;margin-top: 0px">
|
||||||
<el-container>
|
<el-container style="height: 50px;margin-top: 10px">
|
||||||
<el-aside width="30%">
|
<el-aside width="50%" style="line-height: 40px;">
|
||||||
{{$t('api_test.home_page.detail_card.rate.pass')+":"}}
|
{{$t('api_test.home_page.detail_card.rate.pass')+":"}}
|
||||||
</el-aside>
|
</el-aside>
|
||||||
<el-main style="padding: 0px 0px 0px 0px; line-height: 100px; text-align: center;">
|
<el-main style="padding: 0px 0px 0px 0px; line-height: 40px; text-align: center;">
|
||||||
<span class="count-number">
|
<span class="rows-count-number">
|
||||||
{{sceneCountData.passRage}}
|
{{sceneCountData.passRage}}
|
||||||
</span>
|
</span>
|
||||||
</el-main>
|
</el-main>
|
||||||
</el-container>
|
</el-container>
|
||||||
|
<el-container style="height: 50px;margin-top: 1px">
|
||||||
|
<el-aside width="50%" style="line-height: 40px;">
|
||||||
|
<span>{{$t('api_test.home_page.detail_card.rate.interface_coverage')+":"}}</span>
|
||||||
|
</el-aside>
|
||||||
|
<el-main style="padding: 0px 0px 0px 0px; line-height: 40px; text-align: center;">
|
||||||
|
<span v-if="interfaceCoverage === 'waitting...'">
|
||||||
|
<i class="el-icon-loading lading-icon"></i>
|
||||||
|
</span>
|
||||||
|
<span v-else class="rows-count-number">
|
||||||
|
{{interfaceCoverage}}
|
||||||
|
</span>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
</el-aside>
|
</el-aside>
|
||||||
<el-main style="padding: 5px">
|
<el-main style="padding: 5px">
|
||||||
<el-card class="no-shadow-card" body-style="padding-left:5px;padding-right:5px">
|
<el-card class="no-shadow-card" body-style="padding-left:5px;padding-right:5px">
|
||||||
|
@ -120,6 +133,7 @@ export default {
|
||||||
|
|
||||||
props:{
|
props:{
|
||||||
sceneCountData:{},
|
sceneCountData:{},
|
||||||
|
interfaceCoverage:String,
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -141,6 +155,17 @@ export default {
|
||||||
margin:20px auto;
|
margin:20px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rows-count-number{
|
||||||
|
font-family:'ArialMT', 'Arial', sans-serif;
|
||||||
|
font-size:30px;
|
||||||
|
color: var(--count_number);
|
||||||
|
margin:20px auto;
|
||||||
|
}
|
||||||
|
.lading-icon{
|
||||||
|
font-size: 25px;
|
||||||
|
color: var(--count_number);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
.main-number-show {
|
.main-number-show {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
<ms-node-tree
|
<ms-node-tree
|
||||||
v-loading="result.loading"
|
v-loading="result.loading"
|
||||||
:tree-nodes="data"
|
:tree-nodes="data"
|
||||||
:allLabel="$t('commons.module.default_module')"
|
|
||||||
@add="add"
|
@add="add"
|
||||||
:type="'edit'"
|
:type="'edit'"
|
||||||
@edit="edit"
|
@edit="edit"
|
||||||
|
|
|
@ -379,14 +379,17 @@
|
||||||
|
|
||||||
.common-tree {
|
.common-tree {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
height: 200px;
|
min-height: 200px;
|
||||||
|
max-height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ms-tree-select {
|
.ms-tree-select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 111;
|
z-index: 111;
|
||||||
}
|
}
|
||||||
|
/deep/.el-tree-node__children{
|
||||||
|
overflow: inherit;
|
||||||
|
}
|
||||||
.ok {
|
.ok {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
|
@ -711,7 +711,7 @@ export default {
|
||||||
.border-hidden >>> .el-textarea__inner {
|
.border-hidden >>> .el-textarea__inner {
|
||||||
border-style: hidden;
|
border-style: hidden;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
color: #606266;
|
color: #060505;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cast_label {
|
.cast_label {
|
||||||
|
@ -785,4 +785,5 @@ p {
|
||||||
height: 550px;
|
height: 550px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -549,4 +549,10 @@ export default {
|
||||||
.comment-card >>> .el-card__body {
|
.comment-card >>> .el-card__body {
|
||||||
height: calc(100vh - 120px);
|
height: calc(100vh - 120px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tb-edit >>> .el-textarea__inner {
|
||||||
|
border-style: hidden;
|
||||||
|
background-color: white;
|
||||||
|
color: #060505;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1001,6 +1001,7 @@ export default {
|
||||||
coverage: "Coverage rate",
|
coverage: "Coverage rate",
|
||||||
pass: "Pass rate",
|
pass: "Pass rate",
|
||||||
success: "Success rate",
|
success: "Success rate",
|
||||||
|
interface_coverage: "Interface coverage",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
api_details_card: {
|
api_details_card: {
|
||||||
|
|
|
@ -1005,6 +1005,7 @@ export default {
|
||||||
coverage: "覆盖率",
|
coverage: "覆盖率",
|
||||||
pass: "通过率",
|
pass: "通过率",
|
||||||
success: "成功率",
|
success: "成功率",
|
||||||
|
interface_coverage: "接口覆盖率",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
api_details_card: {
|
api_details_card: {
|
||||||
|
|
|
@ -1003,6 +1003,7 @@ export default {
|
||||||
coverage: "覆蓋率",
|
coverage: "覆蓋率",
|
||||||
pass: "通過率",
|
pass: "通過率",
|
||||||
success: "成功率",
|
success: "成功率",
|
||||||
|
interface_coverage: "接口覆蓋率",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
api_details_card: {
|
api_details_card: {
|
||||||
|
|
Loading…
Reference in New Issue