提交 17bb0102 authored 作者: 黄夏豪's avatar 黄夏豪

feat(web): 新增了执行历史相关的接口

refactor(base):优化了执行器对外部执行的接口 方便其他外部调用
上级 3c7867f9
......@@ -42,7 +42,6 @@ import java.util.stream.Collectors;
@Component
public class HttpClientActuator implements Actuator {
final
CloseableHttpClient client;
......
package org.matrix.actuators.usecase;
import com.alibaba.fastjson.JSON;
import org.apache.http.client.methods.CloseableHttpResponse;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.matrix.actuators.Actuator;
import org.matrix.actuators.checkpoint.CheckPointActuator;
import org.matrix.actuators.httpclient.HttpClientActuator;
import org.matrix.actuators.move.MoveActuator;
import org.matrix.actuators.move.MoveStrategy;
import org.matrix.actuators.util.ThreadUtil;
import org.matrix.database.entity.TestCase;
import org.matrix.database.entity.*;
import org.matrix.actuators.checkpoint.CheckPoint;
import org.matrix.actuators.checkpoint.CheckPointResult;
import org.matrix.actuators.httpclient.HttpRequestDetail;
import org.matrix.actuators.httpclient.HttpResponseDetail;
import org.matrix.database.entity.TestCaseBTO;
import org.matrix.database.entity.TestCaseListDataBto;
import org.matrix.database.entity.TestData;
import org.matrix.database.mapper.TestDataMapper;
import org.matrix.socket.LogQueueRuntime;
import org.springframework.beans.factory.annotation.Autowired;
import org.matrix.database.service.IExecutionHistoryService;
import org.matrix.enums.ExecutionHistoryStatus;
import org.matrix.exception.GlobalException;
import org.matrix.socket.*;
import org.matrix.util.SpringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.UUID;
/**
* 测试用例执行器
* todo 打印LOG
*
* @author huangxiahao
*/
@Component
......@@ -36,101 +38,121 @@ public class CaseActuator implements Actuator {
private final HttpClientActuator httpClientActuator;
private final String baseJsPath = "syntaxCheck.js";
private final IExecutionHistoryService executionHistoryService;
@Autowired
TestDataMapper testDataMapper;
private final String baseJsPath = "syntaxCheck.js";
public CaseActuator(CheckPointActuator checkPointActuator, HttpClientActuator httpClientActuator) {
public CaseActuator(CheckPointActuator checkPointActuator, HttpClientActuator httpClientActuator, IExecutionHistoryService executionHistoryService) {
this.checkPointActuator = checkPointActuator;
this.httpClientActuator = httpClientActuator;
this.executionHistoryService = executionHistoryService;
}
/**
* 执行测试用例
* @param projectId 项目ID
* @param envId 环境ID
* 执行测试用例,这个方法对内使用,由其他执行器调用
*/
public TestCaseExecuteResult executeTestCase(TestCaseBTO testCaseBto, Long envId, Long projectId) {
try {
LogQueueRuntime.addNewLog("[用例执行器] 开始执行单条用例数据!!");
//todo 李迪凡 执行前置动作
LogQueueRuntime.addNewLog(String.format("[用例执行器] 当前正在执行用例ID: %s 用例名: %s", testCaseBto.getTestCase().getId(), testCaseBto.getTestCase().getName()));
//执行前置动作
executeMove(testCaseBto.getTestCase().getMoveAfterCase()
, envId, projectId, null, MoveStrategy.PRE_MOVE);
//向线程中设置当前正在执行的DataId
LogQueueRuntime.setTestData(testCaseBto.getTestData().getId());
//执行测试用例的本体内容
HttpResponseDetail baseTestCaseResponseDetail = getHttpResponseDetail(
envId,
projectId,
testCaseBto.getTestCase(),
testCaseBto.getTestData());
//todo 李迪凡 执行测试后动作
//执行中置动作
executeMove(testCaseBto.getTestCase().getMoveAfterTest()
, envId, projectId, baseTestCaseResponseDetail.getResponseBody(), MoveStrategy.MID_MOVE);
//进行检验
CheckPointResult checkPointResult = getCheckPointResult(testCaseBto.getTestCase(),
testCaseBto.getTestData(),
envId,
projectId,
baseTestCaseResponseDetail);
;
//todo 李迪凡 执行后置动作
//执行后置动作
executeMove(testCaseBto.getTestCase().getMoveAfterTest()
, envId, projectId, baseTestCaseResponseDetail.getResponseBody(), MoveStrategy.AFT_MOVE);
TestCaseExecuteResult testCaseExecuteResult = new TestCaseExecuteResult(baseTestCaseResponseDetail, checkPointResult);
LogQueueRuntime.addNewLog(JSON.toJSONString(testCaseExecuteResult));
//将线程中正在执行的DataId清除
LogQueueRuntime.clearTestData();
return testCaseExecuteResult;
} catch (Exception e) {
throw e;
} finally {
LogQueueRuntime.remove(ThreadUtil.currentThreadId());
}
}
/**
* todo 执行一条测试用例的多条测试数据
* 执行一条测试用例的多条测试数据
*/
public List<TestCaseExecuteResult> executeTestCases(TestCaseListDataBto testCaseBto, Long envId, Long projectId) {
for (int i = 0; i < 100; i++) {
LogQueueRuntime.addNewLog("[用例执行器] 开始执行用例!!"+i);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
LogQueueRuntime.addNewLog("[用例执行器] 开始执行用例!!" );
try {
LogQueueRuntime.addNewLog(String.format("[用例执行器] 当前正在执行用例ID: %s 用例名: %s",testCaseBto.getTestCase().getId(),testCaseBto.getTestCase().getName()));
//todo 李迪凡 执行前置动作
LogQueueRuntime.addNewLog(String.format("[用例执行器] 当前正在执行用例ID: %s 用例名: %s", testCaseBto.getTestCase().getId(), testCaseBto.getTestCase().getName()));
//todo 黄夏豪 执行前置动作
LogQueueRuntime.addNewLog("[用例执行器] 开始执行前置动作!!");
//执行测试用例的本体内容
List<HttpResponseDetail> baseTestCaseResponseDetailList = new ArrayList<>();
List<TestCaseExecuteResult> resultList = new ArrayList<>();
for (TestData testData : testCaseBto.getTestDataList()) {
//向线程中设置当前正在执行的DataId
LogQueueRuntime.setTestData(testData.getId());
changeExecutionHistoryStatus(ExecutionHistoryStatus.RUN);
LogQueueRuntime.addNewLog("[用例执行器] 开始执行数据组ID:" + testData.getId());
HttpResponseDetail baseTestCaseResponseDetail = getHttpResponseDetail(
envId,
projectId,
testCaseBto.getTestCase(),
testData);
baseTestCaseResponseDetailList.add(baseTestCaseResponseDetail);
}
//将线程中正在执行的DataId清除
LogQueueRuntime.clearTestData();
//todo 李迪凡 执行测试后动作
List<TestCaseExecuteResult> resultList = new ArrayList<>();
for (int i = 0; i < testCaseBto.getTestDataList().size(); i++) {
//向线程中设置当前正在执行的DataId
LogQueueRuntime.setTestData(testCaseBto.getTestDataList().get(i).getId());
LogQueueRuntime.addNewLog("[用例执行器] 即将开始执行中置动作");
//todo 黄夏豪 执行测试后动作
CheckPointResult checkPointResult = getCheckPointResult(testCaseBto.getTestCase(),
testCaseBto.getTestDataList().get(i),
testData,
envId,
projectId,
baseTestCaseResponseDetailList.get(i));
TestCaseExecuteResult testCaseExecuteResult = new TestCaseExecuteResult(baseTestCaseResponseDetailList.get(i), checkPointResult);
baseTestCaseResponseDetail);
TestCaseExecuteResult testCaseExecuteResult = new TestCaseExecuteResult(baseTestCaseResponseDetail, checkPointResult);
resultList.add(testCaseExecuteResult);
LogQueueRuntime.addNewLog(JSON.toJSONString(testCaseExecuteResult));
//todo 黄夏豪 执行后置动作
changeExecutionHistoryStatus(ExecutionHistoryStatus.FINISH);
}
//将线程中正在执行的DataId清除
LogQueueRuntime.clearTestData();
//todo 李迪凡 执行后置动作
return resultList;
} catch (Exception e) {
throw e;
} finally {
LogQueueRuntime.remove(ThreadUtil.currentThreadId());
}
}
/**
* 调用动作
*
* @param moveString 用例中的moveId组 例如 1,2,3,4
* @param envId 环境id
* @param projectId 项目id
* @param caseResultData 测试用例执行后保留下的结果集,该参数在前置策略中可以为null,在中间/后置策略中必须要提供合理的结果集对象
* @param strategy 动作的策略,即是前置/中间/后置,具体查看{@link MoveStrategy}
*/
private void executeMove(String moveString, Long envId, Long projectId, String caseResultData, MoveStrategy strategy) {
String[] moveIds = moveString.split(",");
for (String moveId : moveIds) {
getMoveActuator().runMove(
Long.valueOf(moveId),
envId,
projectId,
caseResultData,
strategy
);
}
}
......@@ -162,21 +184,102 @@ public class CaseActuator implements Actuator {
private CheckPoint getCheckPointEntity(TestData testData) {
CheckPoint checkPoint = new CheckPoint();
if (testData.getAbnormalCheckpoint() == 0) {
checkPoint.setUseExceptionCheck(false);
} else {
checkPoint.setUseExceptionCheck(true);
}
if (testData.getNoEmptyCheckpoint() == 0) {
checkPoint.setUseNullCheck(false);
} else {
checkPoint.setUseNullCheck(true);
}
checkPoint.setUseExceptionCheck(testData.getAbnormalCheckpoint() != 0);
checkPoint.setUseNullCheck(testData.getNoEmptyCheckpoint() != 0);
checkPoint.setContainCheckPoint(testData.getContainCheckpoint());
checkPoint.setNoContainCheckPoint(testData.getNoContainCheckpoint());
checkPoint.setJsonPathCheckPoint(testData.getJsonpathCheckpoint());
return checkPoint;
}
private static MoveActuator getMoveActuator() {
return SpringUtils.getBean("moveActuator");
}
/**
* @param caseExecuteVo 执行所需要的详细信息
* @param session 如果是从socket运行并希望获取实时日志请从这里运行
* 外界如果执行测试用例的话请走这个接口,执行TestCase,并控制运行态日志池,进行日志的生成。
*/
public List<TestCaseExecuteResult> runTestCase(WebSocketSession session, CaseExecuteVo caseExecuteVo) {
String unionKey = UUID.randomUUID().toString();
Long currentThreadId = ThreadUtil.currentThreadId();
try {
//将websocketSession 加入到socket池子中
TestCaseExecuteSocketPool.add(currentThreadId, session);
List<TestCaseListDataBto> testCaseListDataBtoList = caseExecuteVo.getTestCaseListDataBtoList();
//建立执行历史(ExecutionHistory)
insertExecutionHistory(unionKey, caseExecuteVo);
for (TestCaseListDataBto testCaseListDataBto : testCaseListDataBtoList) {
LogQueueRuntime
.initTestCaseLog(
caseExecuteVo.getJobId(),
caseExecuteVo.getUserId()
, testCaseListDataBto.getTestCase().getId()
, caseExecuteVo.getType()
, unionKey
);
//执行测试用例
return executeTestCases(testCaseListDataBto, caseExecuteVo.getEnvId(), caseExecuteVo.getProjectId());
}
} catch (GlobalException e) {
try {
if (session != null && session.isOpen()) {
TestExecuteLog currentTestExecute = LogQueueRuntime.getCurrentTestExecute(ThreadUtil.currentThreadId());
if (currentTestExecute!=null){
LogQueueRuntime.addNewLog(e.getMessage());
}else {
//这里加-1.-1.-1是用来表明这里发出去的数据是不属于任何执行中的用例的
session.sendMessage(new TextMessage(String.format("-1.-1.-1.%s", e.getMessage())));
}
}
} catch (IOException ioException) {
ioException.printStackTrace();
}
} finally {
//将本次产生的数据清除
TestCaseExecuteSocketPool.remove(currentThreadId);
//将本次产生的日志从执行状态设为停止状态
LogQueueRuntime.remove(currentThreadId);
//todo 黄夏豪 将数据库中的执行历史状态设置为完成
}
return null;
}
public void insertExecutionHistory(String unionKey, CaseExecuteVo caseExecuteVo) {
List<TestCaseListDataBto> testCaseListDataBtoList = caseExecuteVo.getTestCaseListDataBtoList();
for (TestCaseListDataBto testCaseListDataBto : testCaseListDataBtoList) {
List<TestData> testDataList = testCaseListDataBto.getTestDataList();
TestCase testCase = testCaseListDataBto.getTestCase();
for (TestData testData : testDataList) {
ExecutionHistory executionHistory = new ExecutionHistory();
executionHistory.setJobId(caseExecuteVo.getJobId());
executionHistory.setCaseId(testCase.getId());
executionHistory.setDataId(testData.getId());
executionHistory.setUnionKey(unionKey);
executionHistoryService.save(executionHistory);
}
}
}
public void changeExecutionHistoryStatus(
ExecutionHistoryStatus executionHistoryStatus) {
TestExecuteLog currentTestExecute = LogQueueRuntime.getCurrentTestExecute(ThreadUtil.currentThreadId());
if (currentTestExecute!=null){
ExecutionHistory executionHistory = new ExecutionHistory();
executionHistory.setStatus(executionHistoryStatus);
executionHistoryService.update(executionHistory, Wrappers
.lambdaUpdate(ExecutionHistory.class)
.eq(ExecutionHistory::getJobId,currentTestExecute.getJobId())
.eq(ExecutionHistory::getCaseId,currentTestExecute.getTestCaseId())
.eq(ExecutionHistory::getDataId,currentTestExecute.getTestDataId())
.eq(ExecutionHistory::getUnionKey,currentTestExecute.getUnionKey())
);
}
}
}
......@@ -66,7 +66,6 @@ public class CompleteExpressionUtil {
* @return jsonpath解析结果
*/
public String completeJsonPathExpression(String expression, Object jsonObject) {
String result = expression;
result = regexExpression(result, JSON_PATH_REG,
jsonObject, (o, s) -> JSON.toJSONString(JsonPath.read(o, s.substring(1, s.length() - 1))));
......@@ -89,6 +88,4 @@ public class CompleteExpressionUtil {
return SpringUtils.getBean("sqlExpActuator");
}
}
package org.matrix.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.cache.CacheManager;
......@@ -32,5 +35,4 @@ public class CacheConfig {
return caffeineCacheManager;
}
}
package org.matrix.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.matrix.socket.HttpAuthHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
......
package org.matrix.database.controller;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.matrix.actuators.usecase.CaseActuator;
import org.matrix.actuators.usecase.TestCaseExecuteResult;
import org.matrix.database.entity.TestCase;
import org.matrix.database.entity.TestCaseBTO;
import org.matrix.database.entity.TestData;
import org.matrix.database.mapper.ExecutionHistoryMapper;
import org.matrix.database.service.IExecutionHistoryService;
import org.matrix.database.vo.ExecutionHistoryVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
......@@ -24,6 +29,9 @@ public class TestController {
@Autowired
CaseActuator caseActuator;
@Autowired
IExecutionHistoryService executionHistoryService;
/**
* 获得表名,用于测试http执行器的调用
*/
......
......@@ -46,4 +46,6 @@ public class Action extends BaseEntity {
@ApiModelProperty("环境参数id")
private Long envId;
}
package org.matrix.database.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.matrix.enums.ExecutionHistoryStatus;
/**
* <p>
......@@ -17,7 +19,8 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "")
public class KtExecutionHistory extends BaseEntity {
@TableName(value = "kt_execution_history")
public class ExecutionHistory extends BaseEntity {
private String unionKey;
......@@ -27,12 +30,8 @@ public class KtExecutionHistory extends BaseEntity {
private Long jobId;
private Integer status;
private ExecutionHistoryStatus status = ExecutionHistoryStatus.READY;
private String caseName;
private String dataName;
private String jobName;
}
......@@ -37,9 +37,6 @@ public class ExecutionRecord extends BaseEntity {
@ApiModelProperty("key用来记录执行批次")
private String unionKey;
@ApiModelProperty("执行状态,0:关闭,1:开启")
private ExecutionRecType status = ExecutionRecType.RUN;
@ApiModelProperty("类型")
private TestExecuteType type;
......
package org.matrix.database.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Select;
import org.matrix.database.entity.ExecutionHistory;
import org.matrix.database.vo.ExecutionHistoryVo;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* <p>
* Mapper 接口
* </p>
*
* @author mry
* @since 2022-03-11
*/
@Repository
public interface ExecutionHistoryMapper extends BaseMapper<ExecutionHistory> {
/**
* 根据CaseId和JobId 查询对应的数据列表
* @param page 分页数据
* @param jobId 测试任务ID
* @param caseId 用例ID
* @return 执行记录列表
*/
@Select("<script>" +
"SELECT\n" +
"\tkeh.id,\n" +
"\tkeh.union_key unionKey,\n" +
"\tkeh.case_id caseId,\n" +
"\tkeh.data_id dataId,\n" +
"\tkeh.job_id jobId,\n" +
"\tkeh.`status` `status`,\n" +
"\tktc.`name` caseName,\n" +
"\tktd.`name` dataName \n" +
"FROM\n" +
"\tkt_execution_history keh\n" +
"\tLEFT JOIN kt_test_case ktc ON ktc.id = keh.case_id\n" +
"\tLEFT JOIN kt_test_data ktd ON ktd.id = keh.data_id" +
"<where>" +
"<if test=\"caseId!=null\">\n" +
"and keh.case_id= #{caseId} \n" +
" </if>" +
"<if test=\"jobId!=null\">\n" +
"and keh.job_id= #{jobId} \n" +
" </if>" +
"</where>" +
"</script>")
IPage<ExecutionHistoryVo> pageByCaseIdAndJobId(IPage<ExecutionHistoryVo> page, Long caseId, Long jobId);
}
package org.matrix.database.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.matrix.database.entity.KtExecutionHistory;
import org.springframework.stereotype.Repository;
/**
* <p>
* Mapper 接口
* </p>
*
* @author mry
* @since 2022-03-11
*/
@Repository
public interface KtExecutionHistoryMapper extends BaseMapper<KtExecutionHistory> {
}
package org.matrix.database.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import org.matrix.database.entity.ExecutionHistory;
import org.matrix.database.vo.ExecutionHistoryVo;
import org.springframework.web.bind.annotation.RequestParam;
/**
* <p>
* 服务类
* </p>
*
* @author mry
* @since 2022-03-11
*/
public interface IExecutionHistoryService extends IService<ExecutionHistory> {
/**
* 根据CaseId和JobId 查询对应的数据列表
* @param pageNum 页码
* @param pageSize 分页条数
* @param jobId 测试任务ID
* @param caseId 用例ID
* @return 执行记录列表
*/
IPage<ExecutionHistoryVo> pageByCaseIdAndJobId( Long caseId, Long jobId,int pageSize, int pageNum);
}
package org.matrix.database.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.matrix.database.entity.KtExecutionHistory;
/**
* <p>
* 服务类
* </p>
*
* @author mry
* @since 2022-03-11
*/
public interface IKtExecutionHistoryService extends IService<KtExecutionHistory> {
}
package org.matrix.database.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.matrix.database.entity.ExecutionHistory;
import org.matrix.database.mapper.ExecutionHistoryMapper;
import org.matrix.database.service.IExecutionHistoryService;
import org.matrix.database.vo.ExecutionHistoryVo;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author mry
* @since 2022-03-11
*/
@Service
public class ExecutionHistoryServiceImpl extends ServiceImpl<ExecutionHistoryMapper, ExecutionHistory> implements IExecutionHistoryService {
@Override
public IPage<ExecutionHistoryVo> pageByCaseIdAndJobId(Long caseId, Long jobId, int pageNum,int pageSize ) {
Page<ExecutionHistoryVo> page = new Page<>(pageNum, pageSize);
return baseMapper.pageByCaseIdAndJobId(page,caseId,jobId);
}
}
package org.matrix.database.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.matrix.actuators.datasource.IDataSourceService;
import org.matrix.database.entity.ExecutionRecord;
import org.matrix.database.mapper.ExecutionRecordMapper;
import org.matrix.database.service.IExecutionRecordService;
import org.matrix.enums.ExecutionRecType;
import org.matrix.exception.GlobalException;
import org.matrix.socket.TestExecuteLog;
import org.matrix.socket.TestExecuteType;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* @author mry
*/
@Service
public class ExecutionRecordImpl extends ServiceImpl<ExecutionRecordMapper, ExecutionRecord> implements IExecutionRecordService {
@Autowired
IDataSourceService iDataSourceService;
@Override
public void addExecutionRecord(TestExecuteLog testExecuteLog,String log) {
ExecutionRecord executionRecord = new ExecutionRecord();
......@@ -39,7 +40,6 @@ public class ExecutionRecordImpl extends ServiceImpl<ExecutionRecordMapper, Exec
throw new GlobalException("不能根据空的unionKey修改执行状态");
}else {
ExecutionRecord executionRecord = new ExecutionRecord();
executionRecord.setStatus(status);
update(executionRecord,Wrappers
.lambdaUpdate(ExecutionRecord.class)
.eq(ExecutionRecord::getUnionKey,unionKey)
......
package org.matrix.database.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.matrix.database.entity.KtExecutionHistory;
import org.matrix.database.mapper.KtExecutionHistoryMapper;
import org.matrix.database.service.IKtExecutionHistoryService;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author mry
* @since 2022-03-11
*/
@Service
public class KtExecutionHistoryServiceImpl extends ServiceImpl<KtExecutionHistoryMapper, KtExecutionHistory> implements IKtExecutionHistoryService {
}
package org.matrix.database.vo;
import lombok.Data;
import org.matrix.database.entity.ExecutionHistory;
/**
* ExecutionHistory 给前端用于展示的VO
* @author huangxiahao
*/
@Data
public class ExecutionHistoryVo extends ExecutionHistory {
private String caseName;
private String dataName;
}
package org.matrix.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 执行历史中的执行状态
* @author huangxiahao
*/
@Getter
@AllArgsConstructor
public enum ExecutionHistoryStatus {
/**
* SQL类型动作,该动作执行SQL语句
*/
READY(0, "准备阶段"),
/**
* HTTP类型动作,该动作执行HTTP接口
*/
RUN(1, "执行阶段"),
/**
* 用例类型动作,该动作执行另外的测试用例
*/
FINISH(2, "完成阶段");
/**
* 数据库里记录的字段使用该字段来记录
*/
@EnumValue
private final int code;
private final String des;
}
......@@ -15,11 +15,13 @@ public class CaseExecuteVo extends SocketVo {
private TestExecuteType type = TestExecuteType.TEST_CASE;
private Long userId;
private Long userId = -1L;
private Long projectId;
private Long projectId = -1L;
private Long envId;
private Long envId = -1L;
private Long jobId = -1L ;
private List<TestCaseListDataBto> testCaseListDataBtoList;
}
......@@ -3,8 +3,6 @@ package org.matrix.socket;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import org.matrix.actuators.usecase.CaseActuator;
import org.matrix.actuators.util.ThreadUtil;
import org.matrix.database.entity.TestCaseListDataBto;
import org.matrix.database.service.ITestDataService;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
......@@ -12,9 +10,6 @@ import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.util.List;
import java.util.UUID;
/**
* webSocket处理
*
......@@ -26,26 +21,34 @@ public class HttpAuthHandler extends TextWebSocketHandler {
final CaseActuator caseActuator;
final ITestDataService testDataService;
public HttpAuthHandler(CaseActuator caseActuator, ITestDataService testDataService) {
this.caseActuator = caseActuator;
this.testDataService = testDataService;
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
System.out.println("连接成功了");
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
System.out.println("收到了: " + message.toString());
session.sendMessage(new TextMessage("message get success"));
String payload = message.getPayload();
//检验客户端是否进行测试用例调试
try {
SocketVo socketVo = JSON.parseObject(payload, SocketVo.class);
//如果客户端发送测试用例调试命令,则进行测试用例的执行
if (socketVo != null && socketVo.getSocketType()!=null) {
if (socketVo != null && socketVo.getSocketType() != null) {
if (SocketType.TEST_CASE_EXECUTE.equals(socketVo.getSocketType())) {
runTestCase(session, payload);
}else if (SocketType.TEST_CASE_MONITOR.equals(socketVo.getSocketType())){
CaseExecuteVo caseExecuteVo = JSON.parseObject(payload, CaseExecuteVo.class);
caseActuator.runTestCase(session, caseExecuteVo);
} else if (SocketType.TEST_CASE_MONITOR.equals(socketVo.getSocketType())) {
ExecuteMonitorVo caseExecuteVo = JSON.parseObject(payload, ExecuteMonitorVo.class);
ExecuteMonitorSocketPool.add(caseExecuteVo.getUnionKey(),session);
ExecuteMonitorSocketPool.add(caseExecuteVo.getUnionKey(), session);
} else {
session.sendMessage(new TextMessage("入参不符合规定"));
}
......@@ -61,30 +64,6 @@ public class HttpAuthHandler extends TextWebSocketHandler {
ExecuteMonitorSocketPool.remove(session);
}
/**
* 执行TestCase,并控制运行态日志池,进行日志的生成。
*/
private void runTestCase(WebSocketSession session, String payload) {
//将websocketSession 加入到socket池子中
Long currentThreadId = ThreadUtil.currentThreadId();
TestCaseExecuteSocketPool.add(currentThreadId, session);
CaseExecuteVo caseExecuteVo = JSON.parseObject(payload, CaseExecuteVo.class);
List<TestCaseListDataBto> testCaseListDataBtoList = caseExecuteVo.getTestCaseListDataBtoList();
String unionKey = UUID.randomUUID().toString();
for (TestCaseListDataBto testCaseListDataBto : testCaseListDataBtoList) {
LogQueueRuntime
.initTestCaseLog(caseExecuteVo.getUserId()
,testCaseListDataBto.getTestCase().getId()
, caseExecuteVo.getType()
, unionKey
);
//执行测试用例
caseActuator.executeTestCases(testCaseListDataBto, caseExecuteVo.getEnvId(), caseExecuteVo.getProjectId());
}
//将本次产生的数据清除
TestCaseExecuteSocketPool.remove(Thread.currentThread().getId());
//将本次产生的日志从执行状态设为停止状态
LogQueueRuntime.stopExecutionRecords(unionKey);
}
}
......@@ -11,7 +11,6 @@ import org.matrix.util.SpringUtils;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
......@@ -38,10 +37,6 @@ public class LogQueueRuntime {
getExecutionRecordService().addExecutionRecord(testExecuteLog,log);
}
public static void stopExecutionRecords(String unionKey){
getExecutionRecordService().setStatusByUnionKey(unionKey,ExecutionRecType.STOP);
}
/**
* 如果返回false,表示该用例已经在线程中执行了
......@@ -73,20 +68,21 @@ public class LogQueueRuntime {
}
public static void remove(Long threadId) {
TestCaseExecuteSocketPool.remove(threadId);
LOG_MAP.remove(threadId);
}
public static void sendMessage(Long threadId,String unionKey, Object log) {
try {
TestExecuteLog testExecuteLog = LOG_MAP.get(threadId);
// 测试任务ID.测试用例ID.测试数据ID
String message = JSONObject.toJSONString(log);
String logMessage =
String.format(
"%s.%s.%s.%s"
,testExecuteLog.getTestJobId()
,testExecuteLog.getJobId()
,testExecuteLog.getTestCaseId()
,testExecuteLog.getTestDataId()
,JSONObject.toJSONString(log)
,message.substring(1,message.length()-1)
);
//如果unionKey中存在监听的socket则向该socket发送消息
List<WebSocketSession> webSocketSessions = ExecuteMonitorSocketPool.get(unionKey);
......@@ -110,13 +106,30 @@ public class LogQueueRuntime {
}
}
public static void initTestCaseLog(Long userId, Long caseId,TestExecuteType type,String unionKey) {
public static void initTestCaseLog(Long jobId,Long userId, Long caseId,TestExecuteType type,String unionKey) {
TestExecuteLog testExecuteLog = new TestExecuteLog();
testExecuteLog.setJobId(jobId);
testExecuteLog.setTestCaseId(caseId);
testExecuteLog.setUserId(userId);
testExecuteLog.setType(type);
testExecuteLog.setUnionKey(unionKey);
if (checkIsInRun(testExecuteLog)){
throw new GlobalException("当前用例正在执行中");
}
LogQueueRuntime.put(ThreadUtil.currentThreadId(), testExecuteLog);
}
public static Boolean checkIsInRun(TestExecuteLog testExecuteLog){
for (TestExecuteLog value : LOG_MAP.values()) {
if (value.equals(testExecuteLog)){
return true;
}
}
return false;
}
public static TestExecuteLog getCurrentTestExecute(Long threadId){
return LOG_MAP.get(threadId);
}
}
......@@ -4,6 +4,7 @@ import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* 用例日志临时存储对象
......@@ -13,15 +14,15 @@ import java.util.List;
public class TestExecuteLog {
private Long testJobId = -1L;
private Long jobId = -1L;
/**
* 用于记录执行记录的唯一KEY,同一批次的执行记录应该具有相同的KEY。
*/
private String unionKey;
private TestExecuteType type;
private TestExecuteType type = TestExecuteType.TEST_CASE;
private Long userId;
private Long userId = -1L ;
private Long testCaseId = -1L;
......@@ -38,5 +39,20 @@ public class TestExecuteLog {
this.log.add(log);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof TestExecuteLog)) {
return false;
}
TestExecuteLog that = (TestExecuteLog) o;
return getType() == that.getType() && Objects.equals(getUserId(), that.getUserId()) && Objects.equals(getTestCaseId(), that.getTestCaseId());
}
@Override
public int hashCode() {
return Objects.hash(getType(), getUserId(), getTestCaseId());
}
}
......@@ -66,6 +66,9 @@ public class TestPigeon extends AbstractTestNGSpringContextTests {
@Autowired
private HttpClientActuator httpClientActuator;
@Autowired
private CaseActuator caseActuator;
@Parameters({"sql", "envId", "projectId"})
@BeforeClass
public void beforeClass(String sql, Long envId, Long projectId) {
......@@ -84,8 +87,8 @@ public class TestPigeon extends AbstractTestNGSpringContextTests {
@Test(dataProvider = "testData")
public void test(Map<String, String> data) {
//todo 黄夏豪 这里的调用接口我改掉了 等我改完了再喊你改
long caseId = Long.parseLong(data.get("id"));
CaseActuator caseActuator = new CaseActuator(checkPointActuator, httpClientActuator);
TestCase testCase = java.util.Optional.of(caseService.getById(caseId))
.orElseThrow(() -> new GlobalException(String.format("没有找到id = %d 的TestCase", caseId)));
List<TestData> testDataList = java.util.Optional.of(dataService.list(Wrappers.lambdaQuery(TestData.class)
......@@ -95,7 +98,8 @@ public class TestPigeon extends AbstractTestNGSpringContextTests {
testCaseBTO.setTestCase(testCase);
String unionKey = UUID.randomUUID().toString();
LogQueueRuntime
.initTestCaseLog(1L
.initTestCaseLog(null
, 1L
, testCase.getId()
, TestExecuteType.TEST_CASE
, unionKey
......@@ -108,8 +112,6 @@ public class TestPigeon extends AbstractTestNGSpringContextTests {
}
//将本次产生的数据清除
TestCaseExecuteSocketPool.remove(Thread.currentThread().getId());
//将本次产生的日志从执行状态设为停止状态
LogQueueRuntime.stopExecutionRecords(unionKey);
}
@AfterMethod
......
package org.matrix.actuators.sql;
package org.matrix.actuators.http;
import com.alibaba.fastjson.JSON;
import org.junit.jupiter.api.Test;
......
......@@ -25,7 +25,7 @@ class MoveActuatorTest {
@Test
void runMoveSQL() {
LogQueueRuntime.initTestCaseLog(null, null, null, null);
LogQueueRuntime.initTestCaseLog(null,null, null, null, null);
moveActuator.runMove(72L, 9L, 1L, null, MoveStrategy.PRE_MOVE);
String result = moveActuator.parseMoveVar("${pre72.url}[1]");
Assertions.assertTrue(result.length() > 0, result);
......@@ -33,7 +33,7 @@ class MoveActuatorTest {
@Test
void runMoveHTTP() {
LogQueueRuntime.initTestCaseLog(null, null, null, null);
LogQueueRuntime.initTestCaseLog(null,null, null, null, null);
moveActuator.runMove(72L, 10L, 1L, null, MoveStrategy.PRE_MOVE);
String result = moveActuator.parseMoveVar("${pre72.responseBody}<$.data.records[1].name>");
Assertions.assertTrue(result.length() > 0, result);
......
package org.matrix.autotest.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.matrix.autotest.utils.PageTools;
import org.matrix.database.entity.ExecutionRecord;
import org.matrix.database.service.IExecutionHistoryService;
import org.matrix.database.service.IExecutionRecordService;
import org.matrix.database.vo.CommonResult;
import org.matrix.database.vo.CommonResultObj;
import org.matrix.database.vo.ExecutionHistoryVo;
import org.matrix.enums.ExecutionRecType;
import org.matrix.socket.TestExecuteType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
/**
* @author hxh
*/
@CrossOrigin
@RestController
@RequestMapping("/executionHistory")
@Api(tags = "对执行历史execution_history的基本操作")
public class ExecutionHistoryController {
private final IExecutionHistoryService executionHistoryService;
public ExecutionHistoryController(IExecutionHistoryService executionHistoryService) {
this.executionHistoryService = executionHistoryService;
}
@GetMapping("/findExecutionHistory")
public ResponseEntity<CommonResultObj<IPage<ExecutionHistoryVo>>> findExecutionHistory(
@RequestParam(defaultValue = "10") int pageSize,
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "-1") Long jobId,
Long caseId
){
IPage<ExecutionHistoryVo> page = executionHistoryService.pageByCaseIdAndJobId(caseId, jobId, pageNum, pageSize);
return page.getRecords().size() != 0
? CommonResult.success(page, "查询成功")
: CommonResult.failed(page, "查询失败或无数据");
}
}
......@@ -62,7 +62,6 @@ public class ExecutionRecordController {
.eq(testDataId != null, ExecutionRecord::getTestDataId, testDataId)
.eq(testCaseId != null, ExecutionRecord::getTestCaseId, testCaseId)
.eq(StringUtils.hasLength(unionKey), ExecutionRecord::getUnionKey, unionKey)
.eq(status != null, ExecutionRecord::getStatus, status)
.eq(type != null, ExecutionRecord::getType, type)
)).orElse(new Page<>());
PageTools.pageTool(pageSize, pageNum, results);
......
......@@ -71,11 +71,14 @@ public class TestCaseController {
@RequestParam(defaultValue = "10") int pageSize,
@RequestParam(defaultValue = "1") int pageNum,
String name, @PathVariable Long projectId) {
final long start = System.currentTimeMillis();
Page<TestCase> results = Optional.ofNullable(testCaseService.page(Page.of(pageNum, pageSize)
, Wrappers.lambdaQuery(TestCase.class).eq(TestCase::getProjectId, projectId)
.like(StringUtils.hasLength(name)
, TestCase::getName, name))).orElse(new Page<>());
PageTools.pageTool(pageSize, pageNum, results);
System.out.println(System.currentTimeMillis() - start);
return results.getRecords().size() != 0
? CommonResult.success(results, "查询成功")
: CommonResult.failed(results, "查询失败或无数据");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论