refactor: check errors

This commit is contained in:
debugtalk
2021-10-04 13:20:31 +08:00
parent a25827fa8a
commit 4fcc7fd590
4 changed files with 160 additions and 63 deletions

View File

@@ -35,13 +35,16 @@ func buildURL(baseURL, stepURL string) string {
return uStep.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) parsedHeaders := make(map[string]string)
headers := parseData(rawHeaders, variablesMapping).(map[string]interface{}) headers, err := parseData(rawHeaders, variablesMapping)
for k, v := range headers { if err != nil {
return rawHeaders, err
}
for k, v := range headers.(map[string]interface{}) {
parsedHeaders[k] = convertString(v) parsedHeaders[k] = convertString(v)
} }
return parsedHeaders return parsedHeaders, nil
} }
func convertString(raw interface{}) string { 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) rawValue := reflect.ValueOf(raw)
switch rawValue.Kind() { switch rawValue.Kind() {
case reflect.String: case reflect.String:
@@ -64,23 +67,33 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) interfa
case reflect.Slice: case reflect.Slice:
parsedSlice := make([]interface{}, rawValue.Len()) parsedSlice := make([]interface{}, rawValue.Len())
for i := 0; i < rawValue.Len(); i++ { 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{} case reflect.Map: // convert any map to map[string]interface{}
parsedMap := make(map[string]interface{}) parsedMap := make(map[string]interface{})
for _, k := range rawValue.MapKeys() { 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) v := rawValue.MapIndex(k)
parsedValue := parseData(v.Interface(), variablesMapping) parsedValue, err := parseData(v.Interface(), variablesMapping)
if err != nil {
return raw, err
}
key := convertString(parsedKey) key := convertString(parsedKey)
parsedMap[key] = parsedValue parsedMap[key] = parsedValue
} }
return parsedMap return parsedMap, nil
default: default:
// other types, e.g. nil, int, float, bool // other types, e.g. nil, int, float, bool
return raw return raw, nil
} }
} }
@@ -97,7 +110,7 @@ var (
) )
// parseString parse string with variables // 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 matchStartPosition := 0
parsedString := "" parsedString := ""
remainedString := raw remainedString := raw
@@ -134,18 +147,21 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{
argsStr := funcMatched[2] argsStr := funcMatched[2]
arguments, err := parseFunctionArguments(argsStr) arguments, err := parseFunctionArguments(argsStr)
if err != nil { if err != nil {
return raw return raw, err
} }
parsedArgs := parseData(arguments, variablesMapping).([]interface{}) parsedArgs, err := parseData(arguments, variablesMapping)
result, err := callFunc(funcName, parsedArgs...)
if err != nil { if err != nil {
return raw return raw, err
}
result, err := callFunc(funcName, parsedArgs.([]interface{})...)
if err != nil {
return raw, err
} }
if funcMatched[0] == raw { if funcMatched[0] == raw {
// raw_string is a function, e.g. "${add_one(3)}", return its eval value directly // 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" // 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 { } else {
varName = varMatched[2] // match $var 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 { if fmt.Sprintf("${%s}", varName) == raw || fmt.Sprintf("$%s", varName) == raw {
// raw string is a variable, $var or ${var}, return its value directly // raw string is a variable, $var or ${var}, return its value directly
return varValue return varValue, nil
} }
matchStartPosition += len(varMatched[0]) matchStartPosition += len(varMatched[0])
@@ -183,7 +202,7 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{
break break
} }
return parsedString return parsedString, nil
} }
// merge two variables mapping, the first variables have higher priority // 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) return variables, fmt.Errorf("variable not defined: %v", undefinedVars)
} }
parsedValue := parseData(varValue, parsedVariables) parsedValue, err := parseData(varValue, parsedVariables)
if parsedValue == nil { if err != nil {
continue continue
} }
parsedVariables[varName] = parsedValue parsedVariables[varName] = parsedValue

View File

@@ -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)"},
{"func1($var_1, ${var_3})", "func1(abc, 123)"}, {"func1($var_1, ${var_3})", "func1(abc, 123)"},
// TODO: fix compatibility with python version // TODO: fix compatibility with python version
{"abc$var_4", "abcmap[a:1]"}, // "abc{'a': 1}" {"abc$var_4", "abcmap[a:1]"}, // "abc{'a': 1}"
{"abc$var_5", "abctrue"}, // "abcTrue" {"abc$var_5", "abctrue"}, // "abcTrue"
{"/api/$SECRET_KEY", "/api/<nil>"}, // raise error
} }
for _, data := range testData { 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() t.Fail()
} }
} }
@@ -202,7 +229,11 @@ func TestParseDataStringWithVariablesAbnormal(t *testing.T) {
} }
for _, data := range testData { 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() t.Fail()
} }
} }
@@ -228,7 +259,11 @@ func TestParseDataMapWithVariables(t *testing.T) {
} }
for _, data := range testData { 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() t.Fail()
} }
} }
@@ -257,7 +292,11 @@ func TestParseHeaders(t *testing.T) {
} }
for _, data := range testData { 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() t.Fail()
} }
} }
@@ -292,14 +331,14 @@ func TestMergeVariables(t *testing.T) {
func TestCallFunction(t *testing.T) { func TestCallFunction(t *testing.T) {
// call function without arguments // call function without arguments
_, err := callFunc("get_timestamp") _, err := callFunc("get_timestamp")
if !assert.Nil(t, err) { if !assert.NoError(t, err) {
t.Fail() t.Fail()
} }
// call function with one argument // call function with one argument
timeStart := time.Now() timeStart := time.Now()
_, err = callFunc("sleep", 1) _, err = callFunc("sleep", 1)
if !assert.Nil(t, err) { if !assert.NoError(t, err) {
t.Fail() t.Fail()
} }
if !assert.Greater(t, time.Since(timeStart), time.Duration(1)*time.Second) { 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 // call function with one argument
result, err := callFunc("gen_random_string", 10) result, err := callFunc("gen_random_string", 10)
if !assert.Nil(t, err) { if !assert.NoError(t, err) {
t.Fail() t.Fail()
} }
if !assert.Equal(t, 10, len(result.(string))) { if !assert.Equal(t, 10, len(result.(string))) {
@@ -317,7 +356,7 @@ func TestCallFunction(t *testing.T) {
// call function with two argument // call function with two argument
result, err = callFunc("max", float64(10), 9.99) result, err = callFunc("max", float64(10), 9.99)
if !assert.Nil(t, err) { if !assert.NoError(t, err) {
t.Fail() t.Fail()
} }
if !assert.Equal(t, float64(10), result.(float64)) { if !assert.Equal(t, float64(10), result.(float64)) {
@@ -345,7 +384,7 @@ func TestLiteralEval(t *testing.T) {
for _, data := range testData { for _, data := range testData {
value, err := literalEval(data.expr) value, err := literalEval(data.expr)
if !assert.Nil(t, err) { if !assert.NoError(t, err) {
t.Fail() t.Fail()
} }
if !assert.Equal(t, data.expect, value) { if !assert.Equal(t, data.expect, value) {
@@ -375,7 +414,7 @@ func TestParseFunctionArguments(t *testing.T) {
for _, data := range testData { for _, data := range testData {
value, err := parseFunctionArguments(data.expr) value, err := parseFunctionArguments(data.expr)
if !assert.Nil(t, err) { if !assert.NoError(t, err) {
t.Fail() t.Fail()
} }
if !assert.Equal(t, data.expect, value) { if !assert.Equal(t, data.expect, value) {
@@ -391,28 +430,43 @@ func TestParseDataStringWithFunctions(t *testing.T) {
"b": 3.45, "b": 3.45,
} }
if !assert.Len(t, parseData("${gen_random_string(5)}", variablesMapping), 5) { testData1 := []struct {
t.Fail() expr string
} expect interface{}
if !assert.Len(t, parseData("${gen_random_string($n)}", variablesMapping), 5) { }{
t.Fail() {"${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) { for _, data := range testData1 {
t.Fail() value, err := parseData(data.expr, variablesMapping)
} if !assert.NoError(t, err) {
if !assert.Len(t, parseData("123${gen_random_string($n)}abc", variablesMapping), 11) { t.Fail()
t.Fail() }
if !assert.Equal(t, data.expect, len(value.(string))) {
t.Fail()
}
} }
if !assert.Equal(t, parseData("${max($a, $b)}", variablesMapping), 12.3) { testData2 := []struct {
t.Fail() 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() for _, data := range testData2 {
} value, err := parseData(data.expr, variablesMapping)
if !assert.Equal(t, parseData("abc${max($a, 3.45)}123", variablesMapping), "abc12.3123") { if !assert.NoError(t, err) {
t.Fail() t.Fail()
}
if !assert.Equal(t, data.expect, value) {
t.Fail()
}
} }
} }
@@ -454,7 +508,7 @@ func TestParseVariables(t *testing.T) {
for _, data := range testData { for _, data := range testData {
value, err := parseVariables(data.rawVars) value, err := parseVariables(data.rawVars)
if !assert.Nil(t, err) { if !assert.NoError(t, err) {
t.Fail() t.Fail()
} }
if !assert.Equal(t, data.expectVars, value) { if !assert.Equal(t, data.expectVars, value) {

View File

@@ -86,14 +86,17 @@ func (v *ResponseObject) Extract(extractors map[string]string) map[string]interf
return extractMapping 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 { for _, validator := range validators {
// parse check value // parse check value
checkItem := validator.Check checkItem := validator.Check
var checkValue interface{} var checkValue interface{}
if strings.Contains(checkItem, "$") { if strings.Contains(checkItem, "$") {
// reference variable // reference variable
checkValue = parseData(checkItem, variablesMapping) checkValue, err = parseData(checkItem, variablesMapping)
if err != nil {
return err
}
} else { } else {
checkValue = v.searchJmespath(checkItem) checkValue = v.searchJmespath(checkItem)
} }
@@ -103,7 +106,10 @@ func (v *ResponseObject) Validate(validators []TValidator, variablesMapping map[
assertFunc := builtin.Assertions[assertMethod] assertFunc := builtin.Assertions[assertMethod]
// parse expected value // parse expected value
expectValue := parseData(validator.Expect, variablesMapping) expectValue, err := parseData(validator.Expect, variablesMapping)
if err != nil {
return err
}
// do assertion // do assertion
result := assertFunc(v.t, expectValue, checkValue) result := assertFunc(v.t, expectValue, checkValue)

View File

@@ -114,19 +114,31 @@ func (r *Runner) runStepRequest(step *TStep) (stepData *StepData, err error) {
// prepare request args // prepare request args
var v []interface{} var v []interface{}
if len(step.Request.Headers) > 0 { 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)) v = append(v, req.Header(headers))
} }
if len(step.Request.Params) > 0 { 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{}))) v = append(v, req.Param(params.(map[string]interface{})))
} }
if step.Request.Data != nil { 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) v = append(v, data)
} }
if step.Request.JSON != nil { 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)) v = append(v, req.BodyJSON(jsonData))
} }
@@ -186,11 +198,17 @@ func (r *Runner) parseConfig(config *TConfig) error {
config.Variables = parsedVariables config.Variables = parsedVariables
// parse config name // 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) config.Name = convertString(parsedName)
// parse config base url // 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) config.BaseURL = convertString(parsedBaseURL)
return nil return nil