From 2c2ffadf43dae07c127171c30a47c7c2d6713c14 Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Thu, 24 Feb 2022 22:30:53 +0800 Subject: [PATCH 01/10] fix: append jmespath info when assertion failed Change-Id: I576aa651086bb2c22343aa69f862955448e1b209 --- response.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/response.go b/response.go index 2861b119..d1780523 100644 --- a/response.go +++ b/response.go @@ -160,6 +160,7 @@ func (v *responseObject) Validate(validators []Validator, variablesMapping map[s } v.validationResults = append(v.validationResults, validResult) log.Info(). + Str("jmespath", validator.Check). Str("assertMethod", assertMethod). Interface("expectValue", expectValue). Interface("checkValue", checkValue). @@ -168,7 +169,8 @@ func (v *responseObject) Validate(validators []Validator, variablesMapping map[s if !result { v.t.Fail() return errors.New(fmt.Sprintf( - "do assertion failed, assertMethod: %v, checkValue: %v, expectValue: %v", + "do assertion failed, jmespath: %v, assertMethod: %v, checkValue: %v, expectValue: %v", + validator.Check, assertMethod, checkValue, expectValue, From 6aedf35dad1ce562fdc0e68b7fc9f5e2722614f1 Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Thu, 24 Feb 2022 22:32:16 +0800 Subject: [PATCH 02/10] compat: convert testcase generated by HttpRunner Change-Id: Iabc58b6796b7cd88f7b93e415b3d88dca85b288d --- convert.go | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ models.go | 31 +++++++++++--------- 2 files changed, 101 insertions(+), 14 deletions(-) diff --git a/convert.go b/convert.go index 396416f8..94ab969e 100644 --- a/convert.go +++ b/convert.go @@ -6,6 +6,8 @@ import ( "fmt" "os" "path/filepath" + "reflect" + "strings" "github.com/rs/zerolog/log" "gopkg.in/yaml.v3" @@ -29,6 +31,10 @@ func loadFromJSON(path string) (*TCase, error) { decoder := json.NewDecoder(bytes.NewReader(file)) decoder.UseNumber() err = decoder.Decode(tc) + if err != nil { + return tc, err + } + err = convertCompatTestCase(tc) return tc, err } @@ -48,9 +54,87 @@ func loadFromYAML(path string) (*TCase, error) { tc := &TCase{} err = yaml.Unmarshal(file, tc) + if err != nil { + return tc, nil + } + err = convertCompatTestCase(tc) return tc, err } +func convertCompatTestCase(tc *TCase) (err error) { + defer func() { + if p := recover(); p != nil { + err = fmt.Errorf("convert compat testcase error: %v", p) + } + }() + for _, step := range tc.TestSteps { + // 1. deal with body compatible with HttpRunner + if step.Request.Body == nil { + if step.Request.Json != nil { + // Content-Type is "application/json; charset=UTF-8" + step.Request.Body = step.Request.Json + } else if step.Request.Data != nil { + dataValue := reflect.ValueOf(step.Request.Data) + switch dataValue.Kind() { + case reflect.String: + // Content-Type is "text/plain" + step.Request.Body = dataValue.String() + case reflect.Map: + // Content-Type is "application/x-www-form-urlencoded" + var paramsList []string + mapRange := dataValue.MapRange() + for mapRange.Next() { + paramsList = append(paramsList, fmt.Sprintf("%s=%s", mapRange.Key(), mapRange.Value())) + } + step.Request.Body = strings.Join(paramsList, "&") + default: + log.Error().Msgf("[convert compat testcase] unexpected body type: %v", dataValue.Kind()) + } + } + } + + // 2. deal with validators compatible with HttpRunner + for _, iValidator := range step.ValidatorsCompat { + validatorMap, ok := iValidator.(map[string]interface{}) + if !ok || len(validatorMap) == 0 { + // pass invalid or empty validator + continue + } + // check priority: HRP > HttpRunner + validator := Validator{} + if len(validatorMap) == 4 { + // HRP validator format + validator.Check = validatorMap["check"].(string) + validator.Assert = validatorMap["assert"].(string) + validator.Expect = validatorMap["expect"] + if msg, exist := validatorMap["msg"]; exist { + validator.Message = msg.(string) + } + } else if len(validatorMap) == 1 { + // HttpRunner validator format + validatorValue := reflect.ValueOf(validatorMap) + assertMethod := validatorValue.MapKeys()[0] + iValidatorContent := validatorValue.MapIndex(assertMethod).Interface().([]interface{}) + validator.Check = iValidatorContent[0].(string) + validator.Assert = assertMethod.String() + validator.Expect = iValidatorContent[1] + } else { + log.Error().Msgf("[convert compat testcase] unexpected validator format: %v", validatorMap) + } + // deal with headers format in HttpRunner + // e.g. headers.Content-Type => headers.\"Content-Type\" + if strings.Contains(validator.Check, "headers.") && + !strings.Contains(validator.Check, "\"") && + strings.Contains(validator.Check, "-") { + replacedHeader := fmt.Sprintf("headers.\"%s\"", validator.Check[len("headers."):]) + validator.Check = replacedHeader + } + step.Validators = append(step.Validators, validator) + } + } + return err +} + func (tc *TCase) ToTestCase() (*TestCase, error) { testCase := &TestCase{ Config: tc.Config, diff --git a/models.go b/models.go index f5e43cfa..050e2600 100644 --- a/models.go +++ b/models.go @@ -97,6 +97,8 @@ type Request struct { Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"` Cookies map[string]string `json:"cookies,omitempty" yaml:"cookies,omitempty"` Body interface{} `json:"body,omitempty" yaml:"body,omitempty"` + Json interface{} `json:"json,omitempty" yaml:"json,omitempty"` + Data interface{} `json:"data,omitempty" yaml:"data,omitempty"` Timeout float32 `json:"timeout,omitempty" yaml:"timeout,omitempty"` AllowRedirects bool `json:"allow_redirects,omitempty" yaml:"allow_redirects,omitempty"` Verify bool `json:"verify,omitempty" yaml:"verify,omitempty"` @@ -113,17 +115,18 @@ type Validator struct { // TStep represents teststep data structure. // Each step maybe two different type: make one HTTP request or reference another testcase. type TStep struct { - Name string `json:"name" yaml:"name"` // required - Request *Request `json:"request,omitempty" yaml:"request,omitempty"` - TestCase *TestCase `json:"testcase,omitempty" yaml:"testcase,omitempty"` - Transaction *Transaction `json:"transaction,omitempty" yaml:"transaction,omitempty"` - Rendezvous *Rendezvous `json:"rendezvous,omitempty" yaml:"rendezvous,omitempty"` - Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"` - SetupHooks []string `json:"setup_hooks,omitempty" yaml:"setup_hooks,omitempty"` - TeardownHooks []string `json:"teardown_hooks,omitempty" yaml:"teardown_hooks,omitempty"` - Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"` - Validators []Validator `json:"validate,omitempty" yaml:"validate,omitempty"` - Export []string `json:"export,omitempty" yaml:"export,omitempty"` + Name string `json:"name" yaml:"name"` // required + Request *Request `json:"request,omitempty" yaml:"request,omitempty"` + TestCase *TestCase `json:"testcase,omitempty" yaml:"testcase,omitempty"` + Transaction *Transaction `json:"transaction,omitempty" yaml:"transaction,omitempty"` + Rendezvous *Rendezvous `json:"rendezvous,omitempty" yaml:"rendezvous,omitempty"` + Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"` + SetupHooks []string `json:"setup_hooks,omitempty" yaml:"setup_hooks,omitempty"` + TeardownHooks []string `json:"teardown_hooks,omitempty" yaml:"teardown_hooks,omitempty"` + Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"` + ValidatorsCompat []interface{} `json:"validate,omitempty" yaml:"validate,omitempty"` + Export []string `json:"export,omitempty" yaml:"export,omitempty"` + Validators []Validator } type stepType string @@ -301,11 +304,11 @@ type testCaseInOut struct { type testCaseSummary struct { Name string `json:"name" yaml:"name"` Success bool `json:"success" yaml:"success"` - CaseId string `json:"case_id,omitempty" yaml:"case_id,omitempty"` //TODO + CaseId string `json:"case_id,omitempty" yaml:"case_id,omitempty"` // TODO Stat *testStepStat `json:"stat" yaml:"stat"` Time *testCaseTime `json:"time" yaml:"time"` InOut *testCaseInOut `json:"in_out" yaml:"in_out"` - Log string `json:"log,omitempty" yaml:"log,omitempty"` //TODO + Log string `json:"log,omitempty" yaml:"log,omitempty"` // TODO Records []*stepData `json:"records" yaml:"records"` } @@ -330,7 +333,7 @@ type address struct { type SessionData struct { Success bool `json:"success" yaml:"success"` ReqResps *reqResps `json:"req_resps" yaml:"req_resps"` - Address *address `json:"address,omitempty" yaml:"address,omitempty"` //TODO + Address *address `json:"address,omitempty" yaml:"address,omitempty"` // TODO Validators []*validationResult `json:"validators,omitempty" yaml:"validators,omitempty"` } From b4ada29bab55a736b731148f98c6cc4c2eec5b8a Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Mon, 28 Feb 2022 16:51:31 +0800 Subject: [PATCH 03/10] fix: func convertCompatTestCase --- convert.go | 55 +++++++++++++++++++---------------------------- docs/CHANGELOG.md | 1 + 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/convert.go b/convert.go index 94ab969e..dc2717e8 100644 --- a/convert.go +++ b/convert.go @@ -6,7 +6,6 @@ import ( "fmt" "os" "path/filepath" - "reflect" "strings" "github.com/rs/zerolog/log" @@ -71,25 +70,9 @@ func convertCompatTestCase(tc *TCase) (err error) { // 1. deal with body compatible with HttpRunner if step.Request.Body == nil { if step.Request.Json != nil { - // Content-Type is "application/json; charset=UTF-8" step.Request.Body = step.Request.Json } else if step.Request.Data != nil { - dataValue := reflect.ValueOf(step.Request.Data) - switch dataValue.Kind() { - case reflect.String: - // Content-Type is "text/plain" - step.Request.Body = dataValue.String() - case reflect.Map: - // Content-Type is "application/x-www-form-urlencoded" - var paramsList []string - mapRange := dataValue.MapRange() - for mapRange.Next() { - paramsList = append(paramsList, fmt.Sprintf("%s=%s", mapRange.Key(), mapRange.Value())) - } - step.Request.Body = strings.Join(paramsList, "&") - default: - log.Error().Msgf("[convert compat testcase] unexpected body type: %v", dataValue.Kind()) - } + step.Request.Body = step.Request.Data } } @@ -110,31 +93,37 @@ func convertCompatTestCase(tc *TCase) (err error) { if msg, exist := validatorMap["msg"]; exist { validator.Message = msg.(string) } + convertCompatHeader(&validator) + step.Validators = append(step.Validators, validator) } else if len(validatorMap) == 1 { // HttpRunner validator format - validatorValue := reflect.ValueOf(validatorMap) - assertMethod := validatorValue.MapKeys()[0] - iValidatorContent := validatorValue.MapIndex(assertMethod).Interface().([]interface{}) - validator.Check = iValidatorContent[0].(string) - validator.Assert = assertMethod.String() - validator.Expect = iValidatorContent[1] + for assertMethod, iValidatorContent := range validatorMap { + checkAndExpect := iValidatorContent.([]interface{}) + validator.Check = checkAndExpect[0].(string) + validator.Assert = assertMethod + validator.Expect = checkAndExpect[1] + } + convertCompatHeader(&validator) + step.Validators = append(step.Validators, validator) } else { log.Error().Msgf("[convert compat testcase] unexpected validator format: %v", validatorMap) } - // deal with headers format in HttpRunner - // e.g. headers.Content-Type => headers.\"Content-Type\" - if strings.Contains(validator.Check, "headers.") && - !strings.Contains(validator.Check, "\"") && - strings.Contains(validator.Check, "-") { - replacedHeader := fmt.Sprintf("headers.\"%s\"", validator.Check[len("headers."):]) - validator.Check = replacedHeader - } - step.Validators = append(step.Validators, validator) } } return err } +// convertCompatHeader deals with headers format in HttpRunner +// e.g. headers.Content-Type => headers.\"Content-Type\" +func convertCompatHeader(validator *Validator) { + if strings.Contains(validator.Check, "headers.") && + !strings.Contains(validator.Check, "\"") && + strings.Contains(validator.Check, "-") { + replacedHeader := fmt.Sprintf("headers.\"%s\"", validator.Check[len("headers."):]) + validator.Check = replacedHeader + } +} + func (tc *TCase) ToTestCase() (*TestCase, error) { testCase := &TestCase{ Config: tc.Config, diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index cc54b050..c8d6ab2a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -3,6 +3,7 @@ ## v0.6.3 (2022-02-22) - feat: support customized setup/teardown hooks (variable assignment not supported) +- compat: support testcase generated by HttpRunner ## v0.6.2 (2022-02-22) From 54d938286e16d44dca31f34dbd81bc520285560d Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Mon, 28 Feb 2022 17:39:09 +0800 Subject: [PATCH 04/10] fix: nil step.Request pointer --- convert.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/convert.go b/convert.go index dc2717e8..2efc28c3 100644 --- a/convert.go +++ b/convert.go @@ -68,7 +68,7 @@ func convertCompatTestCase(tc *TCase) (err error) { }() for _, step := range tc.TestSteps { // 1. deal with body compatible with HttpRunner - if step.Request.Body == nil { + if step.Request != nil && step.Request.Body == nil { if step.Request.Json != nil { step.Request.Body = step.Request.Json } else if step.Request.Data != nil { From c144d14a4ed92d579817dac423e97ce62c61a79a Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Mon, 28 Feb 2022 17:45:28 +0800 Subject: [PATCH 05/10] fix: return checkExpr info when do assertion failed --- response.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/response.go b/response.go index d1780523..e4eecd8d 100644 --- a/response.go +++ b/response.go @@ -160,7 +160,7 @@ func (v *responseObject) Validate(validators []Validator, variablesMapping map[s } v.validationResults = append(v.validationResults, validResult) log.Info(). - Str("jmespath", validator.Check). + Str("checkExpr", validator.Check). Str("assertMethod", assertMethod). Interface("expectValue", expectValue). Interface("checkValue", checkValue). @@ -169,7 +169,7 @@ func (v *responseObject) Validate(validators []Validator, variablesMapping map[s if !result { v.t.Fail() return errors.New(fmt.Sprintf( - "do assertion failed, jmespath: %v, assertMethod: %v, checkValue: %v, expectValue: %v", + "do assertion failed, checkExpr: %v, assertMethod: %v, checkValue: %v, expectValue: %v", validator.Check, assertMethod, checkValue, From b04a57eb0974389df62aadeaf7f0a4e2ea1ce0bd Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Tue, 1 Mar 2022 18:05:13 +0800 Subject: [PATCH 06/10] fix: change Validators type, check json body format --- convert.go | 13 +++++++------ internal/har2case/core.go | 2 +- internal/har2case/core_test.go | 7 ++++--- models.go | 23 +++++++++++------------ response.go | 8 ++++++-- runner.go | 9 +++++++++ step_test.go | 4 ++-- 7 files changed, 40 insertions(+), 26 deletions(-) diff --git a/convert.go b/convert.go index 2efc28c3..6800d3fd 100644 --- a/convert.go +++ b/convert.go @@ -67,9 +67,10 @@ func convertCompatTestCase(tc *TCase) (err error) { } }() for _, step := range tc.TestSteps { - // 1. deal with body compatible with HttpRunner + // 1. deal with request body compatible with HttpRunner if step.Request != nil && step.Request.Body == nil { if step.Request.Json != nil { + step.Request.Headers["Content-Type"] = "application/json; charset=utf-8" step.Request.Body = step.Request.Json } else if step.Request.Data != nil { step.Request.Body = step.Request.Data @@ -77,9 +78,9 @@ func convertCompatTestCase(tc *TCase) (err error) { } // 2. deal with validators compatible with HttpRunner - for _, iValidator := range step.ValidatorsCompat { - validatorMap, ok := iValidator.(map[string]interface{}) - if !ok || len(validatorMap) == 0 { + for i, iValidator := range step.Validators { + validatorMap := iValidator.(map[string]interface{}) + if len(validatorMap) == 0 { // pass invalid or empty validator continue } @@ -94,7 +95,7 @@ func convertCompatTestCase(tc *TCase) (err error) { validator.Message = msg.(string) } convertCompatHeader(&validator) - step.Validators = append(step.Validators, validator) + step.Validators[i] = validator } else if len(validatorMap) == 1 { // HttpRunner validator format for assertMethod, iValidatorContent := range validatorMap { @@ -104,7 +105,7 @@ func convertCompatTestCase(tc *TCase) (err error) { validator.Expect = checkAndExpect[1] } convertCompatHeader(&validator) - step.Validators = append(step.Validators, validator) + step.Validators[i] = validator } else { log.Error().Msgf("[convert compat testcase] unexpected validator format: %v", validatorMap) } diff --git a/internal/har2case/core.go b/internal/har2case/core.go index ed2ce221..b22ba0a0 100644 --- a/internal/har2case/core.go +++ b/internal/har2case/core.go @@ -145,7 +145,7 @@ func (h *har) prepareTestStep(entry *Entry) (*hrp.TStep, error) { step := &tStep{ TStep: hrp.TStep{ Request: &hrp.Request{}, - Validators: make([]hrp.Validator, 0), + Validators: make([]interface{}, 0), }, } if err := step.makeRequestMethod(entry); err != nil { diff --git a/internal/har2case/core_test.go b/internal/har2case/core_test.go index 96b0c232..9060893f 100644 --- a/internal/har2case/core_test.go +++ b/internal/har2case/core_test.go @@ -1,6 +1,7 @@ package har2case import ( + "github.com/httprunner/hrp" "testing" "github.com/stretchr/testify/assert" @@ -98,13 +99,13 @@ func TestMakeTestCase(t *testing.T) { } // make validators - if !assert.Equal(t, "status_code", tCase.TestSteps[0].Validators[0].Check) { + if validator, ok := tCase.TestSteps[0].Validators[0].(hrp.Validator); !ok || !assert.Equal(t, "status_code", validator.Check) { t.Fail() } - if !assert.Equal(t, "headers.\"Content-Type\"", tCase.TestSteps[0].Validators[1].Check) { + if validator, ok := tCase.TestSteps[0].Validators[1].(hrp.Validator); !ok || !assert.Equal(t, "headers.\"Content-Type\"", validator.Check) { t.Fail() } - if !assert.Equal(t, "body.url", tCase.TestSteps[0].Validators[2].Check) { + if validator, ok := tCase.TestSteps[0].Validators[2].(hrp.Validator); !ok || !assert.Equal(t, "body.url", validator.Check) { t.Fail() } } diff --git a/models.go b/models.go index 050e2600..9b524c1e 100644 --- a/models.go +++ b/models.go @@ -115,18 +115,17 @@ type Validator struct { // TStep represents teststep data structure. // Each step maybe two different type: make one HTTP request or reference another testcase. type TStep struct { - Name string `json:"name" yaml:"name"` // required - Request *Request `json:"request,omitempty" yaml:"request,omitempty"` - TestCase *TestCase `json:"testcase,omitempty" yaml:"testcase,omitempty"` - Transaction *Transaction `json:"transaction,omitempty" yaml:"transaction,omitempty"` - Rendezvous *Rendezvous `json:"rendezvous,omitempty" yaml:"rendezvous,omitempty"` - Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"` - SetupHooks []string `json:"setup_hooks,omitempty" yaml:"setup_hooks,omitempty"` - TeardownHooks []string `json:"teardown_hooks,omitempty" yaml:"teardown_hooks,omitempty"` - Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"` - ValidatorsCompat []interface{} `json:"validate,omitempty" yaml:"validate,omitempty"` - Export []string `json:"export,omitempty" yaml:"export,omitempty"` - Validators []Validator + Name string `json:"name" yaml:"name"` // required + Request *Request `json:"request,omitempty" yaml:"request,omitempty"` + TestCase *TestCase `json:"testcase,omitempty" yaml:"testcase,omitempty"` + Transaction *Transaction `json:"transaction,omitempty" yaml:"transaction,omitempty"` + Rendezvous *Rendezvous `json:"rendezvous,omitempty" yaml:"rendezvous,omitempty"` + Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"` + SetupHooks []string `json:"setup_hooks,omitempty" yaml:"setup_hooks,omitempty"` + TeardownHooks []string `json:"teardown_hooks,omitempty" yaml:"teardown_hooks,omitempty"` + Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"` + Validators []interface{} `json:"validate,omitempty" yaml:"validate,omitempty"` + Export []string `json:"export,omitempty" yaml:"export,omitempty"` } type stepType string diff --git a/response.go b/response.go index e4eecd8d..1085c382 100644 --- a/response.go +++ b/response.go @@ -114,8 +114,12 @@ func (v *responseObject) Extract(extractors map[string]string) map[string]interf return extractMapping } -func (v *responseObject) Validate(validators []Validator, variablesMapping map[string]interface{}) (err error) { - for _, validator := range validators { +func (v *responseObject) Validate(iValidators []interface{}, variablesMapping map[string]interface{}) (err error) { + for _, iValidator := range iValidators { + validator, ok := iValidator.(Validator) + if !ok { + return errors.New("validator type error") + } // parse check value checkItem := validator.Check var checkValue interface{} diff --git a/runner.go b/runner.go index 7dd0178b..75a15ee5 100644 --- a/runner.go +++ b/runner.go @@ -703,6 +703,15 @@ func (r *caseRunner) runStepRequest(step *TStep) (stepResult *stepData, err erro if err != nil { return stepResult, err } + // check request body format if Content-Type specified as application/json + if strings.HasPrefix(req.Header.Get("Content-Type"), "application/json") { + switch data.(type) { + case bool, float64, string, map[string]interface{}, []interface{}, nil: + break + default: + return stepResult, errors.Errorf("request body type inconsistent with Content-Type: %v", req.Header.Get("Content-Type")) + } + } requestMap["body"] = data var dataBytes []byte switch vv := data.(type) { diff --git a/step_test.go b/step_test.go index f180983f..1610cd29 100644 --- a/step_test.go +++ b/step_test.go @@ -42,7 +42,7 @@ func TestRunRequestGetToStruct(t *testing.T) { if tStep.Request.Cookies["user"] != "debugtalk" { t.Fatalf("tStep.Request.Cookies mismatch") } - if tStep.Validators[0].Check != "status_code" || tStep.Validators[0].Expect != 200 { + if validator, ok := tStep.Validators[0].(Validator); !ok || validator.Check != "status_code" || validator.Expect != 200 { t.Fatalf("tStep.Validators mismatch") } } @@ -67,7 +67,7 @@ func TestRunRequestPostDataToStruct(t *testing.T) { if tStep.Request.Body != "a=1&b=2" { t.Fatalf("tStep.Request.Data mismatch") } - if tStep.Validators[0].Check != "status_code" || tStep.Validators[0].Expect != 200 { + if validator, ok := tStep.Validators[0].(Validator); !ok || validator.Check != "status_code" || validator.Expect != 200 { t.Fatalf("tStep.Validators mismatch") } } From 1613eb4724cc40b83fe3b714ca8a616441900099 Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Tue, 1 Mar 2022 20:58:24 +0800 Subject: [PATCH 07/10] fix: len(validatorMap) == 3 --- convert.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/convert.go b/convert.go index 6800d3fd..c83b36e7 100644 --- a/convert.go +++ b/convert.go @@ -80,13 +80,9 @@ func convertCompatTestCase(tc *TCase) (err error) { // 2. deal with validators compatible with HttpRunner for i, iValidator := range step.Validators { validatorMap := iValidator.(map[string]interface{}) - if len(validatorMap) == 0 { - // pass invalid or empty validator - continue - } // check priority: HRP > HttpRunner validator := Validator{} - if len(validatorMap) == 4 { + if len(validatorMap) == 4 || len(validatorMap) == 3 { // HRP validator format validator.Check = validatorMap["check"].(string) validator.Assert = validatorMap["assert"].(string) @@ -108,6 +104,7 @@ func convertCompatTestCase(tc *TCase) (err error) { step.Validators[i] = validator } else { log.Error().Msgf("[convert compat testcase] unexpected validator format: %v", validatorMap) + step.Validators[i] = validator } } } From a3fafd8c40dc213f096b1cf8007b95648c4b61b1 Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Thu, 3 Mar 2022 18:31:10 +0800 Subject: [PATCH 08/10] fix compat convert, add unittest --- convert.go | 29 +++---- docs/CHANGELOG.md | 2 +- examples/compat_test.go | 24 ++++++ examples/demo_httprunner.json | 135 +++++++++++++++++++++++++++++++++ examples/demo_httprunner.yaml | 81 ++++++++++++++++++++ internal/har2case/core_test.go | 12 ++- response.go | 15 ++++ response_test.go | 27 +++++++ step_test.go | 6 +- 9 files changed, 305 insertions(+), 26 deletions(-) create mode 100644 examples/compat_test.go create mode 100644 examples/demo_httprunner.json create mode 100644 examples/demo_httprunner.yaml diff --git a/convert.go b/convert.go index c83b36e7..7bbad0c6 100644 --- a/convert.go +++ b/convert.go @@ -6,7 +6,6 @@ import ( "fmt" "os" "path/filepath" - "strings" "github.com/rs/zerolog/log" "gopkg.in/yaml.v3" @@ -80,48 +79,40 @@ func convertCompatTestCase(tc *TCase) (err error) { // 2. deal with validators compatible with HttpRunner for i, iValidator := range step.Validators { validatorMap := iValidator.(map[string]interface{}) - // check priority: HRP > HttpRunner validator := Validator{} - if len(validatorMap) == 4 || len(validatorMap) == 3 { + _, checkExisted := validatorMap["check"] + _, assertExisted := validatorMap["assert"] + _, expectExisted := validatorMap["expect"] + // check priority: HRP > HttpRunner + if checkExisted && assertExisted && expectExisted { // HRP validator format validator.Check = validatorMap["check"].(string) validator.Assert = validatorMap["assert"].(string) validator.Expect = validatorMap["expect"] - if msg, exist := validatorMap["msg"]; exist { + if msg, existed := validatorMap["msg"]; existed { validator.Message = msg.(string) } - convertCompatHeader(&validator) step.Validators[i] = validator } else if len(validatorMap) == 1 { // HttpRunner validator format for assertMethod, iValidatorContent := range validatorMap { checkAndExpect := iValidatorContent.([]interface{}) + if len(checkAndExpect) != 2 { + return fmt.Errorf("unexpected validator format: %v", validatorMap) + } validator.Check = checkAndExpect[0].(string) validator.Assert = assertMethod validator.Expect = checkAndExpect[1] } - convertCompatHeader(&validator) step.Validators[i] = validator } else { - log.Error().Msgf("[convert compat testcase] unexpected validator format: %v", validatorMap) - step.Validators[i] = validator + return fmt.Errorf("unexpected validator format: %v", validatorMap) } } } return err } -// convertCompatHeader deals with headers format in HttpRunner -// e.g. headers.Content-Type => headers.\"Content-Type\" -func convertCompatHeader(validator *Validator) { - if strings.Contains(validator.Check, "headers.") && - !strings.Contains(validator.Check, "\"") && - strings.Contains(validator.Check, "-") { - replacedHeader := fmt.Sprintf("headers.\"%s\"", validator.Check[len("headers."):]) - validator.Check = replacedHeader - } -} - func (tc *TCase) ToTestCase() (*TestCase, error) { testCase := &TestCase{ Config: tc.Config, diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index c8d6ab2a..f00f32c2 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,6 @@ # Release History -## v0.6.3 (2022-02-22) +## v0.6.3 (2022-03-03) - feat: support customized setup/teardown hooks (variable assignment not supported) - compat: support testcase generated by HttpRunner diff --git a/examples/compat_test.go b/examples/compat_test.go new file mode 100644 index 00000000..76edc545 --- /dev/null +++ b/examples/compat_test.go @@ -0,0 +1,24 @@ +package examples + +import ( + "testing" + + "github.com/httprunner/hrp" +) + +const demoHttpRunnerJSONPath = "../examples/demo_httprunner.json" +const demoHttpRunnerYAMLPath = "../examples/demo_httprunner.yaml" + +func TestCompatTestCase(t *testing.T) { + testcaseFromJSON := &hrp.TestCasePath{Path: demoHttpRunnerJSONPath} + err := hrp.NewRunner(t).Run(testcaseFromJSON) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } + + testcaseFromYAML := &hrp.TestCasePath{Path: demoHttpRunnerYAMLPath} + err = hrp.NewRunner(t).Run(testcaseFromYAML) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } +} diff --git a/examples/demo_httprunner.json b/examples/demo_httprunner.json new file mode 100644 index 00000000..de017c96 --- /dev/null +++ b/examples/demo_httprunner.json @@ -0,0 +1,135 @@ +{ + "config": { + "name": "testcase description", + "variables": {}, + "verify": false + }, + "teststeps": [ + { + "name": "/get", + "request": { + "url": "https://postman-echo.com/get", + "params": { + "foo1": "HDnY8", + "foo2": "34.5" + }, + "method": "GET", + "headers": { + "Host": "postman-echo.com", + "User-Agent": "HttpRunnerPlus", + "Accept-Encoding": "gzip" + } + }, + "validate": [ + { + "eq": [ + "status_code", + 200 + ] + }, + { + "eq": [ + "headers.Content-Type", + "application/json; charset=utf-8" + ] + }, + { + "eq": [ + "body.url", + "https://postman-echo.com/get?foo1=HDnY8&foo2=34.5" + ] + } + ] + }, + { + "name": "/post", + "request": { + "url": "https://postman-echo.com/post", + "method": "POST", + "cookies": { + "sails.sid": "s%3Az_LpglkKxTvJ_eHVUH6V67drKp0AGWW-.PidabaXOnatLRP47hVyqqepl6BdrpEQzRlJQXtbIiwk" + }, + "headers": { + "Host": "postman-echo.com", + "User-Agent": "Go-http-client/1.1", + "Content-Length": "28", + "Content-Type": "application/json; charset=UTF-8", + "Cookie": "sails.sid=s%3Az_LpglkKxTvJ_eHVUH6V67drKp0AGWW-.PidabaXOnatLRP47hVyqqepl6BdrpEQzRlJQXtbIiwk", + "Accept-Encoding": "gzip" + }, + "json": { + "foo1": "HDnY8", + "foo2": 12.3 + } + }, + "validate": [ + { + "eq": [ + "status_code", + 200 + ] + }, + { + "eq": [ + "headers.Content-Type", + "application/json; charset=utf-8" + ] + }, + { + "eq": [ + "body.url", + "https://postman-echo.com/post" + ] + } + ] + }, + { + "name": "/post", + "request": { + "url": "https://postman-echo.com/post", + "method": "POST", + "cookies": { + "sails.sid": "s%3AS5e7w0zQ0xAsCwh9L8T6R7QLYCO7_gtD.r8%2B2w9IWqEIfuVkrZjnxzm2xADIk34zKAWXRPapr%2FAw" + }, + "headers": { + "Host": "postman-echo.com", + "User-Agent": "Go-http-client/1.1", + "Content-Length": "20", + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", + "Cookie": "sails.sid=s%3AS5e7w0zQ0xAsCwh9L8T6R7QLYCO7_gtD.r8%2B2w9IWqEIfuVkrZjnxzm2xADIk34zKAWXRPapr%2FAw", + "Accept-Encoding": "gzip" + }, + "data": { + "foo1": "HDnY8", + "foo2": "12.3" + } + }, + "validate": [ + { + "eq": [ + "status_code", + 200 + ] + }, + { + "eq": [ + "headers.Content-Type", + "application/json; charset=utf-8" + ] + }, + { + "eq": [ + "body.data", + "" + ] + }, + { + "eq": [ + "body.url", + "https://postman-echo.com/post" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/demo_httprunner.yaml b/examples/demo_httprunner.yaml new file mode 100644 index 00000000..0f39723f --- /dev/null +++ b/examples/demo_httprunner.yaml @@ -0,0 +1,81 @@ +config: + name: testcase description + variables: {} + verify: false +teststeps: +- name: /get + request: + headers: + Accept-Encoding: gzip + Host: postman-echo.com + User-Agent: HttpRunnerPlus + method: GET + params: + foo1: HDnY8 + foo2: '34.5' + url: https://postman-echo.com/get + validate: + - eq: + - status_code + - 200 + - eq: + - headers.Content-Type + - application/json; charset=utf-8 + - eq: + - body.url + - https://postman-echo.com/get?foo1=HDnY8&foo2=34.5 +- name: /post + request: + cookies: + sails.sid: s%3Az_LpglkKxTvJ_eHVUH6V67drKp0AGWW-.PidabaXOnatLRP47hVyqqepl6BdrpEQzRlJQXtbIiwk + headers: + Accept-Encoding: gzip + Content-Length: '28' + Content-Type: application/json; charset=UTF-8 + Cookie: sails.sid=s%3Az_LpglkKxTvJ_eHVUH6V67drKp0AGWW-.PidabaXOnatLRP47hVyqqepl6BdrpEQzRlJQXtbIiwk + Host: postman-echo.com + User-Agent: Go-http-client/1.1 + json: + foo1: HDnY8 + foo2: 12.3 + method: POST + url: https://postman-echo.com/post + validate: + - eq: + - status_code + - 200 + - eq: + - headers.Content-Type + - application/json; charset=utf-8 + - eq: + - body.url + - https://postman-echo.com/post +- name: /post + request: + cookies: + sails.sid: s%3AS5e7w0zQ0xAsCwh9L8T6R7QLYCO7_gtD.r8%2B2w9IWqEIfuVkrZjnxzm2xADIk34zKAWXRPapr%2FAw + data: + foo1: HDnY8 + foo2: '12.3' + headers: + Accept-Encoding: gzip + Content-Length: '20' + Content-Type: application/x-www-form-urlencoded; charset=UTF-8 + Cookie: sails.sid=s%3AS5e7w0zQ0xAsCwh9L8T6R7QLYCO7_gtD.r8%2B2w9IWqEIfuVkrZjnxzm2xADIk34zKAWXRPapr%2FAw + Host: postman-echo.com + User-Agent: Go-http-client/1.1 + method: POST + url: https://postman-echo.com/post + validate: + - eq: + - status_code + - 200 + - eq: + - headers.Content-Type + - application/json; charset=utf-8 + - eq: + - body.data + - '' + - eq: + - body.url + - https://postman-echo.com/post diff --git a/internal/har2case/core_test.go b/internal/har2case/core_test.go index 9060893f..db71fbd2 100644 --- a/internal/har2case/core_test.go +++ b/internal/har2case/core_test.go @@ -1,10 +1,11 @@ package har2case import ( - "github.com/httprunner/hrp" "testing" "github.com/stretchr/testify/assert" + + "github.com/httprunner/hrp" ) var ( @@ -99,13 +100,16 @@ func TestMakeTestCase(t *testing.T) { } // make validators - if validator, ok := tCase.TestSteps[0].Validators[0].(hrp.Validator); !ok || !assert.Equal(t, "status_code", validator.Check) { + validator, ok := tCase.TestSteps[0].Validators[0].(hrp.Validator) + if !ok || !assert.Equal(t, "status_code", validator.Check) { t.Fail() } - if validator, ok := tCase.TestSteps[0].Validators[1].(hrp.Validator); !ok || !assert.Equal(t, "headers.\"Content-Type\"", validator.Check) { + validator, ok = tCase.TestSteps[0].Validators[1].(hrp.Validator) + if !ok || !assert.Equal(t, "headers.\"Content-Type\"", validator.Check) { t.Fail() } - if validator, ok := tCase.TestSteps[0].Validators[2].(hrp.Validator); !ok || !assert.Equal(t, "body.url", validator.Check) { + validator, ok = tCase.TestSteps[0].Validators[2].(hrp.Validator) + if !ok || !assert.Equal(t, "body.url", validator.Check) { t.Fail() } } diff --git a/response.go b/response.go index 1085c382..3ed46dc0 100644 --- a/response.go +++ b/response.go @@ -185,6 +185,7 @@ func (v *responseObject) Validate(iValidators []interface{}, variablesMapping ma } func (v *responseObject) searchJmespath(expr string) interface{} { + expr = convertJmespath(expr) checkValue, err := jmespath.Search(expr, v.respObjMeta) if err != nil { log.Error().Str("expr", expr).Err(err).Msg("search jmespath failed") @@ -200,6 +201,20 @@ func (v *responseObject) searchJmespath(expr string) interface{} { return checkValue } +// convertJmespath deals with check expression including hyphen +func convertJmespath(checkExpr string) string { + if strings.Contains(checkExpr, textExtractorSubRegexp) { + return checkExpr + } + checkItems := strings.Split(checkExpr, ".") + for i, checkItem := range checkItems { + if strings.Contains(checkItem, "-") && !strings.Contains(checkItem, "\"") { + checkItems[i] = fmt.Sprintf("\"%s\"", checkItem) + } + } + return strings.Join(checkItems, ".") +} + func (v *responseObject) searchRegexp(expr string) interface{} { respMap, ok := v.respObjMeta.(map[string]interface{}) if !ok { diff --git a/response_test.go b/response_test.go index cfd3143d..89aa9003 100644 --- a/response_test.go +++ b/response_test.go @@ -33,3 +33,30 @@ func TestSearchRegexp(t *testing.T) { } } } + +func Test_convertJmespath(t *testing.T) { + exprs := []struct { + before string + after string + }{ + // normal check expression + {"a.b.c", "a.b.c"}, + {"headers.\"Content-Type\"", "headers.\"Content-Type\""}, + // check expression using regex + {"covering (.*) testing,", "covering (.*) testing,"}, + {" (.*) a-b-c", " (.*) a-b-c"}, + // abnormal check expression + {"-", "\"-\""}, + {"b-c", "\"b-c\""}, + {"a.b-c.d", "a.\"b-c\".d"}, + {"a-b.c-d", "\"a-b\".\"c-d\""}, + {"\"a-b\".c-d", "\"a-b\".\"c-d\""}, + {"headers.Content-Type", "headers.\"Content-Type\""}, + {"body.I-am-a-Key.name", "body.\"I-am-a-Key\".name"}, + } + for _, expr := range exprs { + if !assert.Equal(t, convertJmespath(expr.before), expr.after) { + t.Fail() + } + } +} diff --git a/step_test.go b/step_test.go index 1610cd29..64d03785 100644 --- a/step_test.go +++ b/step_test.go @@ -42,7 +42,8 @@ func TestRunRequestGetToStruct(t *testing.T) { if tStep.Request.Cookies["user"] != "debugtalk" { t.Fatalf("tStep.Request.Cookies mismatch") } - if validator, ok := tStep.Validators[0].(Validator); !ok || validator.Check != "status_code" || validator.Expect != 200 { + validator, ok := tStep.Validators[0].(Validator) + if !ok || validator.Check != "status_code" || validator.Expect != 200 { t.Fatalf("tStep.Validators mismatch") } } @@ -67,7 +68,8 @@ func TestRunRequestPostDataToStruct(t *testing.T) { if tStep.Request.Body != "a=1&b=2" { t.Fatalf("tStep.Request.Data mismatch") } - if validator, ok := tStep.Validators[0].(Validator); !ok || validator.Check != "status_code" || validator.Expect != 200 { + validator, ok := tStep.Validators[0].(Validator) + if !ok || validator.Check != "status_code" || validator.Expect != 200 { t.Fatalf("tStep.Validators mismatch") } } From 026131b6047e7b65aed3c99588765c2970353070 Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Thu, 3 Mar 2022 19:29:35 +0800 Subject: [PATCH 09/10] move convertCheckExpr to convertCompatTestCase --- convert.go | 17 +++++++++++++++++ convert_test.go | 27 +++++++++++++++++++++++++++ response.go | 15 --------------- response_test.go | 27 --------------------------- 4 files changed, 44 insertions(+), 42 deletions(-) diff --git a/convert.go b/convert.go index 7bbad0c6..8ef22ea7 100644 --- a/convert.go +++ b/convert.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/rs/zerolog/log" "gopkg.in/yaml.v3" @@ -92,6 +93,7 @@ func convertCompatTestCase(tc *TCase) (err error) { if msg, existed := validatorMap["msg"]; existed { validator.Message = msg.(string) } + validator.Check = convertCheckExpr(validator.Check) step.Validators[i] = validator } else if len(validatorMap) == 1 { // HttpRunner validator format @@ -104,6 +106,7 @@ func convertCompatTestCase(tc *TCase) (err error) { validator.Assert = assertMethod validator.Expect = checkAndExpect[1] } + validator.Check = convertCheckExpr(validator.Check) step.Validators[i] = validator } else { return fmt.Errorf("unexpected validator format: %v", validatorMap) @@ -113,6 +116,20 @@ func convertCompatTestCase(tc *TCase) (err error) { return err } +// convertCheckExpr deals with check expression including hyphen +func convertCheckExpr(checkExpr string) string { + if strings.Contains(checkExpr, textExtractorSubRegexp) { + return checkExpr + } + checkItems := strings.Split(checkExpr, ".") + for i, checkItem := range checkItems { + if strings.Contains(checkItem, "-") && !strings.Contains(checkItem, "\"") { + checkItems[i] = fmt.Sprintf("\"%s\"", checkItem) + } + } + return strings.Join(checkItems, ".") +} + func (tc *TCase) ToTestCase() (*TestCase, error) { testCase := &TestCase{ Config: tc.Config, diff --git a/convert_test.go b/convert_test.go index 53a88e56..75aea861 100644 --- a/convert_test.go +++ b/convert_test.go @@ -34,3 +34,30 @@ func TestLoadCase(t *testing.T) { t.Fail() } } + +func Test_convertCheckExpr(t *testing.T) { + exprs := []struct { + before string + after string + }{ + // normal check expression + {"a.b.c", "a.b.c"}, + {"headers.\"Content-Type\"", "headers.\"Content-Type\""}, + // check expression using regex + {"covering (.*) testing,", "covering (.*) testing,"}, + {" (.*) a-b-c", " (.*) a-b-c"}, + // abnormal check expression + {"-", "\"-\""}, + {"b-c", "\"b-c\""}, + {"a.b-c.d", "a.\"b-c\".d"}, + {"a-b.c-d", "\"a-b\".\"c-d\""}, + {"\"a-b\".c-d", "\"a-b\".\"c-d\""}, + {"headers.Content-Type", "headers.\"Content-Type\""}, + {"body.I-am-a-Key.name", "body.\"I-am-a-Key\".name"}, + } + for _, expr := range exprs { + if !assert.Equal(t, convertCheckExpr(expr.before), expr.after) { + t.Fail() + } + } +} diff --git a/response.go b/response.go index 3ed46dc0..1085c382 100644 --- a/response.go +++ b/response.go @@ -185,7 +185,6 @@ func (v *responseObject) Validate(iValidators []interface{}, variablesMapping ma } func (v *responseObject) searchJmespath(expr string) interface{} { - expr = convertJmespath(expr) checkValue, err := jmespath.Search(expr, v.respObjMeta) if err != nil { log.Error().Str("expr", expr).Err(err).Msg("search jmespath failed") @@ -201,20 +200,6 @@ func (v *responseObject) searchJmespath(expr string) interface{} { return checkValue } -// convertJmespath deals with check expression including hyphen -func convertJmespath(checkExpr string) string { - if strings.Contains(checkExpr, textExtractorSubRegexp) { - return checkExpr - } - checkItems := strings.Split(checkExpr, ".") - for i, checkItem := range checkItems { - if strings.Contains(checkItem, "-") && !strings.Contains(checkItem, "\"") { - checkItems[i] = fmt.Sprintf("\"%s\"", checkItem) - } - } - return strings.Join(checkItems, ".") -} - func (v *responseObject) searchRegexp(expr string) interface{} { respMap, ok := v.respObjMeta.(map[string]interface{}) if !ok { diff --git a/response_test.go b/response_test.go index 89aa9003..cfd3143d 100644 --- a/response_test.go +++ b/response_test.go @@ -33,30 +33,3 @@ func TestSearchRegexp(t *testing.T) { } } } - -func Test_convertJmespath(t *testing.T) { - exprs := []struct { - before string - after string - }{ - // normal check expression - {"a.b.c", "a.b.c"}, - {"headers.\"Content-Type\"", "headers.\"Content-Type\""}, - // check expression using regex - {"covering (.*) testing,", "covering (.*) testing,"}, - {" (.*) a-b-c", " (.*) a-b-c"}, - // abnormal check expression - {"-", "\"-\""}, - {"b-c", "\"b-c\""}, - {"a.b-c.d", "a.\"b-c\".d"}, - {"a-b.c-d", "\"a-b\".\"c-d\""}, - {"\"a-b\".c-d", "\"a-b\".\"c-d\""}, - {"headers.Content-Type", "headers.\"Content-Type\""}, - {"body.I-am-a-Key.name", "body.\"I-am-a-Key\".name"}, - } - for _, expr := range exprs { - if !assert.Equal(t, convertJmespath(expr.before), expr.after) { - t.Fail() - } - } -} From 8459e3fab4a6f29a88f4d0aeebca7dd75c4affcf Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Thu, 3 Mar 2022 19:43:36 +0800 Subject: [PATCH 10/10] fix example JSON/YAML file path --- examples/compat_test.go | 5 +++-- examples/rendezvous_test.go | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/compat_test.go b/examples/compat_test.go index 76edc545..85a61b01 100644 --- a/examples/compat_test.go +++ b/examples/compat_test.go @@ -6,8 +6,9 @@ import ( "github.com/httprunner/hrp" ) -const demoHttpRunnerJSONPath = "../examples/demo_httprunner.json" -const demoHttpRunnerYAMLPath = "../examples/demo_httprunner.yaml" +// generated by examples/har/demo.har using HttpRunner v3.1.6 +const demoHttpRunnerJSONPath = "demo_httprunner.json" +const demoHttpRunnerYAMLPath = "demo_httprunner.yaml" func TestCompatTestCase(t *testing.T) { testcaseFromJSON := &hrp.TestCasePath{Path: demoHttpRunnerJSONPath} diff --git a/examples/rendezvous_test.go b/examples/rendezvous_test.go index 1d004a2e..409167f0 100644 --- a/examples/rendezvous_test.go +++ b/examples/rendezvous_test.go @@ -7,6 +7,8 @@ import ( "github.com/httprunner/hrp/internal/builtin" ) +const rendezvousTestJSONPath = "rendezvous_test.json" + var rendezvousTestcase = &hrp.TestCase{ Config: hrp.NewConfig("run request with functions"). SetBaseURL("https://postman-echo.com"). @@ -59,7 +61,7 @@ func TestRendezvousDump2JSON(t *testing.T) { if err != nil { t.Fatalf("ToTCase error: %v", err) } - err = builtin.Dump2JSON(tCase, "rendezvous_test.json") + err = builtin.Dump2JSON(tCase, rendezvousTestJSONPath) if err != nil { t.Fatalf("dump to json error: %v", err) }