feat(接口测试): MockServer功能开发

This commit is contained in:
song-tianyang 2024-02-21 18:38:48 +08:00 committed by Craftsman
parent c95a5c047c
commit 9372b82e98
31 changed files with 2009 additions and 288 deletions

View File

@ -27,6 +27,9 @@ public class FilterChainUtils {
filterChainDefinitionMap.put("/authsource/list/allenable", "anon");
filterChainDefinitionMap.put("/sso/callback/**", "anon");
filterChainDefinitionMap.put("/license/validate", "anon");
//mock-server
filterChainDefinitionMap.put("/mock-server/**", "anon");
//功能用例副文本访问
filterChainDefinitionMap.put("/attachment/download/file/**", "anon");
//用例评审副文本访问

View File

@ -4,6 +4,7 @@ package io.metersphere.sdk.util;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
@ -11,10 +12,15 @@ import org.dom4j.io.XMLWriter;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.XMLFilterImpl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class XMLUtils {
public static final boolean IS_TRANS = false;
@ -26,7 +32,7 @@ public class XMLUtils {
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
}catch (Exception e){
} catch (Exception e) {
LogUtils.error(e);
}
if (!IS_TRANS) {
@ -100,4 +106,40 @@ public class XMLUtils {
}
};
}
public static Document stringToDocument(String xml) throws Exception {
return getDocument(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8.name())));
}
public static Map<String, Object> xmlStringToJson(String xml) {
try {
return elementToMap(stringToDocument(xml).getRootElement());
} catch (Exception e) {
LogUtils.error(e);
}
return new LinkedHashMap<>();
}
private static Map<String, Object> elementToMap(Element node) {
LinkedHashMap<String, Object> result = new LinkedHashMap<>();
List<Element> listElement = node.elements();// 所有一级子节点的list
if (!listElement.isEmpty()) {
List<Map<String, Object>> list = new ArrayList<>();
for (Element e : listElement) {// 遍历所有一级子节点
Map<String, Object> jsonObject = elementToMap(e);
list.add(jsonObject);
}
if (list.size() == 1) {
result.put(node.getName(), list.get(0));
} else {
result.put(node.getName(), list);
}
} else {
if (!StringUtils.isAllBlank(node.getName(), node.getText())) {
result.put(node.getName(), node.getText());
}
}
return result;
}
}

View File

@ -4,5 +4,6 @@ public enum ApiResource {
PROJECT,
API_DEFINITION,
API_TEST_CASE,
API_SCENARIO
API_SCENARIO,
API_DEFINITION_MOCK,
}

View File

@ -0,0 +1,6 @@
package io.metersphere.api.constants.mockserver;
// mock匹配规则
public enum ParamConditionEnums {
EQUALS, NOT_EQUALS, CONTAINS, LENGTH_EQUALS, LENGTH_NOT_EQUALS, LENGTH_LARGE, LENGTH_SHOT, REGULAR_MATCH
}

View File

@ -2,12 +2,15 @@ package io.metersphere.api.controller.definition;
import com.github.pagehelper.Page;
import com.github.pagehelper.page.PageMethod;
import io.metersphere.api.constants.ApiResource;
import io.metersphere.api.domain.ApiDefinitionMock;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockAddRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockPageRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockUpdateRequest;
import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.service.ApiValidateService;
import io.metersphere.api.service.definition.ApiDefinitionMockLogService;
import io.metersphere.api.service.definition.ApiDefinitionMockNoticeService;
import io.metersphere.api.service.definition.ApiDefinitionMockService;
@ -43,12 +46,17 @@ public class ApiDefinitionMockController {
@Resource
private ApiDefinitionMockService apiDefinitionMockService;
@Resource
private ApiFileResourceService apiFileResourceService;
@Resource
private ApiValidateService apiValidateService;
@PostMapping("/page")
@Operation(summary = "接口测试-接口管理-接口 Mock")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_MOCK_READ)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public Pager<List<ApiDefinitionMockDTO>> getPage(@Validated @RequestBody ApiDefinitionMockPageRequest request) {
apiValidateService.validateApiMenuInProject(request.getProjectId(), ApiResource.PROJECT.name());
Page<Object> page = PageMethod.startPage(request.getCurrent(), request.getPageSize(),
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "create_time desc");
return PageUtils.setPageInfo(page, apiDefinitionMockService.getPage(request));
@ -59,6 +67,7 @@ public class ApiDefinitionMockController {
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_MOCK_READ)
@CheckOwner(resourceId = "#request.getId()", resourceType = "api_definition_mock")
public ApiDefinitionMockDTO detail(@Validated @RequestBody ApiDefinitionMockRequest request) {
apiValidateService.validateApiMenuInProject(request.getProjectId(), ApiResource.PROJECT.name());
return apiDefinitionMockService.detail(request);
}
@ -69,6 +78,7 @@ public class ApiDefinitionMockController {
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
@SendNotice(taskType = NoticeConstants.TaskType.API_DEFINITION_TASK, event = NoticeConstants.Event.MOCK_CREATE, target = "#targetClass.getApiMockDTO(#request)", targetClass = ApiDefinitionMockNoticeService.class)
public ApiDefinitionMock add(@Validated @RequestBody ApiDefinitionMockAddRequest request) {
apiValidateService.validateApiMenuInProject(request.getApiDefinitionId(), ApiResource.API_DEFINITION.name());
return apiDefinitionMockService.create(request, SessionUtils.getUserId());
}
@ -79,6 +89,7 @@ public class ApiDefinitionMockController {
@CheckOwner(resourceId = "#request.getId()", resourceType = "api_definition_mock")
@SendNotice(taskType = NoticeConstants.TaskType.API_DEFINITION_TASK, event = NoticeConstants.Event.MOCK_UPDATE, target = "#targetClass.getApiMockDTO(#request)", targetClass = ApiDefinitionMockNoticeService.class)
public ApiDefinitionMock update(@Validated @RequestBody ApiDefinitionMockUpdateRequest request) {
apiValidateService.validateApiMenuInProject(request.getId(), ApiResource.API_DEFINITION_MOCK.name());
return apiDefinitionMockService.update(request, SessionUtils.getUserId());
}
@ -88,6 +99,7 @@ public class ApiDefinitionMockController {
@Log(type = OperationLogType.UPDATE, expression = "#msClass.updateEnableLog(#id)", msClass = ApiDefinitionMockLogService.class)
@CheckOwner(resourceId = "#id", resourceType = "api_definition_mock")
public void updateEnable(@PathVariable String id) {
apiValidateService.validateApiMenuInProject(id, ApiResource.API_DEFINITION_MOCK.name());
apiDefinitionMockService.updateEnable(id);
}
@ -98,6 +110,7 @@ public class ApiDefinitionMockController {
@CheckOwner(resourceId = "#request.getId()", resourceType = "api_definition_mock")
@SendNotice(taskType = NoticeConstants.TaskType.API_DEFINITION_TASK, event = NoticeConstants.Event.MOCK_DELETE, target = "#targetClass.getApiMockDTO(#request.id)", targetClass = ApiDefinitionMockNoticeService.class)
public void delete(@Validated @RequestBody ApiDefinitionMockRequest request) {
apiValidateService.validateApiMenuInProject(request.getId(), ApiResource.API_DEFINITION_MOCK.name());
apiDefinitionMockService.delete(request, SessionUtils.getUserId());
}
@ -107,6 +120,7 @@ public class ApiDefinitionMockController {
@Log(type = OperationLogType.UPDATE, expression = "#msClass.copyLog(#request)", msClass = ApiDefinitionMockLogService.class)
@CheckOwner(resourceId = "#request.getId()", resourceType = "api_definition_mock")
public ApiDefinitionMock copy(@Validated @RequestBody ApiDefinitionMockRequest request) {
apiValidateService.validateApiMenuInProject(request.getId(), ApiResource.API_DEFINITION_MOCK.name());
return apiDefinitionMockService.copy(request, SessionUtils.getUserId());
}
@ -114,8 +128,6 @@ public class ApiDefinitionMockController {
@Operation(summary = "上传接口 Mock 所需的文件资源并返回文件ID")
@RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.PROJECT_API_DEFINITION_MOCK_ADD, PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE})
public String uploadTempFile(@RequestParam("file") MultipartFile file) {
return apiDefinitionMockService.uploadTempFile(file);
return apiFileResourceService.uploadTempFile(file);
}
}

View File

@ -0,0 +1,40 @@
package io.metersphere.api.controller.mockserver;
import io.metersphere.api.service.mockserver.MockServerService;
import io.metersphere.api.utils.MockServerUtils;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.system.controller.handler.annotation.NoResultHolder;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
@RequestMapping(value = "/mock-server")
@Tag(name = "接口测试-接口管理-接口定义-Mock")
public class MockServerController {
@Resource
private MockServerService mockServerService;
@RequestMapping(value = "/{projectNum}/{apiInfo}/**", method = RequestMethod.OPTIONS)
@NoResultHolder
public Object optionsRequest(@PathVariable String projectNum, @PathVariable String apiInfo, HttpServletRequest request, HttpServletResponse response) {
Map<String, String> requestHeaderMap = MockServerUtils.getHttpRequestHeader(request);
return mockServerService.execute(HttpMethodConstants.OPTIONS.name(), requestHeaderMap, projectNum, apiInfo, request, response);
}
@RequestMapping(value = "/{projectNum}/{apiInfo}/**")
@NoResultHolder
public Object mockRequest(@PathVariable String projectNum, @PathVariable String apiInfo, HttpServletRequest request, HttpServletResponse response) {
Map<String, String> requestHeaderMap = MockServerUtils.getHttpRequestHeader(request);
String method = request.getMethod();
return mockServerService.execute(method, requestHeaderMap, projectNum, apiInfo, request, response);
}
}

View File

@ -1,13 +1,12 @@
package io.metersphere.api.dto.definition;
import io.metersphere.api.domain.ApiDefinitionMock;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.api.dto.mockserver.MockMatchRule;
import io.metersphere.api.dto.mockserver.MockResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* @author lan
*/
@ -16,10 +15,10 @@ import java.util.List;
public class ApiDefinitionMockDTO extends ApiDefinitionMock {
@Schema(description = "请求内容")
private AbstractMsTestElement matching;
private MockMatchRule matching;
@Schema(description = "响应内容")
private List<HttpResponse> response;
private MockResponse response;
@Schema(description = "创建人名称")
private String createUserName;

View File

@ -1,7 +1,6 @@
package io.metersphere.api.dto.definition;
import io.metersphere.api.dto.request.http.Header;
import io.metersphere.api.dto.request.http.body.Body;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@ -11,6 +10,7 @@ import java.util.List;
/**
* @author: LAN
* @editer: 建国
* @date: 2023/11/21 18:12
* @version: 1.0
*/
@ -20,30 +20,22 @@ public class HttpResponse implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "唯一ID")
private String id;
@Schema(description = "响应码")
private String statusCode;
@Schema(description = "默认响应标识")
private boolean defaultFlag;
@Schema(description = "响应名称")
private String name;
/**
* 请求头
*/
@Schema(description = "响应请求头")
private List<Header> headers;
/**
* 请求体
*/
@Schema(description = "响应请求体")
private Body body;
private ResponseBody body;
/**
* 请求方法
*/
@Schema(description = "响应请求方法")
private String statusCode;
/**
* 默认标识
*/
@Schema(description = "默认响应标识")
private Boolean defaultFlag;
}

View File

@ -0,0 +1,10 @@
package io.metersphere.api.dto.definition;
import io.metersphere.api.dto.ApiFile;
import lombok.Data;
@Data
public class ResponseBinaryBody extends ApiFile {
private boolean sendAsBody;
private String description;
}

View File

@ -0,0 +1,37 @@
package io.metersphere.api.dto.definition;
import io.metersphere.api.dto.request.http.body.JsonBody;
import io.metersphere.api.dto.request.http.body.RawBody;
import io.metersphere.api.dto.request.http.body.XmlBody;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serializable;
/**
* 接口响应体mock期望响应体中使用到的body
*/
@Data
public class ResponseBody implements Serializable {
private static final long serialVersionUID = 1L;
@NotBlank
@Size(max = 20)
private String bodyType;
@Valid
private JsonBody jsonBody;
@Valid
private XmlBody xmlBody;
@Valid
private RawBody rawBody;
@Valid
private ResponseBinaryBody binaryBody;
}

View File

@ -1,5 +1,7 @@
package io.metersphere.api.dto.definition.request;
import io.metersphere.api.dto.mockserver.MockMatchRule;
import io.metersphere.api.dto.mockserver.MockResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
@ -32,17 +34,14 @@ public class ApiDefinitionMockAddRequest implements Serializable {
@Schema(description = "标签")
private LinkedHashSet<
@NotBlank
@Size(min = 1, max = 64, message = "{api_test_case.tag.length_range}")
String> tags;
@Schema(description = "请求内容")
@NotBlank
private String matching;
private MockMatchRule mockMatchRule;
@Schema(description = "请求内容")
@NotBlank
private String response;
private MockResponse response;
@Schema(description = "接口fk", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_mock.api_definition_id.not_blank}")

View File

@ -26,10 +26,4 @@ public class ApiDefinitionMockRequest implements Serializable {
@NotBlank(message = "{api_definition_mock.project_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition_mock.project_id.length_range}")
private String projectId;
@Schema(description = "接口fk", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_mock.api_definition_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition_mock.api_definition_id.length_range}")
private String apiDefinitionId;
}

View File

@ -0,0 +1,115 @@
package io.metersphere.api.dto.mockserver;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.XMLUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import java.util.Map;
@Data
public class BodyParamMatchRole {
@Schema(description = "参数类型(kv/json/xml/raw 默认为raw)")
private String paramType;
@Schema(description = "formData的匹配规则")
private keyValueMatchRole formDataMatch;
@Schema(description = "文本匹配规则")
private String raw;
public boolean matchXml(Map<String, Object> requestMap) {
Map<String, Object> mockMap = XMLUtils.xmlStringToJson(raw);
return this.matchMap(mockMap, requestMap);
}
public boolean matchJson(String requestJson) {
if (StringUtils.startsWith(requestJson, "{") && StringUtils.endsWith(requestJson, "}")) {
//入参是Object如果mock期望设置的不是Object视为无法匹配
if (StringUtils.startsWith(this.raw, "{") && StringUtils.endsWith(this.raw, "}")) {
Map<String, Object> mockMap = JSON.parseMap(this.raw);
Map<String, Object> requestMap = JSON.parseMap(requestJson);
return this.matchObject(mockMap, requestMap);
} else {
return false;
}
}
if (StringUtils.startsWith(requestJson, "[") && StringUtils.endsWith(requestJson, "]")) {
List<Object> requestList = JSON.parseArray(requestJson, Object.class);
if (StringUtils.startsWith(this.raw, "{") && StringUtils.endsWith(this.raw, "}")) {
//入参是Array如果mock期望设置是Object则入参中的任意一个匹配视为匹配
Map<String, Object> mockMap = JSON.parseMap(this.raw);
for (Object requestObj : requestList) {
if (this.matchObject(mockMap, requestObj)) {
return true;
}
}
return false;
} else if (StringUtils.startsWith(this.raw, "[") && StringUtils.endsWith(this.raw, "]")) {
//入参是Array如果mock期望设置也是Array则Mock中的每个数据都匹配才视为匹配
List<Object> mockList = JSON.parseArray(requestJson, Object.class);
for (Object mockObj : mockList) {
boolean match = false;
for (int i = 0; i < requestList.size(); i++) {
Object requestObj = requestList.get(i);
match = this.matchObject(mockObj, requestObj);
if (match) {
requestList.remove(i);
break;
}
}
if (!match) {
return false;
}
}
return true;
}
return false;
}
return false;
}
private boolean matchMap(Map<String, Object> mockMap, Map<String, Object> requestMap) {
for (Map.Entry<String, Object> entry : mockMap.entrySet()) {
if (!this.matchObject(entry.getValue(), requestMap.get(entry.getKey()))) {
return false;
}
}
return true;
}
private boolean matchObject(Object mockRule, Object requestParam) {
if (ObjectUtils.anyNull(mockRule, requestParam)) {
return false;
}
if (mockRule instanceof List<?> && requestParam instanceof List<?>) {
List<Object> mockList = (List<Object>) mockRule;
List<Object> requestList = (List<Object>) requestParam;
if (mockList.size() != requestList.size()) {
return false;
}
for (int i = 0; i < mockList.size(); i++) {
if (!this.matchObject(mockList.get(i), requestList.get(i))) {
return false;
}
}
return true;
} else if (mockRule instanceof Map<?, ?> && requestParam instanceof Map<?, ?>) {
Map<String, Object> mockMap = (Map<String, Object>) mockRule;
Map<String, Object> requestMap = (Map<String, Object>) requestParam;
for (Map.Entry<?, ?> entry : mockMap.entrySet()) {
if (!this.matchObject(entry.getValue(), requestMap.get(entry.getKey()))) {
return false;
}
}
return true;
}
//既不是list也不是object直接进行值比对
return StringUtils.equals(String.valueOf(mockRule), String.valueOf(requestParam));
}
}

View File

@ -0,0 +1,40 @@
package io.metersphere.api.dto.mockserver;
import io.metersphere.api.dto.request.http.body.Body;
import io.metersphere.sdk.util.XMLUtils;
import lombok.Data;
import java.util.LinkedHashMap;
import java.util.Map;
@Data
public class HttpRequestParam {
private boolean isPost;
private LinkedHashMap<String, String> restParams;
//form-data的kv类型参数也存储在queryParamObj中
private LinkedHashMap<String, String> queryParamsObj;
private String paramType;
//JSONArray JSONObject
private String jsonString;
private Map<String, Object> xmlToJsonParam;
private String raw;
public void setXmlParam(String xmlParam) {
this.setParamType(Body.BodyType.XML.name());
this.setRaw(xmlParam);
Map<String, Object> jsonMap = XMLUtils.xmlStringToJson(xmlParam);
this.setXmlToJsonParam(jsonMap);
}
public void setJsonParam(String requestPostString) {
this.setParamType(Body.BodyType.JSON.name());
this.jsonString = requestPostString;
}
}

View File

@ -0,0 +1,70 @@
package io.metersphere.api.dto.mockserver;
import io.metersphere.api.constants.mockserver.ParamConditionEnums;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Data
public class KeyValueInfo {
@Schema(description = "Key")
private String key;
@Schema(description = "Value")
private String value;
@Schema(description = "条件")
private String condition;
@Schema(description = "描述")
private String description;
public boolean matchValue(String value) {
if (StringUtils.isBlank(this.condition) || StringUtils.equals(this.condition, ParamConditionEnums.EQUALS.name())) {
return StringUtils.equals(this.value, value);
} else if (StringUtils.equals(this.condition, ParamConditionEnums.NOT_EQUALS.name())) {
return !StringUtils.equals(this.value, value);
} else if (StringUtils.equals(this.condition, ParamConditionEnums.CONTAINS.name())) {
return StringUtils.contains(this.value, value);
} else if (StringUtils.equals(this.condition, ParamConditionEnums.LENGTH_EQUALS.name())) {
try {
int length = Integer.parseInt(value);
return this.value.length() == length;
} catch (Exception e) {
return false;
}
} else if (StringUtils.equals(this.condition, ParamConditionEnums.LENGTH_NOT_EQUALS.name())) {
try {
int length = Integer.parseInt(value);
return this.value.length() != length;
} catch (Exception e) {
return false;
}
} else if (StringUtils.equals(this.condition, ParamConditionEnums.LENGTH_SHOT.name())) {
try {
int length = Integer.parseInt(value);
return this.value.length() < length;
} catch (Exception e) {
return false;
}
} else if (StringUtils.equals(this.condition, ParamConditionEnums.LENGTH_LARGE.name())) {
try {
int length = Integer.parseInt(value);
return this.value.length() > length;
} catch (Exception e) {
return false;
}
} else if (StringUtils.equals(this.condition, ParamConditionEnums.REGULAR_MATCH.name())) {
try {
Pattern pattern = Pattern.compile(value);
Matcher matcher = pattern.matcher(this.value);
boolean isMatch = matcher.matches();
return isMatch;
} catch (Exception e) {
return false;
}
} else {
return false;
}
}
}

View File

@ -0,0 +1,71 @@
package io.metersphere.api.dto.mockserver;
import io.metersphere.api.dto.request.http.body.Body;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.io.Serializable;
import java.util.Map;
//mock匹配规则
@Data
public class MockMatchRule implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "请求头匹配规则")
private keyValueMatchRole header = new keyValueMatchRole();
@Schema(description = "query参数匹配规则")
private keyValueMatchRole query = new keyValueMatchRole();
@Schema(description = "REST参数匹配规则")
private keyValueMatchRole rest = new keyValueMatchRole();
@Schema(description = "body参数匹配规则")
private BodyParamMatchRole body = new BodyParamMatchRole();
public boolean keyValueMatch(String matchType, Map<String, String> matchParam) {
keyValueMatchRole matchRole = null;
switch (matchType) {
case "header":
matchRole = header;
break;
case "query":
matchRole = query;
break;
case "rest":
matchRole = rest;
break;
case "body":
if (body != null) {
matchRole = body.getFormDataMatch();
}
break;
default:
break;
}
if (matchRole == null) {
return true;
}
return matchRole.match(matchParam);
}
public boolean requestParamMatch(HttpRequestParam httpRequestParam) {
if (!this.keyValueMatch("rest", httpRequestParam.getRestParams())) {
return false;
}
if (httpRequestParam.isPost()) {
if (StringUtils.equalsIgnoreCase(body.getParamType(), Body.BodyType.XML.name())) {
return body.matchXml(httpRequestParam.getXmlToJsonParam());
} else if (StringUtils.equalsIgnoreCase(body.getParamType(), Body.BodyType.JSON.name())) {
return body.matchJson(httpRequestParam.getJsonString());
} else if (StringUtils.equalsIgnoreCase(body.getParamType(), Body.BodyType.FORM_DATA.name())) {
return this.keyValueMatch("body", httpRequestParam.getQueryParamsObj());
} else if (StringUtils.isNotBlank(body.getRaw())) {
return StringUtils.contains(body.getRaw(), httpRequestParam.getRaw());
}
} else {
return this.keyValueMatch("query", httpRequestParam.getQueryParamsObj());
}
return true;
}
}

View File

@ -0,0 +1,30 @@
package io.metersphere.api.dto.mockserver;
import io.metersphere.api.dto.definition.ResponseBody;
import io.metersphere.api.dto.request.http.Header;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class MockResponse implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "响应码")
private int statusCode;
@Schema(description = "响应请求头")
private List<Header> headers;
@Schema(description = "是否使用api响应体")
private boolean useApiResponse;
@Schema(description = "接口响应IDuseApiResponse为true时使用")
private String apiResponseId;
@Schema(description = "响应请求体")
private ResponseBody body;
}

View File

@ -0,0 +1,37 @@
package io.metersphere.api.dto.mockserver;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.apache.commons.collections4.MapUtils;
import java.util.List;
import java.util.Map;
@Data
public class keyValueMatchRole {
@Schema(description = "是否是全部匹配 false为任意匹配")
private boolean isMatchAll;
@Schema(description = "匹配规则")
private List<KeyValueInfo> matchRules;
public boolean match(Map<String, String> matchParam) {
if (MapUtils.isEmpty(matchParam)) {
return true;
}
if (isMatchAll) {
for (KeyValueInfo matchRule : matchRules) {
if (!matchRule.matchValue(matchParam.get(matchRule.getKey()))) {
return false;
}
}
return true;
} else {
for (KeyValueInfo matchRule : matchRules) {
if (matchRule.matchValue(matchParam.get(matchRule.getKey()))) {
return true;
}
}
return false;
}
}
}

View File

@ -64,4 +64,6 @@ public interface ExtApiDefinitionMapper {
Long getLastPos(@Param("projectId") String projectId, @Param("basePos") Long basePos);
List<ApiResourceModuleInfo> getModuleInfoByIds(@Param("ids") List<String> ids);
ApiDefinition selectByProjectNumAndApiNum(@Param("projectNum") String projectNum, @Param("apiNum") String apiNum);
}

View File

@ -550,4 +550,14 @@
#{id}
</foreach>
</select>
<select id="selectByProjectNumAndApiNum" resultType="io.metersphere.api.domain.ApiDefinition">
select *
from api_definition
where api_definition.num = #{apiNum}
AND project_id IN (SELECT id
FROM project
WHERE num = #{projectNum})
AND latest = true
limit 1
</select>
</mapper>

View File

@ -1,9 +1,10 @@
package io.metersphere.api.parser.api;
import io.metersphere.api.constants.ApiConstants;
import io.metersphere.api.dto.definition.HttpResponse;
import io.metersphere.api.dto.converter.ApiDefinitionImport;
import io.metersphere.api.dto.converter.ApiDefinitionImportDetail;
import io.metersphere.api.dto.definition.HttpResponse;
import io.metersphere.api.dto.definition.ResponseBody;
import io.metersphere.api.dto.request.ImportRequest;
import io.metersphere.api.dto.request.http.*;
import io.metersphere.api.dto.request.http.auth.NoAuth;
@ -184,14 +185,13 @@ public class Swagger3Parser<T> implements ImportParser<ApiDefinitionImport> {
body.setWwwFormBody(wwwFormBody);
}
private void parseResponse(ApiResponses responseBody, List<HttpResponse> response) {
if (responseBody != null) {
responseBody.forEach((key, value) -> {
HttpResponse httpResponse = new HttpResponse();
//TODO headers
httpResponse.setStatusCode(key);
Body body = new Body();
ResponseBody body = new ResponseBody();
Map<String, io.swagger.v3.oas.models.headers.Header> headers = value.getHeaders();
if (MapUtils.isNotEmpty(headers)) {
List<Header> headerList = new ArrayList<>();
@ -218,6 +218,49 @@ public class Swagger3Parser<T> implements ImportParser<ApiDefinitionImport> {
}
private void setBodyData(String k, io.swagger.v3.oas.models.media.MediaType value, ResponseBody body) {
//TODO body 默认如果json格式
JsonSchemaItem jsonSchemaItem = parseSchema(value.getSchema());
switch (k) {
case MediaType.APPLICATION_JSON_VALUE, MediaType.ALL_VALUE -> {
body.setBodyType(Body.BodyType.JSON.name());
JsonBody jsonBody = new JsonBody();
jsonBody.setJsonSchema(jsonSchemaItem);
jsonBody.setEnableJsonSchema(true);
if (ObjectUtils.isNotEmpty(value.getExample())) {
jsonBody.setJsonValue(ApiDataUtils.toJSONString(value.getExample()));
}
body.setJsonBody(jsonBody);
}
case MediaType.APPLICATION_XML_VALUE -> {
if (StringUtils.isBlank(body.getBodyType())) {
body.setBodyType(Body.BodyType.XML.name());
}
XmlBody xml = new XmlBody();
//xml.setValue(XMLUtils.jsonToXmlStr(jsonValue));
body.setXmlBody(xml);
}
case MediaType.MULTIPART_FORM_DATA_VALUE -> {
if (StringUtils.isBlank(body.getBodyType())) {
body.setBodyType(Body.BodyType.FORM_DATA.name());
}
}
case MediaType.APPLICATION_OCTET_STREAM_VALUE -> {
if (StringUtils.isBlank(body.getBodyType())) {
body.setBodyType(Body.BodyType.BINARY.name());
}
}
case MediaType.TEXT_PLAIN_VALUE -> {
if (StringUtils.isBlank(body.getBodyType())) {
body.setBodyType(Body.BodyType.RAW.name());
}
RawBody rawBody = new RawBody();
body.setRawBody(rawBody);
}
default -> body.setBodyType(Body.BodyType.NONE.name());
}
}
private void setBodyData(String k, io.swagger.v3.oas.models.media.MediaType value, Body body) {
//TODO body 默认如果json格式
JsonSchemaItem jsonSchemaItem = parseSchema(value.getSchema());

View File

@ -1,10 +1,8 @@
package io.metersphere.api.service;
import io.metersphere.api.constants.ApiResource;
import io.metersphere.project.constants.ProjectMenuConstants;
import io.metersphere.system.service.CommonProjectService;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Collections;
@ -16,16 +14,7 @@ public class ApiValidateService {
//校验接口菜单是否开启
public void validateApiMenuInProject(String resourceId, String resourceType) {
String tableName = null;
if (StringUtils.equals(resourceType, ApiResource.PROJECT.name())) {
tableName = "project";
} else if (StringUtils.equals(resourceType, ApiResource.API_DEFINITION.name())) {
tableName = "api_definition";
} else if (StringUtils.equals(resourceType, ApiResource.API_TEST_CASE.name())) {
tableName = "api_test_case";
} else if (StringUtils.equals(resourceType, ApiResource.API_SCENARIO.name())) {
tableName = "api_scenario";
}
String tableName = resourceType.toLowerCase();
commonProjectService.checkProjectHasModuleMenu(Collections.singletonList(ProjectMenuConstants.MODULE_MENU_API_TEST), resourceId, tableName);
}
}

View File

@ -5,24 +5,25 @@ import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.HttpResponse;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockAddRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockPageRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockUpdateRequest;
import io.metersphere.api.dto.mockserver.MockMatchRule;
import io.metersphere.api.dto.mockserver.MockResponse;
import io.metersphere.api.mapper.ApiDefinitionMapper;
import io.metersphere.api.mapper.ApiDefinitionMockConfigMapper;
import io.metersphere.api.mapper.ApiDefinitionMockMapper;
import io.metersphere.api.mapper.ExtApiDefinitionMockMapper;
import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.service.ProjectService;
import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.FileAssociationSourceUtil;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator;
@ -31,7 +32,6 @@ import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Optional;
@ -68,7 +68,7 @@ public class ApiDefinitionMockService {
ApiDefinitionMock apiDefinitionMock = checkApiDefinitionMock(request.getId());
ApiDefinitionMockDTO apiDefinitionMockDTO = new ApiDefinitionMockDTO();
handleMockConfig(request.getId(), apiDefinitionMockDTO);
handleApiDefinition(request.getApiDefinitionId(), apiDefinitionMockDTO);
handleApiDefinition(apiDefinitionMock.getApiDefinitionId(), apiDefinitionMockDTO);
BeanUtils.copyBean(apiDefinitionMockDTO, apiDefinitionMock);
return apiDefinitionMockDTO;
}
@ -76,8 +76,8 @@ public class ApiDefinitionMockService {
public void handleMockConfig(String id, ApiDefinitionMockDTO apiDefinitionMockDTO) {
Optional<ApiDefinitionMockConfig> apiDefinitionMockConfigOptional = Optional.ofNullable(apiDefinitionMockConfigMapper.selectByPrimaryKey(id));
apiDefinitionMockConfigOptional.ifPresent(config -> {
apiDefinitionMockDTO.setMatching(ApiDataUtils.parseObject(new String(config.getMatching()), AbstractMsTestElement.class));
apiDefinitionMockDTO.setResponse(ApiDataUtils.parseArray(new String(config.getResponse()), HttpResponse.class));
apiDefinitionMockDTO.setMatching(ApiDataUtils.parseObject(new String(config.getMatching()), MockMatchRule.class));
apiDefinitionMockDTO.setResponse(ApiDataUtils.parseObject(new String(config.getResponse()), MockResponse.class));
});
}
@ -102,6 +102,13 @@ public class ApiDefinitionMockService {
public ApiDefinitionMock create(ApiDefinitionMockAddRequest request, String userId) {
ProjectService.checkResourceExist(request.getProjectId());
if (request.getMockMatchRule() == null) {
request.setMockMatchRule(new MockMatchRule());
}
if (request.getResponse() == null) {
request.setResponse(new MockResponse());
}
ApiDefinitionMock apiDefinitionMock = new ApiDefinitionMock();
BeanUtils.copyBean(apiDefinitionMock, request);
checkAddExist(apiDefinitionMock);
@ -119,8 +126,8 @@ public class ApiDefinitionMockService {
apiDefinitionMockMapper.insertSelective(apiDefinitionMock);
ApiDefinitionMockConfig apiDefinitionMockConfig = new ApiDefinitionMockConfig();
apiDefinitionMockConfig.setId(apiDefinitionMock.getId());
apiDefinitionMockConfig.setMatching(request.getMatching().getBytes());
apiDefinitionMockConfig.setResponse(request.getResponse().getBytes());
apiDefinitionMockConfig.setMatching(JSON.toJSONString(request.getMockMatchRule()).getBytes());
apiDefinitionMockConfig.setResponse(JSON.toJSONString(request.getResponse()).getBytes());
apiDefinitionMockConfigMapper.insertSelective(apiDefinitionMockConfig);
// 处理文件
@ -176,8 +183,12 @@ public class ApiDefinitionMockService {
apiDefinitionMockMapper.updateByPrimaryKeySelective(apiDefinitionMock);
ApiDefinitionMockConfig apiDefinitionMockConfig = new ApiDefinitionMockConfig();
apiDefinitionMockConfig.setId(apiDefinitionMock.getId());
apiDefinitionMockConfig.setMatching(request.getMatching().getBytes());
apiDefinitionMockConfig.setResponse(request.getResponse().getBytes());
if (request.getMockMatchRule() != null) {
apiDefinitionMockConfig.setMatching(JSON.toJSONString(request.getMockMatchRule()).getBytes());
}
if (request.getResponse() != null) {
apiDefinitionMockConfig.setResponse(JSON.toJSONString(request.getResponse()).getBytes());
}
apiDefinitionMockConfigMapper.updateByPrimaryKeySelective(apiDefinitionMockConfig);
// 处理文件
@ -244,10 +255,6 @@ public class ApiDefinitionMockService {
apiDefinitionMockMapper.updateByPrimaryKeySelective(update);
}
public String uploadTempFile(MultipartFile file) {
return apiFileResourceService.uploadTempFile(file);
}
public void deleteByApiIds(List<String> apiIds, String userId) {
ApiDefinitionMockExample apiDefinitionMockExample = new ApiDefinitionMockExample();
apiDefinitionMockExample.createCriteria().andApiDefinitionIdIn(apiIds);

View File

@ -187,7 +187,11 @@ public class ApiDefinitionService {
ApiDefinitionBlob apiDefinitionBlob = new ApiDefinitionBlob();
apiDefinitionBlob.setId(apiDefinition.getId());
apiDefinitionBlob.setRequest(getMsTestElementStr(request.getRequest()).getBytes());
apiDefinitionBlob.setResponse(JSON.toJSONString(request.getResponse()).getBytes());
if (request.getResponse() != null) {
List<HttpResponse> msHttpResponse = JSON.parseArray(JSON.toJSONString(request.getResponse()), HttpResponse.class);
msHttpResponse.forEach(item -> item.setId(IDGenerator.nextStr()));
apiDefinitionBlob.setResponse(JSON.toJSONString(msHttpResponse).getBytes());
}
apiDefinitionBlobMapper.insertSelective(apiDefinitionBlob);
// 处理文件

View File

@ -0,0 +1,213 @@
package io.metersphere.api.service.mockserver;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.definition.HttpResponse;
import io.metersphere.api.dto.definition.ResponseBody;
import io.metersphere.api.dto.mockserver.HttpRequestParam;
import io.metersphere.api.dto.mockserver.MockResponse;
import io.metersphere.api.dto.request.http.Header;
import io.metersphere.api.dto.request.http.body.Body;
import io.metersphere.api.mapper.*;
import io.metersphere.api.utils.MockServerUtils;
import io.metersphere.project.domain.FileMetadata;
import io.metersphere.project.mapper.FileMetadataMapper;
import io.metersphere.project.service.FileManagementService;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.file.FileCenter;
import io.metersphere.sdk.file.FileRepository;
import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.TempFileUtils;
import io.metersphere.sdk.util.Translator;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
@Service
@Transactional(rollbackFor = Exception.class)
public class MockServerService {
@Resource
private ExtApiDefinitionMapper extApiDefinitionMapper;
@Resource
private ApiDefinitionBlobMapper apiDefinitionBlobMapper;
@Resource
private ApiDefinitionMockMapper apiDefinitionMockMapper;
@Resource
private ApiDefinitionMockConfigMapper apiDefinitionMockConfigMapper;
@Resource
private ApiFileResourceMapper apiFileResourceMapper;
@Resource
FileMetadataMapper fileMetadataMapper;
@Resource
FileManagementService fileManagementService;
private boolean isUrlParamMethod(String method) {
return StringUtils.equalsAnyIgnoreCase(method, HttpMethodConstants.GET.name(), HttpMethodConstants.DELETE.name(), HttpMethodConstants.OPTIONS.name(), HttpMethodConstants.HEAD.name());
}
public Object execute(String method, Map<String, String> requestHeaderMap, String projectNum, String apiNumInfo, HttpServletRequest request, HttpServletResponse response) {
ApiDefinition apiDefinition = extApiDefinitionMapper.selectByProjectNumAndApiNum(projectNum, apiNumInfo);
if (apiDefinition == null) {
return this.requestNotFound(response);
}
String url = request.getRequestURL().toString();
String requestUrlSuffix = MockServerUtils.getUrlSuffix(StringUtils.joinWith("/", "/mock-server", projectNum, apiNumInfo), request);
if (StringUtils.equalsIgnoreCase(method, apiDefinition.getMethod()) && !MockServerUtils.checkUrlMatch(apiDefinition.getPath(), requestUrlSuffix)) {
return this.requestNotFound(response);
}
HttpRequestParam requestMockParams = MockServerUtils.getHttpRequestParam(request, requestUrlSuffix, apiDefinition.getPath(), !this.isUrlParamMethod(method));
LogUtils.info("Mock [" + url + "] Header:{}", requestHeaderMap);
LogUtils.info("Mock [" + url + "] request:{}", JSON.toJSONString(requestMockParams));
ApiDefinitionMockConfig compareMockConfig = this.match(apiDefinition.getId(), requestHeaderMap, requestMockParams);
try {
Object returnObj = this.getReturn(compareMockConfig, apiDefinition.getId(), apiDefinition.getProjectId(), response);
return returnObj;
} catch (Exception e) {
return this.requestNotFound(response);
}
}
private ApiDefinitionMockConfig match(String apiId, Map<String, String> requestHeaderMap, HttpRequestParam requestMockParams) {
ApiDefinitionMockConfig compareMockConfig = null;
ApiDefinitionMockExample mockExample = new ApiDefinitionMockExample();
mockExample.createCriteria().andApiDefinitionIdEqualTo(apiId).andEnableEqualTo(true);
List<ApiDefinitionMock> apiDefinitionMockList = apiDefinitionMockMapper.selectByExample(mockExample);
if (CollectionUtils.isNotEmpty(apiDefinitionMockList)) {
ApiDefinitionMockConfigExample mockConfigExample = new ApiDefinitionMockConfigExample();
mockConfigExample.createCriteria().andIdIn(apiDefinitionMockList.stream().map(ApiDefinitionMock::getId).toList());
List<ApiDefinitionMockConfig> mockConfigs = apiDefinitionMockConfigMapper.selectByExampleWithBLOBs(mockConfigExample);
for (ApiDefinitionMockConfig mockConfig : mockConfigs) {
if (MockServerUtils.matchMockConfig(mockConfig.getMatching(), requestHeaderMap, requestMockParams)) {
compareMockConfig = mockConfig;
break;
}
}
}
return compareMockConfig;
}
private Object getReturn(ApiDefinitionMockConfig compareMockConfig, String apiId, String projectId, HttpServletResponse response) {
ResponseBody responseBody = null;
List<Header> responseHeader = null;
int responseCode = -1;
String useApiResponseId = null;
if (compareMockConfig != null) {
MockResponse mockResponse = JSON.parseObject(new String(compareMockConfig.getResponse()), MockResponse.class);
if (mockResponse.isUseApiResponse()) {
useApiResponseId = mockResponse.getApiResponseId();
} else {
responseCode = mockResponse.getStatusCode();
responseHeader = mockResponse.getHeaders();
responseBody = mockResponse.getBody();
}
}
if (StringUtils.isNotBlank(useApiResponseId) || responseCode == -1) {
HttpResponse mockSelectResponse = null;
ApiDefinitionBlob blob = apiDefinitionBlobMapper.selectByPrimaryKey(apiId);
if (blob != null) {
List<HttpResponse> responseList = JSON.parseArray(new String(blob.getResponse()), HttpResponse.class);
HttpResponse defaultHttpResponse = null;
for (HttpResponse httpResponse : responseList) {
if (httpResponse.isDefaultFlag()) {
defaultHttpResponse = httpResponse;
}
if (StringUtils.equals(httpResponse.getId(), useApiResponseId)) {
mockSelectResponse = httpResponse;
break;
}
}
if (mockSelectResponse == null) {
mockSelectResponse = defaultHttpResponse;
}
}
if (mockSelectResponse == null) {
return this.requestNotFound(response);
} else {
responseCode = Integer.parseInt(mockSelectResponse.getStatusCode());
responseHeader = mockSelectResponse.getHeaders();
responseBody = mockSelectResponse.getBody();
}
}
//返回响应码
response.setStatus(responseCode);
if (CollectionUtils.isNotEmpty(responseHeader)) {
responseHeader.forEach(header -> {
if (header.getEnable()) {
response.addHeader(header.getKey(), header.getValue());
}
});
}
if (responseBody == null) {
return StringUtils.EMPTY;
} else {
if (StringUtils.equalsIgnoreCase(responseBody.getBodyType(), Body.BodyType.JSON.name())) {
return responseBody.getJsonBody().getJsonValue();
} else if (StringUtils.equalsIgnoreCase(responseBody.getBodyType(), Body.BodyType.XML.name())) {
return responseBody.getXmlBody().getValue();
} else if (StringUtils.equalsIgnoreCase(responseBody.getBodyType(), Body.BodyType.RAW.name())) {
return responseBody.getRawBody().getValue();
} else {
String fileId = responseBody.getBinaryBody().getFileId();
String fileName = responseBody.getBinaryBody().getFileName();
String fileType = StringUtils.substring(fileName, fileName.lastIndexOf(".") + 1);
MediaType mediaType = MediaType.parseMediaType("application/octet-stream");
if (responseBody.getBinaryBody().isSendAsBody()) {
String contentType = "text/plain";
if (TempFileUtils.isImage(fileType)) {
contentType = "image/" + fileType;
}
mediaType = MediaType.parseMediaType(contentType);
}
byte[] bytes = new byte[0];
FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(fileId);
if (fileMetadata != null) {
try {
String filePath = TempFileUtils.createFile(TempFileUtils.getTmpFilePath(fileMetadata.getId()), fileManagementService.getFile(fileMetadata));
bytes = TempFileUtils.getFile(filePath);
} catch (Exception ignore) {
return StringUtils.EMPTY;
}
} else {
ApiFileResource apiFileResource = apiFileResourceMapper.selectByPrimaryKey(compareMockConfig.getId(), fileId);
if (apiFileResource != null) {
FileRepository defaultRepository = FileCenter.getDefaultRepository();
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(apiFileResource.getFileName());
fileRequest.setFolder(DefaultRepositoryDir.getApiDefinitionDir(projectId, compareMockConfig.getId()) + "/" + fileId);
try {
bytes = defaultRepository.getFile(fileRequest);
} catch (Exception ignore) {
}
}
}
return ResponseEntity.ok()
.contentType(mediaType)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
.body(bytes);
}
}
}
private String requestNotFound(HttpServletResponse response) {
response.setStatus(404);
return Translator.get("mock_warning");
}
}

View File

@ -0,0 +1,179 @@
package io.metersphere.api.utils;
import io.metersphere.api.dto.mockserver.HttpRequestParam;
import io.metersphere.api.dto.mockserver.MockMatchRule;
import io.metersphere.api.dto.request.http.body.Body;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
public class MockServerUtils {
public static Map<String, String> getHttpRequestHeader(HttpServletRequest request) {
Map<String, String> returnMap = new HashMap<>();
Enumeration<String> headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String header = headers.nextElement();
String headerValue = request.getHeader(header);
returnMap.put(header, headerValue);
}
return returnMap;
}
public static HttpRequestParam getHttpRequestParam(HttpServletRequest request, String requestUrlSuffix, String apiDefinitionPath, boolean isPost) {
HttpRequestParam requestParam = new HttpRequestParam();
requestParam.setPost(isPost);
LinkedHashMap<String, String> restParamMap = MockServerUtils.getRestParam(apiDefinitionPath, requestUrlSuffix);
requestParam.setRestParams(restParamMap);
//解析k-v参数
LinkedHashMap<String, String> queryParamsMap = new LinkedHashMap<>();
Enumeration<String> paramNameItr = request.getParameterNames();
while (paramNameItr.hasMoreElements()) {
String key = paramNameItr.nextElement();
String value = request.getParameter(key);
queryParamsMap.put(key, value);
}
requestParam.setQueryParamsObj(queryParamsMap);
//解析body参数
String requestPostString = getRequestStr(request);
requestParam.setRaw(requestPostString);
//解析paramType
if (StringUtils.startsWithIgnoreCase(request.getContentType(), "application/json")) {
requestParam.setJsonParam(requestPostString);
} else if (StringUtils.endsWith(request.getContentType(), "/xml")) {
requestParam.setXmlParam(requestPostString);
} else if (StringUtils.startsWithIgnoreCase(request.getContentType(), "application/x-www-form-urlencoded")) {
requestParam.setParamType(Body.BodyType.FORM_DATA.name());
} else if (StringUtils.startsWithIgnoreCase(request.getContentType(), "text/plain")) {
requestParam.setParamType(Body.BodyType.RAW.name());
} else if (isPost) {
requestParam.setParamType(Body.BodyType.RAW.name());
}
return requestParam;
}
public static LinkedHashMap<String, String> getRestParam(String apiPath, String requestUrl) {
LinkedHashMap<String, String> restParams = new LinkedHashMap<>();
if (StringUtils.isNotEmpty(apiPath)) {
if (apiPath.startsWith("/")) {
apiPath = apiPath.substring(1);
}
if (requestUrl.startsWith("/")) {
requestUrl = requestUrl.substring(1);
}
String[] pathArr = apiPath.split("/");
String[] sendParamArr = requestUrl.split("/");
//获取 url的<参数名-参数值>通过匹配api的接口设置和实际发送的url
for (int i = 0; i < pathArr.length; i++) {
String param = pathArr[i];
if (param.startsWith("{") && param.endsWith("}")) {
param = param.substring(1, param.length() - 1);
String value = StringUtils.EMPTY;
if (sendParamArr.length > i) {
value = sendParamArr[i];
}
restParams.put(param, value);
}
}
}
return restParams;
}
private static String getRequestStr(HttpServletRequest request) {
String inputLine;
// 接收到的数据
StringBuilder receiveData = new StringBuilder();
try (BufferedReader in = new BufferedReader(new InputStreamReader(
request.getInputStream(), StandardCharsets.UTF_8))) {
while ((inputLine = in.readLine()) != null) {
receiveData.append(inputLine);
}
} catch (IOException ignored) {
}
return receiveData.toString();
}
public static String getUrlSuffix(String mockUrlInfo, HttpServletRequest request) {
String requestUri = request.getRequestURI();
String[] urlParamArr = requestUri.split(mockUrlInfo);
return urlParamArr.length == 0 ? "" : urlParamArr[urlParamArr.length - 1];
}
public static boolean checkUrlMatch(String apiDefinitionPath, String requestUrlSuffix) {
if (StringUtils.equalsAny(apiDefinitionPath, requestUrlSuffix, "/" + requestUrlSuffix)) {
return true;
} else {
if (StringUtils.isNotEmpty(apiDefinitionPath)) {
String urlSuffix = requestUrlSuffix;
//去掉前缀的/"
if (urlSuffix.startsWith("/")) {
urlSuffix = urlSuffix.substring(1);
}
if (apiDefinitionPath.startsWith("/")) {
apiDefinitionPath = apiDefinitionPath.substring(1);
}
//如果请求后缀以"/"结尾需要特殊处理
boolean urlSuffixEndEmpty = false;
if (urlSuffix.endsWith("/")) {
urlSuffixEndEmpty = true;
urlSuffix = urlSuffix + "emptyStrForSplit";
}
String[] requestUrlDomainArr = urlSuffix.split("/");
if (urlSuffixEndEmpty) {
requestUrlDomainArr[requestUrlDomainArr.length - 1] = StringUtils.EMPTY;
}
urlSuffixEndEmpty = false;
if (apiDefinitionPath.endsWith("/")) {
urlSuffixEndEmpty = true;
apiDefinitionPath = apiDefinitionPath + "emptyStrForSplit";
}
String[] apiPathDomainArr = apiDefinitionPath.split("/");
if (urlSuffixEndEmpty) {
apiPathDomainArr[apiPathDomainArr.length - 1] = StringUtils.EMPTY;
}
if (apiPathDomainArr.length == requestUrlDomainArr.length) {
boolean isFetch = true;
for (int i = 0; i < requestUrlDomainArr.length; i++) {
String pathItem = apiPathDomainArr[i];
if (!(pathItem.startsWith("{") && pathItem.endsWith("}"))) {
if (!StringUtils.equals(apiPathDomainArr[i], requestUrlDomainArr[i])) {
return false;
}
}
}
return isFetch;
}
}
}
return false;
}
public static boolean matchMockConfig(byte[] mockMatchBytes, Map<String, String> requestHeaderMap, HttpRequestParam httpRequestParam) {
try {
MockMatchRule matchRule = JSON.parseObject(new String(mockMatchBytes), MockMatchRule.class);
return matchRule.keyValueMatch("header", requestHeaderMap) && matchRule.requestParamMatch(httpRequestParam);
} catch (Exception e) {
LogUtils.info(e.getMessage());
}
return false;
}
}

View File

@ -1,57 +1,68 @@
package io.metersphere.api.controller;
import io.metersphere.api.constants.ApiConstants;
import io.metersphere.api.constants.ApiDefinitionStatus;
import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.domain.ApiDefinitionMock;
import io.metersphere.api.domain.ApiDefinitionMockConfig;
import io.metersphere.api.domain.ApiFileResource;
import io.metersphere.api.dto.definition.ApiDefinitionAddRequest;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.HttpResponse;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockAddRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockPageRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockUpdateRequest;
import io.metersphere.api.dto.mockserver.KeyValueInfo;
import io.metersphere.api.dto.mockserver.MockMatchRule;
import io.metersphere.api.dto.mockserver.MockResponse;
import io.metersphere.api.dto.request.http.Header;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.mapper.ApiDefinitionMapper;
import io.metersphere.api.mapper.ApiDefinitionMockConfigMapper;
import io.metersphere.api.mapper.ApiDefinitionMockMapper;
import io.metersphere.api.dto.request.http.body.Body;
import io.metersphere.api.mapper.*;
import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.service.MockServerTestService;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.dto.filemanagement.FileInfo;
import io.metersphere.project.domain.FileMetadata;
import io.metersphere.project.dto.filemanagement.request.FileUploadRequest;
import io.metersphere.project.service.FileAssociationService;
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
import io.metersphere.project.mapper.FileMetadataMapper;
import io.metersphere.project.service.FileManagementService;
import io.metersphere.project.service.FileMetadataService;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.file.FileCenter;
import io.metersphere.sdk.file.FileRepository;
import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.CommonBeanFactory;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.TempFileUtils;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.controller.handler.result.MsHttpResultCode;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.utils.Pager;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.security.MessageDigest;
import java.util.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@ -71,26 +82,60 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
private static final String UPLOAD_TEMP_FILE = BASE_PATH + "/upload/temp/file";
private static final String DEFAULT_API_ID = "1001";
private static Long NO_MOCK_NO_RESPONSE_API_NUM;
private static ApiDefinitionMock apiDefinitionMock;
@Resource
private MockServerTestService mockServerTestService;
@Resource
private ApiDefinitionMapper apiDefinitionMapper;
@Resource
private ApiDefinitionBlobMapper apiDefinitionBlobMapper;
@Resource
private ApiDefinitionMockMapper apiDefinitionMockMapper;
@Resource
private ApiDefinitionMockConfigMapper apiDefinitionMockConfigMapper;
@Resource
private ExtBaseProjectVersionMapper extBaseProjectVersionMapper;
@Resource
private ApiFileResourceService apiFileResourceService;
@Resource
private FileMetadataMapper fileMetadataMapper;
@Resource
private FileManagementService fileManagementService;
@Resource
private FileMetadataService fileMetadataService;
@Resource
private ApiFileResourceMapper apiFileResourceMapper;
//文件管理中已存在的ID
private static String fileMetadataId;
private static String uploadFileId;
private static Map<String, ApiDefinition> METHOD_API_MAP = new LinkedHashMap<>();
private static Map<ApiDefinition, List<ApiDefinitionMock>> API_MOCK_MAP = new LinkedHashMap<>();
private static String[] HTTP_METHODS = {"POST", "GET", "HEAD", "PUT", "PATCH", "DELETE", "OPTIONS", "TRACE"};
/**
* 文件管理插入一条数据
* 便于测试关联文件
*/
private void uploadFileMetadata() throws Exception {
FileUploadRequest fileUploadRequest = new FileUploadRequest();
fileUploadRequest.setProjectId(DEFAULT_PROJECT_ID);
//导入正常文件
MockMultipartFile file = new MockMultipartFile("file", "mock_file_upload.JPG", MediaType.APPLICATION_OCTET_STREAM_VALUE, "file-metadata.file".getBytes());
fileMetadataId = fileMetadataService.upload(fileUploadRequest, "admin", file);
}
public String doUploadTempFile(MockMultipartFile file) throws Exception {
return JSON.parseObject(requestUploadFileWithOkAndReturn(UPLOAD_TEMP_FILE, file)
.getResponse()
.getContentAsString(), ResultHolder.class)
.getData().toString();
}
@Test
@Order(0)
@ -98,7 +143,7 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
// 准备数据上传文件管理文件
uploadFileMetadata();
// @@请求成功
MockMultipartFile file = getMockMultipartFile("file_upload.JPG");
MockMultipartFile file = mockServerTestService.getMockMultipartFile("file_upload.JPG");
String fileId = doUploadTempFile(file);
// 校验文件存在
@ -109,95 +154,69 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
requestUploadPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_ADD, UPLOAD_TEMP_FILE, file);
requestUploadPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE, UPLOAD_TEMP_FILE, file);
}
private String doUploadTempFile(MockMultipartFile file) throws Exception {
return JSON.parseObject(requestUploadFileWithOkAndReturn(UPLOAD_TEMP_FILE, file)
.getResponse()
.getContentAsString(), ResultHolder.class)
.getData().toString();
}
// 这个api是用于测试没有配置任何mock以及默认响应的情况
String defaultVersion = extBaseProjectVersionMapper.getDefaultVersion(DEFAULT_PROJECT_ID);
ApiDefinitionAddRequest noMockNoResponseApiRequest = new ApiDefinitionAddRequest();
noMockNoResponseApiRequest.setName("MockApi_No_Response");
noMockNoResponseApiRequest.setProtocol(ApiConstants.HTTP_PROTOCOL);
noMockNoResponseApiRequest.setProjectId(DEFAULT_PROJECT_ID);
noMockNoResponseApiRequest.setMethod("GET");
noMockNoResponseApiRequest.setPath("/mock/api/notMatch/");
noMockNoResponseApiRequest.setStatus(ApiDefinitionStatus.PREPARE.getValue());
noMockNoResponseApiRequest.setModuleId(ModuleConstants.DEFAULT_NODE_ID);
noMockNoResponseApiRequest.setVersionId(defaultVersion);
noMockNoResponseApiRequest.setDescription("desc");
noMockNoResponseApiRequest.setRequest(JSON.parseObject(ApiDataUtils.toJSONString(MsHTTPElementTest.getMsHttpElement())));
noMockNoResponseApiRequest.setResponse(new ArrayList<>());
NO_MOCK_NO_RESPONSE_API_NUM = getResultData(this.requestPostWithOkAndReturn("/api/definition/add", noMockNoResponseApiRequest), ApiDefinition.class).getNum();
private static MockMultipartFile getMockMultipartFile(String fileName) {
return new MockMultipartFile(
"file",
fileName,
MediaType.APPLICATION_OCTET_STREAM_VALUE,
"Hello, World!".getBytes()
);
}
/**
* 文件管理插入一条数据
* 便于测试关联文件
*/
private void uploadFileMetadata() throws Exception {
FileUploadRequest fileUploadRequest = new FileUploadRequest();
fileUploadRequest.setProjectId(DEFAULT_PROJECT_ID);
//导入正常文件
MockMultipartFile file = new MockMultipartFile("file", "mock_file_upload.JPG", MediaType.APPLICATION_OCTET_STREAM_VALUE, "mock".getBytes());
fileMetadataId = fileMetadataService.upload(fileUploadRequest, "admin", file);
}
/**
* 校验上传的文件
* @param id
* @param fileIds 全部的文件ID
*/
public static void assertUploadFile(String id, List<String> fileIds) throws Exception {
if (fileIds != null) {
ApiFileResourceService apiFileResourceService = CommonBeanFactory.getBean(ApiFileResourceService.class);
// 验证文件的关联关系以及是否存入对象存储
List<ApiFileResource> apiFileResources = apiFileResourceService.getByResourceId(id);
Assertions.assertEquals(apiFileResources.size(), fileIds.size());
String apiDefinitionDir = DefaultRepositoryDir.getApiDefinitionDir(DEFAULT_PROJECT_ID, id);
FileRequest fileRequest = new FileRequest();
if (!fileIds.isEmpty()) {
for (ApiFileResource apiFileResource : apiFileResources) {
Assertions.assertEquals(DEFAULT_PROJECT_ID, apiFileResource.getProjectId());
fileRequest.setFolder(apiDefinitionDir + "/" + apiFileResource.getFileId());
fileRequest.setFileName(apiFileResource.getFileName());
Assertions.assertNotNull(FileCenter.getDefaultRepository().getFile(fileRequest));
}
fileRequest.setFolder(apiDefinitionDir);
} else {
fileRequest.setFolder(apiDefinitionDir);
Assertions.assertTrue(CollectionUtils.isEmpty(FileCenter.getDefaultRepository().getFolderFileNames(fileRequest)));
if (MapUtils.isEmpty(METHOD_API_MAP)) {
for (String method : HTTP_METHODS) {
// 创建并返回一个 ApiDefinitionAddRequest 对象用于测试
ApiDefinitionAddRequest request = new ApiDefinitionAddRequest();
request.setName("MockApi:" + method);
request.setProtocol(ApiConstants.HTTP_PROTOCOL);
request.setProjectId(DEFAULT_PROJECT_ID);
request.setMethod(method);
request.setPath("/mock/api/" + method + "/{param1}/{param2}");
request.setStatus(ApiDefinitionStatus.PREPARE.getValue());
request.setModuleId(ModuleConstants.DEFAULT_NODE_ID);
request.setVersionId(defaultVersion);
request.setDescription("desc");
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
request.setRequest(JSON.parseObject(ApiDataUtils.toJSONString(msHttpElement)));
request.setResponse(MsHTTPElementTest.get2MsHttpResponse(request.getPath()));
MvcResult mvcResult = this.requestPostWithOkAndReturn("/api/definition/add", request);
ApiDefinition resultData = getResultData(mvcResult, ApiDefinition.class);
METHOD_API_MAP.put(method, resultData);
}
}
}
/**
* 校验上传的文件
* @param id
* @param fileIds 全部的文件ID
*/
private static void assertLinkFile(String id, List<String> fileIds) {
FileAssociationService fileAssociationService = CommonBeanFactory.getBean(FileAssociationService.class);
List<String> linkFileIds = fileAssociationService.getFiles(id)
.stream()
.map(FileInfo::getFileId)
.toList();
Assertions.assertEquals(fileIds, linkFileIds);
}
@Test
@Order(1)
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void testAdd() throws Exception {
LogUtils.info("create api mock test");
// 创建测试数据
ApiDefinitionMockAddRequest request = new ApiDefinitionMockAddRequest();
request.setName("接口定义test");
request.setProjectId(DEFAULT_PROJECT_ID);
request.setApiDefinitionId(DEFAULT_API_ID);
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
request.setMatching(ApiDataUtils.toJSONString(msHttpElement));
List<HttpResponse> msHttpResponse = MsHTTPElementTest.getMsHttpResponse();
request.setResponse(ApiDataUtils.toJSONString(msHttpResponse));
uploadFileId = doUploadTempFile(getMockMultipartFile("file_upload.JPG"));
MockMatchRule mockMatchRule = new MockMatchRule();
request.setMockMatchRule(mockMatchRule);
uploadFileId = doUploadTempFile(mockServerTestService.getMockMultipartFile("file_upload.JPG"));
MockResponse mockResponse = new MockResponse();
mockResponse.setBody(new ResponseBody() {{
this.setBinaryBody(new ResponseBinaryBody() {{
this.setFileId(uploadFileId);
this.setFileName("file_upload.JPG");
}});
}});
request.setResponse(mockResponse);
request.setUploadFileIds(List.of(uploadFileId));
request.setLinkFileIds(List.of(fileMetadataId));
@ -205,9 +224,9 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
MvcResult mvcResult = this.requestPostWithOkAndReturn(ADD, request);
// 校验请求成功数据
ApiDefinitionMock resultData = getResultData(mvcResult, ApiDefinitionMock.class);
apiDefinitionMock = assertAddApiDefinitionMock(request, msHttpElement, resultData.getId());
assertUploadFile(apiDefinitionMock.getId(), List.of(uploadFileId));
assertLinkFile(apiDefinitionMock.getId(), List.of(fileMetadataId));
apiDefinitionMock = mockServerTestService.assertAddApiDefinitionMock(request, mockMatchRule, resultData.getId());
mockServerTestService.assertUploadFile(apiDefinitionMock.getId(), List.of(uploadFileId));
mockServerTestService.assertLinkFile(apiDefinitionMock.getId(), List.of(fileMetadataId));
// 再插入一条数据便于修改时重名校验
request.setName("重名接口定义test");
@ -216,7 +235,7 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
request.setLinkFileIds(null);
mvcResult = this.requestPostWithOkAndReturn(ADD, request);
resultData = getResultData(mvcResult, ApiDefinitionMock.class);
assertAddApiDefinitionMock(request, msHttpElement, resultData.getId());
mockServerTestService.assertAddApiDefinitionMock(request, mockMatchRule, resultData.getId());
// @@重名校验异常
assertErrorCode(this.requestPost(ADD, request), ApiResultCode.API_DEFINITION_MOCK_EXIST);
@ -233,19 +252,9 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
request.setProjectId(DEFAULT_PROJECT_ID);
request.setName("permission-st-6");
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_ADD, ADD, request);
}
private ApiDefinitionMock assertAddApiDefinitionMock(Object request, MsHTTPElement msHttpElement, String id) {
ApiDefinitionMock apiDefinitionMock = apiDefinitionMockMapper.selectByPrimaryKey(id);
ApiDefinitionMockConfig apiDefinitionMockConfig = apiDefinitionMockConfigMapper.selectByPrimaryKey(id);
ApiDefinitionMock copyApiDefinitionMock = BeanUtils.copyBean(new ApiDefinitionMock(), apiDefinitionMock);
BeanUtils.copyBean(copyApiDefinitionMock, request);
Assertions.assertEquals(apiDefinitionMock, copyApiDefinitionMock);
if(apiDefinitionMockConfig != null){
Assertions.assertEquals(msHttpElement, ApiDataUtils.parseObject(new String(apiDefinitionMockConfig.getMatching()), AbstractMsTestElement.class));
}
return apiDefinitionMock;
}
@Test
@Order(2)
@ -253,7 +262,6 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
ApiDefinitionMockRequest apiDefinitionMockRequest = new ApiDefinitionMockRequest();
apiDefinitionMockRequest.setId(apiDefinitionMock.getId());
apiDefinitionMockRequest.setProjectId(DEFAULT_PROJECT_ID);
apiDefinitionMockRequest.setApiDefinitionId(apiDefinitionMock.getApiDefinitionId());
// @@请求成功
MvcResult mvcResult = this.requestPostWithOkAndReturn(DETAIL, apiDefinitionMockRequest);
ApiDefinitionMockDTO apiDefinitionMockDTO = ApiDataUtils.parseObject(JSON.toJSONString(parseResponse(mvcResult).get("data")), ApiDefinitionMockDTO.class);
@ -269,8 +277,8 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
ApiDefinitionMockConfig apiDefinitionMockConfig = apiDefinitionMockConfigMapper.selectByPrimaryKey(apiDefinitionMock.getId());
if(apiDefinitionMockConfig != null){
copyApiDefinitionMockDTO.setMatching(ApiDataUtils.parseObject(new String(apiDefinitionMockConfig.getMatching()), AbstractMsTestElement.class));
copyApiDefinitionMockDTO.setResponse(ApiDataUtils.parseArray(new String(apiDefinitionMockConfig.getResponse()), HttpResponse.class));
copyApiDefinitionMockDTO.setMatching(ApiDataUtils.parseObject(new String(apiDefinitionMockConfig.getMatching()), MockMatchRule.class));
copyApiDefinitionMockDTO.setResponse(ApiDataUtils.parseObject(new String(apiDefinitionMockConfig.getResponse()), MockResponse.class));
}
Assertions.assertEquals(apiDefinitionMockDTO, copyApiDefinitionMockDTO);
@ -287,14 +295,12 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
public void testUpdate() throws Exception {
LogUtils.info("update api mock test");
MockMatchRule mockMatchRule = new MockMatchRule();
ApiDefinitionMockUpdateRequest request = new ApiDefinitionMockUpdateRequest();
BeanUtils.copyBean(request, apiDefinitionMock);
request.setName("test1test1test1test1test1test1");
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
request.setMatching(ApiDataUtils.toJSONString(msHttpElement));
List<HttpResponse> msHttpResponse = MsHTTPElementTest.getMsHttpResponse();
request.setResponse(ApiDataUtils.toJSONString(msHttpResponse));
request.setMockMatchRule(mockMatchRule);
request.setResponse(new MockResponse());
// 清除文件的更新
request.setUnLinkRefIds(List.of(fileMetadataId));
@ -302,43 +308,43 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
this.requestPostWithOk(UPDATE, request);
// 校验请求成功数据
apiDefinitionMock = assertAddApiDefinitionMock(request, msHttpElement, request.getId());
assertUploadFile(apiDefinitionMock.getId(), List.of());
assertLinkFile(apiDefinitionMock.getId(), List.of());
apiDefinitionMock = mockServerTestService.assertAddApiDefinitionMock(request, mockMatchRule, request.getId());
mockServerTestService.assertUploadFile(apiDefinitionMock.getId(), List.of());
mockServerTestService.assertLinkFile(apiDefinitionMock.getId(), List.of());
// 带文件的更新
String fileId = doUploadTempFile(getMockMultipartFile("file_upload.JPG"));
String fileId = doUploadTempFile(mockServerTestService.getMockMultipartFile("file_upload.JPG"));
request.setUploadFileIds(List.of(fileId));
request.setLinkFileIds(List.of(fileMetadataId));
request.setDeleteFileIds(null);
request.setUnLinkRefIds(null);
this.requestPostWithOk(UPDATE, request);
// 校验请求成功数据
apiDefinitionMock = assertAddApiDefinitionMock(request, msHttpElement, request.getId());
assertUploadFile(apiDefinitionMock.getId(), List.of(fileId));
assertLinkFile(apiDefinitionMock.getId(), List.of(fileMetadataId));
apiDefinitionMock = mockServerTestService.assertAddApiDefinitionMock(request, mockMatchRule, request.getId());
mockServerTestService.assertUploadFile(apiDefinitionMock.getId(), List.of(fileId));
mockServerTestService.assertLinkFile(apiDefinitionMock.getId(), List.of(fileMetadataId));
// 删除了上一次上传的文件重新上传一个文件
request.setDeleteFileIds(List.of(fileId));
String newFileId1 = doUploadTempFile(getMockMultipartFile("file_upload.JPG"));
String newFileId1 = doUploadTempFile(mockServerTestService.getMockMultipartFile("file_upload.JPG"));
request.setUploadFileIds(List.of(newFileId1));
request.setUnLinkRefIds(List.of(fileMetadataId));
request.setLinkFileIds(List.of(fileMetadataId));
this.requestPostWithOk(UPDATE, request);
apiDefinitionMock = assertAddApiDefinitionMock(request, msHttpElement, request.getId());
assertUploadFile(apiDefinitionMock.getId(), List.of(newFileId1));
assertLinkFile(apiDefinitionMock.getId(), List.of(fileMetadataId));
apiDefinitionMock = mockServerTestService.assertAddApiDefinitionMock(request, mockMatchRule, request.getId());
mockServerTestService.assertUploadFile(apiDefinitionMock.getId(), List.of(newFileId1));
mockServerTestService.assertLinkFile(apiDefinitionMock.getId(), List.of(fileMetadataId));
// 已有一个文件再上传一个文件
String newFileId2 = doUploadTempFile(getMockMultipartFile("file_update_upload.JPG"));
String newFileId2 = doUploadTempFile(mockServerTestService.getMockMultipartFile("file_update_upload.JPG"));
request.setUploadFileIds(List.of(newFileId2));
request.setUnLinkRefIds(null);
request.setDeleteFileIds(null);
request.setLinkFileIds(null);
this.requestPostWithOk(UPDATE, request);
apiDefinitionMock = assertAddApiDefinitionMock(request, msHttpElement, request.getId());
assertUploadFile(apiDefinitionMock.getId(), List.of(newFileId1, newFileId2));
assertLinkFile(apiDefinitionMock.getId(), List.of(fileMetadataId));
apiDefinitionMock = mockServerTestService.assertAddApiDefinitionMock(request, mockMatchRule, request.getId());
mockServerTestService.assertUploadFile(apiDefinitionMock.getId(), List.of(newFileId1, newFileId2));
mockServerTestService.assertLinkFile(apiDefinitionMock.getId(), List.of(fileMetadataId));
// 修改 tags
request.setUploadFileIds(null);
request.setUnLinkRefIds(null);
@ -346,14 +352,10 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
request.setLinkFileIds(null);
request.setTags(new LinkedHashSet<>(List.of("tag1", "tag2-update")));
request.setName("接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test");
MsHTTPElement msHttpElementTag = MsHTTPElementTest.getMsHttpElement();
request.setMatching(ApiDataUtils.toJSONString(msHttpElementTag));
List<HttpResponse> msHttpResponseTag = MsHTTPElementTest.getMsHttpResponse();
request.setResponse(ApiDataUtils.toJSONString(msHttpResponseTag));
this.requestPostWithOk(UPDATE, request);
// 校验请求成功数据
assertAddApiDefinitionMock(request, msHttpElement, request.getId());
mockServerTestService.assertAddApiDefinitionMock(request, mockMatchRule, request.getId());
request.setName("重名接口定义test");
// @@重名校验异常
@ -367,7 +369,7 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
// 校验数据是否存在
request.setId("111");
request.setName("test123");
assertErrorCode(this.requestPost(UPDATE, request), MsHttpResultCode.NOT_FOUND);
this.requestPost(UPDATE, request).andExpect(status().is5xxServerError());
// @@校验日志
checkLog(apiDefinitionMock.getId(), OperationLogType.UPDATE, UPDATE);
@ -393,7 +395,7 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
// @@校验日志
checkLog(apiDefinitionMock.getId(), OperationLogType.UPDATE, ENABLE + apiDefinitionMock.getId());
assertErrorCode(this.requestGet(ENABLE + "111"), MsHttpResultCode.NOT_FOUND);
assertErrorCode(this.requestGet(ENABLE + "111"), MsHttpResultCode.FAILED);
// @@开启
// @@请求成功
@ -403,7 +405,7 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
// @@校验日志
checkLog(apiDefinitionMock.getId(), OperationLogType.UPDATE, ENABLE + apiDefinitionMock.getId());
assertErrorCode(this.requestGet(ENABLE + "111"), MsHttpResultCode.NOT_FOUND);
assertErrorCode(this.requestGet(ENABLE + "111"), MsHttpResultCode.FAILED);
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE, ENABLE + apiDefinitionMock.getId());
}
@ -415,7 +417,6 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
ApiDefinitionMockRequest request = new ApiDefinitionMockRequest();
request.setId(apiDefinitionMock.getId());
request.setProjectId(DEFAULT_PROJECT_ID);
request.setApiDefinitionId(apiDefinitionMock.getApiDefinitionId());
MvcResult mvcResult = this.requestPostWithOkAndReturn(COPY, request);
ApiDefinitionMock resultData = getResultData(mvcResult, ApiDefinitionMock.class);
// @数据验证
@ -426,28 +427,10 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
}
Assertions.assertTrue(resultData.getName().contains("copy_"));
ApiDefinitionMockUpdateRequest apiDefinitionMockUpdateRequest = new ApiDefinitionMockUpdateRequest();
BeanUtils.copyBean(apiDefinitionMockUpdateRequest, apiDefinitionMock);
apiDefinitionMockUpdateRequest.setName("test1test1test1test1test1test1");
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
apiDefinitionMockUpdateRequest.setMatching(ApiDataUtils.toJSONString(msHttpElement));
List<HttpResponse> msHttpResponse = MsHTTPElementTest.getMsHttpResponse();
apiDefinitionMockUpdateRequest.setResponse(ApiDataUtils.toJSONString(msHttpResponse));
this.requestPostWithOk(UPDATE, apiDefinitionMockUpdateRequest);
// 校验请求成功数据
apiDefinitionMock = assertAddApiDefinitionMock(request, msHttpElement, request.getId());
request.setId(apiDefinitionMock.getId());
MvcResult mvcResultCopy = this.requestPostWithOkAndReturn(COPY, request);
ApiDefinitionMock resultDataCopy = getResultData(mvcResultCopy, ApiDefinitionMock.class);
// @数据验证
Assertions.assertTrue(resultDataCopy.getName().contains("copy_"));
// @@校验日志
checkLog(resultData.getId(), OperationLogType.UPDATE);
request.setId("121");
assertErrorCode(this.requestPost(COPY, request), MsHttpResultCode.NOT_FOUND);
assertErrorCode(this.requestPost(COPY, request), MsHttpResultCode.FAILED);
// @@校验权限
request.setId(apiDefinitionMock.getId());
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE, COPY, request);
@ -511,7 +494,6 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
ApiDefinitionMockRequest apiDefinitionMockRequest = new ApiDefinitionMockRequest();
apiDefinitionMockRequest.setId(apiDefinitionMock.getId());
apiDefinitionMockRequest.setProjectId(DEFAULT_PROJECT_ID);
apiDefinitionMockRequest.setApiDefinitionId(apiDefinitionMock.getApiDefinitionId());
// @@请求成功
this.requestPostWithOkAndReturn(DELETE, apiDefinitionMockRequest);
checkLog(apiDefinitionMock.getId(), OperationLogType.DELETE);
@ -528,12 +510,433 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
checkLog(apiDefinitionMockRequest.getId(), OperationLogType.DELETE);
apiDefinitionMockRequest.setId("121");
assertErrorCode(this.requestPost(DELETE, apiDefinitionMockRequest), MsHttpResultCode.NOT_FOUND);
assertErrorCode(this.requestPost(DELETE, apiDefinitionMockRequest), MsHttpResultCode.FAILED);
// @@校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_DELETE, DELETE, apiDefinitionMockRequest);
}
@Test
@Order(99)
public void mockServerTest() throws Exception {
this.initMockConfigTestData();
//测试匹配不到任何一个api
mockServerTestService.testNoMatchApi();
//测试匹配到了api但是路径不一样
mockServerTestService.testApiNoMockConfigAndNoResponse("/mock-server/100001/" + NO_MOCK_NO_RESPONSE_API_NUM + "/mock/api/testStr/");
//测试匹配到的api没用配置mockConfig以及没有定义默认返回项
mockServerTestService.testApiNoMockConfigAndNoResponse("/mock-server/100001/" + NO_MOCK_NO_RESPONSE_API_NUM + "/mock/api/notMatch/");
/*
测试用例
* rest全匹配: 返回接口定义的响应
* rest半匹配: 尝试返回文件
* header全匹配: 尝试返回body-xml
* header半匹配: 尝试返回body-json
* get类型的请求测试 Query全匹配 Query半匹配
* post类型的请求测试 Body-kv全匹配Body-kv半匹配Body-json包含匹配Body-xml包含匹配RAW包含匹配
*/
for (Map.Entry<ApiDefinition, List<ApiDefinitionMock>> entry : API_MOCK_MAP.entrySet()) {
ApiDefinition apiDefinition = entry.getKey();
List<ApiDefinitionMock> apiDefinitionMockList = entry.getValue();
String method = apiDefinition.getMethod();
if (StringUtils.equalsIgnoreCase(method, "TRACE")) {
//这种不测试
continue;
}
String url = "/mock-server/100001/" + apiDefinition.getNum() + apiDefinition.getPath();
//先做一个没有匹配到任何的mock期望的测试
mockServerTestService.testNoMatchMockConfig(method, url, apiDefinition.getPath());
for (ApiDefinitionMock mock : apiDefinitionMockList) {
//重置url
url = "/mock-server/100001/" + apiDefinition.getNum() + apiDefinition.getPath();
ApiDefinitionBlob apiDefinitionBlob = apiDefinitionBlobMapper.selectByPrimaryKey(apiDefinition.getId());
ApiDefinitionMockConfig mockConfig = apiDefinitionMockConfigMapper.selectByPrimaryKey(mock.getId());
MockMatchRule mockMatchRule = JSON.parseObject(new String(mockConfig.getMatching()), MockMatchRule.class);
MockResponse mockResponse = JSON.parseObject(new String(mockConfig.getResponse()), MockResponse.class);
List<HttpResponse> apiResponseList = JSON.parseArray(new String(apiDefinitionBlob.getResponse()), HttpResponse.class);
HttpResponse MockUseApiRsponse = null;
for (HttpResponse apiResponse : apiResponseList) {
if (mockResponse.isUseApiResponse() && StringUtils.equals(mockResponse.getApiResponseId(), apiResponse.getId())) {
MockUseApiRsponse = apiResponse;
}
}
String[] mockNameArr = mock.getName().split("_");
String methodType = mockNameArr[1]; //
String conditionType = mockNameArr[2];
//替换rest参数
for (KeyValueInfo keyValueInfo : mockMatchRule.getRest().getMatchRules()) {
url = StringUtils.replace(url, "{" + keyValueInfo.getKey() + "}", keyValueInfo.getValue());
}
//设置query参数
StringBuilder queryParamBuilder = new StringBuilder();
if (CollectionUtils.isNotEmpty(mockMatchRule.getQuery().getMatchRules())) {
for (KeyValueInfo keyValueInfo : mockMatchRule.getQuery().getMatchRules()) {
if (!queryParamBuilder.isEmpty()) {
queryParamBuilder.append("&");
}
queryParamBuilder.append(keyValueInfo.getKey());
queryParamBuilder.append("=");
queryParamBuilder.append(keyValueInfo.getValue());
if (!mockMatchRule.getQuery().isMatchAll()) {
break;
}
}
url = url + "?" + queryParamBuilder;
}
//开始创建请求
MockHttpServletRequestBuilder requestBuilder = mockServerTestService.getRequestBuilder(method, url);
//设置请求头 如果匹配类型是body-json或者body-xml需要设置content-type
if (StringUtils.equalsIgnoreCase(conditionType, "body-json")) {
requestBuilder.header("content-type", "application/json");
} else if (StringUtils.equalsIgnoreCase(conditionType, "body-xml")) {
requestBuilder.header("content-type", "application/xml");
} else if (StringUtils.equalsIgnoreCase(conditionType, "body-kv-x-www")) {
requestBuilder.header("content-type", "application/x-www-form-urlencoded");
} else if (StringUtils.equalsIgnoreCase(conditionType, "Body-raw")) {
requestBuilder.header("content-type", "text/plain");
}
if (CollectionUtils.isNotEmpty(mockMatchRule.getHeader().getMatchRules())) {
for (KeyValueInfo keyValueInfo : mockMatchRule.getHeader().getMatchRules()) {
requestBuilder.header(keyValueInfo.getKey(), keyValueInfo.getValue());
if (!mockMatchRule.getHeader().isMatchAll()) {
break;
}
}
}
//设置body参数 (get类型的请求不设置
if (this.isNotGetTypeMethod(methodType) && StringUtils.equalsIgnoreCase(mockMatchRule.getBody().getParamType(), Body.BodyType.FORM_DATA.name())) {
for (KeyValueInfo keyValueInfo : mockMatchRule.getBody().getFormDataMatch().getMatchRules()) {
requestBuilder.param(keyValueInfo.getKey(), keyValueInfo.getValue());
if (!mockMatchRule.getBody().getFormDataMatch().isMatchAll()) {
break;
}
}
} else if (StringUtils.isNotBlank(mockMatchRule.getBody().getRaw())) {
requestBuilder.content(mockMatchRule.getBody().getRaw());
}
//发送请求
ResultActions action = mockMvc.perform(requestBuilder);
//判断响应
List<Header> headers;
int statusCode;
ResponseBody responseBody;
if (mockResponse.isUseApiResponse()) {
headers = MockUseApiRsponse.getHeaders();
statusCode = Integer.parseInt(MockUseApiRsponse.getStatusCode());
responseBody = MockUseApiRsponse.getBody();
} else {
headers = mockResponse.getHeaders();
statusCode = mockResponse.getStatusCode();
responseBody = mockResponse.getBody();
}
MockHttpServletResponse mockServerResponse = action.andReturn().getResponse();
//判断响应码
Assertions.assertEquals(mockServerResponse.getStatus(), statusCode);
//判断响应头
for (Header header : headers) {
if (header.getEnable()) {
Assertions.assertEquals(mockServerResponse.getHeader(header.getKey()), header.getValue());
}
}
//判断响应体
if (StringUtils.equals(responseBody.getBodyType(), Body.BodyType.BINARY.name())) {
byte[] returnFileBytes = mockServerResponse.getContentAsByteArray();
String fileId = responseBody.getBinaryBody().getFileId();
byte[] bytes = new byte[0];
FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(fileId);
if (fileMetadata != null) {
String filePath = TempFileUtils.createFile(TempFileUtils.getTmpFilePath(fileMetadata.getId()), fileManagementService.getFile(fileMetadata));
bytes = TempFileUtils.getFile(filePath);
} else {
ApiFileResource apiFileResource = apiFileResourceMapper.selectByPrimaryKey(mock.getId(), fileId);
if (apiFileResource != null) {
FileRepository defaultRepository = FileCenter.getDefaultRepository();
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(apiFileResource.getFileName());
fileRequest.setFolder(DefaultRepositoryDir.getApiDefinitionDir(apiDefinition.getProjectId(), mock.getId()) + "/" + fileId);
try {
bytes = defaultRepository.getFile(fileRequest);
} catch (Exception ignore) {
}
}
}
//通过MD5判断是否是同一个文件
String fileMD5 = this.getFileMD5(bytes);
String downloadMD5 = this.getFileMD5(returnFileBytes);
Assertions.assertEquals(fileMD5, downloadMD5);
} else {
String returnStr = mockServerResponse.getContentAsString(StandardCharsets.UTF_8);
String compareStr = "";
switch (responseBody.getBodyType()) {
case "JSON":
compareStr = responseBody.getJsonBody().getJsonValue();
break;
case "XML":
compareStr = responseBody.getXmlBody().getValue();
break;
case "RAW":
compareStr = responseBody.getRawBody().getValue();
break;
default:
break;
}
Assertions.assertEquals(returnStr, compareStr);
}
}
}
}
private boolean isNotGetTypeMethod(String methodType) {
return !StringUtils.equalsAnyIgnoreCase(methodType, HttpMethodConstants.GET.name(), HttpMethodConstants.DELETE.name(), HttpMethodConstants.OPTIONS.name(), HttpMethodConstants.HEAD.name());
}
private void initMockConfigTestData() throws Exception {
if (MapUtils.isEmpty(METHOD_API_MAP)) {
this.uploadTempFile();
}
//为METHOD_API_MAP每个api创建一个mock期望用于做mockServer的测试
for (Map.Entry<String, ApiDefinition> apiDefinitionEntry : METHOD_API_MAP.entrySet()) {
String method = apiDefinitionEntry.getKey();
ApiDefinition apiDefinition = apiDefinitionEntry.getValue();
ApiDefinitionBlob apiDefinitionBlob = apiDefinitionBlobMapper.selectByPrimaryKey(apiDefinition.getId());
List<ApiDefinitionMock> mockList = new ArrayList<>();
//rest全匹配 返回接口定义的响应
{
ApiDefinitionMockAddRequest mockServerRequest = new ApiDefinitionMockAddRequest();
mockServerRequest.setName("Mock_" + method + "_Rest_Full_Match1");
mockServerRequest.setProjectId(apiDefinition.getProjectId());
mockServerRequest.setApiDefinitionId(apiDefinition.getId());
mockServerRequest.setMockMatchRule(mockServerTestService.genMockMatchRule("Rest_Full_Match1", false, true, null, true));
mockServerRequest.setResponse(mockServerTestService.genMockResponse(null, 444, "Rest_Full_Match1", uploadFileId, null, apiDefinitionBlob));
MvcResult mockServerResult = this.requestPostWithOkAndReturn(ADD, mockServerRequest);
ApiDefinitionMock definitionMock = getResultData(mockServerResult, ApiDefinitionMock.class);
mockServerTestService.assertAddApiDefinitionMock(mockServerRequest, mockServerRequest.getMockMatchRule(), definitionMock.getId());
mockList.add(definitionMock);
}
//rest全匹配 返回本地上传的文件send by body)
{
String mockFileMatch2Id = doUploadTempFile(mockServerTestService.getMockMultipartFile("mockFileMatch2.txt", "mockFileMatch2"));
ApiDefinitionMockAddRequest mockServerRequest = new ApiDefinitionMockAddRequest();
mockServerRequest.setName("Mock_" + method + "_Rest_Full_Match2");
mockServerRequest.setProjectId(apiDefinition.getProjectId());
mockServerRequest.setApiDefinitionId(apiDefinition.getId());
mockServerRequest.setMockMatchRule(mockServerTestService.genMockMatchRule("Rest_Full_Match2", false, true, null, false));
mockServerRequest.setResponse(mockServerTestService.genMockResponse("file-body", 200, "Rest_Full_Match2", mockFileMatch2Id, "mockFileMatch2.txt", null));
mockServerRequest.setUploadFileIds(List.of(mockFileMatch2Id));
MvcResult mockServerResult = this.requestPostWithOkAndReturn(ADD, mockServerRequest);
ApiDefinitionMock definitionMock = getResultData(mockServerResult, ApiDefinitionMock.class);
mockServerTestService.assertAddApiDefinitionMock(mockServerRequest, mockServerRequest.getMockMatchRule(), definitionMock.getId());
mockList.add(definitionMock);
}
//rest全匹配 返回本地上传的文件send by download)
{
String mockFileMatch3Id = doUploadTempFile(mockServerTestService.getMockMultipartFile("mockFileMatch3.txt", "mockFileMatch3"));
ApiDefinitionMockAddRequest mockServerRequest = new ApiDefinitionMockAddRequest();
mockServerRequest.setName("Mock_" + method + "_Rest_Full_Match3");
mockServerRequest.setProjectId(apiDefinition.getProjectId());
mockServerRequest.setApiDefinitionId(apiDefinition.getId());
mockServerRequest.setMockMatchRule(mockServerTestService.genMockMatchRule("Rest_Full_Match3", false, true, null, false));
mockServerRequest.setResponse(mockServerTestService.genMockResponse("file", 200, "Rest_Full_Match3", mockFileMatch3Id, "mockFileMatch3.txt", null));
mockServerRequest.setUploadFileIds(List.of(mockFileMatch3Id));
MvcResult mockServerResult = this.requestPostWithOkAndReturn(ADD, mockServerRequest);
ApiDefinitionMock definitionMock = getResultData(mockServerResult, ApiDefinitionMock.class);
mockServerTestService.assertAddApiDefinitionMock(mockServerRequest, mockServerRequest.getMockMatchRule(), definitionMock.getId());
mockList.add(definitionMock);
}
//rest全匹配 返回文件管理的文件send by download)
{
ApiDefinitionMockAddRequest mockServerRequest = new ApiDefinitionMockAddRequest();
mockServerRequest.setName("Mock_" + method + "_Rest_Full_Match4");
mockServerRequest.setProjectId(apiDefinition.getProjectId());
mockServerRequest.setApiDefinitionId(apiDefinition.getId());
mockServerRequest.setMockMatchRule(mockServerTestService.genMockMatchRule("Rest_Full_Match4", false, true, null, false));
mockServerRequest.setResponse(mockServerTestService.genMockResponse("file", 200, "Rest_Full_Match4", fileMetadataId, "fileMetadata.txt", null));
mockServerRequest.setLinkFileIds(List.of(fileMetadataId));
MvcResult mockServerResult = this.requestPostWithOkAndReturn(ADD, mockServerRequest);
ApiDefinitionMock definitionMock = getResultData(mockServerResult, ApiDefinitionMock.class);
mockServerTestService.assertAddApiDefinitionMock(mockServerRequest, mockServerRequest.getMockMatchRule(), definitionMock.getId());
mockList.add(definitionMock);
}
//header全匹配 尝试返回body-xml
{
ApiDefinitionMockAddRequest mockServerRequest = new ApiDefinitionMockAddRequest();
mockServerRequest.setName("Mock_" + method + "_Header_Full_Match");
mockServerRequest.setProjectId(apiDefinition.getProjectId());
mockServerRequest.setApiDefinitionId(apiDefinition.getId());
mockServerRequest.setMockMatchRule(mockServerTestService.genMockMatchRule("Header_Full_Match", false, false, null, true));
mockServerRequest.setResponse(mockServerTestService.genMockResponse("xml", 201, "Header_Full_Match", uploadFileId, null, null));
MvcResult mockServerResult = this.requestPostWithOkAndReturn(ADD, mockServerRequest);
ApiDefinitionMock definitionMock = getResultData(mockServerResult, ApiDefinitionMock.class);
mockServerTestService.assertAddApiDefinitionMock(mockServerRequest, mockServerRequest.getMockMatchRule(), definitionMock.getId());
mockList.add(definitionMock);
}
//header半匹配 尝试返回body-json
{
ApiDefinitionMockAddRequest mockServerRequest = new ApiDefinitionMockAddRequest();
mockServerRequest.setName("Mock_" + method + "_Header_Half_Match");
mockServerRequest.setProjectId(apiDefinition.getProjectId());
mockServerRequest.setApiDefinitionId(apiDefinition.getId());
mockServerRequest.setMockMatchRule(mockServerTestService.genMockMatchRule("Header_Half_Match", false, false, null, false));
mockServerRequest.setResponse(mockServerTestService.genMockResponse("json", 202, "Header_Half_Match", uploadFileId, null, null));
MvcResult mockServerResult = this.requestPostWithOkAndReturn(ADD, mockServerRequest);
ApiDefinitionMock definitionMock = getResultData(mockServerResult, ApiDefinitionMock.class);
mockServerTestService.assertAddApiDefinitionMock(mockServerRequest, mockServerRequest.getMockMatchRule(), definitionMock.getId());
mockList.add(definitionMock);
}
//query全匹配
{
ApiDefinitionMockAddRequest mockServerRequest = new ApiDefinitionMockAddRequest();
mockServerRequest.setName("Mock_" + method + "_Query_Full_Match");
mockServerRequest.setProjectId(apiDefinition.getProjectId());
mockServerRequest.setApiDefinitionId(apiDefinition.getId());
mockServerRequest.setMockMatchRule(mockServerTestService.genMockMatchRule("Query_Full_Match", true, true, null, true));
mockServerRequest.setResponse(mockServerTestService.genMockResponse("raw", 203, "Query_Full_Match", uploadFileId, null, null));
MvcResult mockServerResult = this.requestPostWithOkAndReturn(ADD, mockServerRequest);
ApiDefinitionMock definitionMock = getResultData(mockServerResult, ApiDefinitionMock.class);
mockServerTestService.assertAddApiDefinitionMock(mockServerRequest, mockServerRequest.getMockMatchRule(), definitionMock.getId());
mockList.add(definitionMock);
}
//query半匹配
{
ApiDefinitionMockAddRequest mockServerRequest = new ApiDefinitionMockAddRequest();
mockServerRequest.setName("Mock_" + method + "_Query_Half_Match");
mockServerRequest.setProjectId(apiDefinition.getProjectId());
mockServerRequest.setApiDefinitionId(apiDefinition.getId());
mockServerRequest.setMockMatchRule(mockServerTestService.genMockMatchRule("Query_Half_Match", true, true, null, false));
mockServerRequest.setResponse(mockServerTestService.genMockResponse("raw", 204, "Query_Half_Match", uploadFileId, null, null));
MvcResult mockServerResult = this.requestPostWithOkAndReturn(ADD, mockServerRequest);
ApiDefinitionMock definitionMock = getResultData(mockServerResult, ApiDefinitionMock.class);
mockServerTestService.assertAddApiDefinitionMock(mockServerRequest, mockServerRequest.getMockMatchRule(), definitionMock.getId());
mockList.add(definitionMock);
}
//body-kv 全匹配
{
ApiDefinitionMockAddRequest mockServerRequest = new ApiDefinitionMockAddRequest();
mockServerRequest.setName("Mock_" + method + "_Body-kv_Full_Match");
mockServerRequest.setProjectId(apiDefinition.getProjectId());
mockServerRequest.setApiDefinitionId(apiDefinition.getId());
mockServerRequest.setMockMatchRule(mockServerTestService.genMockMatchRule("Body-kv_Full_Match", false, true, "kv", true));
mockServerRequest.setResponse(mockServerTestService.genMockResponse("raw", 204, "Body-kv_Full_Match", uploadFileId, null, null));
MvcResult mockServerResult = this.requestPostWithOkAndReturn(ADD, mockServerRequest);
ApiDefinitionMock definitionMock = getResultData(mockServerResult, ApiDefinitionMock.class);
mockServerTestService.assertAddApiDefinitionMock(mockServerRequest, mockServerRequest.getMockMatchRule(), definitionMock.getId());
mockList.add(definitionMock);
}
//body-kv 半匹配
{
ApiDefinitionMockAddRequest mockServerRequest = new ApiDefinitionMockAddRequest();
mockServerRequest.setName("Mock_" + method + "_Body-kv_Half_Match");
mockServerRequest.setProjectId(apiDefinition.getProjectId());
mockServerRequest.setApiDefinitionId(apiDefinition.getId());
mockServerRequest.setMockMatchRule(mockServerTestService.genMockMatchRule("Body-kv_Half_Match", false, true, "kv", false));
mockServerRequest.setResponse(mockServerTestService.genMockResponse("raw", 204, "Body-kv_Half_Match", uploadFileId, null, null));
MvcResult mockServerResult = this.requestPostWithOkAndReturn(ADD, mockServerRequest);
ApiDefinitionMock definitionMock = getResultData(mockServerResult, ApiDefinitionMock.class);
mockServerTestService.assertAddApiDefinitionMock(mockServerRequest, mockServerRequest.getMockMatchRule(), definitionMock.getId());
mockList.add(definitionMock);
}
//body-kv 半匹配x-www-form-urlencoded
{
ApiDefinitionMockAddRequest mockServerRequest = new ApiDefinitionMockAddRequest();
mockServerRequest.setName("Mock_" + method + "_Body-kv-x-www_Half_Match");
mockServerRequest.setProjectId(apiDefinition.getProjectId());
mockServerRequest.setApiDefinitionId(apiDefinition.getId());
mockServerRequest.setMockMatchRule(mockServerTestService.genMockMatchRule("Body-kv-x-www_Half_Match", false, true, "kv", false));
mockServerRequest.setResponse(mockServerTestService.genMockResponse("raw", 204, "Body-kv-x-www_Half_Match", uploadFileId, null, null));
MvcResult mockServerResult = this.requestPostWithOkAndReturn(ADD, mockServerRequest);
ApiDefinitionMock definitionMock = getResultData(mockServerResult, ApiDefinitionMock.class);
mockServerTestService.assertAddApiDefinitionMock(mockServerRequest, mockServerRequest.getMockMatchRule(), definitionMock.getId());
mockList.add(definitionMock);
}
//body-json包含匹配
{
ApiDefinitionMockAddRequest mockServerRequest = new ApiDefinitionMockAddRequest();
mockServerRequest.setName("Mock_" + method + "_Body-json_Half_Match");
mockServerRequest.setProjectId(apiDefinition.getProjectId());
mockServerRequest.setApiDefinitionId(apiDefinition.getId());
mockServerRequest.setMockMatchRule(mockServerTestService.genMockMatchRule("Body-json_Half_Match", false, true, "json", false));
mockServerRequest.setResponse(mockServerTestService.genMockResponse("raw", 204, "Body-json_Half_Match", uploadFileId, null, null));
MvcResult mockServerResult = this.requestPostWithOkAndReturn(ADD, mockServerRequest);
ApiDefinitionMock definitionMock = getResultData(mockServerResult, ApiDefinitionMock.class);
mockServerTestService.assertAddApiDefinitionMock(mockServerRequest, mockServerRequest.getMockMatchRule(), definitionMock.getId());
mockList.add(definitionMock);
}
//body-xml包含匹配
{
ApiDefinitionMockAddRequest mockServerRequest = new ApiDefinitionMockAddRequest();
mockServerRequest.setName("Mock_" + method + "_Body-xml_Half_Match");
mockServerRequest.setProjectId(apiDefinition.getProjectId());
mockServerRequest.setApiDefinitionId(apiDefinition.getId());
mockServerRequest.setMockMatchRule(mockServerTestService.genMockMatchRule("Body-xml_Half_Match", false, true, "xml", false));
mockServerRequest.setResponse(mockServerTestService.genMockResponse("raw", 204, "Body-xml_Half_Match", uploadFileId, null, null));
MvcResult mockServerResult = this.requestPostWithOkAndReturn(ADD, mockServerRequest);
ApiDefinitionMock definitionMock = getResultData(mockServerResult, ApiDefinitionMock.class);
mockServerTestService.assertAddApiDefinitionMock(mockServerRequest, mockServerRequest.getMockMatchRule(), definitionMock.getId());
mockList.add(definitionMock);
}
//raw包含匹配
{
ApiDefinitionMockAddRequest mockServerRequest = new ApiDefinitionMockAddRequest();
mockServerRequest.setName("Mock_" + method + "_Body-raw_Half_Match");
mockServerRequest.setProjectId(apiDefinition.getProjectId());
mockServerRequest.setApiDefinitionId(apiDefinition.getId());
mockServerRequest.setMockMatchRule(mockServerTestService.genMockMatchRule("body-raw_Half_Match", false, true, "raw", false));
mockServerRequest.setResponse(mockServerTestService.genMockResponse("raw", 204, "body-raw_Half_Match", uploadFileId, null, null));
MvcResult mockServerResult = this.requestPostWithOkAndReturn(ADD, mockServerRequest);
ApiDefinitionMock definitionMock = getResultData(mockServerResult, ApiDefinitionMock.class);
mockServerTestService.assertAddApiDefinitionMock(mockServerRequest, mockServerRequest.getMockMatchRule(), definitionMock.getId());
mockList.add(definitionMock);
}
API_MOCK_MAP.put(apiDefinition, mockList);
}
}
public static String getFileMD5(byte[] bytes) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(bytes, 0, bytes.length);
BigInteger bigInt = new BigInteger(1, digest.digest());
return bigInt.toString(16);
} catch (Exception e) {
return null;
}
}
}

View File

@ -6,6 +6,7 @@ import io.metersphere.api.dto.assertion.MsAssertionConfig;
import io.metersphere.api.dto.debug.ModuleCreateRequest;
import io.metersphere.api.dto.definition.ApiDefinitionAddRequest;
import io.metersphere.api.dto.definition.ApiTestCaseAddRequest;
import io.metersphere.api.dto.definition.HttpResponse;
import io.metersphere.api.dto.request.http.Header;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.dto.request.http.QueryParam;
@ -57,7 +58,6 @@ import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.domain.Plugin;
import io.metersphere.system.domain.Schedule;
import io.metersphere.system.dto.request.PluginUpdateRequest;
import io.metersphere.system.dto.sdk.BaseTreeNode;
import io.metersphere.system.dto.sdk.request.PosRequest;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.mapper.ScheduleMapper;
@ -68,6 +68,7 @@ import io.metersphere.system.utils.CheckLogModel;
import io.metersphere.system.utils.Pager;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
@ -75,7 +76,6 @@ import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.FileInputStream;
@ -528,7 +528,8 @@ public class ApiScenarioControllerTests extends BaseTest {
queryParam2.setValue("bbb2");
msHttpElement.setQuery(List.of(queryParam1, queryParam2));
apiDefinitionAddRequest.setRequest(getMsElementParam(msHttpElement));
apiDefinitionAddRequest.setResponse("{}");
HttpResponse httpResponse = new HttpResponse();
apiDefinitionAddRequest.setResponse(Collections.singletonList(httpResponse));
apiDefinition = apiDefinitionService.create(apiDefinitionAddRequest, "admin");
ApiTestCaseAddRequest apiTestCaseAddRequest = new ApiTestCaseAddRequest();

View File

@ -4,6 +4,7 @@ import io.metersphere.api.dto.ApiFile;
import io.metersphere.api.dto.ApiParamConfig;
import io.metersphere.api.dto.assertion.MsAssertionConfig;
import io.metersphere.api.dto.definition.HttpResponse;
import io.metersphere.api.dto.definition.ResponseBody;
import io.metersphere.api.dto.request.MsCommonElement;
import io.metersphere.api.dto.request.http.*;
import io.metersphere.api.dto.request.http.body.*;
@ -475,24 +476,68 @@ public class MsHTTPElementTest {
header.setDescription("desc");
httpResponse.setHeaders(List.of(header));
FormDataBody formDataBody = new FormDataBody();
FormDataKV formDataKV = new FormDataKV();
formDataKV.setEnable(false);
formDataKV.setContentType("text/plain");
formDataKV.setEncode(true);
formDataKV.setMaxLength(10);
formDataKV.setMinLength(8);
formDataKV.setParamType("text");
formDataKV.setDescription("test");
formDataKV.setRequired(true);
formDataKV.setValue("value");
formDataKV.setKey("key");
formDataBody.setFormValues(List.of(formDataKV));
Body body = new Body();
body.setBodyType(Body.BodyType.FORM_DATA.name());
ResponseBody body = new ResponseBody();
body.setBodyType(Body.BodyType.RAW.name());
httpResponse.setBody(body);
httpResponses.add(httpResponse);
return httpResponses;
}
public static List<HttpResponse> get2MsHttpResponse(String returnPrefix) {
List<HttpResponse> httpResponses = new ArrayList<>();
HttpResponse http1Response = new HttpResponse();
http1Response.setName("Response1");
http1Response.setStatusCode("222");
http1Response.setDefaultFlag(true);
http1Response.setHeaders(new ArrayList<>() {{
this.add(new Header() {{
this.setEnable(false);
this.setValue("valueA1");
this.setKey("keyA1");
this.setDescription("descA1");
}});
this.add(new Header() {{
this.setEnable(true);
this.setValue("headerDefaultValue");
this.setKey("headerDefault");
this.setDescription("headerDefaultDescA2");
}});
}});
ResponseBody body1 = new ResponseBody();
body1.setBodyType(Body.BodyType.RAW.name());
body1.setRawBody(new RawBody() {{
this.setValue(returnPrefix + "___responseDefault");
}});
http1Response.setBody(body1);
httpResponses.add(http1Response);
HttpResponse http2Response = new HttpResponse();
http2Response.setName("Response2");
http2Response.setStatusCode("222");
http2Response.setDefaultFlag(false);
http2Response.setHeaders(new ArrayList<>() {{
this.add(new Header() {{
this.setEnable(false);
this.setValue("valueB1");
this.setKey("keyB1");
this.setDescription("descB1");
}});
this.add(new Header() {{
this.setEnable(true);
this.setValue("valueB2");
this.setKey("keyB2");
this.setDescription("descB2");
}});
}});
ResponseBody body2 = new ResponseBody();
body2.setBodyType(Body.BodyType.RAW.name());
body2.setRawBody(new RawBody() {{
this.setValue(returnPrefix + "___response2");
}});
http2Response.setBody(body2);
httpResponses.add(http2Response);
return httpResponses;
}
}

View File

@ -0,0 +1,360 @@
package io.metersphere.api.service;
import io.metersphere.api.domain.ApiDefinitionBlob;
import io.metersphere.api.domain.ApiDefinitionMock;
import io.metersphere.api.domain.ApiDefinitionMockConfig;
import io.metersphere.api.domain.ApiFileResource;
import io.metersphere.api.dto.definition.HttpResponse;
import io.metersphere.api.dto.definition.ResponseBinaryBody;
import io.metersphere.api.dto.definition.ResponseBody;
import io.metersphere.api.dto.mockserver.*;
import io.metersphere.api.dto.request.http.Header;
import io.metersphere.api.dto.request.http.body.Body;
import io.metersphere.api.dto.request.http.body.JsonBody;
import io.metersphere.api.dto.request.http.body.RawBody;
import io.metersphere.api.dto.request.http.body.XmlBody;
import io.metersphere.api.mapper.ApiDefinitionMockConfigMapper;
import io.metersphere.api.mapper.ApiDefinitionMockMapper;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.project.dto.filemanagement.FileInfo;
import io.metersphere.project.service.FileAssociationService;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.file.FileCenter;
import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.CommonBeanFactory;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.junit.jupiter.api.Assertions;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import static io.metersphere.api.service.BaseResourcePoolTestService.DEFAULT_PROJECT_ID;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@Service
public class MockServerTestService {
@Resource
private ApiDefinitionMockMapper apiDefinitionMockMapper;
@Resource
private ApiDefinitionMockConfigMapper apiDefinitionMockConfigMapper;
public static MockMultipartFile getMockMultipartFile(String fileName) {
return new MockMultipartFile(
"file",
fileName,
MediaType.APPLICATION_OCTET_STREAM_VALUE,
"Hello, World!".getBytes()
);
}
public static MockMultipartFile getMockMultipartFile(String fileName, String fileContent) {
return new MockMultipartFile(
"file",
fileName,
MediaType.APPLICATION_OCTET_STREAM_VALUE,
fileContent.getBytes()
);
}
/**
* 校验上传的文件
*
* @param id
* @param fileIds 全部的文件ID
*/
public static void assertUploadFile(String id, List<String> fileIds) throws Exception {
if (fileIds != null) {
ApiFileResourceService apiFileResourceService = CommonBeanFactory.getBean(ApiFileResourceService.class);
// 验证文件的关联关系以及是否存入对象存储
List<ApiFileResource> apiFileResources = apiFileResourceService.getByResourceId(id);
Assertions.assertEquals(apiFileResources.size(), fileIds.size());
String apiDefinitionDir = DefaultRepositoryDir.getApiDefinitionDir(DEFAULT_PROJECT_ID, id);
FileRequest fileRequest = new FileRequest();
if (!fileIds.isEmpty()) {
for (ApiFileResource apiFileResource : apiFileResources) {
Assertions.assertEquals(DEFAULT_PROJECT_ID, apiFileResource.getProjectId());
fileRequest.setFolder(apiDefinitionDir + "/" + apiFileResource.getFileId());
fileRequest.setFileName(apiFileResource.getFileName());
Assertions.assertNotNull(FileCenter.getDefaultRepository().getFile(fileRequest));
}
fileRequest.setFolder(apiDefinitionDir);
} else {
fileRequest.setFolder(apiDefinitionDir);
Assertions.assertTrue(CollectionUtils.isEmpty(FileCenter.getDefaultRepository().getFolderFileNames(fileRequest)));
}
}
}
/**
* 校验上传的文件
*
* @param id
* @param fileIds 全部的文件ID
*/
public static void assertLinkFile(String id, List<String> fileIds) {
FileAssociationService fileAssociationService = CommonBeanFactory.getBean(FileAssociationService.class);
List<String> linkFileIds = fileAssociationService.getFiles(id)
.stream()
.map(FileInfo::getFileId)
.toList();
Assertions.assertEquals(fileIds, linkFileIds);
}
public ApiDefinitionMock assertAddApiDefinitionMock(Object request, MockMatchRule mockMatchRule, String id) {
ApiDefinitionMock apiDefinitionMock = apiDefinitionMockMapper.selectByPrimaryKey(id);
ApiDefinitionMockConfig apiDefinitionMockConfig = apiDefinitionMockConfigMapper.selectByPrimaryKey(id);
ApiDefinitionMock copyApiDefinitionMock = BeanUtils.copyBean(new ApiDefinitionMock(), apiDefinitionMock);
BeanUtils.copyBean(copyApiDefinitionMock, request);
Assertions.assertEquals(apiDefinitionMock, copyApiDefinitionMock);
if (apiDefinitionMockConfig != null) {
Assertions.assertEquals(mockMatchRule, ApiDataUtils.parseObject(new String(apiDefinitionMockConfig.getMatching()), MockMatchRule.class));
}
return apiDefinitionMock;
}
public MockMatchRule genMockMatchRule(String valuePrefix, boolean hasQuery, boolean hasHeader, String bodyParamType, boolean matchAll) {
MockMatchRule mockMatchRule = new MockMatchRule();
keyValueMatchRole restMatchRule = new keyValueMatchRole();
restMatchRule.setMatchAll(matchAll);
restMatchRule.setMatchRules(new ArrayList<>() {{
this.add(new KeyValueInfo() {{
this.setKey("param1");
this.setValue(valuePrefix + "__query-" + hasQuery + "_header-" + hasHeader);
}});
this.add(new KeyValueInfo() {{
this.setKey("param2");
this.setValue(valuePrefix + "-Param2");
}});
}});
mockMatchRule.setRest(restMatchRule);
if (hasQuery) {
keyValueMatchRole queryMatchRule = new keyValueMatchRole();
queryMatchRule.setMatchAll(matchAll);
queryMatchRule.setMatchRules(new ArrayList<>() {{
this.add(new KeyValueInfo() {{
this.setKey("queryParam1");
this.setValue(valuePrefix + "_queryParam1Value");
}});
this.add(new KeyValueInfo() {{
this.setKey("queryParam2");
this.setValue(valuePrefix + "_queryParam2Value");
}});
this.add(new KeyValueInfo() {{
this.setKey("queryParam3");
this.setValue(valuePrefix + "_queryParam3Value");
}});
}});
mockMatchRule.setQuery(queryMatchRule);
}
if (hasHeader) {
keyValueMatchRole headerMatchRule = new keyValueMatchRole();
headerMatchRule.setMatchAll(matchAll);
headerMatchRule.setMatchRules(new ArrayList<>() {{
this.add(new KeyValueInfo() {{
this.setKey("headerA");
this.setValue(valuePrefix + "-header-1");
}});
this.add(new KeyValueInfo() {{
this.setKey("headerB");
this.setValue(valuePrefix + "-header-2");
}});
this.add(new KeyValueInfo() {{
this.setKey("headerC");
this.setValue(valuePrefix + "-header-3");
}});
}});
mockMatchRule.setHeader(headerMatchRule);
}
if (StringUtils.equalsIgnoreCase(bodyParamType, "kv")) {
mockMatchRule.setBody(new BodyParamMatchRole() {{
this.setParamType(Body.BodyType.FORM_DATA.name());
this.setFormDataMatch(new keyValueMatchRole() {{
this.setMatchAll(matchAll);
this.setMatchRules(new ArrayList<>() {{
this.add(new KeyValueInfo() {{
this.setKey("bodyKvParam1");
this.setValue(valuePrefix + "_bodyKvParam1");
}});
this.add(new KeyValueInfo() {{
this.setKey("bodyParam2");
this.setValue(valuePrefix + "_bodyKvParam2");
}});
this.add(new KeyValueInfo() {{
this.setKey("bodyParam3");
this.setValue(valuePrefix + "_bodyKvParam3");
}});
}});
}});
}});
} else if (StringUtils.equalsIgnoreCase(bodyParamType, "raw")) {
mockMatchRule.setBody(new BodyParamMatchRole() {{
this.setParamType(Body.BodyType.RAW.name());
this.setRaw(valuePrefix + "_inputRawBody");
}});
} else if (StringUtils.equalsIgnoreCase(bodyParamType, "json")) {
mockMatchRule.setBody(new BodyParamMatchRole() {{
this.setParamType(Body.BodyType.JSON.name());
this.setRaw("{\"inputAge\":123}");
}});
} else if (StringUtils.equalsIgnoreCase(bodyParamType, "xml")) {
mockMatchRule.setBody(new BodyParamMatchRole() {{
this.setParamType(Body.BodyType.XML.name());
this.setRaw("<xml>input123</xml>");
}});
}
return mockMatchRule;
}
public MockResponse genMockResponse(String returnType, int status, String valueKeyWord, String fileId, String fileName, ApiDefinitionBlob apiDefinitionBlob) {
MockResponse mockResponse = new MockResponse();
mockResponse.setStatusCode(status);
if (apiDefinitionBlob != null) {
mockResponse.setUseApiResponse(true);
List<HttpResponse> msHttpResponseList = JSON.parseArray(new String(apiDefinitionBlob.getResponse()), HttpResponse.class);
msHttpResponseList.forEach(item -> {
if (!item.isDefaultFlag()) {
//特意使用非默认的响应
mockResponse.setApiResponseId(item.getId());
}
});
} else {
ResponseBody body = new ResponseBody();
switch (returnType) {
case "file":
body.setBodyType(Body.BodyType.BINARY.name());
body.setBinaryBody(new ResponseBinaryBody() {{
this.setSendAsBody(false);
this.setFileId(fileId);
this.setFileName(fileName);
}});
break;
case "file-body":
body.setBodyType(Body.BodyType.BINARY.name());
body.setBinaryBody(new ResponseBinaryBody() {{
this.setSendAsBody(false);
this.setFileId(fileId);
this.setFileName(fileName);
}});
break;
case "json":
body.setBodyType(Body.BodyType.JSON.name());
body.setJsonBody(new JsonBody() {{
this.setJsonValue("{\"inputAge\":123, \"testKeyWord\":\"" + valueKeyWord + "\"}");
}});
break;
case "xml":
body.setBodyType(Body.BodyType.XML.name());
body.setXmlBody(new XmlBody() {{
this.setValue("<xml>" + valueKeyWord + "</xml>");
}});
break;
case "raw":
body.setBodyType(Body.BodyType.RAW.name());
body.setRawBody(new RawBody() {{
this.setValue("Raw body content:" + valueKeyWord);
}});
break;
}
mockResponse.setBody(body);
}
List<Header> headers = new ArrayList<>() {{
this.add(new Header() {{
this.setKey("rspHeaderA");
this.setValue("header-1");
}});
this.add(new Header() {{
this.setKey("rspHeaderB");
this.setValue("header-2");
this.setEnable(false);
}});
this.add(new Header() {{
this.setKey("rspHeaderC");
this.setValue("header-3");
}});
}};
mockResponse.setHeaders(headers);
return mockResponse;
}
public MockHttpServletRequestBuilder getRequestBuilder(String method, String url) {
MockHttpServletRequestBuilder requestBuilder = null;
if (StringUtils.equalsIgnoreCase(method, "get")) {
requestBuilder = MockMvcRequestBuilders.get(url);
} else if (StringUtils.equalsIgnoreCase(method, "post")) {
requestBuilder = MockMvcRequestBuilders.post(url);
} else if (StringUtils.equalsIgnoreCase(method, "put")) {
requestBuilder = MockMvcRequestBuilders.put(url);
} else if (StringUtils.equalsIgnoreCase(method, "delete")) {
requestBuilder = MockMvcRequestBuilders.delete(url);
} else if (StringUtils.equalsIgnoreCase(method, "patch")) {
requestBuilder = MockMvcRequestBuilders.patch(url);
} else if (StringUtils.equalsIgnoreCase(method, "head")) {
requestBuilder = MockMvcRequestBuilders.head(url);
} else if (StringUtils.equalsIgnoreCase(method, "options")) {
requestBuilder = MockMvcRequestBuilders.options(url);
} else if (StringUtils.equalsIgnoreCase(method, "trace")) {
requestBuilder = MockMvcRequestBuilders.request(HttpMethod.TRACE, url);
}
return requestBuilder;
}
@Resource
private MockMvc mockMvc;
public void testNoMatchMockConfig(String method, String url, String apiDefinitionPath) throws Exception {
url = StringUtils.replace(url, "{param1}", "param1");
url = StringUtils.replace(url, "{param2}", "param2");
MockHttpServletRequestBuilder requestBuilder;
if (StringUtils.equalsAnyIgnoreCase(method, "get", "delete")) {
requestBuilder = getRequestBuilder(method, url + IDGenerator.nextStr());
Assertions.assertNotNull(requestBuilder);
} else {
requestBuilder = getRequestBuilder(method, url);
}
ResultActions action = mockMvc.perform(requestBuilder);
MvcResult mockResult = action.andReturn();
String returnStr = mockResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
Assertions.assertEquals(returnStr, apiDefinitionPath + "___responseDefault");
Assertions.assertEquals(mockResult.getResponse().getStatus(), 222);
}
public void testNoMatchApi() throws Exception {
String url = "/mock-server/100001/" + "error" + "/test/error";
this.testApiNoMockConfigAndNoResponse(url);
}
public void testApiNoMockConfigAndNoResponse(String url) throws Exception {
MockHttpServletRequestBuilder requestBuilder = getRequestBuilder("get", url);
ResultActions action = mockMvc.perform(requestBuilder);
action.andExpect(status().isNotFound());
MvcResult mockResult = action.andReturn();
String returnStr = mockResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
Assertions.assertEquals(returnStr, Translator.get("mock_warning"));
}
}

View File

@ -26,13 +26,13 @@ import io.metersphere.system.utils.CheckLogModel;
import io.metersphere.system.utils.Pager;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.apache.commons.lang3.StringUtils;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@ -203,40 +203,6 @@ public class FileRepositoryControllerTest extends BaseTest {
Assertions.assertEquals(response.getToken(), GITEA_TOKEN);
Assertions.assertEquals(response.getUrl(), GITEA_URL);
//测试创建gitee的
String giteeUrl = "https://gitee.com/testformeterspere/gitee-test.git";
String giteeUserName = "testformetersphere";
String giteeToken = "4548d369bb595738d726512742e4478f";
createRequest = new FileRepositoryCreateRequest();
createRequest.setProjectId(project.getId());
createRequest.setPlatform(ModuleConstants.NODE_TYPE_GITEE);
createRequest.setUrl(giteeUrl);
createRequest.setUserName(giteeUserName);
createRequest.setToken(giteeToken);
createRequest.setName("GITEE存储库");
int tryCount = 0;
while (true) {
//github连接gitee有时会连不到这里重试10次
result = this.requestPost(FileManagementRequestUtils.URL_FILE_REPOSITORY_CREATE, createRequest).andReturn();
if (result.getResponse().getStatus() == 200) {
break;
} else {
tryCount++;
if (tryCount > 10) {
throw new Exception("无法创建gitee存储库");
} else {
Thread.sleep(1000);
}
}
}
returnStr = result.getResponse().getContentAsString();
rh = JSON.parseObject(returnStr, ResultHolder.class);
this.checkFileRepository(rh.getData().toString(), createRequest.getProjectId(), createRequest.getName(), createRequest.getPlatform(), createRequest.getUrl(), createRequest.getToken(), createRequest.getUserName());
LOG_CHECK_LIST.add(
new CheckLogModel(rh.getData().toString(), OperationLogType.ADD, FileManagementRequestUtils.URL_FILE_REPOSITORY_CREATE)
);
//参数测试 没有url
createRequest = new FileRepositoryCreateRequest();
@ -283,12 +249,13 @@ public class FileRepositoryControllerTest extends BaseTest {
createRequest.setToken(GITEA_TOKEN);
createRequest.setName("GITEA存储库");
this.requestPost(FileManagementRequestUtils.URL_FILE_REPOSITORY_CREATE, createRequest).andExpect(status().is5xxServerError());
//报错测试 gitee仓库不填写用户名
createRequest = new FileRepositoryCreateRequest();
createRequest.setProjectId(project.getId());
createRequest.setPlatform(ModuleConstants.NODE_TYPE_GITEE);
createRequest.setUrl(giteeUrl);
createRequest.setToken(giteeToken);
createRequest.setUrl("gitee/test/url");
createRequest.setToken("gitee-token");
createRequest.setName("Gitee无用户名存储库");
this.requestPost(FileManagementRequestUtils.URL_FILE_REPOSITORY_CREATE, createRequest).andExpect(status().is5xxServerError());