feat(接口测试): 增加TCP协议的接口测试

This commit is contained in:
q4speed 2020-10-09 18:08:49 +08:00
parent b63a3ebbbc
commit 8582a4f04f
14 changed files with 507 additions and 177 deletions

View File

@ -171,6 +171,13 @@
<version>${jmeter.version}</version> <version>${jmeter.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_tcp</artifactId>
<version>${jmeter.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.microsoft.sqlserver</groupId> <groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId> <artifactId>mssql-jdbc</artifactId>

View File

@ -4,8 +4,6 @@ import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.annotation.JSONType; import com.alibaba.fastjson.annotation.JSONType;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.dto.scenario.KeyValue;
import io.metersphere.api.dto.scenario.processor.BeanShellPostProcessor;
import io.metersphere.api.dto.scenario.processor.BeanShellPreProcessor;
import io.metersphere.api.dto.scenario.request.dubbo.ConfigCenter; import io.metersphere.api.dto.scenario.request.dubbo.ConfigCenter;
import io.metersphere.api.dto.scenario.request.dubbo.ConsumerAndService; import io.metersphere.api.dto.scenario.request.dubbo.ConsumerAndService;
import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter; import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter;
@ -39,8 +37,4 @@ public class DubboRequest extends Request {
private List<KeyValue> args; private List<KeyValue> args;
@JSONField(ordinal = 9) @JSONField(ordinal = 9)
private List<KeyValue> attachmentArgs; private List<KeyValue> attachmentArgs;
@JSONField(ordinal = 12)
private BeanShellPreProcessor beanShellPreProcessor;
@JSONField(ordinal = 13)
private BeanShellPostProcessor beanShellPostProcessor;
} }

View File

@ -4,8 +4,6 @@ import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.annotation.JSONType; import com.alibaba.fastjson.annotation.JSONType;
import io.metersphere.api.dto.scenario.Body; import io.metersphere.api.dto.scenario.Body;
import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.dto.scenario.KeyValue;
import io.metersphere.api.dto.scenario.processor.BeanShellPostProcessor;
import io.metersphere.api.dto.scenario.processor.BeanShellPreProcessor;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -31,10 +29,6 @@ public class HttpRequest extends Request {
private List<KeyValue> headers; private List<KeyValue> headers;
@JSONField(ordinal = 8) @JSONField(ordinal = 8)
private Body body; private Body body;
@JSONField(ordinal = 11)
private BeanShellPreProcessor beanShellPreProcessor;
@JSONField(ordinal = 12)
private BeanShellPostProcessor beanShellPostProcessor;
@JSONField(ordinal = 14) @JSONField(ordinal = 14)
private Long connectTimeout; private Long connectTimeout;
@JSONField(ordinal = 15) @JSONField(ordinal = 15)

View File

@ -16,7 +16,8 @@ import lombok.Data;
@JsonSubTypes({ @JsonSubTypes({
@JsonSubTypes.Type(value = HttpRequest.class, name = RequestType.HTTP), @JsonSubTypes.Type(value = HttpRequest.class, name = RequestType.HTTP),
@JsonSubTypes.Type(value = DubboRequest.class, name = RequestType.DUBBO), @JsonSubTypes.Type(value = DubboRequest.class, name = RequestType.DUBBO),
@JsonSubTypes.Type(value = SqlRequest.class, name = RequestType.SQL) @JsonSubTypes.Type(value = SqlRequest.class, name = RequestType.SQL),
@JsonSubTypes.Type(value = TCPRequest.class, name = RequestType.TCP)
}) })
@JSONType(seeAlso = {HttpRequest.class, DubboRequest.class, SqlRequest.class}, typeKey = "type") @JSONType(seeAlso = {HttpRequest.class, DubboRequest.class, SqlRequest.class}, typeKey = "type")
@Data @Data

View File

@ -7,4 +7,6 @@ public class RequestType {
public static final String DUBBO = "DUBBO"; public static final String DUBBO = "DUBBO";
public static final String SQL = "SQL"; public static final String SQL = "SQL";
public static final String TCP = "TCP";
} }

View File

@ -0,0 +1,42 @@
package io.metersphere.api.dto.scenario.request;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.annotation.JSONType;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
@JSONType(typeName = RequestType.TCP)
public class TCPRequest extends Request {
// type 必须放最前面以便能够转换正确的类
private String type = RequestType.TCP;
@JSONField(ordinal = 50)
private Boolean useEnvironment;
@JSONField(ordinal = 51)
private String classname;
@JSONField(ordinal = 52)
private String server;
@JSONField(ordinal = 53)
private Integer port;
@JSONField(ordinal = 54)
private Integer ctimeout;
@JSONField(ordinal = 55)
private Integer timeout;
@JSONField(ordinal = 56)
private Boolean reUseConnection;
@JSONField(ordinal = 57)
private Boolean nodelay;
@JSONField(ordinal = 58)
private Boolean closeConnection;
@JSONField(ordinal = 59)
private String soLinger;
@JSONField(ordinal = 60)
private String eolByte;
@JSONField(ordinal = 61)
private String request;
@JSONField(ordinal = 62)
private String username;
@JSONField(ordinal = 63)
private String password;
}

View File

@ -50,6 +50,7 @@
<el-radio :label="types.HTTP">HTTP</el-radio> <el-radio :label="types.HTTP">HTTP</el-radio>
<el-radio :label="types.DUBBO">DUBBO</el-radio> <el-radio :label="types.DUBBO">DUBBO</el-radio>
<el-radio :label="types.SQL">SQL</el-radio> <el-radio :label="types.SQL">SQL</el-radio>
<el-radio :label="types.TCP">TCP</el-radio>
</el-radio-group> </el-radio-group>
<el-button slot="reference" :disabled="isReadOnly" <el-button slot="reference" :disabled="isReadOnly"
class="request-create" type="primary" size="mini" icon="el-icon-plus" plain/> class="request-create" type="primary" size="mini" icon="el-icon-plus" plain/>

View File

@ -2,22 +2,32 @@
<div class="request-form"> <div class="request-form">
<component @runDebug="runDebug" :is="component" :is-read-only="isReadOnly" :request="request" :scenario="scenario"/> <component @runDebug="runDebug" :is="component" :is-read-only="isReadOnly" :request="request" :scenario="scenario"/>
<el-divider v-if="isCompleted"></el-divider> <el-divider v-if="isCompleted"></el-divider>
<ms-request-result-tail v-loading="debugReportLoading" v-if="isCompleted" :request="request.debugRequestResult ? request.debugRequestResult : {responseResult: {}, subRequestResults: []}" <ms-request-result-tail v-loading="debugReportLoading" v-if="isCompleted"
:scenario-name="request.debugScenario ? request.debugScenario.name : ''" ref="msDebugResult"/> :request="request.debugRequestResult ? request.debugRequestResult : {responseResult: {}, subRequestResults: []}"
:scenario-name="request.debugScenario ? request.debugScenario.name : ''"
ref="msDebugResult"/>
</div> </div>
</template> </template>
<script> <script>
import {JSR223Processor, Request, RequestFactory, Scenario} from "../../model/ScenarioModel"; import {JSR223Processor, Request, RequestFactory, Scenario} from "../../model/ScenarioModel";
import MsApiHttpRequestForm from "./ApiHttpRequestForm"; import MsApiHttpRequestForm from "./ApiHttpRequestForm";
import MsApiDubboRequestForm from "./ApiDubboRequestForm"; import MsApiTcpRequestForm from "./ApiTcpRequestForm";
import MsScenarioResults from "../../../report/components/ScenarioResults"; import MsApiDubboRequestForm from "./ApiDubboRequestForm";
import MsRequestResultTail from "../../../report/components/RequestResultTail"; import MsScenarioResults from "../../../report/components/ScenarioResults";
import MsApiSqlRequestForm from "./ApiSqlRequestForm"; import MsRequestResultTail from "../../../report/components/RequestResultTail";
import MsApiSqlRequestForm from "./ApiSqlRequestForm";
export default { export default {
name: "MsApiRequestForm", name: "MsApiRequestForm",
components: {MsApiSqlRequestForm, MsRequestResultTail, MsScenarioResults, MsApiDubboRequestForm, MsApiHttpRequestForm}, components: {
MsApiSqlRequestForm,
MsRequestResultTail,
MsScenarioResults,
MsApiDubboRequestForm,
MsApiHttpRequestForm,
MsApiTcpRequestForm
},
props: { props: {
scenario: Scenario, scenario: Scenario,
request: Request, request: Request,
@ -30,7 +40,7 @@ export default {
data() { data() {
return { return {
reportId: "", reportId: "",
content: {scenarios:[]}, content: {scenarios: []},
debugReportLoading: false, debugReportLoading: false,
showDebugReport: false showDebugReport: false
} }
@ -45,6 +55,9 @@ export default {
case RequestFactory.TYPES.SQL: case RequestFactory.TYPES.SQL:
name = "MsApiSqlRequestForm"; name = "MsApiSqlRequestForm";
break; break;
case RequestFactory.TYPES.TCP:
name = "MsApiTcpRequestForm";
break;
default: default:
name = "MsApiHttpRequestForm"; name = "MsApiHttpRequestForm";
} }
@ -112,19 +125,19 @@ export default {
this.$emit('runDebug', this.request); this.$emit('runDebug', this.request);
} }
} }
} }
</script> </script>
<style scoped> <style scoped>
.scenario-results { .scenario-results {
margin-top: 20px; margin-top: 20px;
} }
.request-form >>> .debug-button { .request-form >>> .debug-button {
margin-left: auto; margin-left: auto;
display: block; display: block;
margin-right: 10px; margin-right: 10px;
} }
</style> </style>

View File

@ -0,0 +1,175 @@
<template>
<el-form class="tcp" :model="request" :rules="rules" ref="request" label-width="120px" :disabled="isReadOnly">
<el-form-item :label="$t('api_test.request.name')" prop="name">
<el-input v-model="request.name" maxlength="300" show-word-limit/>
</el-form-item>
<el-form-item label="TCPClient" prop="classname">
<el-select v-model="request.classname" style="width: 100%">
<el-option v-for="c in classes" :key="c" :label="c" :value="c"/>
</el-select>
</el-form-item>
<el-row :gutter="10">
<el-col :span="12">
<el-form-item :label="$t('api_test.request.tcp.server')" prop="server">
<el-input v-model="request.server" maxlength="300" show-word-limit/>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item :label="$t('api_test.request.tcp.port')" prop="port">
<el-input-number v-model="request.port" controls-position="right" :min="0" :max="65535"/>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item :label="$t('api_test.request.tcp.connect')" prop="ctimeout">
<el-input-number v-model="request.ctimeout" controls-position="right" :min="0"/>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item :label="$t('api_test.request.tcp.response')" prop="timeout">
<el-input-number v-model="request.timeout" controls-position="right" :min="0"/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="4">
<el-form-item>
<el-switch
v-model="request.useEnvironment"
:active-text="$t('api_test.request.refer_to_environment')"
@change="useEnvironmentChange">
</el-switch>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label-width="0">
<el-switch
v-model="request.reUseConnection"
:active-text="$t('api_test.request.tcp.re_use_connection')">
</el-switch>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label-width="0">
<el-switch
v-model="request.closeConnection"
:active-text="$t('api_test.request.tcp.close_connection')">
</el-switch>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label-width="0">
<el-switch
v-model="request.nodelay"
:active-text="$t('api_test.request.tcp.no_delay')">
</el-switch>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item :label="$t('api_test.request.tcp.so_linger')" prop="soLinger">
<el-input v-model="request.soLinger"/>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item :label="$t('api_test.request.tcp.eol_byte')" prop="eolByte">
<el-input v-model="request.eolByte"/>
</el-form-item>
</el-col>
</el-row>
<el-form-item :label="$t('api_test.request.tcp.request')" prop="request">
<el-input type="textarea" v-model="request.request" :autosize="{minRows: 4, maxRows: 6}">
</el-input>
</el-form-item>
<el-row :gutter="10">
<el-col :span="12">
<el-form-item :label="$t('api_test.request.tcp.username')" prop="username">
<el-input v-model="request.username" maxlength="100" show-word-limit/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('api_test.request.tcp.password')" prop="password">
<el-input v-model="request.password" maxlength="30" show-word-limit show-password
autocomplete="new-password"/>
</el-form-item>
</el-col>
</el-row>
<el-button :disabled="!request.enable || !scenario.enable || isReadOnly" class="debug-button" size="small"
type="primary" @click="runDebug">{{ $t('api_test.request.debug') }}
</el-button>
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('api_test.request.assertions.label')" name="assertions">
<ms-api-assertions :is-read-only="isReadOnly" :assertions="request.assertions"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.extract.label')" name="extract">
<ms-api-extract :is-read-only="isReadOnly" :extract="request.extract"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.processor.pre_exec_script')" name="beanShellPreProcessor">
<ms-jsr233-processor :is-read-only="isReadOnly" :jsr223-processor="request.jsr223PreProcessor"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.processor.post_exec_script')" name="beanShellPostProcessor">
<ms-jsr233-processor :is-read-only="isReadOnly" :jsr223-processor="request.jsr223PostProcessor"/>
</el-tab-pane>
</el-tabs>
</el-form>
</template>
<script>
import {Scenario, TCPRequest} from "@/business/components/api/test/model/ScenarioModel";
import MsApiAssertions from "@/business/components/api/test/components/assertion/ApiAssertions";
import MsApiExtract from "@/business/components/api/test/components/extract/ApiExtract";
import MsJsr233Processor from "@/business/components/api/test/components/processor/Jsr233Processor";
export default {
name: "MsApiTcpRequestForm",
components: {MsJsr233Processor, MsApiExtract, MsApiAssertions},
props: {
request: TCPRequest,
scenario: Scenario,
isReadOnly: {
type: Boolean,
default: false
}
},
data() {
return {
activeName: "assertions",
classes: TCPRequest.CLASSES,
rules: {
server: [
{
required: true,
message: this.$t('commons.required', [this.$t('api_test.request.tcp.server')]),
trigger: 'blur'
}
],
}
}
},
methods: {
useEnvironmentChange(value) {
if (value && !this.request.environment) {
this.$error(this.$t('api_test.request.please_add_environment_to_scenario'), 2000);
this.request.useEnvironment = false;
}
this.$refs["request"].clearValidate();
},
runDebug() {
this.$emit('runDebug');
}
},
}
</script>
<style scoped>
.tcp >>> .el-input-number {
width: 100%;
}
</style>

View File

@ -291,6 +291,26 @@ export class JDBCSampler extends DefaultTestElement {
} }
} }
export class TCPSampler extends DefaultTestElement {
constructor(testName, request = {}) {
super('TCPSampler', 'TCPSamplerGui', 'TCPSampler', testName);
this.stringProp("TCPSampler.classname", request.classname);
this.stringProp("TCPSampler.server", request.server);
this.stringProp("TCPSampler.port", request.port);
this.stringProp("TCPSampler.ctimeout", request.ctimeout);
this.stringProp("TCPSampler.timeout", request.timeout);
this.boolProp("TCPSampler.reUseConnection", request.reUseConnection);
this.boolProp("TCPSampler.nodelay", request.nodelay);
this.boolProp("TCPSampler.closeConnection", request.closeConnection);
this.stringProp("TCPSampler.soLinger", request.soLinger);
this.stringProp("TCPSampler.EolByte", request.eolByte);
this.stringProp("TCPSampler.request", request.request);
this.stringProp("ConfigTestElement.username", request.username);
this.stringProp("ConfigTestElement.password", request.password);
}
}
export class HTTPSamplerProxy extends DefaultTestElement { export class HTTPSamplerProxy extends DefaultTestElement {
constructor(testName, options = {}) { constructor(testName, options = {}) {
super('HTTPSamplerProxy', 'HttpTestSampleGui', 'HTTPSamplerProxy', testName); super('HTTPSamplerProxy', 'HttpTestSampleGui', 'HTTPSamplerProxy', testName);

View File

@ -25,7 +25,7 @@ import {
ThreadGroup, ThreadGroup,
XPath2Extractor, XPath2Extractor,
IfController as JMXIfController, IfController as JMXIfController,
ConstantTimer as JMXConstantTimer, ConstantTimer as JMXConstantTimer, TCPSampler,
} from "./JMX"; } from "./JMX";
import Mock from "mockjs"; import Mock from "mockjs";
import {funcFilters} from "@/common/js/func-filter"; import {funcFilters} from "@/common/js/func-filter";
@ -286,6 +286,7 @@ export class RequestFactory {
HTTP: "HTTP", HTTP: "HTTP",
DUBBO: "DUBBO", DUBBO: "DUBBO",
SQL: "SQL", SQL: "SQL",
TCP: "TCP",
} }
constructor(options = {}) { constructor(options = {}) {
@ -295,6 +296,8 @@ export class RequestFactory {
return new DubboRequest(options); return new DubboRequest(options);
case RequestFactory.TYPES.SQL: case RequestFactory.TYPES.SQL:
return new SqlRequest(options); return new SqlRequest(options);
case RequestFactory.TYPES.TCP:
return new TCPRequest(options);
default: default:
return new HttpRequest(options); return new HttpRequest(options);
} }
@ -305,9 +308,15 @@ export class Request extends BaseConfig {
constructor(type, options = {}) { constructor(type, options = {}) {
super(); super();
this.type = type; this.type = type;
options.id = options.id || uuid(); this.id = options.id || uuid();
this.timer = options.timer = new ConstantTimer(options.timer); this.name = options.name;
this.controller = options.controller = new IfController(options.controller); this.enable = options.enable === undefined ? true : options.enable;
this.assertions = new Assertions(options.assertions);
this.extract = new Extract(options.extract);
this.jsr223PreProcessor = new JSR223Processor(options.jsr223PreProcessor);
this.jsr223PostProcessor = new JSR223Processor(options.jsr223PostProcessor);
this.timer = new ConstantTimer(options.timer);
this.controller = new IfController(options.controller);
} }
showType() { showType() {
@ -322,41 +331,22 @@ export class Request extends BaseConfig {
export class HttpRequest extends Request { export class HttpRequest extends Request {
constructor(options) { constructor(options) {
super(RequestFactory.TYPES.HTTP, options); super(RequestFactory.TYPES.HTTP, options);
this.name = undefined; this.url = options.url;
this.url = undefined; this.path = options.path;
this.path = undefined; this.method = options.method || "GET";
this.method = undefined;
this.parameters = []; this.parameters = [];
this.headers = []; this.headers = [];
this.body = undefined; this.body = new Body(options.body);
this.assertions = undefined; this.environment = options.environment;
this.extract = undefined; this.useEnvironment = options.useEnvironment;
this.environment = undefined;
this.useEnvironment = undefined;
this.debugReport = undefined; this.debugReport = undefined;
this.beanShellPreProcessor = undefined; this.connectTimeout = options.connectTimeout || 60 * 1000;
this.beanShellPostProcessor = undefined; this.responseTimeout = options.responseTimeout;
this.jsr223PreProcessor = undefined; this.followRedirects = options.followRedirects === undefined ? true : options.followRedirects;
this.jsr223PostProcessor = undefined;
this.enable = true;
this.connectTimeout = 60 * 1000;
this.responseTimeout = undefined;
this.followRedirects = true;
this.set(options);
this.sets({parameters: KeyValue, headers: KeyValue}, options); this.sets({parameters: KeyValue, headers: KeyValue}, options);
} }
initOptions(options = {}) {
options.method = options.method || "GET";
options.body = new Body(options.body);
options.assertions = new Assertions(options.assertions);
options.extract = new Extract(options.extract);
options.jsr223PreProcessor = new JSR223Processor(options.jsr223PreProcessor);
options.jsr223PostProcessor = new JSR223Processor(options.jsr223PostProcessor);
return options;
}
isValid(environmentId, environment) { isValid(environmentId, environment) {
if (this.enable) { if (this.enable) {
if (this.useEnvironment) { if (this.useEnvironment) {
@ -412,7 +402,6 @@ export class DubboRequest extends Request {
constructor(options = {}) { constructor(options = {}) {
super(RequestFactory.TYPES.DUBBO, options); super(RequestFactory.TYPES.DUBBO, options);
this.name = options.name;
this.protocol = options.protocol || DubboRequest.PROTOCOLS.DUBBO; this.protocol = options.protocol || DubboRequest.PROTOCOLS.DUBBO;
this.interface = options.interface; this.interface = options.interface;
this.method = options.method; this.method = options.method;
@ -421,16 +410,9 @@ export class DubboRequest extends Request {
this.consumerAndService = new ConsumerAndService(options.consumerAndService); this.consumerAndService = new ConsumerAndService(options.consumerAndService);
this.args = []; this.args = [];
this.attachmentArgs = []; this.attachmentArgs = [];
this.assertions = new Assertions(options.assertions);
this.extract = new Extract(options.extract);
// Scenario.dubboConfig // Scenario.dubboConfig
this.dubboConfig = undefined; this.dubboConfig = undefined;
this.debugReport = undefined; this.debugReport = undefined;
this.beanShellPreProcessor = new BeanShellProcessor(options.beanShellPreProcessor);
this.beanShellPostProcessor = new BeanShellProcessor(options.beanShellPostProcessor);
this.enable = options.enable === undefined ? true : options.enable;
this.jsr223PreProcessor = new JSR223Processor(options.jsr223PreProcessor);
this.jsr223PostProcessor = new JSR223Processor(options.jsr223PostProcessor);
this.sets({args: KeyValue, attachmentArgs: KeyValue}, options); this.sets({args: KeyValue, attachmentArgs: KeyValue}, options);
} }
@ -485,8 +467,6 @@ export class SqlRequest extends Request {
constructor(options = {}) { constructor(options = {}) {
super(RequestFactory.TYPES.SQL, options); super(RequestFactory.TYPES.SQL, options);
this.id = options.id || uuid();
this.name = options.name;
this.useEnvironment = options.useEnvironment; this.useEnvironment = options.useEnvironment;
this.resultVariable = options.resultVariable; this.resultVariable = options.resultVariable;
this.variableNames = options.variableNames; this.variableNames = options.variableNames;
@ -495,11 +475,6 @@ export class SqlRequest extends Request {
this.query = options.query; this.query = options.query;
// this.queryType = options.queryType; // this.queryType = options.queryType;
this.queryTimeout = options.queryTimeout || 60000; this.queryTimeout = options.queryTimeout || 60000;
this.enable = options.enable === undefined ? true : options.enable;
this.assertions = new Assertions(options.assertions);
this.extract = new Extract(options.extract);
this.jsr223PreProcessor = new JSR223Processor(options.jsr223PreProcessor);
this.jsr223PostProcessor = new JSR223Processor(options.jsr223PostProcessor);
this.sets({args: KeyValue, attachmentArgs: KeyValue}, options); this.sets({args: KeyValue, attachmentArgs: KeyValue}, options);
} }
@ -537,6 +512,59 @@ export class SqlRequest extends Request {
} }
} }
export class TCPRequest extends Request {
static CLASSES = ["TCPClientImpl", "BinaryTCPClientImpl", "LengthPrefixedBinaryTCPClientImpl"]
constructor(options = {}) {
super(RequestFactory.TYPES.TCP, options);
this.useEnvironment = options.useEnvironment;
this.debugReport = undefined;
this.classname = options.classname || TCPRequest.CLASSES[0];
this.server = options.server;
this.port = options.port;
this.ctimeout = options.ctimeout; // Connect
this.timeout = options.timeout; // Response
this.reUseConnection = options.reUseConnection === undefined ? true : options.reUseConnection;
this.nodelay = options.nodelay === undefined ? false : options.nodelay;
this.closeConnection = options.closeConnection === undefined ? false : options.closeConnection;
this.soLinger = options.soLinger;
this.eolByte = options.eolByte;
this.request = options.request;
this.username = options.username;
this.password = options.password;
}
isValid() {
if (this.enable) {
if (!this.server) {
return {
isValid: false,
info: 'api_test.request.tcp.server_cannot_be_empty'
}
}
}
return {
isValid: true
}
}
showType() {
return "TCP";
}
showMethod() {
return "TCP";
}
clone() {
return new TCPRequest(this);
}
}
export class ConfigCenter extends BaseConfig { export class ConfigCenter extends BaseConfig {
static PROTOCOLS = ["zookeeper", "nacos", "apollo"]; static PROTOCOLS = ["zookeeper", "nacos", "apollo"];
@ -658,7 +686,7 @@ export class Body extends BaseConfig {
export class KeyValue extends BaseConfig { export class KeyValue extends BaseConfig {
constructor(options) { constructor(options) {
options = options || {}; options = options || {};
options.enable = options.enable != false ? true : false; options.enable = options.enable === undefined ? true : options.enable;
super(); super();
this.name = undefined; this.name = undefined;
@ -1063,6 +1091,8 @@ class JMXGenerator {
} else if (request instanceof SqlRequest) { } else if (request instanceof SqlRequest) {
request.dataSource = scenario.databaseConfigMap.get(request.dataSource); request.dataSource = scenario.databaseConfigMap.get(request.dataSource);
sampler = new JDBCSampler(request.name || "", request); sampler = new JDBCSampler(request.name || "", request);
} else if (request instanceof TCPRequest) {
sampler = new TCPSampler(request.name || "", request);
} }
this.addRequestExtractor(sampler, request); this.addRequestExtractor(sampler, request);
@ -1297,7 +1327,7 @@ class JMXGenerator {
body.push({name: '', value: request.body.raw, encode: false, enable: true}); body.push({name: '', value: request.body.raw, encode: false, enable: true});
} }
if (request.method != 'GET') { if (request.method !== 'GET') {
httpSamplerProxy.add(new HTTPSamplerArguments(body)); httpSamplerProxy.add(new HTTPSamplerArguments(body));
} }
} }
@ -1306,7 +1336,7 @@ class JMXGenerator {
let files = []; let files = [];
let kvs = this.filterKVFile(request.body.kvs); let kvs = this.filterKVFile(request.body.kvs);
kvs.forEach(kv => { kvs.forEach(kv => {
if ((kv.enable != false) && kv.files) { if ((kv.enable !== false) && kv.files) {
kv.files.forEach(file => { kv.files.forEach(file => {
let arg = {}; let arg = {};
arg.name = kv.name; arg.name = kv.name;

View File

@ -113,6 +113,7 @@ export default {
formatErr: 'Format Error', formatErr: 'Format Error',
id: 'ID', id: 'ID',
cannot_be_null: 'not null ', cannot_be_null: 'not null ',
required: "{0} is required",
millisecond: 'ms', millisecond: 'ms',
please_upload: 'Please upload file', please_upload: 'Please upload file',
reference_documentation: "Reference documentation", reference_documentation: "Reference documentation",
@ -555,7 +556,6 @@ export default {
input_registry_center: "Please enter the registry center", input_registry_center: "Please enter the registry center",
input_consumer_service: "Please enter the consumer & service", input_consumer_service: "Please enter the consumer & service",
check_registry_center: "Can't get interface list, please check the registry center", check_registry_center: "Can't get interface list, please check the registry center",
}
}, },
sql: { sql: {
dataSource: "Data Source", dataSource: "Data Source",
@ -572,6 +572,23 @@ export default {
result_variable: "Result variable", result_variable: "Result variable",
variable_names: "Variable names", variable_names: "Variable names",
}, },
tcp: {
server: "Server Name or IP",
port: "Port Number",
connect: "Connect(ms)",
response: "Response(ms)",
re_use_connection: "Re-use connection",
no_delay: "Set NoDelay",
close_connection: "Close connection",
so_linger: "SO LINGER",
eol_byte: "End of line byte value",
request: "Text to Send",
username: "Username",
password: "Password",
login: "Login Configuration",
server_cannot_be_empty: "Server name or IP cannot be empty",
},
},
api_import: { api_import: {
label: "Import", label: "Import",
title: "API test import", title: "API test import",

View File

@ -116,6 +116,7 @@ export default {
id: 'ID', id: 'ID',
millisecond: '毫秒', millisecond: '毫秒',
cannot_be_null: '不能为空', cannot_be_null: '不能为空',
required: "{0}是必填的",
already_exists: '名称不能重复', already_exists: '名称不能重复',
date: { date: {
select_date: '选择日期', select_date: '选择日期',
@ -572,6 +573,22 @@ export default {
dataSource_cannot_be_empty: "SQL请求数据源不能为空", dataSource_cannot_be_empty: "SQL请求数据源不能为空",
result_variable: "存储结果", result_variable: "存储结果",
variable_names: "按列存储", variable_names: "按列存储",
},
tcp: {
server: "服务器名或IP",
port: "端口",
connect: "连接(ms)",
response: "响应(ms)",
re_use_connection: "Re-use connection",
no_delay: "设置无延迟",
close_connection: "关闭连接",
so_linger: "SO LINGER",
eol_byte: "行尾(EOL)字节值",
request: "要发送的文本",
username: "用户名",
password: "密码",
login: "登录设置",
server_cannot_be_empty: "服务器名或IP不能为空",
} }
}, },
api_import: { api_import: {

View File

@ -116,6 +116,7 @@ export default {
id: 'ID', id: 'ID',
millisecond: '毫秒', millisecond: '毫秒',
cannot_be_null: '不能為空', cannot_be_null: '不能為空',
required: "{0}是必填的",
already_exists: '名稱不能重復', already_exists: '名稱不能重復',
date: { date: {
select_date: '選擇日期', select_date: '選擇日期',
@ -574,6 +575,22 @@ export default {
variable_names: "按列存儲", variable_names: "按列存儲",
} }
}, },
tcp: {
server: "服務器名或IP",
port: "端口",
connect: "連接(ms)",
response: "響應(ms)",
re_use_connection: "Re-use connection",
no_delay: "設置無延遲",
close_connection: "關閉連接",
so_linger: "SO LINGER",
eol_byte: "行尾EOL字節值",
request: "要發送的文本",
username: "用戶名",
password: "密碼",
login: "登錄設置",
server_cannot_be_empty: "服務器名或IP不能為空",
},
api_import: { api_import: {
label: "導入", label: "導入",
title: "接口測試導入", title: "接口測試導入",