Skip to content

Commit 8773fb8

Browse files
committed
feat: support to run test case in batch mode
1 parent dcdac9e commit 8773fb8

File tree

15 files changed

+1188
-644
lines changed

15 files changed

+1188
-644
lines changed

console/atest-ui/src/views/StoreManager.vue

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup lang="ts">
22
import { ElMessage } from 'element-plus'
33
import { reactive, ref, watch } from 'vue'
4-
import { Edit, Delete, QuestionFilled } from '@element-plus/icons-vue'
4+
import { Edit, Delete, QuestionFilled, Help } from '@element-plus/icons-vue'
55
import type { FormInstance, FormRules } from 'element-plus'
66
import type { Pair } from './types'
77
import { API } from './net'
@@ -111,8 +111,7 @@ Magic.Keys(addStore, ['Alt+KeyN'])
111111
112112
const rules = reactive<FormRules<Store>>({
113113
name: [{ required: true, message: 'Name is required', trigger: 'blur' }],
114-
url: [{ required: true, message: 'URL is required', trigger: 'blur' }],
115-
pluginName: [{ required: true, message: 'Plugin is required', trigger: 'blur' }]
114+
url: [{ required: true, message: 'URL is required', trigger: 'blur' }]
116115
})
117116
const submitForm = async (formEl: FormInstance | undefined) => {
118117
if (!formEl) return
@@ -130,6 +129,7 @@ const submitForm = async (formEl: FormInstance | undefined) => {
130129
watch(() => storeForm.kind.name, (name) => {
131130
const ext = SupportedExtension(name)
132131
if (ext) {
132+
storeExtLink.value = ext.link
133133
let pro = storeForm.properties.slice()
134134
135135
for (var i = 0; i < pro.length;) {
@@ -198,6 +198,8 @@ function updateKeys() {
198198
} as Pair)
199199
}
200200
}
201+
202+
const storeExtLink = ref('')
201203
</script>
202204

203205
<template>
@@ -294,6 +296,11 @@ function updateKeys() {
294296
:value="item.name"
295297
/>
296298
</el-select>
299+
<el-icon v-if="storeExtLink && storeExtLink !== ''">
300+
<el-link :href="storeExtLink" target="_blank">
301+
<Help />
302+
</el-link>
303+
</el-icon>
297304
</el-form-item>
298305
<el-form-item :label="t('field.pluginURL')" prop="plugin">
299306
<el-input v-model="storeForm.kind.url" test-id="store-form-plugin" />

console/atest-ui/src/views/TestCase.vue

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { Pair, TestResult, TestCaseWithSuite, TestCase } from './types'
77
import { NewSuggestedAPIsQuery, CreateFilter, GetHTTPMethods, FlattenObject } from './types'
88
import { Cache } from './cache'
99
import { API } from './net'
10+
import type { RunTestCaseRequest } from './net'
1011
import { UIAPI } from './net-vue'
1112
import type { TestCaseResponse } from './cache'
1213
import { Magic } from './magicKeys'
@@ -63,23 +64,39 @@ const runTestCase = () => {
6364
requestLoading.value = true
6465
const name = props.name
6566
const suite = props.suite
66-
API.RunTestCase({
67+
const request = {
6768
suiteName: suite,
6869
name: name,
6970
parameters: parameters.value
70-
}, (e) => {
71-
handleTestResult(e)
72-
requestLoading.value = false
73-
}, (e) => {
74-
parameters.value = []
71+
} as RunTestCaseRequest
72+
73+
if (batchRunMode.value) {
74+
API.BatchRunTestCase({
75+
count: batchRunCount.value,
76+
request: request
77+
}, (d) => {
78+
requestLoading.value = false
79+
ElMessage({
80+
showClose: true,
81+
message: d,
82+
type: 'success'
83+
})
84+
})
85+
} else {
86+
API.RunTestCase(request, (e) => {
87+
handleTestResult(e)
88+
requestLoading.value = false
89+
}, (e) => {
90+
parameters.value = []
7591
76-
requestLoading.value = false
77-
UIAPI.ErrorTip(e)
78-
parseResponseBody(e.body)
79-
})
92+
requestLoading.value = false
93+
UIAPI.ErrorTip(e)
94+
parseResponseBody(e.body)
95+
})
96+
}
8097
}
8198
82-
const parseResponseBody = (body) => {
99+
const parseResponseBody = (body: any) => {
83100
if (body === '') {
84101
return
85102
}
@@ -93,7 +110,7 @@ const parseResponseBody = (body) => {
93110
}
94111
}
95112
96-
const handleTestResult = (e) => {
113+
const handleTestResult = (e: any) => {
97114
testResult.value = e;
98115
99116
if (!isHistoryTestCase.value) {
@@ -149,6 +166,12 @@ function responseBodyFilter() {
149166
}
150167
151168
const parameterDialogOpened = ref(false)
169+
const batchRunMode = ref(false)
170+
const batchRunCount = ref(1)
171+
const openBatchRunDialog = () => {
172+
batchRunMode.value = true
173+
openParameterDialog()
174+
}
152175
function openParameterDialog() {
153176
API.GetTestSuite(props.suite, (e) => {
154177
parameters.value = e.param
@@ -159,6 +182,7 @@ function openParameterDialog() {
159182
function sendRequestWithParameter() {
160183
parameterDialogOpened.value = false
161184
sendRequest()
185+
batchRunMode.value = false
162186
}
163187
164188
function generateCode() {
@@ -915,6 +939,7 @@ Magic.Keys(() => {
915939
<template #dropdown>
916940
<el-dropdown-menu>
917941
<el-dropdown-item @click="openParameterDialog">{{ t('button.sendWithParam') }}</el-dropdown-item>
942+
<el-dropdown-item @click="openBatchRunDialog">Batch Send</el-dropdown-item>
918943
</el-dropdown-menu>
919944
</template>
920945
</el-dropdown>
@@ -1238,6 +1263,16 @@ Magic.Keys(() => {
12381263
<h4>{{ t('title.apiRequestParameter') }}</h4>
12391264
</template>
12401265
<template #default>
1266+
<div v-if="batchRunMode">
1267+
<el-row>
1268+
<el-col :span="6">
1269+
Count:
1270+
</el-col>
1271+
<el-col :span="18">
1272+
<el-input v-model="batchRunCount" type="number" />
1273+
</el-col>
1274+
</el-row>
1275+
</div>
12411276
<el-table :data="parameters" style="width: 100%"
12421277
:empty-text="t('tip.noParameter')">
12431278
<el-table-column :label="t('field.key')" width="180">

console/atest-ui/src/views/net.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ interface RunTestCaseRequest {
282282
parameters: any
283283
}
284284

285+
interface BatchRunTestCaseRequest {
286+
count: number
287+
request: RunTestCaseRequest
288+
}
289+
285290
function RunTestCase(request: RunTestCaseRequest,
286291
callback: (d: any) => void, errHandle?: (e: any) => void | null) {
287292
const requestOptions = {
@@ -301,6 +306,41 @@ function RunTestCase(request: RunTestCaseRequest,
301306
.then(callback).catch(emptyOrDefault(errHandle))
302307
}
303308

309+
const BatchRunTestCase = (request: BatchRunTestCaseRequest, callback: (d: any) => void) => {
310+
const requestOptions = {
311+
method: 'POST',
312+
headers: {
313+
'X-Store-Name': Cache.GetCurrentStore().name,
314+
'Accept': 'text/event-stream',
315+
'X-Auth': getToken()
316+
},
317+
body: JSON.stringify({
318+
suiteName: request.request.suiteName,
319+
caseName: request.request.name,
320+
parameters: request.request.parameters,
321+
count: request.count,
322+
})
323+
}
324+
fetch(`/api/v1/batchRun`, requestOptions)
325+
.then((response: any) => {
326+
if (response.ok) {
327+
const reader = response.body.getReader();
328+
let { done, value } = reader.read();
329+
while (!done) {
330+
const chunk = new TextDecoder().decode(value, { stream: true });
331+
332+
console.log(chunk);
333+
// callback(chunk);
334+
335+
({ done, value } = reader.read());
336+
}
337+
callback('');
338+
} else {
339+
throw new Error('Network response was not ok.');
340+
}
341+
})
342+
}
343+
304344
function DuplicateTestCase(sourceSuiteName: string, targetSuiteName: string,
305345
sourceTestCaseName: string, targetTestCaseName: string,
306346
callback: (d: any) => void, errHandle?: ((reason: any) => PromiseLike<never>) | undefined | null ) {
@@ -690,7 +730,7 @@ export const API = {
690730
DefaultResponseProcess,
691731
GetVersion,
692732
CreateTestSuite, UpdateTestSuite, ImportTestSuite, GetTestSuite, DeleteTestSuite, ConvertTestSuite, DuplicateTestSuite, GetTestSuiteYaml,
693-
CreateTestCase, UpdateTestCase, GetTestCase, ListTestCase, DeleteTestCase, RunTestCase,
733+
CreateTestCase, UpdateTestCase, GetTestCase, ListTestCase, DeleteTestCase, RunTestCase, BatchRunTestCase,
694734
GetHistoryTestCaseWithResult, DeleteHistoryTestCase,GetHistoryTestCase, GetTestCaseAllHistory, DeleteAllHistoryTestCase, DownloadResponseFile,
695735
GenerateCode, ListCodeGenerator, HistoryGenerateCode,
696736
PopularHeaders,

console/atest-ui/src/views/store.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import type { Pair } from './types'
1717

1818
interface Store {
1919
name: string;
20+
link: string;
2021
params: Pair[];
2122
}
2223

@@ -37,7 +38,8 @@ const storeExtensions = [
3738
}, {
3839
key: 'name',
3940
description: 'See also: git config --local user.name xxx'
40-
}]
41+
}],
42+
link: 'https://github.com/LinuxSuRen/atest-ext-store-git'
4143
},
4244
{
4345
name: 'atest-store-s3',
@@ -55,26 +57,29 @@ const storeExtensions = [
5557
key: 'forcepathstyle'
5658
}, {
5759
key: 'bucket'
58-
}]
60+
}],
61+
link: 'https://github.com/LinuxSuRen/atest-ext-store-s3'
5962
},
6063
{
6164
name: 'atest-store-orm',
6265
params: [{
6366
key: 'driver',
6467
defaultValue: 'mysql',
65-
description: 'Supported: mysql, postgres'
68+
description: 'Supported: mysql, postgres, sqlite'
6669
}, {
6770
key: 'database',
6871
defaultValue: 'atest'
6972
}, {
7073
key: 'historyLimit',
7174
defaultValue: '',
7275
description: 'Set the limit of the history record count'
73-
}]
76+
}],
77+
link: 'https://github.com/LinuxSuRen/atest-ext-store-orm'
7478
},
7579
{
7680
name: 'atest-store-etcd',
77-
params: []
81+
params: [],
82+
link: 'https://github.com/LinuxSuRen/atest-ext-store-etcd'
7883
},
7984
{
8085
name: 'atest-store-mongodb',
@@ -83,7 +88,8 @@ const storeExtensions = [
8388
}, {
8489
key: 'database',
8590
defaultValue: 'atest'
86-
}]
91+
}],
92+
link: 'https://github.com/LinuxSuRen/atest-ext-store-mongodb'
8793
}
8894
] as Store[]
8995

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,5 +90,5 @@ require (
9090
golang.org/x/text v0.14.0 // indirect
9191
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
9292
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
93-
gopkg.in/yaml.v2 v2.4.0
93+
gopkg.in/yaml.v2 v2.4.0 // indirect
9494
)

pkg/runner/monitor/monitor.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/runner/monitor/monitor_grpc.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/server/remote_server.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
reflect "reflect"
3030
"regexp"
3131
"strings"
32+
"time"
3233

3334
"github.com/prometheus/client_golang/prometheus"
3435
"github.com/prometheus/client_golang/prometheus/promauto"
@@ -294,6 +295,44 @@ func (s *server) Run(ctx context.Context, task *TestTask) (reply *TestResult, er
294295
return
295296
}
296297

298+
func (s *server) BatchRun(srv Runner_BatchRunServer) (err error) {
299+
ctx := srv.Context()
300+
for {
301+
select {
302+
case <-ctx.Done():
303+
return ctx.Err()
304+
default:
305+
var in *BatchTestTask
306+
in, err = srv.Recv()
307+
if err != nil {
308+
if err == io.EOF {
309+
return nil
310+
}
311+
return err
312+
}
313+
314+
for i := 0; i < int(in.Count); i++ {
315+
var reply *TestCaseResult
316+
if reply, err = s.RunTestCase(ctx, &TestCaseIdentity{
317+
Suite: in.SuiteName,
318+
Testcase: in.CaseName,
319+
}); err != nil {
320+
return
321+
}
322+
323+
if err = srv.Send(&TestResult{
324+
TestCaseResult: []*TestCaseResult{reply},
325+
Error: reply.Error,
326+
}); err != nil {
327+
return err
328+
}
329+
time.Sleep(time.Second * 2)
330+
}
331+
}
332+
}
333+
return
334+
}
335+
297336
func handleLargeResponseBody(resp runner.SimpleResponse, suite string, caseName string) (reply runner.SimpleResponse, err error) {
298337
const maxSize = 5120
299338
prefix := "isFilePath-" + strings.Join([]string{suite, caseName}, "-")

0 commit comments

Comments
 (0)