From 4fcc7fd5906b432420a64976f413da7fd798b2ef Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 4 Oct 2021 13:20:31 +0800 Subject: [PATCH] refactor: check errors --- parser.go | 65 +++++++++++++++++---------- parser_test.go | 116 ++++++++++++++++++++++++++++++++++++------------- response.go | 12 +++-- runner.go | 30 ++++++++++--- 4 files changed, 160 insertions(+), 63 deletions(-) diff --git a/parser.go b/parser.go index 51f816f6..f5179cb3 100644 --- a/parser.go +++ b/parser.go @@ -35,13 +35,16 @@ func buildURL(baseURL, stepURL string) string { return uStep.String() } -func parseHeaders(rawHeaders map[string]string, variablesMapping map[string]interface{}) map[string]string { +func parseHeaders(rawHeaders map[string]string, variablesMapping map[string]interface{}) (map[string]string, error) { parsedHeaders := make(map[string]string) - headers := parseData(rawHeaders, variablesMapping).(map[string]interface{}) - for k, v := range headers { + headers, err := parseData(rawHeaders, variablesMapping) + if err != nil { + return rawHeaders, err + } + for k, v := range headers.(map[string]interface{}) { parsedHeaders[k] = convertString(v) } - return parsedHeaders + return parsedHeaders, nil } func convertString(raw interface{}) string { @@ -54,7 +57,7 @@ func convertString(raw interface{}) string { } } -func parseData(raw interface{}, variablesMapping map[string]interface{}) interface{} { +func parseData(raw interface{}, variablesMapping map[string]interface{}) (interface{}, error) { rawValue := reflect.ValueOf(raw) switch rawValue.Kind() { case reflect.String: @@ -64,23 +67,33 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) interfa case reflect.Slice: parsedSlice := make([]interface{}, rawValue.Len()) for i := 0; i < rawValue.Len(); i++ { - parsedSlice[i] = parseData(rawValue.Index(i).Interface(), variablesMapping) + parsedValue, err := parseData(rawValue.Index(i).Interface(), variablesMapping) + if err != nil { + return raw, err + } + parsedSlice[i] = parsedValue } - return parsedSlice + return parsedSlice, nil case reflect.Map: // convert any map to map[string]interface{} parsedMap := make(map[string]interface{}) for _, k := range rawValue.MapKeys() { - parsedKey := parseString(k.String(), variablesMapping) + parsedKey, err := parseString(k.String(), variablesMapping) + if err != nil { + return raw, err + } v := rawValue.MapIndex(k) - parsedValue := parseData(v.Interface(), variablesMapping) + parsedValue, err := parseData(v.Interface(), variablesMapping) + if err != nil { + return raw, err + } key := convertString(parsedKey) parsedMap[key] = parsedValue } - return parsedMap + return parsedMap, nil default: // other types, e.g. nil, int, float, bool - return raw + return raw, nil } } @@ -97,7 +110,7 @@ var ( ) // parseString parse string with variables -func parseString(raw string, variablesMapping map[string]interface{}) interface{} { +func parseString(raw string, variablesMapping map[string]interface{}) (interface{}, error) { matchStartPosition := 0 parsedString := "" remainedString := raw @@ -134,18 +147,21 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ argsStr := funcMatched[2] arguments, err := parseFunctionArguments(argsStr) if err != nil { - return raw + return raw, err } - parsedArgs := parseData(arguments, variablesMapping).([]interface{}) - - result, err := callFunc(funcName, parsedArgs...) + parsedArgs, err := parseData(arguments, variablesMapping) if err != nil { - return raw + return raw, err + } + + result, err := callFunc(funcName, parsedArgs.([]interface{})...) + if err != nil { + return raw, err } if funcMatched[0] == raw { // raw_string is a function, e.g. "${add_one(3)}", return its eval value directly - return result + return result, nil } // raw_string contains one or many functions, e.g. "abc${add_one(3)}def" @@ -165,11 +181,14 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ } else { varName = varMatched[2] // match $var } - varValue := variablesMapping[varName] + varValue, ok := variablesMapping[varName] + if !ok { + return raw, fmt.Errorf("variable %s not found", varName) + } if fmt.Sprintf("${%s}", varName) == raw || fmt.Sprintf("$%s", varName) == raw { // raw string is a variable, $var or ${var}, return its value directly - return varValue + return varValue, nil } matchStartPosition += len(varMatched[0]) @@ -183,7 +202,7 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ break } - return parsedString + return parsedString, nil } // merge two variables mapping, the first variables have higher priority @@ -353,8 +372,8 @@ func parseVariables(variables map[string]interface{}) (map[string]interface{}, e return variables, fmt.Errorf("variable not defined: %v", undefinedVars) } - parsedValue := parseData(varValue, parsedVariables) - if parsedValue == nil { + parsedValue, err := parseData(varValue, parsedVariables) + if err != nil { continue } parsedVariables[varName] = parsedValue diff --git a/parser_test.go b/parser_test.go index a1f735e3..2943cce4 100644 --- a/parser_test.go +++ b/parser_test.go @@ -156,13 +156,40 @@ func TestParseDataStringWithVariables(t *testing.T) { {"func1($var_1, $var_3)", "func1(abc, 123)"}, {"func1($var_1, ${var_3})", "func1(abc, 123)"}, // TODO: fix compatibility with python version - {"abc$var_4", "abcmap[a:1]"}, // "abc{'a': 1}" - {"abc$var_5", "abctrue"}, // "abcTrue" - {"/api/$SECRET_KEY", "/api/"}, // raise error + {"abc$var_4", "abcmap[a:1]"}, // "abc{'a': 1}" + {"abc$var_5", "abctrue"}, // "abcTrue" } for _, data := range testData { - if !assert.Equal(t, data.expect, parseData(data.expr, variablesMapping)) { + parsedData, err := parseData(data.expr, variablesMapping) + if !assert.NoError(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expect, parsedData) { + t.Fail() + } + } +} + +func TestParseDataStringWithUndefinedVariables(t *testing.T) { + variablesMapping := map[string]interface{}{ + "var_1": "abc", + "var_2": "def", + } + + testData := []struct { + expr string + expect interface{} + }{ + {"/api/$SECRET_KEY", "/api/$SECRET_KEY"}, // raise error + } + + for _, data := range testData { + parsedData, err := parseData(data.expr, variablesMapping) + if !assert.Error(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expect, parsedData) { t.Fail() } } @@ -202,7 +229,11 @@ func TestParseDataStringWithVariablesAbnormal(t *testing.T) { } for _, data := range testData { - if !assert.Equal(t, data.expect, parseData(data.expr, variablesMapping)) { + parsedData, err := parseData(data.expr, variablesMapping) + if !assert.NoError(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expect, parsedData) { t.Fail() } } @@ -228,7 +259,11 @@ func TestParseDataMapWithVariables(t *testing.T) { } for _, data := range testData { - if !assert.Equal(t, data.expect, parseData(data.expr, variablesMapping)) { + parsedData, err := parseData(data.expr, variablesMapping) + if !assert.NoError(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expect, parsedData) { t.Fail() } } @@ -257,7 +292,11 @@ func TestParseHeaders(t *testing.T) { } for _, data := range testData { - if !assert.Equal(t, data.expectHeaders, parseHeaders(data.rawHeaders, variablesMapping)) { + parsedHeaders, err := parseHeaders(data.rawHeaders, variablesMapping) + if !assert.NoError(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expectHeaders, parsedHeaders) { t.Fail() } } @@ -292,14 +331,14 @@ func TestMergeVariables(t *testing.T) { func TestCallFunction(t *testing.T) { // call function without arguments _, err := callFunc("get_timestamp") - if !assert.Nil(t, err) { + if !assert.NoError(t, err) { t.Fail() } // call function with one argument timeStart := time.Now() _, err = callFunc("sleep", 1) - if !assert.Nil(t, err) { + if !assert.NoError(t, err) { t.Fail() } if !assert.Greater(t, time.Since(timeStart), time.Duration(1)*time.Second) { @@ -308,7 +347,7 @@ func TestCallFunction(t *testing.T) { // call function with one argument result, err := callFunc("gen_random_string", 10) - if !assert.Nil(t, err) { + if !assert.NoError(t, err) { t.Fail() } if !assert.Equal(t, 10, len(result.(string))) { @@ -317,7 +356,7 @@ func TestCallFunction(t *testing.T) { // call function with two argument result, err = callFunc("max", float64(10), 9.99) - if !assert.Nil(t, err) { + if !assert.NoError(t, err) { t.Fail() } if !assert.Equal(t, float64(10), result.(float64)) { @@ -345,7 +384,7 @@ func TestLiteralEval(t *testing.T) { for _, data := range testData { value, err := literalEval(data.expr) - if !assert.Nil(t, err) { + if !assert.NoError(t, err) { t.Fail() } if !assert.Equal(t, data.expect, value) { @@ -375,7 +414,7 @@ func TestParseFunctionArguments(t *testing.T) { for _, data := range testData { value, err := parseFunctionArguments(data.expr) - if !assert.Nil(t, err) { + if !assert.NoError(t, err) { t.Fail() } if !assert.Equal(t, data.expect, value) { @@ -391,28 +430,43 @@ func TestParseDataStringWithFunctions(t *testing.T) { "b": 3.45, } - if !assert.Len(t, parseData("${gen_random_string(5)}", variablesMapping), 5) { - t.Fail() - } - if !assert.Len(t, parseData("${gen_random_string($n)}", variablesMapping), 5) { - t.Fail() + testData1 := []struct { + expr string + expect interface{} + }{ + {"${gen_random_string(5)}", 5}, + {"${gen_random_string($n)}", 5}, + {"123${gen_random_string(5)}abc", 11}, + {"123${gen_random_string($n)}abc", 11}, } - if !assert.Len(t, parseData("123${gen_random_string(5)}abc", variablesMapping), 11) { - t.Fail() - } - if !assert.Len(t, parseData("123${gen_random_string($n)}abc", variablesMapping), 11) { - t.Fail() + for _, data := range testData1 { + value, err := parseData(data.expr, variablesMapping) + if !assert.NoError(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expect, len(value.(string))) { + t.Fail() + } } - if !assert.Equal(t, parseData("${max($a, $b)}", variablesMapping), 12.3) { - t.Fail() + testData2 := []struct { + expr string + expect interface{} + }{ + {"${max($a, $b)}", 12.3}, + {"abc${max($a, $b)}123", "abc12.3123"}, + {"abc${max($a, 3.45)}123", "abc12.3123"}, } - if !assert.Equal(t, parseData("abc${max($a, $b)}123", variablesMapping), "abc12.3123") { - t.Fail() - } - if !assert.Equal(t, parseData("abc${max($a, 3.45)}123", variablesMapping), "abc12.3123") { - t.Fail() + + for _, data := range testData2 { + value, err := parseData(data.expr, variablesMapping) + if !assert.NoError(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expect, value) { + t.Fail() + } } } @@ -454,7 +508,7 @@ func TestParseVariables(t *testing.T) { for _, data := range testData { value, err := parseVariables(data.rawVars) - if !assert.Nil(t, err) { + if !assert.NoError(t, err) { t.Fail() } if !assert.Equal(t, data.expectVars, value) { diff --git a/response.go b/response.go index 60c01b6b..beaca56c 100644 --- a/response.go +++ b/response.go @@ -86,14 +86,17 @@ func (v *ResponseObject) Extract(extractors map[string]string) map[string]interf return extractMapping } -func (v *ResponseObject) Validate(validators []TValidator, variablesMapping map[string]interface{}) error { +func (v *ResponseObject) Validate(validators []TValidator, variablesMapping map[string]interface{}) (err error) { for _, validator := range validators { // parse check value checkItem := validator.Check var checkValue interface{} if strings.Contains(checkItem, "$") { // reference variable - checkValue = parseData(checkItem, variablesMapping) + checkValue, err = parseData(checkItem, variablesMapping) + if err != nil { + return err + } } else { checkValue = v.searchJmespath(checkItem) } @@ -103,7 +106,10 @@ func (v *ResponseObject) Validate(validators []TValidator, variablesMapping map[ assertFunc := builtin.Assertions[assertMethod] // parse expected value - expectValue := parseData(validator.Expect, variablesMapping) + expectValue, err := parseData(validator.Expect, variablesMapping) + if err != nil { + return err + } // do assertion result := assertFunc(v.t, expectValue, checkValue) diff --git a/runner.go b/runner.go index 0af2b28d..6565f120 100644 --- a/runner.go +++ b/runner.go @@ -114,19 +114,31 @@ func (r *Runner) runStepRequest(step *TStep) (stepData *StepData, err error) { // prepare request args var v []interface{} if len(step.Request.Headers) > 0 { - headers := parseHeaders(step.Request.Headers, step.Variables) + headers, err := parseHeaders(step.Request.Headers, step.Variables) + if err != nil { + return nil, err + } v = append(v, req.Header(headers)) } if len(step.Request.Params) > 0 { - params := parseData(step.Request.Params, step.Variables) + params, err := parseData(step.Request.Params, step.Variables) + if err != nil { + return nil, err + } v = append(v, req.Param(params.(map[string]interface{}))) } if step.Request.Data != nil { - data := parseData(step.Request.Data, step.Variables) + data, err := parseData(step.Request.Data, step.Variables) + if err != nil { + return nil, err + } v = append(v, data) } if step.Request.JSON != nil { - jsonData := parseData(step.Request.JSON, step.Variables) + jsonData, err := parseData(step.Request.JSON, step.Variables) + if err != nil { + return nil, err + } v = append(v, req.BodyJSON(jsonData)) } @@ -186,11 +198,17 @@ func (r *Runner) parseConfig(config *TConfig) error { config.Variables = parsedVariables // parse config name - parsedName := parseString(config.Name, config.Variables) + parsedName, err := parseString(config.Name, config.Variables) + if err != nil { + return err + } config.Name = convertString(parsedName) // parse config base url - parsedBaseURL := parseString(config.BaseURL, config.Variables) + parsedBaseURL, err := parseString(config.BaseURL, config.Variables) + if err != nil { + return err + } config.BaseURL = convertString(parsedBaseURL) return nil