package httpboomer import ( "encoding/json" "log" "strings" "testing" "github.com/imroc/req" "github.com/jmespath/go-jmespath" "github.com/httprunner/httpboomer/builtin" ) func NewResponseObject(t *testing.T, resp *req.Resp) *ResponseObject { // prepare response headers headers := make(map[string]string) for k, v := range resp.Response().Header { if len(v) > 0 { headers[k] = v[0] } } // prepare response cookies cookies := make(map[string]string) for _, cookie := range resp.Response().Cookies() { cookies[cookie.Name] = cookie.Value } // parse response body var body interface{} if err := json.Unmarshal(resp.Bytes(), &body); err != nil { log.Fatalf("[NewResponseObject] json.Unmarshal response body err: %v, body: %v", err, string(resp.Bytes())) return nil } respObjMeta := respObjMeta{ StatusCode: resp.Response().StatusCode, Headers: headers, Cookies: cookies, Body: body, } // convert respObjMeta to interface{} respObjMetaBytes, _ := json.Marshal(respObjMeta) var data interface{} if err := json.Unmarshal(respObjMetaBytes, &data); err != nil { log.Fatalf("[NewResponseObject] json.Unmarshal respObjMeta err: %v, respObjMetaBytes: %v", err, string(respObjMetaBytes)) return nil } return &ResponseObject{ t: t, respObjMeta: data, } } type respObjMeta struct { StatusCode int `json:"status_code"` Headers map[string]string `json:"headers"` Cookies map[string]string `json:"cookies"` Body interface{} `json:"body"` } type ResponseObject struct { t *testing.T respObjMeta interface{} validationResults map[string]interface{} } func (v *ResponseObject) Extract(extractors map[string]string) map[string]interface{} { if extractors == nil { return nil } extractMapping := make(map[string]interface{}) for key, value := range extractors { extractedValue := v.searchJmespath(value) log.Printf("[extract] %s => %v", value, extractedValue) log.Printf("[setVariable] %s = %v", key, extractedValue) extractMapping[key] = extractedValue } return extractMapping } 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, err = parseData(checkItem, variablesMapping) if err != nil { return err } } else { checkValue = v.searchJmespath(checkItem) } // get assert method assertMethod := validator.Assert assertFunc := builtin.Assertions[assertMethod] // parse expected value expectValue, err := parseData(validator.Expect, variablesMapping) if err != nil { return err } // do assertion result := assertFunc(v.t, expectValue, checkValue) log.Printf("[assert] %s <%s> %v => %v", checkItem, assertMethod, expectValue, result) if !result { v.t.Fail() } } return nil } func (v *ResponseObject) searchJmespath(expr string) interface{} { checkValue, err := jmespath.Search(expr, v.respObjMeta) if err != nil { log.Printf("[searchJmespath] jmespath.Search error: %v", err) return expr // jmespath not found, return the expression } return checkValue }