mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-05 23:51:25 +08:00
compat: convert testcase generated by HttpRunner
Change-Id: Iabc58b6796b7cd88f7b93e415b3d88dca85b288d
This commit is contained in:
+84
@@ -6,6 +6,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
@@ -29,6 +31,10 @@ func loadFromJSON(path string) (*TCase, error) {
|
|||||||
decoder := json.NewDecoder(bytes.NewReader(file))
|
decoder := json.NewDecoder(bytes.NewReader(file))
|
||||||
decoder.UseNumber()
|
decoder.UseNumber()
|
||||||
err = decoder.Decode(tc)
|
err = decoder.Decode(tc)
|
||||||
|
if err != nil {
|
||||||
|
return tc, err
|
||||||
|
}
|
||||||
|
err = convertCompatTestCase(tc)
|
||||||
return tc, err
|
return tc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,9 +54,87 @@ func loadFromYAML(path string) (*TCase, error) {
|
|||||||
|
|
||||||
tc := &TCase{}
|
tc := &TCase{}
|
||||||
err = yaml.Unmarshal(file, tc)
|
err = yaml.Unmarshal(file, tc)
|
||||||
|
if err != nil {
|
||||||
|
return tc, nil
|
||||||
|
}
|
||||||
|
err = convertCompatTestCase(tc)
|
||||||
return tc, err
|
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) {
|
func (tc *TCase) ToTestCase() (*TestCase, error) {
|
||||||
testCase := &TestCase{
|
testCase := &TestCase{
|
||||||
Config: tc.Config,
|
Config: tc.Config,
|
||||||
|
|||||||
@@ -97,6 +97,8 @@ type Request struct {
|
|||||||
Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"`
|
Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"`
|
||||||
Cookies map[string]string `json:"cookies,omitempty" yaml:"cookies,omitempty"`
|
Cookies map[string]string `json:"cookies,omitempty" yaml:"cookies,omitempty"`
|
||||||
Body interface{} `json:"body,omitempty" yaml:"body,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"`
|
Timeout float32 `json:"timeout,omitempty" yaml:"timeout,omitempty"`
|
||||||
AllowRedirects bool `json:"allow_redirects,omitempty" yaml:"allow_redirects,omitempty"`
|
AllowRedirects bool `json:"allow_redirects,omitempty" yaml:"allow_redirects,omitempty"`
|
||||||
Verify bool `json:"verify,omitempty" yaml:"verify,omitempty"`
|
Verify bool `json:"verify,omitempty" yaml:"verify,omitempty"`
|
||||||
@@ -113,17 +115,18 @@ type Validator struct {
|
|||||||
// TStep represents teststep data structure.
|
// TStep represents teststep data structure.
|
||||||
// Each step maybe two different type: make one HTTP request or reference another testcase.
|
// Each step maybe two different type: make one HTTP request or reference another testcase.
|
||||||
type TStep struct {
|
type TStep struct {
|
||||||
Name string `json:"name" yaml:"name"` // required
|
Name string `json:"name" yaml:"name"` // required
|
||||||
Request *Request `json:"request,omitempty" yaml:"request,omitempty"`
|
Request *Request `json:"request,omitempty" yaml:"request,omitempty"`
|
||||||
TestCase *TestCase `json:"testcase,omitempty" yaml:"testcase,omitempty"`
|
TestCase *TestCase `json:"testcase,omitempty" yaml:"testcase,omitempty"`
|
||||||
Transaction *Transaction `json:"transaction,omitempty" yaml:"transaction,omitempty"`
|
Transaction *Transaction `json:"transaction,omitempty" yaml:"transaction,omitempty"`
|
||||||
Rendezvous *Rendezvous `json:"rendezvous,omitempty" yaml:"rendezvous,omitempty"`
|
Rendezvous *Rendezvous `json:"rendezvous,omitempty" yaml:"rendezvous,omitempty"`
|
||||||
Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"`
|
Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"`
|
||||||
SetupHooks []string `json:"setup_hooks,omitempty" yaml:"setup_hooks,omitempty"`
|
SetupHooks []string `json:"setup_hooks,omitempty" yaml:"setup_hooks,omitempty"`
|
||||||
TeardownHooks []string `json:"teardown_hooks,omitempty" yaml:"teardown_hooks,omitempty"`
|
TeardownHooks []string `json:"teardown_hooks,omitempty" yaml:"teardown_hooks,omitempty"`
|
||||||
Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"`
|
Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"`
|
||||||
Validators []Validator `json:"validate,omitempty" yaml:"validate,omitempty"`
|
ValidatorsCompat []interface{} `json:"validate,omitempty" yaml:"validate,omitempty"`
|
||||||
Export []string `json:"export,omitempty" yaml:"export,omitempty"`
|
Export []string `json:"export,omitempty" yaml:"export,omitempty"`
|
||||||
|
Validators []Validator
|
||||||
}
|
}
|
||||||
|
|
||||||
type stepType string
|
type stepType string
|
||||||
@@ -301,11 +304,11 @@ type testCaseInOut struct {
|
|||||||
type testCaseSummary struct {
|
type testCaseSummary struct {
|
||||||
Name string `json:"name" yaml:"name"`
|
Name string `json:"name" yaml:"name"`
|
||||||
Success bool `json:"success" yaml:"success"`
|
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"`
|
Stat *testStepStat `json:"stat" yaml:"stat"`
|
||||||
Time *testCaseTime `json:"time" yaml:"time"`
|
Time *testCaseTime `json:"time" yaml:"time"`
|
||||||
InOut *testCaseInOut `json:"in_out" yaml:"in_out"`
|
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"`
|
Records []*stepData `json:"records" yaml:"records"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,7 +333,7 @@ type address struct {
|
|||||||
type SessionData struct {
|
type SessionData struct {
|
||||||
Success bool `json:"success" yaml:"success"`
|
Success bool `json:"success" yaml:"success"`
|
||||||
ReqResps *reqResps `json:"req_resps" yaml:"req_resps"`
|
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"`
|
Validators []*validationResult `json:"validators,omitempty" yaml:"validators,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user