From 54d66f4e1234a9b9c18a18e00cfaa7c6f941db64 Mon Sep 17 00:00:00 2001 From: rick Date: Sun, 25 May 2025 15:59:43 +0800 Subject: [PATCH 1/5] fix: lack of error msg of download binary file --- console/atest-ui/src/views/TestCase.vue | 21 +- console/atest-ui/src/views/net.ts | 7 +- console/atest-ui/src/views/types.ts | 4 +- pkg/runner/http.go | 782 ++++++++++++------------ pkg/util/default.go | 5 + 5 files changed, 415 insertions(+), 404 deletions(-) diff --git a/console/atest-ui/src/views/TestCase.vue b/console/atest-ui/src/views/TestCase.vue index a143f566..9e5c265f 100644 --- a/console/atest-ui/src/views/TestCase.vue +++ b/console/atest-ui/src/views/TestCase.vue @@ -128,11 +128,8 @@ const handleTestResult = (e: any): void => { if (!isHistoryTestCase.value) { handleTestResultError(e) } - const isFilePath = e.body.startsWith("isFilePath-") - - if(isFilePath){ - isResponseFile.value = true - } else if(e.body !== ''){ + isResponseFile.value = e.body.startsWith("isFilePath-") + if(!isResponseFile.value && e.body !== ''){ testResult.value.bodyLength = e.body.length try { // Try to parse as JSON, fallback to plain text if parsing fails @@ -149,11 +146,11 @@ const handleTestResult = (e: any): void => { } } - Cache.SetTestCaseResponseCache(suite + '-' + name, { - body: testResult.value.bodyObject, - output: e.output, - statusCode: testResult.value.statusCode - } as TestCaseResponse) + Cache.SetTestCaseResponseCache(suite + '-' + name, { + body: testResult.value.bodyObject, + output: e.output, + statusCode: testResult.value.statusCode + } as TestCaseResponse) parameters.value = []; } @@ -436,7 +433,9 @@ function downloadResponseFile(){ } else { console.error('No data to download.'); } - }) + }, (e) => { + UIAPI.ErrorTip(e); + }); } function setDefaultValues(e) { diff --git a/console/atest-ui/src/views/net.ts b/console/atest-ui/src/views/net.ts index ec273306..6d43c3d3 100644 --- a/console/atest-ui/src/views/net.ts +++ b/console/atest-ui/src/views/net.ts @@ -67,7 +67,7 @@ async function DefaultResponseProcess(response: any): Promise { // For OK responses, handle content based on Content-Type const contentType = response.headers.get('Content-Type') ?? ''; - if (contentType.startsWith('text/plain')) { + if (isTextReadableExpectJSON(contentType)) { // For text/plain, directly return the text return await response.text(); } @@ -90,6 +90,11 @@ async function DefaultResponseProcess(response: any): Promise { } } +const isTextReadableExpectJSON = (contentType: string): boolean => { + // Check if the content type is text-based + return contentType.startsWith('text/') || contentType === 'application/javascript'; +} + interface AppVersion { version: string commit: string diff --git a/console/atest-ui/src/views/types.ts b/console/atest-ui/src/views/types.ts index 66afe234..c665fe1c 100644 --- a/console/atest-ui/src/views/types.ts +++ b/console/atest-ui/src/views/types.ts @@ -37,7 +37,7 @@ export interface Suite { export interface TestResult { body: string - bodyObject: {} + bodyObject: {} | null bodyText: string bodyLength: number output: string @@ -46,7 +46,7 @@ export interface TestResult { header: Pair[] // inner fields - originBodyObject:{} + originBodyObject:{} | null } export interface Pair { diff --git a/pkg/runner/http.go b/pkg/runner/http.go index c9c8e59e..ebb5c1bf 100644 --- a/pkg/runner/http.go +++ b/pkg/runner/http.go @@ -17,42 +17,42 @@ limitations under the License. package runner import ( - "context" - "crypto/tls" - "errors" - "fmt" - "io" - "net/http" - "net/url" - "os" - "path/filepath" - "strconv" - "strings" - "time" - - "github.com/go-openapi/spec" - - "github.com/andreyvit/diff" - "github.com/expr-lang/expr" - "github.com/expr-lang/expr/vm" - "github.com/linuxsuren/api-testing/pkg/apispec" - "github.com/linuxsuren/api-testing/pkg/render" - "github.com/linuxsuren/api-testing/pkg/testing" - "github.com/linuxsuren/api-testing/pkg/util" - "github.com/xeipuuv/gojsonschema" + "context" + "crypto/tls" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/go-openapi/spec" + + "github.com/andreyvit/diff" + "github.com/expr-lang/expr" + "github.com/expr-lang/expr/vm" + "github.com/linuxsuren/api-testing/pkg/apispec" + "github.com/linuxsuren/api-testing/pkg/render" + "github.com/linuxsuren/api-testing/pkg/testing" + "github.com/linuxsuren/api-testing/pkg/util" + "github.com/xeipuuv/gojsonschema" ) // ReportResult represents the report result of a set of the same API requests type ReportResult struct { - Name string - API string - Count int - Average time.Duration - Max time.Duration - Min time.Duration - QPS int - Error int - LastErrorMessage string + Name string + API string + Count int + Average time.Duration + Max time.Duration + Min time.Duration + QPS int + Error int + LastErrorMessage string } // ReportResultSlice is the alias type of ReportResult slice @@ -60,43 +60,43 @@ type ReportResultSlice []ReportResult // Len returns the count of slice items func (r ReportResultSlice) Len() int { - return len(r) + return len(r) } // Less returns if i bigger than j func (r ReportResultSlice) Less(i, j int) bool { - return r[i].Average > r[j].Average + return r[i].Average > r[j].Average } // Swap swaps the items func (r ReportResultSlice) Swap(i, j int) { - tmp := r[i] - r[i] = r[j] - r[j] = tmp + tmp := r[i] + r[i] = r[j] + r[j] = tmp } type simpleTestCaseRunner struct { - UnimplementedRunner - simpleResponse SimpleResponse - cookies []*http.Cookie - apiSuggestLimit int + UnimplementedRunner + simpleResponse SimpleResponse + cookies []*http.Cookie + apiSuggestLimit int } // NewSimpleTestCaseRunner creates the instance of the simple test case runner func NewSimpleTestCaseRunner() TestCaseRunner { - runner := &simpleTestCaseRunner{ - UnimplementedRunner: NewDefaultUnimplementedRunner(), - simpleResponse: SimpleResponse{}, - cookies: []*http.Cookie{}, - apiSuggestLimit: 10, - } - return runner + runner := &simpleTestCaseRunner{ + UnimplementedRunner: NewDefaultUnimplementedRunner(), + simpleResponse: SimpleResponse{}, + cookies: []*http.Cookie{}, + apiSuggestLimit: 10, + } + return runner } func init() { - RegisterRunner("http", func(*testing.TestSuite) TestCaseRunner { - return NewSimpleTestCaseRunner() - }) + RegisterRunner("http", func(*testing.TestSuite) TestCaseRunner { + return NewSimpleTestCaseRunner() + }) } // ContextKey is the alias type of string for context key @@ -104,403 +104,405 @@ type ContextKey string // NewContextKeyBuilder returns an emtpy context key func NewContextKeyBuilder() ContextKey { - return ContextKey("") + return ContextKey("") } // ParentDir returns the key of the parent directory func (c ContextKey) ParentDir() ContextKey { - return ContextKey("parentDir") + return ContextKey("parentDir") } // GetContextValueOrEmpty returns the value of the context key, if not exist, return empty string func (c ContextKey) GetContextValueOrEmpty(ctx context.Context) string { - if ctx.Value(c) != nil { - return ctx.Value(c).(string) - } - return "" + if ctx.Value(c) != nil { + return ctx.Value(c).(string) + } + return "" } // RunTestCase is the main entry point of a test case func (r *simpleTestCaseRunner) RunTestCase(testcase *testing.TestCase, dataContext interface{}, ctx context.Context) (output interface{}, err error) { - r.log.Info("start to run: '%s'\n", testcase.Name) - record := NewReportRecord() - defer func(rr *ReportRecord) { - rr.Group = testcase.Group - rr.Name = testcase.Name - rr.EndTime = time.Now() - rr.Error = err - rr.API = testcase.Request.API - rr.Method = testcase.Request.Method - r.testReporter.PutRecord(rr) - }(record) - - defer func() { - if err == nil { - err = runJob(testcase.After, dataContext, output) - } - }() - - insecure := false - if r.Secure != nil { - insecure = r.Secure.Insecure - } - client := util.TlsAwareHTTPClient(insecure) // TODO should have a way to change it - contextDir := NewContextKeyBuilder().ParentDir().GetContextValueOrEmpty(ctx) - if err = testcase.Request.Render(dataContext, contextDir); err != nil { - return - } - - // add proxy setting - if r.proxy != nil && r.proxy.HTTP != "" { - var proxyURL *url.URL - if proxyURL, err = url.Parse(r.proxy.HTTP); err == nil { - client.Transport = &http.Transport{ - Proxy: func(req *http.Request) (*url.URL, error) { - for _, noProxy := range strings.Split(r.proxy.No, ",") { - noProxy = strings.TrimSpace(noProxy) - if noProxy != "" && strings.Contains(req.URL.Host, noProxy) { - return nil, nil - } - } - r.log.Info("using proxy: %v\n", proxyURL) - return proxyURL, nil - }, - TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure}, - } - } else { - err = fmt.Errorf("failed to parse proxy URL: %v", err) - return - } - } - - var requestBody io.Reader - if requestBody, err = testcase.Request.GetBody(); err != nil { - return - } - - var request *http.Request - if request, err = http.NewRequestWithContext(ctx, testcase.Request.Method, testcase.Request.API, requestBody); err != nil { - return - } - - q := request.URL.Query() - for k := range testcase.Request.Query { - q.Add(k, testcase.Request.Query.GetValue(k)) - } - request.URL.RawQuery = q.Encode() - - // set headers - for key, val := range testcase.Request.Header { - request.Header.Add(key, val) - } - - if err = runJob(testcase.Before, dataContext, nil); err != nil { - return - } - - for _, cookie := range r.cookies { - request.AddCookie(cookie) - } - for k, v := range testcase.Request.Cookie { - request.AddCookie(&http.Cookie{ - Name: k, - Value: v, - }) - } - r.log.Info("start to send request to %v with method %s\n", request.URL, request.Method) - r.log.Info("request header %v\n", request.Header) - - // TODO only do this for unit testing, should remove it once we have a better way - if strings.HasPrefix(testcase.Request.API, "http://") && r.proxy == nil { - client = http.DefaultClient - } - - // send the HTTP request - var resp *http.Response - if resp, err = client.Do(request); err != nil { - return - } - - r.log.Debug("test case %q, status code: %d\n", testcase.Name, resp.StatusCode) - - if err = testcase.Expect.Render(dataContext); err != nil { - return - } - if err = expectInt(testcase.Name, testcase.Expect.StatusCode, resp.StatusCode); err != nil { - err = fmt.Errorf("error is: %v", err) - } - - for key, val := range testcase.Expect.Header { - actualVal := resp.Header.Get(key) - err = errors.Join(err, expectString(testcase.Name, val, actualVal)) - } - - respType := util.GetFirstHeaderValue(resp.Header, util.ContentType) - - r.withSimpleResponseRecord(resp) - if isNonBinaryContent(respType) { - var responseBodyData []byte - var rErr error - if responseBodyData, rErr = r.withResponseBodyRecord(resp); rErr != nil { - err = errors.Join(err, rErr) - return - } - - record.Body = string(responseBodyData) - r.log.Trace("response body: %s\n", record.Body) - - if output, rErr = verifyResponseBodyData(testcase.Name, testcase.Expect, respType, responseBodyData); rErr != nil { - err = errors.Join(err, rErr) - return - } - - err = errors.Join(err, jsonSchemaValidation(testcase.Expect.Schema, responseBodyData)) - } else { - switch respType { - case util.OctetStream, util.Image: - var data []byte - if data, err = io.ReadAll(resp.Body); err == nil { - r.simpleResponse.RawBody = data - r.simpleResponse, err = HandleLargeResponseBody(r.simpleResponse, testcase.Group, testcase.Name) - } - } - r.log.Debug("skip to read the body due to it is not struct content: %q\n", respType) - } - - r.cookies = append(r.cookies, resp.Cookies()...) - return + r.log.Info("start to run: '%s'\n", testcase.Name) + record := NewReportRecord() + defer func(rr *ReportRecord) { + rr.Group = testcase.Group + rr.Name = testcase.Name + rr.EndTime = time.Now() + rr.Error = err + rr.API = testcase.Request.API + rr.Method = testcase.Request.Method + r.testReporter.PutRecord(rr) + }(record) + + defer func() { + if err == nil { + err = runJob(testcase.After, dataContext, output) + } + }() + + insecure := false + if r.Secure != nil { + insecure = r.Secure.Insecure + } + client := util.TlsAwareHTTPClient(insecure) // TODO should have a way to change it + contextDir := NewContextKeyBuilder().ParentDir().GetContextValueOrEmpty(ctx) + if err = testcase.Request.Render(dataContext, contextDir); err != nil { + return + } + + // add proxy setting + if r.proxy != nil && r.proxy.HTTP != "" { + var proxyURL *url.URL + if proxyURL, err = url.Parse(r.proxy.HTTP); err == nil { + client.Transport = &http.Transport{ + Proxy: func(req *http.Request) (*url.URL, error) { + for _, noProxy := range strings.Split(r.proxy.No, ",") { + noProxy = strings.TrimSpace(noProxy) + if noProxy != "" && strings.Contains(req.URL.Host, noProxy) { + return nil, nil + } + } + r.log.Info("using proxy: %v\n", proxyURL) + return proxyURL, nil + }, + TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure}, + } + } else { + err = fmt.Errorf("failed to parse proxy URL: %v", err) + return + } + } + + var requestBody io.Reader + if requestBody, err = testcase.Request.GetBody(); err != nil { + return + } + + var request *http.Request + if request, err = http.NewRequestWithContext(ctx, testcase.Request.Method, testcase.Request.API, requestBody); err != nil { + return + } + + q := request.URL.Query() + for k := range testcase.Request.Query { + q.Add(k, testcase.Request.Query.GetValue(k)) + } + request.URL.RawQuery = q.Encode() + + // set headers + for key, val := range testcase.Request.Header { + request.Header.Add(key, val) + } + + if err = runJob(testcase.Before, dataContext, nil); err != nil { + return + } + + for _, cookie := range r.cookies { + request.AddCookie(cookie) + } + for k, v := range testcase.Request.Cookie { + request.AddCookie(&http.Cookie{ + Name: k, + Value: v, + }) + } + r.log.Info("start to send request to %v with method %s\n", request.URL, request.Method) + r.log.Info("request header %v\n", request.Header) + + // TODO only do this for unit testing, should remove it once we have a better way + if strings.HasPrefix(testcase.Request.API, "http://") && r.proxy == nil { + client = http.DefaultClient + } + + // send the HTTP request + var resp *http.Response + if resp, err = client.Do(request); err != nil { + return + } + + r.log.Debug("test case %q, status code: %d\n", testcase.Name, resp.StatusCode) + + if err = testcase.Expect.Render(dataContext); err != nil { + return + } + if err = expectInt(testcase.Name, testcase.Expect.StatusCode, resp.StatusCode); err != nil { + err = fmt.Errorf("error is: %v", err) + } + + for key, val := range testcase.Expect.Header { + actualVal := resp.Header.Get(key) + err = errors.Join(err, expectString(testcase.Name, val, actualVal)) + } + + respType := util.GetFirstHeaderValue(resp.Header, util.ContentType) + + r.withSimpleResponseRecord(resp) + if isNonBinaryContent(respType) { + var responseBodyData []byte + var rErr error + if responseBodyData, rErr = r.withResponseBodyRecord(resp); rErr != nil { + err = errors.Join(err, rErr) + return + } + + record.Body = string(responseBodyData) + r.log.Trace("response body: %s\n", record.Body) + + if output, rErr = verifyResponseBodyData(testcase.Name, testcase.Expect, respType, responseBodyData); rErr != nil { + err = errors.Join(err, rErr) + return + } + + err = errors.Join(err, jsonSchemaValidation(testcase.Expect.Schema, responseBodyData)) + } else { + switch respType { + case util.OctetStream, util.Image: + var data []byte + if data, err = io.ReadAll(resp.Body); err == nil { + r.simpleResponse.RawBody = data + r.simpleResponse, err = HandleLargeResponseBody(r.simpleResponse, testcase.Group, testcase.Name) + } + } + r.log.Debug("skip to read the body due to it is not struct content: %q\n", respType) + } + + r.cookies = append(r.cookies, resp.Cookies()...) + return } func HandleLargeResponseBody(resp SimpleResponse, suite string, caseName string) (SimpleResponse, error) { - const maxSize = 5120 - prefix := "isFilePath-" + strings.Join([]string{suite, caseName}, "-") - if len(resp.RawBody) == 0 && len(resp.Body) > 0 { - resp.RawBody = []byte(resp.Body) - } - - if len(resp.RawBody) > maxSize { - tmpFile, err := os.CreateTemp("", prefix+"-") - defer tmpFile.Close() - if err != nil { - return resp, fmt.Errorf("failed to create file: %w", err) - } - - fmt.Println("response body is too large, will be saved to file", "size", len(resp.RawBody), "path", tmpFile.Name()) - if _, err = tmpFile.Write(resp.RawBody); err != nil { - return resp, fmt.Errorf("failed to write response body to file: %w", err) - } - absFilePath, err := filepath.Abs(tmpFile.Name()) - if err != nil { - return resp, fmt.Errorf("failed to get absolute file path: %w", err) - } - resp.Body = filepath.Base(absFilePath) - - // save the original filename into a new file - _ = os.WriteFile(absFilePath+"name", []byte(resp.getFileName()), 0644) - return resp, nil - } - return resp, nil + const maxSize = 5120 + prefix := "isFilePath-" + strings.Join([]string{suite, caseName}, "-") + if len(resp.RawBody) == 0 && len(resp.Body) > 0 { + resp.RawBody = []byte(resp.Body) + } + + if len(resp.RawBody) > maxSize { + tmpFile, err := os.CreateTemp("", prefix+"-") + defer tmpFile.Close() + if err != nil { + return resp, fmt.Errorf("failed to create file: %w", err) + } + + fmt.Println("response body is too large, will be saved to file", "size", len(resp.RawBody), "path", tmpFile.Name()) + if _, err = tmpFile.Write(resp.RawBody); err != nil { + return resp, fmt.Errorf("failed to write response body to file: %w", err) + } + absFilePath, err := filepath.Abs(tmpFile.Name()) + if err != nil { + return resp, fmt.Errorf("failed to get absolute file path: %w", err) + } + resp.Body = filepath.Base(absFilePath) + + // save the original filename into a new file + _ = os.WriteFile(absFilePath+"name", []byte(resp.getFileName()), 0644) + return resp, nil + } + return resp, nil } func ammendHeaders(headers http.Header, body []byte) { - // add content-length if it's missing - if val := headers.Get(util.ContentLength); val == "" { - headers.Add(util.ContentLength, strconv.Itoa(len(body))) - fmt.Printf("add content-length: %d\n", len(body)) - } + // add content-length if it's missing + if val := headers.Get(util.ContentLength); val == "" { + headers.Add(util.ContentLength, strconv.Itoa(len(body))) + fmt.Printf("add content-length: %d\n", len(body)) + } } func (r *simpleTestCaseRunner) GetSuggestedAPIs(suite *testing.TestSuite, api string) (result []*testing.TestCase, err error) { - if suite.Spec.URL == "" || suite.Spec.Kind != "swagger" { - return - } - - var swagger *spec.Swagger - if swagger, err = apispec.ParseURLToSwagger(suite.Spec.URL); err == nil && swagger != nil { - swaggerAPI := apispec.NewSwaggerAPI(swagger) - for api, methods := range swaggerAPI.ApiMap { - for _, method := range methods { - testcase := &testing.TestCase{ - Name: swagger.ID, - Request: testing.Request{ - API: api, - Method: strings.ToUpper(method), - Query: make(testing.SortedKeysStringMap), - }, - } - - switch testcase.Request.Method { - case http.MethodGet: - for _, param := range swagger.Paths.Paths[api].Get.Parameters { - switch param.In { - case "query": - // TODO should have a better way to provide the initial value - (&(testcase.Request)).Query[param.Name] = generateRandomValue(param) - } - } - testcase.Name = swagger.Paths.Paths[api].Get.ID - case http.MethodPost: - testcase.Name = swagger.Paths.Paths[api].Post.ID - case http.MethodPut: - testcase.Name = swagger.Paths.Paths[api].Put.ID - case http.MethodDelete: - testcase.Name = swagger.Paths.Paths[api].Delete.ID - case http.MethodPatch: - testcase.Name = swagger.Paths.Paths[api].Patch.ID - } - result = append(result, testcase) - if len(result) >= r.apiSuggestLimit { - return - } - } - } - } - return + if suite.Spec.URL == "" || suite.Spec.Kind != "swagger" { + return + } + + var swagger *spec.Swagger + if swagger, err = apispec.ParseURLToSwagger(suite.Spec.URL); err == nil && swagger != nil { + swaggerAPI := apispec.NewSwaggerAPI(swagger) + for api, methods := range swaggerAPI.ApiMap { + for _, method := range methods { + testcase := &testing.TestCase{ + Name: swagger.ID, + Request: testing.Request{ + API: api, + Method: strings.ToUpper(method), + Query: make(testing.SortedKeysStringMap), + }, + } + + switch testcase.Request.Method { + case http.MethodGet: + for _, param := range swagger.Paths.Paths[api].Get.Parameters { + switch param.In { + case "query": + // TODO should have a better way to provide the initial value + (&(testcase.Request)).Query[param.Name] = generateRandomValue(param) + } + } + testcase.Name = swagger.Paths.Paths[api].Get.ID + case http.MethodPost: + testcase.Name = swagger.Paths.Paths[api].Post.ID + case http.MethodPut: + testcase.Name = swagger.Paths.Paths[api].Put.ID + case http.MethodDelete: + testcase.Name = swagger.Paths.Paths[api].Delete.ID + case http.MethodPatch: + testcase.Name = swagger.Paths.Paths[api].Patch.ID + } + result = append(result, testcase) + if len(result) >= r.apiSuggestLimit { + return + } + } + } + } + return } func generateRandomValue(param spec.Parameter) interface{} { - switch param.Format { - case "int32", "int64": - return 101 - case "boolean": - return true - case "string": - return "random" - default: - return "random" - } + switch param.Format { + case "int32", "int64": + return 101 + case "boolean": + return true + case "string": + return "random" + default: + return "random" + } } func (r *simpleTestCaseRunner) withSimpleResponseRecord(resp *http.Response) { - r.simpleResponse = SimpleResponse{ - StatusCode: resp.StatusCode, - Header: make(map[string]string), - } - - for key := range resp.Header { - r.simpleResponse.Header[key] = resp.Header.Get(key) - } + r.simpleResponse = SimpleResponse{ + StatusCode: resp.StatusCode, + Header: make(map[string]string), + } + + for key := range resp.Header { + r.simpleResponse.Header[key] = resp.Header.Get(key) + } } func (r *simpleTestCaseRunner) withResponseBodyRecord(resp *http.Response) (responseBodyData []byte, err error) { - responseBodyData, err = io.ReadAll(resp.Body) - r.simpleResponse.Body = string(responseBodyData) + responseBodyData, err = io.ReadAll(resp.Body) + r.simpleResponse.Body = string(responseBodyData) - // add some headers for convenience - ammendHeaders(resp.Header, responseBodyData) - return + // add some headers for convenience + ammendHeaders(resp.Header, responseBodyData) + return } // GetResponseRecord returns the response record func (r *simpleTestCaseRunner) GetResponseRecord() SimpleResponse { - return r.simpleResponse + return r.simpleResponse } func (r *simpleTestCaseRunner) WithAPISuggestLimit(limit int) { - r.apiSuggestLimit = limit + r.apiSuggestLimit = limit } func expectInt(name string, expect, actual int) (err error) { - if expect != actual { - err = fmt.Errorf("case: %s, expect %d, actual %d", name, expect, actual) - } - return + if expect != actual { + err = fmt.Errorf("case: %s, expect %d, actual %d", name, expect, actual) + } + return } func expectString(name, expect, actual string) (err error) { - if expect != actual { - err = fmt.Errorf("case: %s, expect %s, actual %s", name, expect, actual) - } - return + if expect != actual { + err = fmt.Errorf("case: %s, expect %s, actual %s", name, expect, actual) + } + return } func jsonSchemaValidation(schema string, body []byte) (err error) { - if schema == "" { - return - } - - schemaLoader := gojsonschema.NewStringLoader(schema) - jsonLoader := gojsonschema.NewBytesLoader(body) - - var result *gojsonschema.Result - if result, err = gojsonschema.Validate(schemaLoader, jsonLoader); err == nil && !result.Valid() { - err = fmt.Errorf("JSON schema validation failed: %v", result.Errors()) - } - return + if schema == "" { + return + } + + schemaLoader := gojsonschema.NewStringLoader(schema) + jsonLoader := gojsonschema.NewBytesLoader(body) + + var result *gojsonschema.Result + if result, err = gojsonschema.Validate(schemaLoader, jsonLoader); err == nil && !result.Valid() { + err = fmt.Errorf("JSON schema validation failed: %v", result.Errors()) + } + return } func verifyResponseBodyData(caseName string, expect testing.Response, responseType string, responseBodyData []byte) (output interface{}, err error) { - if expect.Body != "" { - if string(responseBodyData) != strings.TrimSpace(expect.Body) { - err = fmt.Errorf("case: %s, got different response body, diff: \n%s", caseName, - diff.LineDiff(expect.Body, string(responseBodyData))) - return - } - } - - verifier := NewBodyVerify(responseType, expect) - if verifier == nil { - runnerLogger.Info("no body verify support with", "response type", responseType) - return - } - - if output, err = verifier.Parse(responseBodyData); err != nil { - return - } - - mapOutput := map[string]interface{}{ - "data": output, - } - if err = verifier.Verify(responseBodyData); err == nil { - err = Verify(expect, mapOutput) - } - return + if expect.Body != "" { + if string(responseBodyData) != strings.TrimSpace(expect.Body) { + err = fmt.Errorf("case: %s, got different response body, diff: \n%s", caseName, + diff.LineDiff(expect.Body, string(responseBodyData))) + return + } + } + + verifier := NewBodyVerify(responseType, expect) + if verifier == nil { + runnerLogger.Info("no body verify support with", "response type", responseType) + return + } + + if output, err = verifier.Parse(responseBodyData); err != nil { + return + } + + mapOutput := map[string]interface{}{ + "data": output, + } + if err = verifier.Verify(responseBodyData); err == nil { + err = Verify(expect, mapOutput) + } + return } func runJob(job *testing.Job, ctx interface{}, current interface{}) (err error) { - if job == nil { - return - } - var program *vm.Program - env := map[string]interface{}{ - "ctx": ctx, - "current": current, - } - - for _, item := range job.Items { - var exprText string - if exprText, err = render.Render("job", item, ctx); err != nil { - err = fmt.Errorf("failed to render: %q, error is: %v", item, err) - break - } - - if program, err = expr.Compile(exprText, expr.Env(env)); err != nil { - fmt.Printf("failed to compile: %q, %v\n", exprText, err) - return - } - - if _, err = expr.Run(program, env); err != nil { - fmt.Printf("failed to Run: %q, %v\n", exprText, err) - return - } - } - return + if job == nil { + return + } + var program *vm.Program + env := map[string]interface{}{ + "ctx": ctx, + "current": current, + } + + for _, item := range job.Items { + var exprText string + if exprText, err = render.Render("job", item, ctx); err != nil { + err = fmt.Errorf("failed to render: %q, error is: %v", item, err) + break + } + + if program, err = expr.Compile(exprText, expr.Env(env)); err != nil { + fmt.Printf("failed to compile: %q, %v\n", exprText, err) + return + } + + if _, err = expr.Run(program, env); err != nil { + fmt.Printf("failed to Run: %q, %v\n", exprText, err) + return + } + } + return } // isNonBinaryContent detect if the content belong to binary func isNonBinaryContent(contentType string) bool { - if IsJSONCompatileType(contentType) { - return true - } - - switch contentType { - case util.JSON, util.YAML, util.Plain, util.OCIImageIndex: - return true - default: - return false - } + if IsJSONCompatileType(contentType) { + return true + } + + switch contentType { + case util.JSON, util.YAML, util.Plain, util.OCIImageIndex, + util.CSS, util.JavaScript, util.HTML, util.XML, + util.SVG: + return true + default: + return false + } } func IsJSONCompatileType(contentType string) bool { - return strings.HasSuffix(contentType, "+json") + return strings.HasSuffix(contentType, "+json") } diff --git a/pkg/util/default.go b/pkg/util/default.go index 7b57e44f..ef07480b 100644 --- a/pkg/util/default.go +++ b/pkg/util/default.go @@ -74,12 +74,17 @@ const ( MultiPartFormData = "multipart/form-data" Form = "application/x-www-form-urlencoded" JSON = "application/json" + JavaScript = "application/javascript" OCIImageIndex = "application/vnd.oci.image.index.v1+json" YAML = "application/yaml" ZIP = "application/zip" + XML = "application/xml" OctetStream = "application/octet-stream" Image = "image/jpeg" + SVG = "image/svg+xml" Plain = "text/plain" + CSS = "text/css" + HTML = "text/html" Authorization = "Authorization" ) From 001714155a2e014adc1d59f2c919ac920e77397c Mon Sep 17 00:00:00 2001 From: rick Date: Sun, 25 May 2025 16:10:16 +0800 Subject: [PATCH 2/5] using the max recv msg size for local grpc connection --- cmd/mock-compose.go | 76 +- cmd/server.go | 7 +- pkg/generator/curl_generator.go | 64 +- pkg/runner/http_test.go | 1162 +++++++++++++------------- pkg/runner/runner.go | 108 +-- pkg/runner/runner_test.go | 100 +-- pkg/server/store_ext_manager.go | 280 +++---- pkg/server/store_ext_manager_test.go | 54 +- pkg/testing/case_test.go | 166 ++-- pkg/util/home/common_test.go | 12 +- 10 files changed, 1016 insertions(+), 1013 deletions(-) diff --git a/cmd/mock-compose.go b/cmd/mock-compose.go index 8dbfbd8c..9ea9ab13 100644 --- a/cmd/mock-compose.go +++ b/cmd/mock-compose.go @@ -17,49 +17,49 @@ limitations under the License. package cmd import ( - "github.com/linuxsuren/api-testing/pkg/mock" - "github.com/spf13/cobra" - "os" - "os/signal" - "syscall" + "github.com/linuxsuren/api-testing/pkg/mock" + "github.com/spf13/cobra" + "os" + "os/signal" + "syscall" ) func createMockComposeCmd() (c *cobra.Command) { - c = &cobra.Command{ - Use: "mock-compose", - Short: "Mock multiple servers", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) (err error) { - reader := mock.NewLocalFileReader(args[0]) + c = &cobra.Command{ + Use: "mock-compose", + Short: "Mock multiple servers", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) (err error) { + reader := mock.NewLocalFileReader(args[0]) - var server *mock.Server - if server, err = reader.Parse(); err != nil { - return - } + var server *mock.Server + if server, err = reader.Parse(); err != nil { + return + } - var subServers []mock.DynamicServer - for _, proxy := range server.Proxies { - subProxy := &mock.Server{ - Proxies: []mock.Proxy{proxy}, - } + var subServers []mock.DynamicServer + for _, proxy := range server.Proxies { + subProxy := &mock.Server{ + Proxies: []mock.Proxy{proxy}, + } - subReader := mock.NewObjectReader(subProxy) - subServer := mock.NewInMemoryServer(c.Context(), proxy.Port) - go subServer.Start(subReader, proxy.Prefix) - subServers = append(subServers, subServer) - } + subReader := mock.NewObjectReader(subProxy) + subServer := mock.NewInMemoryServer(c.Context(), proxy.Port) + go subServer.Start(subReader, proxy.Prefix) + subServers = append(subServers, subServer) + } - clean := make(chan os.Signal, 1) - signal.Notify(clean, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT) - select { - case <-c.Context().Done(): - case <-clean: - } - for _, server := range subServers { - server.Stop() - } - return - }, - } - return + clean := make(chan os.Signal, 1) + signal.Notify(clean, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT) + select { + case <-c.Context().Done(): + case <-clean: + } + for _, server := range subServers { + server.Stop() + } + return + }, + } + return } diff --git a/cmd/server.go b/cmd/server.go index 1e02ae96..f272ae7f 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -22,7 +22,7 @@ import ( "context" "errors" "fmt" - "github.com/linuxsuren/api-testing/pkg/apispec" + "math" "net" "net/http" "os" @@ -33,6 +33,8 @@ import ( "syscall" "time" + "github.com/linuxsuren/api-testing/pkg/apispec" + "github.com/linuxsuren/api-testing/pkg/runner" "github.com/linuxsuren/api-testing/pkg/util/home" @@ -336,7 +338,8 @@ func (o *serverOption) runE(cmd *cobra.Command, args []string) (err error) { server.RegisterMockHandlerFromEndpoint(ctx, mux, gRPCServerAddr, []grpc.DialOption{grpc.WithTransportCredentials(creds)}), server.RegisterDataServerHandlerFromEndpoint(ctx, mux, gRPCServerAddr, []grpc.DialOption{grpc.WithTransportCredentials(creds)})) } else { - dialOption := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} + dialOption := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(math.MaxInt))} err = errors.Join( server.RegisterRunnerHandlerFromEndpoint(ctx, mux, gRPCServerAddr, dialOption), server.RegisterMockHandlerFromEndpoint(ctx, mux, gRPCServerAddr, dialOption), diff --git a/pkg/generator/curl_generator.go b/pkg/generator/curl_generator.go index 76b7ab7b..dbe90940 100644 --- a/pkg/generator/curl_generator.go +++ b/pkg/generator/curl_generator.go @@ -16,56 +16,56 @@ limitations under the License. package generator import ( - "bytes" - _ "embed" - "net/http" - "strings" - "text/template" + "bytes" + _ "embed" + "net/http" + "strings" + "text/template" - "github.com/linuxsuren/api-testing/pkg/testing" + "github.com/linuxsuren/api-testing/pkg/testing" ) type curlGenerator struct { } func NewCurlGenerator() CodeGenerator { - return &curlGenerator{} + return &curlGenerator{} } func (g *curlGenerator) Generate(testSuite *testing.TestSuite, testcase *testing.TestCase) (result string, err error) { - if testcase.Request.Method == "" { - testcase.Request.Method = http.MethodGet - } + if testcase.Request.Method == "" { + testcase.Request.Method = http.MethodGet + } - if !strings.HasSuffix(testcase.Request.API, "?") { - testcase.Request.API += "?" - } + if !strings.HasSuffix(testcase.Request.API, "?") { + testcase.Request.API += "?" + } - queryKeys := testcase.Request.Query.Keys() - for _, k := range queryKeys { - testcase.Request.API += k + "=" + testcase.Request.Query.GetValue(k) + "&" - } + queryKeys := testcase.Request.Query.Keys() + for _, k := range queryKeys { + testcase.Request.API += k + "=" + testcase.Request.Query.GetValue(k) + "&" + } - testcase.Request.API = strings.TrimSuffix(testcase.Request.API, "&") - testcase.Request.API = strings.TrimSuffix(testcase.Request.API, "?") - if err = testcase.Request.Render(nil, ""); err != nil { - return - } + testcase.Request.API = strings.TrimSuffix(testcase.Request.API, "&") + testcase.Request.API = strings.TrimSuffix(testcase.Request.API, "?") + if err = testcase.Request.Render(nil, ""); err != nil { + return + } - var tpl *template.Template - if tpl, err = template.New("curl template").Parse(curlTemplate); err == nil { - buf := new(bytes.Buffer) - if err = tpl.Execute(buf, testcase); err == nil { - result = strings.TrimSpace(buf.String()) + var tpl *template.Template + if tpl, err = template.New("curl template").Parse(curlTemplate); err == nil { + buf := new(bytes.Buffer) + if err = tpl.Execute(buf, testcase); err == nil { + result = strings.TrimSpace(buf.String()) - result = strings.TrimSuffix(result, " \\") - } - } - return + result = strings.TrimSuffix(result, " \\") + } + } + return } func init() { - RegisterCodeGenerator("curl", NewCurlGenerator()) + RegisterCodeGenerator("curl", NewCurlGenerator()) } //go:embed data/curl.tpl diff --git a/pkg/runner/http_test.go b/pkg/runner/http_test.go index 5bf6be7f..5693a270 100644 --- a/pkg/runner/http_test.go +++ b/pkg/runner/http_test.go @@ -16,622 +16,622 @@ limitations under the License. package runner import ( - "bytes" - "context" - "errors" - "io" - "net/http" - "strings" - "testing" + "bytes" + "context" + "errors" + "io" + "net/http" + "strings" + "testing" - _ "embed" + _ "embed" - "github.com/go-openapi/spec" - "github.com/h2non/gock" - atest "github.com/linuxsuren/api-testing/pkg/testing" - "github.com/linuxsuren/api-testing/pkg/util" - fakeruntime "github.com/linuxsuren/go-fake-runtime" - "github.com/stretchr/testify/assert" + "github.com/go-openapi/spec" + "github.com/h2non/gock" + atest "github.com/linuxsuren/api-testing/pkg/testing" + "github.com/linuxsuren/api-testing/pkg/util" + fakeruntime "github.com/linuxsuren/go-fake-runtime" + "github.com/stretchr/testify/assert" ) func TestTestCase(t *testing.T) { - fooRequest := atest.Request{ - API: urlFoo, - } - defaultForm := map[string]string{ - "key": "value", - } - const defaultBody = `{"name":"hello"}` - defaultPrepare := func() { - gock.New(urlLocalhost). - Get("/foo").Reply(http.StatusOK).BodyString(`{"items":[]}`). - SetHeader(util.ContentType, util.JSON) - } - defaultPostPrepare := func() { - gock.New(urlLocalhost). - Post("/foo").Reply(http.StatusOK).BodyString(`{"items":[]}`) - } + fooRequest := atest.Request{ + API: urlFoo, + } + defaultForm := map[string]string{ + "key": "value", + } + const defaultBody = `{"name":"hello"}` + defaultPrepare := func() { + gock.New(urlLocalhost). + Get("/foo").Reply(http.StatusOK).BodyString(`{"items":[]}`). + SetHeader(util.ContentType, util.JSON) + } + defaultPostPrepare := func() { + gock.New(urlLocalhost). + Post("/foo").Reply(http.StatusOK).BodyString(`{"items":[]}`) + } - tests := []struct { - name string - execer fakeruntime.Execer - testCase *atest.TestCase - ctx interface{} - prepare func() - verify func(t *testing.T, output interface{}, err error) - }{{ - name: "failed during the prepare stage", - testCase: &atest.TestCase{ - Before: &atest.Job{ - Items: []string{"demo.yaml"}, - }, - }, - execer: &fakeruntime.FakeExecer{ExpectError: errors.New("fake")}, - }, { - name: "normal, response is map", - testCase: &atest.TestCase{ - Request: atest.Request{ - API: urlFoo, - Header: defaultForm, - Query: map[string]interface{}{ - "name": "linuxsuren", - }, - Cookie: map[string]string{ - "key": "value", - }, - Body: atest.NewRequestBody(`{"foo":"bar"}`), - }, - Expect: atest.Response{ - StatusCode: http.StatusOK, - BodyFieldsExpect: map[string]interface{}{ - "name": "linuxsuren", - "number": 1, - }, - Header: map[string]string{ - "type": "generic", - }, - Verify: []string{ - `data.name == "linuxsuren"`, - }, - }, - Before: &atest.Job{ - Items: []string{"sleep(1)"}, - }, - }, - execer: &fakeruntime.FakeExecer{}, - prepare: func() { - gock.New(urlLocalhost). - Get("/foo"). - MatchHeader("key", "value"). - Reply(http.StatusOK). - SetHeader("type", "generic"). - SetHeader(util.ContentType, util.JSON). - File("testdata/generic_response.json") - }, - verify: func(t *testing.T, output interface{}, err error) { - assert.Nil(t, err) - assert.Equal(t, map[string]interface{}{"name": "linuxsuren", "number": float64(1)}, output) - }, - }, { - name: "normal, response is slice", - testCase: &atest.TestCase{ - Request: fooRequest, - Expect: atest.Response{ - StatusCode: http.StatusOK, - Body: `["foo", "bar"]`, - }, - }, - prepare: func() { - gock.New(urlLocalhost). - Get("/foo"). - Reply(http.StatusOK). - SetHeader(util.ContentType, util.JSON). - BodyString(`["foo", "bar"]`) - }, - verify: func(t *testing.T, output interface{}, err error) { - assert.Nil(t, err) - assert.Equal(t, []interface{}{"foo", "bar"}, output) - }, - }, { - name: "normal, response from file", - testCase: &atest.TestCase{ - Request: atest.Request{ - API: urlFoo, - Method: http.MethodPost, - BodyFromFile: "testdata/generic_response.json", - }, - Expect: atest.Response{ - StatusCode: http.StatusOK, - }, - }, - prepare: func() { - gock.New(urlLocalhost). - Post("/foo").BodyString(genericBody). - Reply(http.StatusOK).BodyString("123"). - SetHeader(util.ContentType, util.JSON) - }, - }, { - name: "response from a not found file", - testCase: &atest.TestCase{ - Request: atest.Request{ - API: urlFoo, - Method: http.MethodPost, - BodyFromFile: "testdata/fake.json", - }, - }, - }, { - name: "bad request", - testCase: &atest.TestCase{ - Request: fooRequest, - Expect: atest.Response{ - StatusCode: http.StatusOK, - }, - }, - prepare: func() { - gock.New(urlLocalhost). - Get("/foo").Reply(http.StatusBadRequest) - }, - }, { - name: "error with request", - testCase: &atest.TestCase{ - Request: fooRequest, - }, - prepare: func() { - gock.New(urlLocalhost). - Get("/foo").ReplyError(errors.New("error")) - }, - }, { - name: "not match with body", - testCase: &atest.TestCase{ - Request: fooRequest, - Expect: atest.Response{ - Body: "bar", - }, - }, - prepare: func() { - gock.New(urlLocalhost). - Get("/foo").Reply(http.StatusOK). - SetHeader(util.ContentType, util.Plain).BodyString("foo") - }, - }, { - name: "not match with header", - testCase: &atest.TestCase{ - Request: fooRequest, - Expect: atest.Response{ - Header: map[string]string{ - "foo": "bar", - }, - }, - }, - prepare: func() { - gock.New(urlLocalhost). - Get("/foo").Reply(http.StatusOK).SetHeader("foo", "value") - }, - }, { - name: "not found from fields", - testCase: &atest.TestCase{ - Request: fooRequest, - Expect: atest.Response{ - BodyFieldsExpect: map[string]interface{}{ - "foo": "bar", - }, - }, - }, - prepare: prepareForFoo, - }, { - name: "body filed not match", - testCase: &atest.TestCase{ - Request: fooRequest, - Expect: atest.Response{ - BodyFieldsExpect: map[string]interface{}{ - "name": "bar", - }, - }, - }, - prepare: prepareForFoo, - }, { - name: "invalid filed finding", - testCase: &atest.TestCase{ - Request: fooRequest, - Expect: atest.Response{ - BodyFieldsExpect: map[string]interface{}{ - "0.items": "bar", - }, - }, - }, - prepare: defaultPrepare, - verify: func(t *testing.T, output interface{}, err error) { - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "not found field") - }, - }, { - name: "verify failed", - testCase: &atest.TestCase{ - Request: atest.Request{ - API: urlFoo, - }, - Expect: atest.Response{ - Verify: []string{ - "len(data.items) > 0", - }, - }, - }, - prepare: defaultPrepare, - verify: func(t *testing.T, output interface{}, err error) { - if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "failed to verify") - } - }, - }, { - name: "failed to compile", - testCase: &atest.TestCase{ - Request: fooRequest, - Expect: atest.Response{ - Verify: []string{ - `println("12")`, - }, - }, - }, - prepare: defaultPrepare, - verify: func(t *testing.T, output interface{}, err error) { - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "unknown name println") - }, - }, { - name: "failed to compile", - testCase: &atest.TestCase{ - Request: fooRequest, - Expect: atest.Response{ - Verify: []string{ - `1 + 1`, - }, - }, - }, - prepare: defaultPrepare, - verify: func(t *testing.T, output interface{}, err error) { - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "expected bool, but got int") - }, - }, { - name: "wrong API format", - testCase: &atest.TestCase{ - Request: atest.Request{ - API: "ssh://localhost/foo", - Method: "fake,fake", - }, - }, - verify: func(t *testing.T, output interface{}, err error) { - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "invalid method") - }, - }, { - name: "failed to render API", - testCase: &atest.TestCase{ - Request: atest.Request{ - API: "http://localhost/foo/{{.abc}", - }, - }, - verify: func(t *testing.T, output interface{}, err error) { - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "template: api:1:") - }, - }, { - name: "multipart form request", - testCase: &atest.TestCase{ - Request: atest.Request{ - API: urlFoo, - Method: http.MethodPost, - Header: map[string]string{ - util.ContentType: "multipart/form-data", - }, - Form: defaultForm, - }, - }, - prepare: defaultPostPrepare, - verify: noError, - }, { - name: "normal form request", - testCase: &atest.TestCase{ - Request: atest.Request{ - API: urlFoo, - Method: http.MethodPost, - Header: map[string]string{ - util.ContentType: "application/x-www-form-urlencoded", - }, - Form: defaultForm, - }, - }, - prepare: defaultPostPrepare, - verify: noError, - }, { - name: "body is a template", - testCase: &atest.TestCase{ - Request: atest.Request{ - API: urlFoo, - Method: http.MethodPost, - Body: atest.NewRequestBody(`{"name":"{{lower "HELLO"}}"}`), - }, - }, - prepare: func() { - gock.New(urlLocalhost). - Post("/foo").BodyString(`{"name":"hello"}`). - Reply(http.StatusOK).BodyString(`{}`) - }, - verify: noError, - }, { - name: "status code not match", - testCase: &atest.TestCase{ - Request: atest.Request{ - API: urlFoo, - Method: http.MethodPost, - }, - Expect: atest.Response{ - StatusCode: http.StatusBadRequest, - }, - }, - prepare: func() { - gock.New(urlLocalhost). - Post("/foo"). - Reply(http.StatusOK). - SetHeader(util.ContentType, util.JSON). - BodyString(defaultBody) - }, - verify: func(t *testing.T, _ interface{}, err error) { - assert.Error(t, err) - }, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer gock.Clean() - if tt.prepare != nil { - tt.prepare() - } - if tt.verify == nil { - tt.verify = hasError - } - runner := NewSimpleTestCaseRunner() - runner.WithOutputWriter(io.Discard) - if tt.execer != nil { - runner.WithExecer(tt.execer) - } - output, err := runner.RunTestCase(tt.testCase, tt.ctx, context.TODO()) - tt.verify(t, output, err) + tests := []struct { + name string + execer fakeruntime.Execer + testCase *atest.TestCase + ctx interface{} + prepare func() + verify func(t *testing.T, output interface{}, err error) + }{{ + name: "failed during the prepare stage", + testCase: &atest.TestCase{ + Before: &atest.Job{ + Items: []string{"demo.yaml"}, + }, + }, + execer: &fakeruntime.FakeExecer{ExpectError: errors.New("fake")}, + }, { + name: "normal, response is map", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: urlFoo, + Header: defaultForm, + Query: map[string]interface{}{ + "name": "linuxsuren", + }, + Cookie: map[string]string{ + "key": "value", + }, + Body: atest.NewRequestBody(`{"foo":"bar"}`), + }, + Expect: atest.Response{ + StatusCode: http.StatusOK, + BodyFieldsExpect: map[string]interface{}{ + "name": "linuxsuren", + "number": 1, + }, + Header: map[string]string{ + "type": "generic", + }, + Verify: []string{ + `data.name == "linuxsuren"`, + }, + }, + Before: &atest.Job{ + Items: []string{"sleep(1)"}, + }, + }, + execer: &fakeruntime.FakeExecer{}, + prepare: func() { + gock.New(urlLocalhost). + Get("/foo"). + MatchHeader("key", "value"). + Reply(http.StatusOK). + SetHeader("type", "generic"). + SetHeader(util.ContentType, util.JSON). + File("testdata/generic_response.json") + }, + verify: func(t *testing.T, output interface{}, err error) { + assert.Nil(t, err) + assert.Equal(t, map[string]interface{}{"name": "linuxsuren", "number": float64(1)}, output) + }, + }, { + name: "normal, response is slice", + testCase: &atest.TestCase{ + Request: fooRequest, + Expect: atest.Response{ + StatusCode: http.StatusOK, + Body: `["foo", "bar"]`, + }, + }, + prepare: func() { + gock.New(urlLocalhost). + Get("/foo"). + Reply(http.StatusOK). + SetHeader(util.ContentType, util.JSON). + BodyString(`["foo", "bar"]`) + }, + verify: func(t *testing.T, output interface{}, err error) { + assert.Nil(t, err) + assert.Equal(t, []interface{}{"foo", "bar"}, output) + }, + }, { + name: "normal, response from file", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: urlFoo, + Method: http.MethodPost, + BodyFromFile: "testdata/generic_response.json", + }, + Expect: atest.Response{ + StatusCode: http.StatusOK, + }, + }, + prepare: func() { + gock.New(urlLocalhost). + Post("/foo").BodyString(genericBody). + Reply(http.StatusOK).BodyString("123"). + SetHeader(util.ContentType, util.JSON) + }, + }, { + name: "response from a not found file", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: urlFoo, + Method: http.MethodPost, + BodyFromFile: "testdata/fake.json", + }, + }, + }, { + name: "bad request", + testCase: &atest.TestCase{ + Request: fooRequest, + Expect: atest.Response{ + StatusCode: http.StatusOK, + }, + }, + prepare: func() { + gock.New(urlLocalhost). + Get("/foo").Reply(http.StatusBadRequest) + }, + }, { + name: "error with request", + testCase: &atest.TestCase{ + Request: fooRequest, + }, + prepare: func() { + gock.New(urlLocalhost). + Get("/foo").ReplyError(errors.New("error")) + }, + }, { + name: "not match with body", + testCase: &atest.TestCase{ + Request: fooRequest, + Expect: atest.Response{ + Body: "bar", + }, + }, + prepare: func() { + gock.New(urlLocalhost). + Get("/foo").Reply(http.StatusOK). + SetHeader(util.ContentType, util.Plain).BodyString("foo") + }, + }, { + name: "not match with header", + testCase: &atest.TestCase{ + Request: fooRequest, + Expect: atest.Response{ + Header: map[string]string{ + "foo": "bar", + }, + }, + }, + prepare: func() { + gock.New(urlLocalhost). + Get("/foo").Reply(http.StatusOK).SetHeader("foo", "value") + }, + }, { + name: "not found from fields", + testCase: &atest.TestCase{ + Request: fooRequest, + Expect: atest.Response{ + BodyFieldsExpect: map[string]interface{}{ + "foo": "bar", + }, + }, + }, + prepare: prepareForFoo, + }, { + name: "body filed not match", + testCase: &atest.TestCase{ + Request: fooRequest, + Expect: atest.Response{ + BodyFieldsExpect: map[string]interface{}{ + "name": "bar", + }, + }, + }, + prepare: prepareForFoo, + }, { + name: "invalid filed finding", + testCase: &atest.TestCase{ + Request: fooRequest, + Expect: atest.Response{ + BodyFieldsExpect: map[string]interface{}{ + "0.items": "bar", + }, + }, + }, + prepare: defaultPrepare, + verify: func(t *testing.T, output interface{}, err error) { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "not found field") + }, + }, { + name: "verify failed", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: urlFoo, + }, + Expect: atest.Response{ + Verify: []string{ + "len(data.items) > 0", + }, + }, + }, + prepare: defaultPrepare, + verify: func(t *testing.T, output interface{}, err error) { + if assert.NotNil(t, err) { + assert.Contains(t, err.Error(), "failed to verify") + } + }, + }, { + name: "failed to compile", + testCase: &atest.TestCase{ + Request: fooRequest, + Expect: atest.Response{ + Verify: []string{ + `println("12")`, + }, + }, + }, + prepare: defaultPrepare, + verify: func(t *testing.T, output interface{}, err error) { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "unknown name println") + }, + }, { + name: "failed to compile", + testCase: &atest.TestCase{ + Request: fooRequest, + Expect: atest.Response{ + Verify: []string{ + `1 + 1`, + }, + }, + }, + prepare: defaultPrepare, + verify: func(t *testing.T, output interface{}, err error) { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "expected bool, but got int") + }, + }, { + name: "wrong API format", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: "ssh://localhost/foo", + Method: "fake,fake", + }, + }, + verify: func(t *testing.T, output interface{}, err error) { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "invalid method") + }, + }, { + name: "failed to render API", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: "http://localhost/foo/{{.abc}", + }, + }, + verify: func(t *testing.T, output interface{}, err error) { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "template: api:1:") + }, + }, { + name: "multipart form request", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: urlFoo, + Method: http.MethodPost, + Header: map[string]string{ + util.ContentType: "multipart/form-data", + }, + Form: defaultForm, + }, + }, + prepare: defaultPostPrepare, + verify: noError, + }, { + name: "normal form request", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: urlFoo, + Method: http.MethodPost, + Header: map[string]string{ + util.ContentType: "application/x-www-form-urlencoded", + }, + Form: defaultForm, + }, + }, + prepare: defaultPostPrepare, + verify: noError, + }, { + name: "body is a template", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: urlFoo, + Method: http.MethodPost, + Body: atest.NewRequestBody(`{"name":"{{lower "HELLO"}}"}`), + }, + }, + prepare: func() { + gock.New(urlLocalhost). + Post("/foo").BodyString(`{"name":"hello"}`). + Reply(http.StatusOK).BodyString(`{}`) + }, + verify: noError, + }, { + name: "status code not match", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: urlFoo, + Method: http.MethodPost, + }, + Expect: atest.Response{ + StatusCode: http.StatusBadRequest, + }, + }, + prepare: func() { + gock.New(urlLocalhost). + Post("/foo"). + Reply(http.StatusOK). + SetHeader(util.ContentType, util.JSON). + BodyString(defaultBody) + }, + verify: func(t *testing.T, _ interface{}, err error) { + assert.Error(t, err) + }, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer gock.Clean() + if tt.prepare != nil { + tt.prepare() + } + if tt.verify == nil { + tt.verify = hasError + } + runner := NewSimpleTestCaseRunner() + runner.WithOutputWriter(io.Discard) + if tt.execer != nil { + runner.WithExecer(tt.execer) + } + output, err := runner.RunTestCase(tt.testCase, tt.ctx, context.TODO()) + tt.verify(t, output, err) - getter, ok := runner.(ResponseRecord) - assert.True(t, ok) - assert.NotNil(t, getter.GetResponseRecord()) - }) - } + getter, ok := runner.(ResponseRecord) + assert.True(t, ok) + assert.NotNil(t, getter.GetResponseRecord()) + }) + } } func TestLevelWriter(t *testing.T) { - tests := []struct { - name string - buf *bytes.Buffer - level string - expect string - }{{ - name: "debug", - buf: new(bytes.Buffer), - level: "debug", - expect: "debuginfo", - }, { - name: "info", - buf: new(bytes.Buffer), - level: "info", - expect: "info", - }} - for _, tt := range tests { - writer := NewDefaultLevelWriter(tt.level, tt.buf) - if assert.NotNil(t, writer) { - writer.Debug("debug") - writer.Info("info") + tests := []struct { + name string + buf *bytes.Buffer + level string + expect string + }{{ + name: "debug", + buf: new(bytes.Buffer), + level: "debug", + expect: "debuginfo", + }, { + name: "info", + buf: new(bytes.Buffer), + level: "info", + expect: "info", + }} + for _, tt := range tests { + writer := NewDefaultLevelWriter(tt.level, tt.buf) + if assert.NotNil(t, writer) { + writer.Debug("debug") + writer.Info("info") - assert.Equal(t, tt.expect, tt.buf.String()) - } - } + assert.Equal(t, tt.expect, tt.buf.String()) + } + } } func TestJSONSchemaValidation(t *testing.T) { - tests := []struct { - name string - schema string - body string - hasErr bool - }{{ - name: "normal", - schema: defaultSchemaForTest, - body: `{"name": "linuxsuren", "age": 100}`, - hasErr: false, - }, { - name: "schema is empty", - schema: "", - hasErr: false, - }, { - name: "failed to validate", - schema: defaultSchemaForTest, - body: `{"name": "linuxsuren", "age": "100"}`, - hasErr: true, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := jsonSchemaValidation(tt.schema, []byte(tt.body)) - assert.Equal(t, tt.hasErr, err != nil, err) - }) - } + tests := []struct { + name string + schema string + body string + hasErr bool + }{{ + name: "normal", + schema: defaultSchemaForTest, + body: `{"name": "linuxsuren", "age": 100}`, + hasErr: false, + }, { + name: "schema is empty", + schema: "", + hasErr: false, + }, { + name: "failed to validate", + schema: defaultSchemaForTest, + body: `{"name": "linuxsuren", "age": "100"}`, + hasErr: true, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := jsonSchemaValidation(tt.schema, []byte(tt.body)) + assert.Equal(t, tt.hasErr, err != nil, err) + }) + } } func TestRunJob(t *testing.T) { - tests := []struct { - name string - job atest.Job - hasErr bool - }{{ - name: "sleep 1s", - job: atest.Job{ - Items: []string{"sleep(1)"}, - }, - hasErr: false, - }, { - name: "no params", - job: atest.Job{ - Items: []string{"sleep()"}, - }, - hasErr: true, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := runJob(&tt.job, nil, nil) - assert.Equal(t, tt.hasErr, err != nil, err) - }) - } + tests := []struct { + name string + job atest.Job + hasErr bool + }{{ + name: "sleep 1s", + job: atest.Job{ + Items: []string{"sleep(1)"}, + }, + hasErr: false, + }, { + name: "no params", + job: atest.Job{ + Items: []string{"sleep()"}, + }, + hasErr: true, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := runJob(&tt.job, nil, nil) + assert.Equal(t, tt.hasErr, err != nil, err) + }) + } } func TestContextKey(t *testing.T) { - assert.Equal(t, ContextKey("parentDir"), NewContextKeyBuilder().ParentDir()) + assert.Equal(t, ContextKey("parentDir"), NewContextKeyBuilder().ParentDir()) - ctx := context.WithValue(context.Background(), NewContextKeyBuilder().ParentDir(), "/tmp") - assert.Equal(t, "/tmp", NewContextKeyBuilder().ParentDir().GetContextValueOrEmpty(ctx)) - assert.Empty(t, ContextKey("fake").GetContextValueOrEmpty(ctx)) + ctx := context.WithValue(context.Background(), NewContextKeyBuilder().ParentDir(), "/tmp") + assert.Equal(t, "/tmp", NewContextKeyBuilder().ParentDir().GetContextValueOrEmpty(ctx)) + assert.Empty(t, ContextKey("fake").GetContextValueOrEmpty(ctx)) } func TestBodyFiledsVerify(t *testing.T) { - tests := []struct { - name string - bodyFields map[string]interface{} - body string - hasErr bool - }{{ - name: "normal", - bodyFields: map[string]interface{}{ - "name": "linuxsuren", - "number": 1, - }, - body: genericBody, - hasErr: false, - }, { - name: "field not found", - bodyFields: map[string]interface{}{ - "project": "", - }, - body: genericBody, - hasErr: true, - }, { - name: "number is not equal", - bodyFields: map[string]interface{}{ - "number": 2, - }, - body: genericBody, - hasErr: true, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - verifier := &jsonBodyVerifier{ - body: &atest.Response{ - BodyFieldsExpect: tt.bodyFields, - }, - } + tests := []struct { + name string + bodyFields map[string]interface{} + body string + hasErr bool + }{{ + name: "normal", + bodyFields: map[string]interface{}{ + "name": "linuxsuren", + "number": 1, + }, + body: genericBody, + hasErr: false, + }, { + name: "field not found", + bodyFields: map[string]interface{}{ + "project": "", + }, + body: genericBody, + hasErr: true, + }, { + name: "number is not equal", + bodyFields: map[string]interface{}{ + "number": 2, + }, + body: genericBody, + hasErr: true, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + verifier := &jsonBodyVerifier{ + body: &atest.Response{ + BodyFieldsExpect: tt.bodyFields, + }, + } - err := verifier.Verify([]byte(tt.body)) - assert.Equal(t, tt.hasErr, err != nil, err) - }) - } + err := verifier.Verify([]byte(tt.body)) + assert.Equal(t, tt.hasErr, err != nil, err) + }) + } } func TestGetSuggestedAPIs(t *testing.T) { - runner := NewSimpleTestCaseRunner() - runner.WithSuite(nil) - runner.WithAPISuggestLimit(6) - // not a swagger - result, err := runner.GetSuggestedAPIs(&atest.TestSuite{}, "") - assert.NoError(t, err, err) - assert.Empty(t, result) + runner := NewSimpleTestCaseRunner() + runner.WithSuite(nil) + runner.WithAPISuggestLimit(6) + // not a swagger + result, err := runner.GetSuggestedAPIs(&atest.TestSuite{}, "") + assert.NoError(t, err, err) + assert.Empty(t, result) - // swagger - defer gock.Off() - gock.New(urlFoo).Get("swagger.json").Reply(http.StatusOK).File("testdata/swagger.json") - result, err = runner.GetSuggestedAPIs(&atest.TestSuite{ - Spec: atest.APISpec{ - Kind: "swagger", - URL: urlFoo + "/swagger.json", - }, - }, "") - assert.NoError(t, err, err) - assert.NotEmpty(t, result) - method := result[0].Request.Method - assert.Equal(t, strings.ToUpper(method), method) + // swagger + defer gock.Off() + gock.New(urlFoo).Get("swagger.json").Reply(http.StatusOK).File("testdata/swagger.json") + result, err = runner.GetSuggestedAPIs(&atest.TestSuite{ + Spec: atest.APISpec{ + Kind: "swagger", + URL: urlFoo + "/swagger.json", + }, + }, "") + assert.NoError(t, err, err) + assert.NotEmpty(t, result) + method := result[0].Request.Method + assert.Equal(t, strings.ToUpper(method), method) } func TestIsStructContent(t *testing.T) { - tests := []struct { - contentType string - expectOk bool - }{{ - contentType: util.JSON, - expectOk: true, - }, { - contentType: util.YAML, - expectOk: true, - }, { - contentType: util.OctetStream, - expectOk: false, - }, { - contentType: util.OCIImageIndex, - expectOk: true, - }, { - contentType: "application/problem+json", - expectOk: true, - }} - for _, tt := range tests { - t.Run(tt.contentType, func(t *testing.T) { - ok := isNonBinaryContent(tt.contentType) - assert.Equal(t, tt.expectOk, ok) - }) - } + tests := []struct { + contentType string + expectOk bool + }{{ + contentType: util.JSON, + expectOk: true, + }, { + contentType: util.YAML, + expectOk: true, + }, { + contentType: util.OctetStream, + expectOk: false, + }, { + contentType: util.OCIImageIndex, + expectOk: true, + }, { + contentType: "application/problem+json", + expectOk: true, + }} + for _, tt := range tests { + t.Run(tt.contentType, func(t *testing.T) { + ok := isNonBinaryContent(tt.contentType) + assert.Equal(t, tt.expectOk, ok) + }) + } } func TestGenerateRandomValue(t *testing.T) { - tests := []struct { - param spec.Parameter - expected interface{} - }{ - { - param: spec.Parameter{ - SimpleSchema: spec.SimpleSchema{ - Format: "int32", - }, - }, - expected: 101, - }, { - param: spec.Parameter{ - SimpleSchema: spec.SimpleSchema{ - Format: "boolean", - }, - }, - expected: true, - }, { - param: spec.Parameter{ - SimpleSchema: spec.SimpleSchema{ - Format: "string", - }, - }, - expected: "random", - }, - } + tests := []struct { + param spec.Parameter + expected interface{} + }{ + { + param: spec.Parameter{ + SimpleSchema: spec.SimpleSchema{ + Format: "int32", + }, + }, + expected: 101, + }, { + param: spec.Parameter{ + SimpleSchema: spec.SimpleSchema{ + Format: "boolean", + }, + }, + expected: true, + }, { + param: spec.Parameter{ + SimpleSchema: spec.SimpleSchema{ + Format: "string", + }, + }, + expected: "random", + }, + } - for _, tt := range tests { - result := generateRandomValue(tt.param) + for _, tt := range tests { + result := generateRandomValue(tt.param) - if result != tt.expected { - t.Errorf("generateRandomValue(%v) = %v, expected %v", tt.param, result, tt.expected) - } - } + if result != tt.expected { + t.Errorf("generateRandomValue(%v) = %v, expected %v", tt.param, result, tt.expected) + } + } } func TestAmmendHeaders(t *testing.T) { - headers := http.Header{"Content-Type": []string{"application/json"}} - ammendHeaders(headers, []byte("good")) - assert.Equal(t, "4", headers.Get(util.ContentLength)) + headers := http.Header{"Content-Type": []string{"application/json"}} + ammendHeaders(headers, []byte("good")) + assert.Equal(t, "4", headers.Get(util.ContentLength)) } const defaultSchemaForTest = `{"properties": { @@ -642,17 +642,17 @@ const defaultSchemaForTest = `{"properties": { }` func hasError(t *testing.T, output interface{}, err error) { - assert.NotNil(t, err) + assert.NotNil(t, err) } func noError(t *testing.T, output interface{}, err error) { - assert.Nil(t, err) + assert.Nil(t, err) } func prepareForFoo() { - gock.New(urlLocalhost). - Get("/foo").Reply(http.StatusOK).BodyString(genericBody). - SetHeader(util.ContentType, util.JSON) + gock.New(urlLocalhost). + Get("/foo").Reply(http.StatusOK).BodyString(genericBody). + SetHeader(util.ContentType, util.JSON) } //go:embed testdata/generic_response.json diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index f746f73f..07f88b95 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -17,60 +17,60 @@ limitations under the License. package runner import ( - "context" - "fmt" - "io" - "strings" + "context" + "fmt" + "io" + "strings" - "github.com/linuxsuren/api-testing/pkg/testing" - fakeruntime "github.com/linuxsuren/go-fake-runtime" + "github.com/linuxsuren/api-testing/pkg/testing" + fakeruntime "github.com/linuxsuren/go-fake-runtime" ) // TestCaseRunner represents a test case runner type TestCaseRunner interface { - RunTestCase(testcase *testing.TestCase, dataContext interface{}, ctx context.Context) (output interface{}, err error) - GetSuggestedAPIs(suite *testing.TestSuite, api string) ([]*testing.TestCase, error) - WithSecure(secure *testing.Secure) - WithOutputWriter(io.Writer) - WithWriteLevel(level string) - WithTestReporter(TestReporter) - WithExecer(fakeruntime.Execer) - WithSuite(*testing.TestSuite) - WithAPISuggestLimit(int) + RunTestCase(testcase *testing.TestCase, dataContext interface{}, ctx context.Context) (output interface{}, err error) + GetSuggestedAPIs(suite *testing.TestSuite, api string) ([]*testing.TestCase, error) + WithSecure(secure *testing.Secure) + WithOutputWriter(io.Writer) + WithWriteLevel(level string) + WithTestReporter(TestReporter) + WithExecer(fakeruntime.Execer) + WithSuite(*testing.TestSuite) + WithAPISuggestLimit(int) } // HTTPResponseRecord represents a http response record type ResponseRecord interface { - GetResponseRecord() SimpleResponse + GetResponseRecord() SimpleResponse } // SimpleResponse represents a simple response type SimpleResponse struct { - Header map[string]string - Body string - RawBody []byte - StatusCode int + Header map[string]string + Body string + RawBody []byte + StatusCode int } func (s SimpleResponse) getFileName() string { - for k, v := range s.Header { - if k == "Content-Disposition" { - v = strings.ReplaceAll(v, "attachment; filename=", "attachment;filename=") - v = strings.ReplaceAll(v, `filename="`, "filename=") - return strings.TrimSuffix(strings.TrimPrefix(v, `attachment;filename=`), `"`) - } - } - return "" + for k, v := range s.Header { + if k == "Content-Disposition" { + v = strings.ReplaceAll(v, "attachment; filename=", "attachment;filename=") + v = strings.ReplaceAll(v, `filename="`, "filename=") + return strings.TrimSuffix(strings.TrimPrefix(v, `attachment;filename=`), `"`) + } + } + return "" } // NewDefaultUnimplementedRunner initializes an unimplementedRunner using the default values. func NewDefaultUnimplementedRunner() UnimplementedRunner { - return UnimplementedRunner{ - testReporter: NewDiscardTestReporter(), - writer: io.Discard, - log: NewDefaultLevelWriter("info", io.Discard), - execer: fakeruntime.NewDefaultExecer(), - } + return UnimplementedRunner{ + testReporter: NewDiscardTestReporter(), + writer: io.Discard, + log: NewDefaultLevelWriter("info", io.Discard), + execer: fakeruntime.NewDefaultExecer(), + } } // UnimplementedRunner implements interface TestCaseRunner except method RunTestCase. @@ -79,56 +79,56 @@ func NewDefaultUnimplementedRunner() UnimplementedRunner { // It is recommended to use NewDefaultUnimplementedRunner to initalize rather than // to fill it manully. type UnimplementedRunner struct { - testReporter TestReporter - writer io.Writer - log LevelWriter - execer fakeruntime.Execer - Secure *testing.Secure - proxy *testing.Proxy + testReporter TestReporter + writer io.Writer + log LevelWriter + execer fakeruntime.Execer + Secure *testing.Secure + proxy *testing.Proxy } func (r *UnimplementedRunner) RunTestCase(testcase *testing.TestCase, dataContext interface{}, ctx context.Context) (output interface{}, err error) { - return nil, fmt.Errorf("unimplemented") + return nil, fmt.Errorf("unimplemented") } // WithOutputWriter sets the io.Writer func (r *UnimplementedRunner) WithOutputWriter(writer io.Writer) { - r.writer = writer + r.writer = writer } // WithWriteLevel sets the level writer func (r *UnimplementedRunner) WithWriteLevel(level string) { - if level != "" { - r.log = NewDefaultLevelWriter(level, r.writer) - } + if level != "" { + r.log = NewDefaultLevelWriter(level, r.writer) + } } // WithTestReporter sets the TestReporter func (r *UnimplementedRunner) WithTestReporter(reporter TestReporter) { - r.testReporter = reporter + r.testReporter = reporter } // WithExecer sets the execer func (r *UnimplementedRunner) WithExecer(execer fakeruntime.Execer) { - r.execer = execer + r.execer = execer } // WithSecure sets the secure option. func (r *UnimplementedRunner) WithSecure(secure *testing.Secure) { - r.Secure = secure + r.Secure = secure } func (r *UnimplementedRunner) GetSuggestedAPIs(suite *testing.TestSuite, api string) (result []*testing.TestCase, err error) { - // empty implement - return + // empty implement + return } func (r *UnimplementedRunner) WithAPISuggestLimit(int) { - // empty implement + // empty implement } func (s *UnimplementedRunner) WithSuite(suite *testing.TestSuite) { - if suite != nil { - s.Secure = suite.Spec.Secure - } + if suite != nil { + s.Secure = suite.Spec.Secure + } } diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index 98edd03a..5de746d2 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -17,68 +17,68 @@ limitations under the License. package runner import ( - "testing" + "testing" - atest "github.com/linuxsuren/api-testing/pkg/testing" - "github.com/stretchr/testify/assert" + atest "github.com/linuxsuren/api-testing/pkg/testing" + "github.com/stretchr/testify/assert" ) func TestRunnerFactory(t *testing.T) { - runner := GetTestSuiteRunner(&atest.TestSuite{}) - assert.IsType(t, NewSimpleTestCaseRunner(), runner) + runner := GetTestSuiteRunner(&atest.TestSuite{}) + assert.IsType(t, NewSimpleTestCaseRunner(), runner) - runner = GetTestSuiteRunner(&atest.TestSuite{Spec: atest.APISpec{Kind: "grpc", RPC: &atest.RPCDesc{}}}) - assert.IsType(t, NewGRPCTestCaseRunner("", atest.RPCDesc{}), runner) + runner = GetTestSuiteRunner(&atest.TestSuite{Spec: atest.APISpec{Kind: "grpc", RPC: &atest.RPCDesc{}}}) + assert.IsType(t, NewGRPCTestCaseRunner("", atest.RPCDesc{}), runner) } func TestUnimplementedRunner(t *testing.T) { - runner := NewDefaultUnimplementedRunner() - output, err := runner.RunTestCase(&atest.TestCase{}, nil, nil) - assert.Nil(t, output) - assert.Error(t, err) + runner := NewDefaultUnimplementedRunner() + output, err := runner.RunTestCase(&atest.TestCase{}, nil, nil) + assert.Nil(t, output) + assert.Error(t, err) - runner.WithWriteLevel("debug") - runner.WithTestReporter(nil) + runner.WithWriteLevel("debug") + runner.WithTestReporter(nil) - var results []*atest.TestCase - results, err = runner.GetSuggestedAPIs(nil, "") - assert.Nil(t, results) - assert.NoError(t, err) + var results []*atest.TestCase + results, err = runner.GetSuggestedAPIs(nil, "") + assert.Nil(t, results) + assert.NoError(t, err) - runner.WithAPISuggestLimit(0) + runner.WithAPISuggestLimit(0) } func TestSimpleResponse(t *testing.T) { - t.Run("get fileName", func(t *testing.T) { - // without filename - assert.Empty(t, SimpleResponse{}.getFileName()) - - // normal case - assert.Equal(t, "a.txt", SimpleResponse{ - Header: map[string]string{ - "Content-Disposition": `attachment; filename="a.txt"`, - }, - }.getFileName()) - - // without space - assert.Equal(t, "a.txt", SimpleResponse{ - Header: map[string]string{ - "Content-Disposition": `attachment;filename="a.txt"`, - }, - }.getFileName()) - - // without quote - assert.Equal(t, "a.txt", SimpleResponse{ - Header: map[string]string{ - "Content-Disposition": `attachment; filename=a.txt`, - }, - }.getFileName()) - - // without quote and space - assert.Equal(t, "a.txt", SimpleResponse{ - Header: map[string]string{ - "Content-Disposition": `attachment;filename=a.txt`, - }, - }.getFileName()) - }) + t.Run("get fileName", func(t *testing.T) { + // without filename + assert.Empty(t, SimpleResponse{}.getFileName()) + + // normal case + assert.Equal(t, "a.txt", SimpleResponse{ + Header: map[string]string{ + "Content-Disposition": `attachment; filename="a.txt"`, + }, + }.getFileName()) + + // without space + assert.Equal(t, "a.txt", SimpleResponse{ + Header: map[string]string{ + "Content-Disposition": `attachment;filename="a.txt"`, + }, + }.getFileName()) + + // without quote + assert.Equal(t, "a.txt", SimpleResponse{ + Header: map[string]string{ + "Content-Disposition": `attachment; filename=a.txt`, + }, + }.getFileName()) + + // without quote and space + assert.Equal(t, "a.txt", SimpleResponse{ + Header: map[string]string{ + "Content-Disposition": `attachment;filename=a.txt`, + }, + }.getFileName()) + }) } diff --git a/pkg/server/store_ext_manager.go b/pkg/server/store_ext_manager.go index 34fa995e..253f49b6 100644 --- a/pkg/server/store_ext_manager.go +++ b/pkg/server/store_ext_manager.go @@ -16,172 +16,172 @@ limitations under the License. package server import ( - "context" - "errors" - "fmt" - "io" - "net/http" - "os" - "path/filepath" - "strings" - sync "sync" - "syscall" - "time" - - "github.com/linuxsuren/api-testing/pkg/util/home" - - "github.com/linuxsuren/api-testing/pkg/downloader" - "github.com/linuxsuren/api-testing/pkg/logging" - - fakeruntime "github.com/linuxsuren/go-fake-runtime" + "context" + "errors" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "strings" + sync "sync" + "syscall" + "time" + + "github.com/linuxsuren/api-testing/pkg/util/home" + + "github.com/linuxsuren/api-testing/pkg/downloader" + "github.com/linuxsuren/api-testing/pkg/logging" + + fakeruntime "github.com/linuxsuren/go-fake-runtime" ) var ( - serverLogger = logging.DefaultLogger(logging.LogLevelInfo).WithName("server") + serverLogger = logging.DefaultLogger(logging.LogLevelInfo).WithName("server") ) type ExtManager interface { - Start(name, socket string) (err error) - StopAll() (err error) - WithDownloader(downloader.PlatformAwareOCIDownloader) + Start(name, socket string) (err error) + StopAll() (err error) + WithDownloader(downloader.PlatformAwareOCIDownloader) } type storeExtManager struct { - execer fakeruntime.Execer - ociDownloader downloader.PlatformAwareOCIDownloader - socketPrefix string - filesNeedToBeRemoved []string - extStatusMap map[string]bool - processs []fakeruntime.Process - processChan chan fakeruntime.Process - stopSingal chan struct{} - lock *sync.RWMutex + execer fakeruntime.Execer + ociDownloader downloader.PlatformAwareOCIDownloader + socketPrefix string + filesNeedToBeRemoved []string + extStatusMap map[string]bool + processs []fakeruntime.Process + processChan chan fakeruntime.Process + stopSingal chan struct{} + lock *sync.RWMutex } var ss *storeExtManager func NewStoreExtManager(execer fakeruntime.Execer) ExtManager { - if ss == nil { - ss = &storeExtManager{ - processChan: make(chan fakeruntime.Process), - stopSingal: make(chan struct{}, 1), - lock: &sync.RWMutex{}, - } - ss.execer = execer - ss.socketPrefix = "unix://" - ss.extStatusMap = map[string]bool{} - ss.processCollect() - ss.WithDownloader(&nonDownloader{}) - } - return ss + if ss == nil { + ss = &storeExtManager{ + processChan: make(chan fakeruntime.Process), + stopSingal: make(chan struct{}, 1), + lock: &sync.RWMutex{}, + } + ss.execer = execer + ss.socketPrefix = "unix://" + ss.extStatusMap = map[string]bool{} + ss.processCollect() + ss.WithDownloader(&nonDownloader{}) + } + return ss } func NewStoreExtManagerInstance(execer fakeruntime.Execer) ExtManager { - ss = &storeExtManager{ - processChan: make(chan fakeruntime.Process), - stopSingal: make(chan struct{}, 1), - lock: &sync.RWMutex{}, - } - ss.execer = execer - ss.socketPrefix = "unix://" - ss.extStatusMap = map[string]bool{} - ss.processCollect() - ss.WithDownloader(&nonDownloader{}) - return ss + ss = &storeExtManager{ + processChan: make(chan fakeruntime.Process), + stopSingal: make(chan struct{}, 1), + lock: &sync.RWMutex{}, + } + ss.execer = execer + ss.socketPrefix = "unix://" + ss.extStatusMap = map[string]bool{} + ss.processCollect() + ss.WithDownloader(&nonDownloader{}) + return ss } func (s *storeExtManager) Start(name, socket string) (err error) { - if v, ok := s.extStatusMap[name]; ok && v { - return - } - targetDir := home.GetUserBinDir() - targetBinaryFile := filepath.Join(targetDir, name) - - var binaryPath string - if _, err = os.Stat(targetBinaryFile); err == nil { - binaryPath = targetBinaryFile - } else { - binaryPath, err = s.execer.LookPath(name) - if err != nil { - err = fmt.Errorf("not found extension, try to download it, error: %v", err) - go func() { - reader, dErr := s.ociDownloader.Download(name, "", "") - if dErr != nil { - serverLogger.Error(dErr, "failed to download extension", "name", name) - } else { - extFile := s.ociDownloader.GetTargetFile() - - targetFile := filepath.Base(extFile) - if dErr = downloader.WriteTo(reader, targetDir, targetFile); dErr == nil { - binaryPath = filepath.Join(targetDir, targetFile) - s.startPlugin(socket, binaryPath, name) - } else { - serverLogger.Error(dErr, "failed to save extension", "targetFile", targetFile) - } - } - }() - } - } - - if err == nil { - go s.startPlugin(socket, binaryPath, name) - } - return + if v, ok := s.extStatusMap[name]; ok && v { + return + } + targetDir := home.GetUserBinDir() + targetBinaryFile := filepath.Join(targetDir, name) + + var binaryPath string + if _, err = os.Stat(targetBinaryFile); err == nil { + binaryPath = targetBinaryFile + } else { + binaryPath, err = s.execer.LookPath(name) + if err != nil { + err = fmt.Errorf("not found extension, try to download it, error: %v", err) + go func() { + reader, dErr := s.ociDownloader.Download(name, "", "") + if dErr != nil { + serverLogger.Error(dErr, "failed to download extension", "name", name) + } else { + extFile := s.ociDownloader.GetTargetFile() + + targetFile := filepath.Base(extFile) + if dErr = downloader.WriteTo(reader, targetDir, targetFile); dErr == nil { + binaryPath = filepath.Join(targetDir, targetFile) + s.startPlugin(socket, binaryPath, name) + } else { + serverLogger.Error(dErr, "failed to save extension", "targetFile", targetFile) + } + } + }() + } + } + + if err == nil { + go s.startPlugin(socket, binaryPath, name) + } + return } func (s *storeExtManager) startPlugin(socketURL, plugin, pluginName string) (err error) { - if strings.Contains(socketURL, ":") && !strings.HasPrefix(socketURL, s.socketPrefix) { - err = s.startPluginViaHTTP(socketURL, plugin, pluginName) - return - } - socketFile := strings.TrimPrefix(socketURL, s.socketPrefix) - _ = os.RemoveAll(socketFile) // always deleting the socket file to avoid start failing + if strings.Contains(socketURL, ":") && !strings.HasPrefix(socketURL, s.socketPrefix) { + err = s.startPluginViaHTTP(socketURL, plugin, pluginName) + return + } + socketFile := strings.TrimPrefix(socketURL, s.socketPrefix) + _ = os.RemoveAll(socketFile) // always deleting the socket file to avoid start failing - s.lock.Lock() - s.filesNeedToBeRemoved = append(s.filesNeedToBeRemoved, socketFile) - s.extStatusMap[pluginName] = true - s.lock.Unlock() + s.lock.Lock() + s.filesNeedToBeRemoved = append(s.filesNeedToBeRemoved, socketFile) + s.extStatusMap[pluginName] = true + s.lock.Unlock() - if err = s.execer.RunCommandWithIO(plugin, "", os.Stdout, os.Stderr, s.processChan, "--socket", socketFile); err != nil { - serverLogger.Info("failed to start ext manager", "socket", socketURL, "error: ", err.Error()) - } - return + if err = s.execer.RunCommandWithIO(plugin, "", os.Stdout, os.Stderr, s.processChan, "--socket", socketFile); err != nil { + serverLogger.Info("failed to start ext manager", "socket", socketURL, "error: ", err.Error()) + } + return } func (s *storeExtManager) startPluginViaHTTP(httpURL, plugin, pluginName string) (err error) { - port := strings.Split(httpURL, ":")[1] - if err = s.execer.RunCommandWithIO(plugin, "", os.Stdout, os.Stderr, s.processChan, "--port", port); err != nil { - serverLogger.Info("failed to start ext manager", "port", port, "error: ", err.Error()) - } - return + port := strings.Split(httpURL, ":")[1] + if err = s.execer.RunCommandWithIO(plugin, "", os.Stdout, os.Stderr, s.processChan, "--port", port); err != nil { + serverLogger.Info("failed to start ext manager", "port", port, "error: ", err.Error()) + } + return } func (s *storeExtManager) StopAll() error { - serverLogger.Info("stop", "extensions", len(s.processs)) - for _, p := range s.processs { - if p != nil { - p.Signal(syscall.SIGTERM) - } - } - s.stopSingal <- struct{}{} - return nil + serverLogger.Info("stop", "extensions", len(s.processs)) + for _, p := range s.processs { + if p != nil { + p.Signal(syscall.SIGTERM) + } + } + s.stopSingal <- struct{}{} + return nil } func (s *storeExtManager) WithDownloader(ociDownloader downloader.PlatformAwareOCIDownloader) { - s.ociDownloader = ociDownloader + s.ociDownloader = ociDownloader } func (s *storeExtManager) processCollect() { - go func() { - for { - select { - case p := <-s.processChan: - s.processs = append(s.processs, p) - case <-s.stopSingal: - return - } - } - }() + go func() { + for { + select { + case p := <-s.processChan: + s.processs = append(s.processs, p) + case <-s.stopSingal: + return + } + } + }() } var ErrDownloadNotSupport = errors.New("no support") @@ -191,44 +191,44 @@ type nonDownloader struct{} var _ downloader.PlatformAwareOCIDownloader = &nonDownloader{} func (n *nonDownloader) WithBasicAuth(username string, password string) { - // Do nothing because this is an empty implementation + // Do nothing because this is an empty implementation } func (n *nonDownloader) Download(image, tag, file string) (reader io.Reader, err error) { - err = ErrDownloadNotSupport - return + err = ErrDownloadNotSupport + return } func (n *nonDownloader) WithOS(string) { - // Do nothing because this is an empty implementation + // Do nothing because this is an empty implementation } func (n *nonDownloader) WithArch(string) { - // Do nothing because this is an empty implementation + // Do nothing because this is an empty implementation } func (n *nonDownloader) WithRegistry(string) { - // Do nothing because this is an empty implementation + // Do nothing because this is an empty implementation } func (n *nonDownloader) WithKind(string) { - // Do nothing because this is an empty implementation + // Do nothing because this is an empty implementation } func (n *nonDownloader) WithImagePrefix(imagePrefix string) { - // Do nothing because this is an empty implementation + // Do nothing because this is an empty implementation } func (d *nonDownloader) WithRoundTripper(rt http.RoundTripper) { - // Do nothing because this is an empty implementation + // Do nothing because this is an empty implementation } func (d *nonDownloader) WithInsecure(bool) { - // Do nothing because this is an empty implementation + // Do nothing because this is an empty implementation } func (d *nonDownloader) WithTimeout(time.Duration) {} func (d *nonDownloader) WithContext(context.Context) {} func (n *nonDownloader) GetTargetFile() string { - return "" + return "" } diff --git a/pkg/server/store_ext_manager_test.go b/pkg/server/store_ext_manager_test.go index 4d189821..6ce5eee8 100644 --- a/pkg/server/store_ext_manager_test.go +++ b/pkg/server/store_ext_manager_test.go @@ -16,35 +16,35 @@ limitations under the License. package server import ( - "errors" - "testing" - "time" + "errors" + "testing" + "time" - fakeruntime "github.com/linuxsuren/go-fake-runtime" - "github.com/stretchr/testify/assert" + fakeruntime "github.com/linuxsuren/go-fake-runtime" + "github.com/stretchr/testify/assert" ) func TestStoreExtManager(t *testing.T) { - t.Run("not found", func(t *testing.T) { - mgr := NewStoreExtManager(&fakeruntime.FakeExecer{ - ExpectLookPathError: errors.New("not found"), - }) - err := mgr.Start("fake", "") - assert.Error(t, err) - }) - - t.Run("exist executable file", func(t *testing.T) { - mgr := NewStoreExtManagerInstance(&fakeruntime.FakeExecer{ - ExpectLookPath: "/usr/local/bin/go", - }) - err := mgr.Start("go", "") - assert.NoError(t, err, err) - - time.Sleep(time.Microsecond * 100) - err = mgr.Start("go", "") - assert.NoError(t, err) - - err = mgr.StopAll() - assert.NoError(t, err) - }) + t.Run("not found", func(t *testing.T) { + mgr := NewStoreExtManager(&fakeruntime.FakeExecer{ + ExpectLookPathError: errors.New("not found"), + }) + err := mgr.Start("fake", "") + assert.Error(t, err) + }) + + t.Run("exist executable file", func(t *testing.T) { + mgr := NewStoreExtManagerInstance(&fakeruntime.FakeExecer{ + ExpectLookPath: "/usr/local/bin/go", + }) + err := mgr.Start("go", "") + assert.NoError(t, err, err) + + time.Sleep(time.Microsecond * 100) + err = mgr.Start("go", "") + assert.NoError(t, err) + + err = mgr.StopAll() + assert.NoError(t, err) + }) } diff --git a/pkg/testing/case_test.go b/pkg/testing/case_test.go index 4107dda5..6c4523da 100644 --- a/pkg/testing/case_test.go +++ b/pkg/testing/case_test.go @@ -16,25 +16,25 @@ limitations under the License. package testing_test import ( - "github.com/linuxsuren/api-testing/pkg/util" - "testing" + "github.com/linuxsuren/api-testing/pkg/util" + "testing" - atesting "github.com/linuxsuren/api-testing/pkg/testing" - "github.com/stretchr/testify/assert" + atesting "github.com/linuxsuren/api-testing/pkg/testing" + "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" + "gopkg.in/yaml.v3" ) func TestInScope(t *testing.T) { - testCase := &atesting.TestCase{Name: "foo"} - assert.True(t, testCase.InScope(nil)) - assert.True(t, testCase.InScope([]string{"foo"})) - assert.False(t, testCase.InScope([]string{"bar"})) + testCase := &atesting.TestCase{Name: "foo"} + assert.True(t, testCase.InScope(nil)) + assert.True(t, testCase.InScope([]string{"foo"})) + assert.False(t, testCase.InScope([]string{"bar"})) } func TestRequestBody(t *testing.T) { - req := &atesting.Request{} - graphqlBody := `api: /api + req := &atesting.Request{} + graphqlBody := `api: /api body: query: query operationName: "" @@ -42,88 +42,88 @@ body: name: rick ` - err := yaml.Unmarshal([]byte(graphqlBody), req) - assert.Nil(t, err) - assert.Equal(t, `{"query":"query","operationName":"","variables":{"name":"rick"}}`, req.Body.String()) + err := yaml.Unmarshal([]byte(graphqlBody), req) + assert.Nil(t, err) + assert.Equal(t, `{"query":"query","operationName":"","variables":{"name":"rick"}}`, req.Body.String()) - var data []byte - data, err = yaml.Marshal(req) - assert.Nil(t, err) - assert.Equal(t, graphqlBody, string(data)) + var data []byte + data, err = yaml.Marshal(req) + assert.Nil(t, err) + assert.Equal(t, graphqlBody, string(data)) - err = yaml.Unmarshal([]byte(`body: plain`), req) - assert.Nil(t, err) - assert.Equal(t, "plain", req.Body.String()) + err = yaml.Unmarshal([]byte(`body: plain`), req) + assert.Nil(t, err) + assert.Equal(t, "plain", req.Body.String()) } func TestResponse(t *testing.T) { - resp := &atesting.Response{ - Body: "body", - BodyFieldsExpect: map[string]interface{}{ - "name": "rick", - }, - } - assert.Equal(t, "body", resp.GetBody()) - assert.Equal(t, map[string]interface{}{"name": "rick"}, resp.GetBodyFieldsExpect()) + resp := &atesting.Response{ + Body: "body", + BodyFieldsExpect: map[string]interface{}{ + "name": "rick", + }, + } + assert.Equal(t, "body", resp.GetBody()) + assert.Equal(t, map[string]interface{}{"name": "rick"}, resp.GetBodyFieldsExpect()) } func TestSortedKeysStringMap(t *testing.T) { - obj := atesting.SortedKeysStringMap{ - "c": "d", - "f": map[string]interface{}{ - "value": "f", - }, - "e": &atesting.Verifier{ - Value: "e", - }, - "a": "b", - } - assert.Equal(t, []string{"a", "c", "e", "f"}, obj.Keys()) - assert.Equal(t, "b", obj.GetValue("a")) - assert.Nil(t, obj.GetVerifier("b")) - assert.Equal(t, "e", obj.GetValue("e")) - assert.Equal(t, "f", obj.GetValue("f")) - assert.Equal(t, "f", obj.GetVerifier("f").Value) - assert.Empty(t, obj.GetValue("not-found")) + obj := atesting.SortedKeysStringMap{ + "c": "d", + "f": map[string]interface{}{ + "value": "f", + }, + "e": &atesting.Verifier{ + Value: "e", + }, + "a": "b", + } + assert.Equal(t, []string{"a", "c", "e", "f"}, obj.Keys()) + assert.Equal(t, "b", obj.GetValue("a")) + assert.Nil(t, obj.GetVerifier("b")) + assert.Equal(t, "e", obj.GetValue("e")) + assert.Equal(t, "f", obj.GetValue("f")) + assert.Equal(t, "f", obj.GetVerifier("f").Value) + assert.Empty(t, obj.GetValue("not-found")) } func TestBodyBytes(t *testing.T) { - const defaultPlainText = "hello" - const defaultBase64Text = "aGVsbG8=" + const defaultPlainText = "hello" + const defaultBase64Text = "aGVsbG8=" - tt := []struct { - name string - rawBody string - expect []byte - }{{ - name: "image base64", - rawBody: util.ImageBase64Prefix + defaultBase64Text, - expect: []byte(defaultPlainText), - }, { - name: "pdf", - rawBody: util.PDFBase64Prefix + defaultBase64Text, - expect: []byte(defaultPlainText), - }, { - name: "zip", - rawBody: util.ZIPBase64Prefix + defaultBase64Text, - expect: []byte(defaultPlainText), - }, { - name: "binary", - rawBody: util.BinaryBase64Prefix + defaultBase64Text, - expect: []byte(defaultPlainText), - }, { - name: "raw", - rawBody: defaultPlainText, - expect: []byte(defaultPlainText), - }} - for _, tc := range tt { - t.Run(tc.name, func(t *testing.T) { - body := atesting.RequestBody{ - Value: tc.rawBody, - } - data := body.Bytes() - assert.Equal(t, tc.expect, data) - assert.False(t, body.IsEmpty()) - }) - } + tt := []struct { + name string + rawBody string + expect []byte + }{{ + name: "image base64", + rawBody: util.ImageBase64Prefix + defaultBase64Text, + expect: []byte(defaultPlainText), + }, { + name: "pdf", + rawBody: util.PDFBase64Prefix + defaultBase64Text, + expect: []byte(defaultPlainText), + }, { + name: "zip", + rawBody: util.ZIPBase64Prefix + defaultBase64Text, + expect: []byte(defaultPlainText), + }, { + name: "binary", + rawBody: util.BinaryBase64Prefix + defaultBase64Text, + expect: []byte(defaultPlainText), + }, { + name: "raw", + rawBody: defaultPlainText, + expect: []byte(defaultPlainText), + }} + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + body := atesting.RequestBody{ + Value: tc.rawBody, + } + data := body.Bytes() + assert.Equal(t, tc.expect, data) + assert.False(t, body.IsEmpty()) + }) + } } diff --git a/pkg/util/home/common_test.go b/pkg/util/home/common_test.go index d0022940..4fcad28d 100644 --- a/pkg/util/home/common_test.go +++ b/pkg/util/home/common_test.go @@ -17,13 +17,13 @@ limitations under the License. package home import ( - "github.com/stretchr/testify/assert" - "testing" + "github.com/stretchr/testify/assert" + "testing" ) func TestGetUserBinDir(t *testing.T) { - assert.Contains(t, GetUserConfigDir(), "atest") - assert.Contains(t, GetUserBinDir(), "bin") - assert.Contains(t, GetUserDataDir(), "data") - assert.Contains(t, GetExtensionSocketPath("fake"), "fake.sock") + assert.Contains(t, GetUserConfigDir(), "atest") + assert.Contains(t, GetUserBinDir(), "bin") + assert.Contains(t, GetUserDataDir(), "data") + assert.Contains(t, GetExtensionSocketPath("fake"), "fake.sock") } From 508ac27ef6e6596208c55a037b6a63cbfcdb9be4 Mon Sep 17 00:00:00 2001 From: rick Date: Sun, 25 May 2025 16:36:10 +0800 Subject: [PATCH 3/5] support to show image --- console/atest-ui/src/views/TestCase.vue | 17 +- console/atest-ui/src/views/net.ts | 8 +- pkg/runner/http.go | 2 +- pkg/runner/monitor/monitor.pb.go | 2 +- pkg/runner/monitor/monitor_grpc.pb.go | 2 +- pkg/server/server.pb.go | 170 ++++++------ pkg/server/server.pb.gw.go | 332 +++++++++++++++++++----- pkg/server/server.proto | 3 +- pkg/server/server_grpc.pb.go | 2 +- pkg/testing/remote/loader.pb.go | 2 +- pkg/testing/remote/loader_grpc.pb.go | 2 +- pkg/util/default.go | 1 + 12 files changed, 373 insertions(+), 170 deletions(-) diff --git a/console/atest-ui/src/views/TestCase.vue b/console/atest-ui/src/views/TestCase.vue index 9e5c265f..321c4549 100644 --- a/console/atest-ui/src/views/TestCase.vue +++ b/console/atest-ui/src/views/TestCase.vue @@ -112,6 +112,9 @@ const parseResponseBody = (body: any) => { } } +const currentContentType = ref('') +const currentImageContent = ref('') + /** * Handles test result data from API response * @@ -124,6 +127,17 @@ const parseResponseBody = (body: any) => { */ const handleTestResult = (e: any): void => { testResult.value = e; + currentContentType.value = e.header.find((h: Pair) => h.key.toLowerCase() === 'content-type')?.value || ''; + if (currentContentType.value.startsWith('image/')) { + API.DownloadResponseFile({ + body: e.body}, (e) => { + if (e && e.data) { + currentImageContent.value = `data:${currentContentType.value};base64,${e.data}`; + } else { + console.error('No data to display as image.'); + } + }); + } if (!isHistoryTestCase.value) { handleTestResultError(e) @@ -1372,7 +1386,8 @@ const renameTestCase = (name: string) => {
-
+ +
Response body is too large, please download to view.
diff --git a/console/atest-ui/src/views/net.ts b/console/atest-ui/src/views/net.ts index 6d43c3d3..176d9bd5 100644 --- a/console/atest-ui/src/views/net.ts +++ b/console/atest-ui/src/views/net.ts @@ -828,16 +828,10 @@ interface ResponseFile { function DownloadResponseFile(testcase: ResponseFile, callback: (d: any) => void, errHandle?: (e: any) => void | null) { const requestOptions = { - method: 'POST', headers: { 'X-Store-Name': Cache.GetCurrentStore().name, 'X-Auth': getToken() - }, - body: JSON.stringify({ - response: { - body: testcase.body, - } - }) + } } fetch(`/api/v1/downloadFile/${testcase.body}`, requestOptions) .then(DefaultResponseProcess) diff --git a/pkg/runner/http.go b/pkg/runner/http.go index ebb5c1bf..38b48062 100644 --- a/pkg/runner/http.go +++ b/pkg/runner/http.go @@ -257,7 +257,7 @@ func (r *simpleTestCaseRunner) RunTestCase(testcase *testing.TestCase, dataConte err = errors.Join(err, jsonSchemaValidation(testcase.Expect.Schema, responseBodyData)) } else { switch respType { - case util.OctetStream, util.Image: + case util.OctetStream, util.Image, util.ImagePNG: var data []byte if data, err = io.ReadAll(resp.Body); err == nil { r.simpleResponse.RawBody = data diff --git a/pkg/runner/monitor/monitor.pb.go b/pkg/runner/monitor/monitor.pb.go index c7c582fa..0d08cb62 100644 --- a/pkg/runner/monitor/monitor.pb.go +++ b/pkg/runner/monitor/monitor.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v4.25.1 +// protoc v4.22.2 // source: pkg/runner/monitor/monitor.proto package monitor diff --git a/pkg/runner/monitor/monitor_grpc.pb.go b/pkg/runner/monitor/monitor_grpc.pb.go index e7dcfaa0..662f57d7 100644 --- a/pkg/runner/monitor/monitor_grpc.pb.go +++ b/pkg/runner/monitor/monitor_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.2.0 -// - protoc v4.25.1 +// - protoc v4.22.2 // source: pkg/runner/monitor/monitor.proto package monitor diff --git a/pkg/server/server.pb.go b/pkg/server/server.pb.go index 1744e984..56ce9c24 100644 --- a/pkg/server/server.pb.go +++ b/pkg/server/server.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v4.25.1 +// protoc v4.22.2 // source: pkg/server/server.proto package server @@ -3997,7 +3997,7 @@ var file_pkg_server_server_proto_rawDesc = []byte{ 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x32, - 0xa1, 0x25, 0x0a, 0x06, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x43, 0x0a, 0x03, 0x52, 0x75, + 0x9e, 0x25, 0x0a, 0x06, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x43, 0x0a, 0x03, 0x52, 0x75, 0x6e, 0x12, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x22, @@ -4235,93 +4235,93 @@ var file_pkg_server_server_proto_rawDesc = []byte{ 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x61, 0x6d, 0x70, - 0x6c, 0x65, 0x12, 0x6b, 0x0a, 0x14, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, + 0x6c, 0x65, 0x12, 0x68, 0x0a, 0x14, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x1a, 0x10, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x22, 0x2f, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x22, 0x2c, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x12, 0x24, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x2f, 0x7b, 0x72, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x62, 0x6f, 0x64, 0x79, 0x7d, 0x3a, 0x01, 0x2a, 0x12, - 0x50, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x73, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x62, 0x6f, 0x64, 0x79, 0x7d, 0x12, 0x50, 0x0a, 0x0d, + 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x73, 0x12, 0x0d, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x73, + 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, + 0x31, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2f, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x12, 0x42, + 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x0d, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0e, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x73, 0x12, 0x46, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, + 0x65, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, + 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, + 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x22, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, + 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x4d, 0x0a, 0x0b, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x1a, + 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2f, + 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x3a, 0x01, 0x2a, 0x12, 0x4a, 0x0a, 0x0b, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x2a, 0x15, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2f, 0x7b, + 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x5d, 0x0a, 0x0b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x53, + 0x74, 0x6f, 0x72, 0x65, 0x12, 0x13, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x69, + 0x6d, 0x70, 0x6c, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2f, 0x76, 0x65, 0x72, 0x69, 0x66, + 0x79, 0x3a, 0x01, 0x2a, 0x12, 0x45, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x73, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x1a, 0x0f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x50, 0x0a, 0x0c, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x0e, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x1a, 0x14, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x76, 0x31, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x54, 0x0a, + 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x0e, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x1a, 0x14, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x2a, 0x16, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x4e, 0x61, + 0x6d, 0x65, 0x7d, 0x12, 0x57, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x12, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1b, 0x1a, 0x16, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x73, 0x2f, 0x7b, 0x4e, 0x61, 0x6d, 0x65, 0x7d, 0x3a, 0x01, 0x2a, 0x12, 0x32, 0x0a, 0x05, + 0x50, 0x50, 0x72, 0x6f, 0x66, 0x12, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, + 0x50, 0x72, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x50, 0x72, 0x6f, 0x66, 0x44, 0x61, 0x74, 0x61, 0x22, 0x00, + 0x32, 0x6b, 0x0a, 0x0f, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x58, 0x0a, 0x03, 0x52, 0x75, 0x6e, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x57, 0x69, 0x74, + 0x68, 0x43, 0x61, 0x73, 0x65, 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, + 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x20, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x74, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2f, 0x72, 0x75, 0x6e, 0x3a, 0x01, 0x2a, 0x32, 0xa0, 0x01, + 0x0a, 0x04, 0x4d, 0x6f, 0x63, 0x6b, 0x12, 0x4b, 0x0a, 0x06, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, + 0x12, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4d, 0x6f, 0x63, 0x6b, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x63, 0x6b, 0x2f, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, + 0x3a, 0x01, 0x2a, 0x12, 0x4b, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, - 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, - 0x6e, 0x64, 0x73, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2f, 0x6b, 0x69, 0x6e, 0x64, - 0x73, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x0d, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0e, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x22, 0x16, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x46, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, - 0x74, 0x6f, 0x72, 0x65, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, - 0x6f, 0x72, 0x65, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, - 0x72, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x22, 0x0e, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x4d, 0x0a, - 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x0d, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x1a, 0x0d, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1a, 0x1a, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x73, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x3a, 0x01, 0x2a, 0x12, 0x4a, 0x0a, 0x0b, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x0d, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x2a, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x73, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x5d, 0x0a, 0x0b, 0x56, 0x65, 0x72, 0x69, - 0x66, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x13, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x1a, 0x17, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2f, 0x76, 0x65, - 0x72, 0x69, 0x66, 0x79, 0x3a, 0x01, 0x2a, 0x12, 0x45, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x50, - 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x0e, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x1a, 0x14, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x3a, 0x01, 0x2a, - 0x12, 0x54, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x12, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x2a, 0x16, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x2f, - 0x7b, 0x4e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x57, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x21, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1b, 0x1a, 0x16, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x4e, 0x61, 0x6d, 0x65, 0x7d, 0x3a, 0x01, 0x2a, 0x12, - 0x32, 0x0a, 0x05, 0x50, 0x50, 0x72, 0x6f, 0x66, 0x12, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x50, 0x50, 0x72, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x50, 0x72, 0x6f, 0x66, 0x44, 0x61, 0x74, - 0x61, 0x22, 0x00, 0x32, 0x6b, 0x0a, 0x0f, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x45, 0x78, 0x74, - 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x58, 0x0a, 0x03, 0x52, 0x75, 0x6e, 0x12, 0x19, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, - 0x57, 0x69, 0x74, 0x68, 0x43, 0x61, 0x73, 0x65, 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x20, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, - 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2f, 0x72, 0x75, 0x6e, 0x3a, 0x01, 0x2a, - 0x32, 0xa0, 0x01, 0x0a, 0x04, 0x4d, 0x6f, 0x63, 0x6b, 0x12, 0x4b, 0x0a, 0x06, 0x52, 0x65, 0x6c, - 0x6f, 0x61, 0x64, 0x12, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4d, 0x6f, 0x63, - 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x63, 0x6b, 0x2f, 0x72, 0x65, 0x6c, - 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x4b, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4d, 0x6f, 0x63, 0x6b, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x63, 0x6b, 0x2f, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x32, 0x60, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x12, 0x52, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x11, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x51, 0x75, 0x65, 0x72, 0x79, 0x1a, 0x17, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x71, 0x75, 0x65, - 0x72, 0x79, 0x3a, 0x01, 0x2a, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x73, 0x75, 0x72, 0x65, 0x6e, 0x2f, 0x61, - 0x70, 0x69, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4d, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x63, 0x6b, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x32, 0x60, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x52, + 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x11, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x44, 0x61, 0x74, 0x61, 0x51, 0x75, 0x65, 0x72, 0x79, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x3a, + 0x01, 0x2a, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x73, 0x75, 0x72, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2d, + 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/server/server.pb.gw.go b/pkg/server/server.pb.gw.go index b28e0dc9..85f0ad70 100644 --- a/pkg/server/server.pb.gw.go +++ b/pkg/server/server.pb.gw.go @@ -35,7 +35,11 @@ func request_Runner_Run_0(ctx context.Context, marshaler runtime.Marshaler, clie var protoReq TestTask var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -48,7 +52,11 @@ func local_request_Runner_Run_0(ctx context.Context, marshaler runtime.Marshaler var protoReq TestTask var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -122,7 +130,11 @@ func request_Runner_CreateTestSuite_0(ctx context.Context, marshaler runtime.Mar var protoReq TestSuiteIdentity var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -135,7 +147,11 @@ func local_request_Runner_CreateTestSuite_0(ctx context.Context, marshaler runti var protoReq TestSuiteIdentity var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -148,7 +164,11 @@ func request_Runner_ImportTestSuite_0(ctx context.Context, marshaler runtime.Mar var protoReq TestSuiteSource var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -161,7 +181,11 @@ func local_request_Runner_ImportTestSuite_0(ctx context.Context, marshaler runti var protoReq TestSuiteSource var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -171,7 +195,7 @@ func local_request_Runner_ImportTestSuite_0(ctx context.Context, marshaler runti } var ( - filter_Runner_GetTestSuite_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + filter_Runner_GetTestSuite_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 2, 0, 0}, Check: []int{0, 1, 2, 2}} ) func request_Runner_GetTestSuite_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -244,7 +268,11 @@ func request_Runner_UpdateTestSuite_0(ctx context.Context, marshaler runtime.Mar var protoReq TestSuite var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -274,7 +302,11 @@ func local_request_Runner_UpdateTestSuite_0(ctx context.Context, marshaler runti var protoReq TestSuite var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -301,7 +333,7 @@ func local_request_Runner_UpdateTestSuite_0(ctx context.Context, marshaler runti } var ( - filter_Runner_DeleteTestSuite_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + filter_Runner_DeleteTestSuite_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 2, 0, 0}, Check: []int{0, 1, 2, 2}} ) func request_Runner_DeleteTestSuite_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -374,7 +406,11 @@ func request_Runner_DuplicateTestSuite_0(ctx context.Context, marshaler runtime. var protoReq TestSuiteDuplicate var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -404,7 +440,11 @@ func local_request_Runner_DuplicateTestSuite_0(ctx context.Context, marshaler ru var protoReq TestSuiteDuplicate var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -434,7 +474,11 @@ func request_Runner_RenameTestSuite_0(ctx context.Context, marshaler runtime.Mar var protoReq TestSuiteDuplicate var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -464,7 +508,11 @@ func local_request_Runner_RenameTestSuite_0(ctx context.Context, marshaler runti var protoReq TestSuiteDuplicate var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -491,7 +539,7 @@ func local_request_Runner_RenameTestSuite_0(ctx context.Context, marshaler runti } var ( - filter_Runner_GetTestSuiteYaml_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + filter_Runner_GetTestSuiteYaml_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 2, 0, 0}, Check: []int{0, 1, 2, 2}} ) func request_Runner_GetTestSuiteYaml_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -561,7 +609,7 @@ func local_request_Runner_GetTestSuiteYaml_0(ctx context.Context, marshaler runt } var ( - filter_Runner_ListTestCase_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + filter_Runner_ListTestCase_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 2, 0, 0}, Check: []int{0, 1, 2, 2}} ) func request_Runner_ListTestCase_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -634,7 +682,11 @@ func request_Runner_RunTestCase_0(ctx context.Context, marshaler runtime.Marshal var protoReq TestCaseIdentity var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -674,7 +726,11 @@ func local_request_Runner_RunTestCase_0(ctx context.Context, marshaler runtime.M var protoReq TestCaseIdentity var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -754,7 +810,7 @@ func request_Runner_BatchRun_0(ctx context.Context, marshaler runtime.Marshaler, } var ( - filter_Runner_GetTestCase_0 = &utilities.DoubleArray{Encoding: map[string]int{"suite": 0, "testcase": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} + filter_Runner_GetTestCase_0 = &utilities.DoubleArray{Encoding: map[string]int{"suite": 0, "testcase": 1}, Base: []int{1, 2, 4, 0, 0, 0, 0}, Check: []int{0, 1, 1, 2, 2, 3, 3}} ) func request_Runner_GetTestCase_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -847,7 +903,11 @@ func request_Runner_CreateTestCase_0(ctx context.Context, marshaler runtime.Mars var protoReq TestCaseWithSuite var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -877,7 +937,11 @@ func local_request_Runner_CreateTestCase_0(ctx context.Context, marshaler runtim var protoReq TestCaseWithSuite var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -907,7 +971,11 @@ func request_Runner_UpdateTestCase_0(ctx context.Context, marshaler runtime.Mars var protoReq TestCaseWithSuite var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -947,7 +1015,11 @@ func local_request_Runner_UpdateTestCase_0(ctx context.Context, marshaler runtim var protoReq TestCaseWithSuite var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -984,7 +1056,7 @@ func local_request_Runner_UpdateTestCase_0(ctx context.Context, marshaler runtim } var ( - filter_Runner_DeleteTestCase_0 = &utilities.DoubleArray{Encoding: map[string]int{"suite": 0, "testcase": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} + filter_Runner_DeleteTestCase_0 = &utilities.DoubleArray{Encoding: map[string]int{"suite": 0, "testcase": 1}, Base: []int{1, 2, 4, 0, 0, 0, 0}, Check: []int{0, 1, 1, 2, 2, 3, 3}} ) func request_Runner_DeleteTestCase_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -1077,7 +1149,11 @@ func request_Runner_DuplicateTestCase_0(ctx context.Context, marshaler runtime.M var protoReq TestCaseDuplicate var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1117,7 +1193,11 @@ func local_request_Runner_DuplicateTestCase_0(ctx context.Context, marshaler run var protoReq TestCaseDuplicate var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1157,7 +1237,11 @@ func request_Runner_RenameTestCase_0(ctx context.Context, marshaler runtime.Mars var protoReq TestCaseDuplicate var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1197,7 +1281,11 @@ func local_request_Runner_RenameTestCase_0(ctx context.Context, marshaler runtim var protoReq TestCaseDuplicate var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1288,7 +1376,7 @@ func local_request_Runner_GetHistorySuites_0(ctx context.Context, marshaler runt } var ( - filter_Runner_GetHistoryTestCaseWithResult_0 = &utilities.DoubleArray{Encoding: map[string]int{"ID": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + filter_Runner_GetHistoryTestCaseWithResult_0 = &utilities.DoubleArray{Encoding: map[string]int{"ID": 0}, Base: []int{1, 2, 0, 0}, Check: []int{0, 1, 2, 2}} ) func request_Runner_GetHistoryTestCaseWithResult_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -1358,7 +1446,7 @@ func local_request_Runner_GetHistoryTestCaseWithResult_0(ctx context.Context, ma } var ( - filter_Runner_GetHistoryTestCase_0 = &utilities.DoubleArray{Encoding: map[string]int{"ID": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + filter_Runner_GetHistoryTestCase_0 = &utilities.DoubleArray{Encoding: map[string]int{"ID": 0}, Base: []int{1, 2, 0, 0}, Check: []int{0, 1, 2, 2}} ) func request_Runner_GetHistoryTestCase_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -1428,7 +1516,7 @@ func local_request_Runner_GetHistoryTestCase_0(ctx context.Context, marshaler ru } var ( - filter_Runner_DeleteHistoryTestCase_0 = &utilities.DoubleArray{Encoding: map[string]int{"ID": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + filter_Runner_DeleteHistoryTestCase_0 = &utilities.DoubleArray{Encoding: map[string]int{"ID": 0}, Base: []int{1, 2, 0, 0}, Check: []int{0, 1, 2, 2}} ) func request_Runner_DeleteHistoryTestCase_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -1498,7 +1586,7 @@ func local_request_Runner_DeleteHistoryTestCase_0(ctx context.Context, marshaler } var ( - filter_Runner_DeleteAllHistoryTestCase_0 = &utilities.DoubleArray{Encoding: map[string]int{"suiteName": 0, "caseName": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} + filter_Runner_DeleteAllHistoryTestCase_0 = &utilities.DoubleArray{Encoding: map[string]int{"suiteName": 0, "caseName": 1}, Base: []int{1, 2, 4, 0, 0, 0, 0}, Check: []int{0, 1, 1, 2, 2, 3, 3}} ) func request_Runner_DeleteAllHistoryTestCase_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -1588,7 +1676,7 @@ func local_request_Runner_DeleteAllHistoryTestCase_0(ctx context.Context, marsha } var ( - filter_Runner_GetTestCaseAllHistory_0 = &utilities.DoubleArray{Encoding: map[string]int{"suiteName": 0, "name": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} + filter_Runner_GetTestCaseAllHistory_0 = &utilities.DoubleArray{Encoding: map[string]int{"suiteName": 0, "name": 1}, Base: []int{1, 2, 4, 0, 0, 0, 0}, Check: []int{0, 1, 1, 2, 2, 3, 3}} ) func request_Runner_GetTestCaseAllHistory_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -1699,7 +1787,11 @@ func request_Runner_GenerateCode_0(ctx context.Context, marshaler runtime.Marsha var protoReq CodeGenerateRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1712,7 +1804,11 @@ func local_request_Runner_GenerateCode_0(ctx context.Context, marshaler runtime. var protoReq CodeGenerateRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1725,7 +1821,11 @@ func request_Runner_HistoryGenerateCode_0(ctx context.Context, marshaler runtime var protoReq CodeGenerateRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1738,7 +1838,11 @@ func local_request_Runner_HistoryGenerateCode_0(ctx context.Context, marshaler r var protoReq CodeGenerateRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1769,7 +1873,11 @@ func request_Runner_ConvertTestSuite_0(ctx context.Context, marshaler runtime.Ma var protoReq CodeGenerateRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1782,7 +1890,11 @@ func local_request_Runner_ConvertTestSuite_0(ctx context.Context, marshaler runt var protoReq CodeGenerateRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1924,14 +2036,14 @@ func local_request_Runner_Sample_0(ctx context.Context, marshaler runtime.Marsha } +var ( + filter_Runner_DownloadResponseFile_0 = &utilities.DoubleArray{Encoding: map[string]int{"response": 0, "body": 1}, Base: []int{1, 2, 3, 2, 0, 0}, Check: []int{0, 1, 1, 2, 4, 3}} +) + func request_Runner_DownloadResponseFile_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq TestCase var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - var ( val string ok bool @@ -1949,6 +2061,13 @@ func request_Runner_DownloadResponseFile_0(ctx context.Context, marshaler runtim return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "response.body", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Runner_DownloadResponseFile_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.DownloadResponseFile(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err @@ -1958,10 +2077,6 @@ func local_request_Runner_DownloadResponseFile_0(ctx context.Context, marshaler var protoReq TestCase var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - var ( val string ok bool @@ -1979,6 +2094,13 @@ func local_request_Runner_DownloadResponseFile_0(ctx context.Context, marshaler return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "response.body", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Runner_DownloadResponseFile_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.DownloadResponseFile(ctx, &protoReq) return msg, metadata, err @@ -2024,7 +2146,11 @@ func request_Runner_CreateStore_0(ctx context.Context, marshaler runtime.Marshal var protoReq Store var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2037,7 +2163,11 @@ func local_request_Runner_CreateStore_0(ctx context.Context, marshaler runtime.M var protoReq Store var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2050,7 +2180,11 @@ func request_Runner_UpdateStore_0(ctx context.Context, marshaler runtime.Marshal var protoReq Store var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2080,7 +2214,11 @@ func local_request_Runner_UpdateStore_0(ctx context.Context, marshaler runtime.M var protoReq Store var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2107,7 +2245,7 @@ func local_request_Runner_UpdateStore_0(ctx context.Context, marshaler runtime.M } var ( - filter_Runner_DeleteStore_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + filter_Runner_DeleteStore_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 2, 0, 0}, Check: []int{0, 1, 2, 2}} ) func request_Runner_DeleteStore_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -2180,7 +2318,11 @@ func request_Runner_VerifyStore_0(ctx context.Context, marshaler runtime.Marshal var protoReq SimpleQuery var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2193,7 +2335,11 @@ func local_request_Runner_VerifyStore_0(ctx context.Context, marshaler runtime.M var protoReq SimpleQuery var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2224,7 +2370,11 @@ func request_Runner_CreateSecret_0(ctx context.Context, marshaler runtime.Marsha var protoReq Secret var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2237,7 +2387,11 @@ func local_request_Runner_CreateSecret_0(ctx context.Context, marshaler runtime. var protoReq Secret var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2247,7 +2401,7 @@ func local_request_Runner_CreateSecret_0(ctx context.Context, marshaler runtime. } var ( - filter_Runner_DeleteSecret_0 = &utilities.DoubleArray{Encoding: map[string]int{"Name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + filter_Runner_DeleteSecret_0 = &utilities.DoubleArray{Encoding: map[string]int{"Name": 0}, Base: []int{1, 2, 0, 0}, Check: []int{0, 1, 2, 2}} ) func request_Runner_DeleteSecret_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -2320,7 +2474,11 @@ func request_Runner_UpdateSecret_0(ctx context.Context, marshaler runtime.Marsha var protoReq Secret var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2350,7 +2508,11 @@ func local_request_Runner_UpdateSecret_0(ctx context.Context, marshaler runtime. var protoReq Secret var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2380,7 +2542,11 @@ func request_Runner_PProf_0(ctx context.Context, marshaler runtime.Marshaler, cl var protoReq PProfRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2393,7 +2559,11 @@ func local_request_Runner_PProf_0(ctx context.Context, marshaler runtime.Marshal var protoReq PProfRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2406,7 +2576,11 @@ func request_RunnerExtension_Run_0(ctx context.Context, marshaler runtime.Marsha var protoReq TestSuiteWithCase var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2419,7 +2593,11 @@ func local_request_RunnerExtension_Run_0(ctx context.Context, marshaler runtime. var protoReq TestSuiteWithCase var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2432,7 +2610,11 @@ func request_Mock_Reload_0(ctx context.Context, marshaler runtime.Marshaler, cli var protoReq MockConfig var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2445,7 +2627,11 @@ func local_request_Mock_Reload_0(ctx context.Context, marshaler runtime.Marshale var protoReq MockConfig var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2476,7 +2662,11 @@ func request_DataServer_Query_0(ctx context.Context, marshaler runtime.Marshaler var protoReq DataQuery var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -2489,7 +2679,11 @@ func local_request_DataServer_Query_0(ctx context.Context, marshaler runtime.Mar var protoReq DataQuery var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -3375,7 +3569,7 @@ func RegisterRunnerHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser }) - mux.Handle("POST", pattern_Runner_DownloadResponseFile_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Runner_DownloadResponseFile_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream @@ -4657,7 +4851,7 @@ func RegisterRunnerHandlerClient(ctx context.Context, mux *runtime.ServeMux, cli }) - mux.Handle("POST", pattern_Runner_DownloadResponseFile_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Runner_DownloadResponseFile_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) diff --git a/pkg/server/server.proto b/pkg/server/server.proto index 7e945b55..c79d8671 100644 --- a/pkg/server/server.proto +++ b/pkg/server/server.proto @@ -232,8 +232,7 @@ service Runner { rpc DownloadResponseFile(TestCase) returns (FileData) { option (google.api.http) = { - post: "/api/v1/downloadFile/{response.body}" - body: "*" + get: "/api/v1/downloadFile/{response.body}" }; } // stores related interfaces diff --git a/pkg/server/server_grpc.pb.go b/pkg/server/server_grpc.pb.go index 529234cd..1ace149c 100644 --- a/pkg/server/server_grpc.pb.go +++ b/pkg/server/server_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.2.0 -// - protoc v4.25.1 +// - protoc v4.22.2 // source: pkg/server/server.proto package server diff --git a/pkg/testing/remote/loader.pb.go b/pkg/testing/remote/loader.pb.go index a4a4e281..9b556f3e 100644 --- a/pkg/testing/remote/loader.pb.go +++ b/pkg/testing/remote/loader.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v4.25.1 +// protoc v4.22.2 // source: pkg/testing/remote/loader.proto package remote diff --git a/pkg/testing/remote/loader_grpc.pb.go b/pkg/testing/remote/loader_grpc.pb.go index 6f09f1b3..49f77ebd 100644 --- a/pkg/testing/remote/loader_grpc.pb.go +++ b/pkg/testing/remote/loader_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.2.0 -// - protoc v4.25.1 +// - protoc v4.22.2 // source: pkg/testing/remote/loader.proto package remote diff --git a/pkg/util/default.go b/pkg/util/default.go index ef07480b..70f53a8d 100644 --- a/pkg/util/default.go +++ b/pkg/util/default.go @@ -81,6 +81,7 @@ const ( XML = "application/xml" OctetStream = "application/octet-stream" Image = "image/jpeg" + ImagePNG = "image/png" SVG = "image/svg+xml" Plain = "text/plain" CSS = "text/css" From 17842284be8a7a5772a570468d338e1a7cfd310a Mon Sep 17 00:00:00 2001 From: rick Date: Sun, 25 May 2025 16:54:39 +0800 Subject: [PATCH 4/5] support more image types --- console/atest-ui/src/views/TestCase.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/console/atest-ui/src/views/TestCase.vue b/console/atest-ui/src/views/TestCase.vue index 321c4549..010e9aaa 100644 --- a/console/atest-ui/src/views/TestCase.vue +++ b/console/atest-ui/src/views/TestCase.vue @@ -1386,7 +1386,7 @@ const renameTestCase = (name: string) => {
- +
From 71319b4fd577d8398918d7534c3f3af740f3fa97 Mon Sep 17 00:00:00 2001 From: rick Date: Mon, 26 May 2025 16:04:30 +0800 Subject: [PATCH 5/5] fix the ui unit testing --- console/atest-ui/package-lock.json | 12 ++++++------ console/atest-ui/src/views/__test__/net.spec.ts | 6 +++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/console/atest-ui/package-lock.json b/console/atest-ui/package-lock.json index 104fc6da..64c5324d 100644 --- a/console/atest-ui/package-lock.json +++ b/console/atest-ui/package-lock.json @@ -2710,9 +2710,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.3.tgz", - "integrity": "sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==", + "version": "29.5.14", + "resolved": "https://registry.npmmirror.com/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -14215,9 +14215,9 @@ } }, "@types/jest": { - "version": "29.5.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.3.tgz", - "integrity": "sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==", + "version": "29.5.14", + "resolved": "https://registry.npmmirror.com/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, "requires": { "expect": "^29.0.0", diff --git a/console/atest-ui/src/views/__test__/net.spec.ts b/console/atest-ui/src/views/__test__/net.spec.ts index 1d1840cc..c0d522ca 100644 --- a/console/atest-ui/src/views/__test__/net.spec.ts +++ b/console/atest-ui/src/views/__test__/net.spec.ts @@ -28,7 +28,11 @@ beforeEach(() => { describe('net', () => { test('GetVersion', () => { - fetchMock.mockResponseOnce(`{"version":"v0.0.2"}`) + fetchMock.mockResponseOnce(`{"version":"v0.0.2"}`, { + headers: { + 'Content-Type': 'application/json' + } + }) API.GetVersion((d) => { expect(d.version).toEqual('v0.0.2') })