Skip to content

Commit

Permalink
feat(executor/http): feature/xml systemout (#720)
Browse files Browse the repository at this point in the history
* output: xml format now will print systemout if errors occur. User can enable to print always with verbose set higher than 1

Signed-off-by: Ivan Velasco <[email protected]>

* http: Result object expanded to include "systemout" this will include the Body or BodyJSON response values

Signed-off-by: Ivan Velasco <[email protected]>

---------

Signed-off-by: Ivan Velasco <[email protected]>
  • Loading branch information
ivan-velasco authored Nov 29, 2023
1 parent 9416431 commit 0e8d78e
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 26 deletions.
57 changes: 35 additions & 22 deletions executors/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ type Result struct {
BodyJSON interface{} `json:"bodyjson,omitempty" yaml:"bodyjson,omitempty"`
Headers Headers `json:"headers,omitempty" yaml:"headers,omitempty"`
Err string `json:"err,omitempty" yaml:"err,omitempty"`
Systemout string `json:"systemout,omitempty" yaml:"systemout,omitempty"`
}

type HTTPRequest struct {
Expand Down Expand Up @@ -100,7 +101,7 @@ func (Executor) Run(ctx context.Context, step venom.TestStep) (interface{}, erro
// dirty: mapstructure doesn't like decoding map[interface{}]interface{}, let's force manually
e.MultipartForm = step["multipart_form"]

r := Result{}
result := Result{}

workdir := venom.StringVarFromCtx(ctx, "venom.testsuite.workdir")

Expand Down Expand Up @@ -179,8 +180,8 @@ func (Executor) Run(ctx context.Context, step venom.TestStep) (interface{}, erro
}

cReq := req.Clone(ctx)
r.Request.Method = cReq.Method
r.Request.URL = req.URL.String()
result.Request.Method = cReq.Method
result.Request.URL = req.URL.String()
if cReq.Body != nil {
body, err := cReq.GetBody()
if err != nil {
Expand All @@ -191,64 +192,76 @@ func (Executor) Run(ctx context.Context, step venom.TestStep) (interface{}, erro
return nil, err
}
defer cReq.Body.Close()
r.Request.Body = string(btes)
result.Request.Body = string(btes)
}
r.Request.Header = cReq.Header
r.Request.Form = cReq.Form
r.Request.PostForm = cReq.PostForm
result.Request.Header = cReq.Header
result.Request.Form = cReq.Form
result.Request.PostForm = cReq.PostForm

start := time.Now()
resp, err := client.Do(req)
if err != nil {
return nil, err
result.Err = err.Error()
return result, err
}
elapsed := time.Since(start)
r.TimeSeconds = elapsed.Seconds()
result.TimeSeconds = elapsed.Seconds()

var bb []byte
if resp.Body != nil {
defer resp.Body.Close()

if !e.SkipBody && isBodySupported(resp) {
var errr error
bb, errr = io.ReadAll(resp.Body)
if errr != nil {
return nil, errr
var err error
bb, err = io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
r.Body = string(bb)
result.Body = string(bb)

if isBodyJSONSupported(resp) {
var m interface{}
decoder := json.NewDecoder(strings.NewReader(string(bb)))
decoder.UseNumber()
if err := decoder.Decode(&m); err == nil {
r.BodyJSON = m
result.BodyJSON = m
}
}

if result.BodyJSON != nil {
// Convert the map to a JSON string
jsonString, err := json.Marshal(result.BodyJSON)
if err != nil {
return nil, err
}
result.Systemout = fmt.Sprintf("%s-----%s----->%s ", resp.Request.Method, resp.Request.URL, string(jsonString))
} else {
result.Systemout = fmt.Sprintf("%s-----%s----->%s ", resp.Request.Method, resp.Request.URL, result.Body)
}
}
}

if !e.SkipHeaders {
r.Headers = make(map[string]string)
result.Headers = make(map[string]string)
for k, v := range resp.Header {
if strings.ToLower(k) == "set-cookie" {
r.Headers[k] = strings.Join(v, "; ")
result.Headers[k] = strings.Join(v, "; ")
} else {
r.Headers[k] = v[0]
result.Headers[k] = v[0]
}
}
}

requestContentType := r.Request.Header.Get("Content-Type")
requestContentType := result.Request.Header.Get("Content-Type")
// if PreserveBodyFile == true, the body is not interpolated.
// So, no need to keep it in request here (to re-inject it in vars)
// this will avoid to be interpolated after in vars too.
if e.PreserveBodyFile || !isContentTypeSupported(requestContentType) {
r.Request.Body = ""
result.Request.Body = ""
}

r.StatusCode = resp.StatusCode
return r, nil
result.StatusCode = resp.StatusCode
return result, nil
}

// getRequest returns the request correctly set for the current executor
Expand Down
17 changes: 13 additions & 4 deletions venom_output.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func (v *Venom) OutputResult() error {
return errors.Wrapf(err, "Error: cannot format output yaml (%s)", err)
}
case "xml":
data, err = outputXMLFormat(*testsResult)
data, err = outputXMLFormat(*testsResult, v.Verbose)
if err != nil {
return errors.Wrapf(err, "Error: cannot format output xml (%s)", err)
}
Expand Down Expand Up @@ -175,7 +175,7 @@ func outputTapFormat(tests Tests) ([]byte, error) {
return buf.Bytes(), nil
}

func outputXMLFormat(tests Tests) ([]byte, error) {
func outputXMLFormat(tests Tests, verbose int) ([]byte, error) {
testsXML := TestsXML{}

for _, ts := range tests.TestSuites {
Expand Down Expand Up @@ -203,8 +203,12 @@ func outputXMLFormat(tests Tests) ([]byte, error) {
Value: failure.Value,
})
}
systemout.Value += result.Systemout
systemerr.Value += result.Systemerr
if len(result.Errors) > 0 {
appendCleanValue(&systemout.Value, result.Systemout)
} else if verbose > 1 {
appendCleanValue(&systemout.Value, result.Systemout)
}
appendCleanValue(&systemerr.Value, result.Systemerr)
}

tcXML := TestCaseXML{
Expand All @@ -230,3 +234,8 @@ func outputXMLFormat(tests Tests) ([]byte, error) {

return data, nil
}

func appendCleanValue(dest *string, source string) {
cleanedValue := strings.ReplaceAll(source, "\x03", "")
*dest += cleanedValue
}

0 comments on commit 0e8d78e

Please sign in to comment.