Merge branch 'main' of github.com:xucong053/hrp

This commit is contained in:
xucong053
2022-02-24 15:25:49 +08:00
15 changed files with 348 additions and 287 deletions

View File

@@ -2,7 +2,7 @@
# install hrp with one shell command
# bash -c "$(curl -ksSL https://httprunner.oss-cn-beijing.aliyuncs.com/install.sh)"
LATEST_VERSION="v0.6.1"
LATEST_VERSION="v0.6.2"
set -e

View File

@@ -1,20 +1,26 @@
# Release History
## v0.6.2 (2022-02-21)
## v0.6.3 (2022-02-22)
- feat: support customized setup/teardown hooks (variable assignment not supported)
## v0.6.2 (2022-02-22)
- feat: support text/html extraction with regex
- change: json unmarshal to json.Number when parsing data
- fix: omit pseudo header names for HTTP/1, e.g. :authority
- fix: generate `headers.\"Content-Type\"` in har2case
- change: json unmarshal to json.Number when parsing data
- fix: incorrect data type when extracting data using jmespath
- fix: decode response body in br/gzip/deflate formats
- fix: decode response body in brotli/gzip/deflate formats
- fix: omit print request/response body for non-text content
- fix: parse data for request cookie value
## v0.6.1 (2022-02-17)
- change: json unmarshal to float64 when parsing data
- fix: set request Content-Type for posting json only when not specified
- fix: failed to generate API test report when data is null
- fix: panic when assertion function not exists
- change: json unmarshal to float64 when parsing data
- fix: broadcast to all rendezvous at once when spawn done
## v0.6.0 (2022-02-08)

View File

@@ -33,4 +33,4 @@ Copyright 2021 debugtalk
* [hrp run](hrp_run.md) - run API test
* [hrp startproject](hrp_startproject.md) - create a scaffold project
###### Auto generated by spf13/cobra on 15-Feb-2022
###### Auto generated by spf13/cobra on 22-Feb-2022

View File

@@ -39,4 +39,4 @@ hrp boom [flags]
* [hrp](hrp.md) - One-stop solution for HTTP(S) testing.
###### Auto generated by spf13/cobra on 15-Feb-2022
###### Auto generated by spf13/cobra on 22-Feb-2022

View File

@@ -15,12 +15,12 @@ hrp har2case $har_path... [flags]
```
-h, --help help for har2case
-d, --output-dir string specify output directory, default to the same dir with har file
-j, --to-json convert to JSON format (default)
-y, --to-yaml convert to JSON format
-j, --to-json convert to JSON format (default true)
-y, --to-yaml convert to YAML format
```
### SEE ALSO
* [hrp](hrp.md) - One-stop solution for HTTP(S) testing.
###### Auto generated by spf13/cobra on 15-Feb-2022
###### Auto generated by spf13/cobra on 22-Feb-2022

View File

@@ -33,4 +33,4 @@ hrp run $path... [flags]
* [hrp](hrp.md) - One-stop solution for HTTP(S) testing.
###### Auto generated by spf13/cobra on 15-Feb-2022
###### Auto generated by spf13/cobra on 22-Feb-2022

View File

@@ -16,4 +16,4 @@ hrp startproject $project_name [flags]
* [hrp](hrp.md) - One-stop solution for HTTP(S) testing.
###### Auto generated by spf13/cobra on 15-Feb-2022
###### Auto generated by spf13/cobra on 22-Feb-2022

View File

@@ -1,169 +1,176 @@
{
"config": {
"name": "demo with complex mechanisms",
"base_url": "https://postman-echo.com",
"variables": {
"a": "${sum(10, 2.3)}",
"b": 3.45,
"n": "${sum_ints(1, 2, 2)}",
"varFoo1": "${gen_random_string($n)}",
"varFoo2": "${max($a, $b)}"
}
},
"teststeps": [
{
"name": "transaction 1 start",
"transaction": {
"name": "tran1",
"type": "start"
}
},
{
"name": "get with params",
"request": {
"method": "GET",
"url": "/get",
"params": {
"foo1": "$varFoo1",
"foo2": "$varFoo2"
},
"headers": {
"User-Agent": "HttpRunnerPlus"
}
},
"variables": {
"b": 34.5,
"n": 3,
"varFoo2": "${max($a, $b)}"
},
"extract": {
"varFoo1": "body.args.foo1"
},
"validate": [
{
"check": "status_code",
"assert": "equals",
"expect": 200,
"msg": "check response status code"
},
{
"check": "headers.\"Content-Type\"",
"assert": "startswith",
"expect": "application/json"
},
{
"check": "body.args.foo1",
"assert": "length_equals",
"expect": 5,
"msg": "check args foo1"
},
{
"check": "$varFoo1",
"assert": "length_equals",
"expect": 5,
"msg": "check args foo1"
},
{
"check": "body.args.foo2",
"assert": "equals",
"expect": "34.5",
"msg": "check args foo2"
}
]
},
{
"name": "transaction 1 end",
"transaction": {
"name": "tran1",
"type": "end"
}
},
{
"name": "post json data",
"request": {
"method": "POST",
"url": "/post",
"body": {
"foo1": "$varFoo1",
"foo2": "${max($a, $b)}"
}
},
"validate": [
{
"check": "status_code",
"assert": "equals",
"expect": 200,
"msg": "check status code"
},
{
"check": "body.json.foo1",
"assert": "length_equals",
"expect": 5,
"msg": "check args foo1"
},
{
"check": "body.json.foo2",
"assert": "equals",
"expect": 12.3,
"msg": "check args foo2"
}
]
},
{
"name": "post form data",
"request": {
"method": "POST",
"url": "/post",
"headers": {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
},
"body": {
"foo1": "$varFoo1",
"foo2": "${max($a, $b)}",
"time": "${get_timestamp()}"
}
},
"extract": {
"varTime": "body.form.time"
},
"validate": [
{
"check": "status_code",
"assert": "equals",
"expect": 200,
"msg": "check status code"
},
{
"check": "body.form.foo1",
"assert": "length_equals",
"expect": 5,
"msg": "check args foo1"
},
{
"check": "body.form.foo2",
"assert": "equals",
"expect": "12.3",
"msg": "check args foo2"
}
]
},
{
"name": "get with timestamp",
"request": {
"method": "GET",
"url": "/get",
"params": {
"time": "$varTime"
}
},
"validate": [
{
"check": "body.args.time",
"assert": "length_equals",
"expect": 13,
"msg": "check extracted var timestamp"
}
]
}
]
}
"config": {
"name": "demo with complex mechanisms",
"base_url": "https://postman-echo.com",
"variables": {
"a": "${sum(10, 2.3)}",
"b": 3.45,
"n": "${sum_ints(1, 2, 2)}",
"varFoo1": "${gen_random_string($n)}",
"varFoo2": "${max($a, $b)}"
}
},
"teststeps": [
{
"name": "transaction 1 start",
"transaction": {
"name": "tran1",
"type": "start"
}
},
{
"name": "get with params",
"request": {
"method": "GET",
"url": "/get",
"params": {
"foo1": "$varFoo1",
"foo2": "$varFoo2"
},
"headers": {
"User-Agent": "HttpRunnerPlus"
}
},
"variables": {
"b": 34.5,
"n": 3,
"name": "get with params",
"varFoo2": "${max($a, $b)}"
},
"setup_hooks": [
"${setup_hook_example($name)}"
],
"teardown_hooks": [
"${teardown_hook_example($name)}"
],
"extract": {
"varFoo1": "body.args.foo1"
},
"validate": [
{
"check": "status_code",
"assert": "equals",
"expect": 200,
"msg": "check response status code"
},
{
"check": "headers.\"Content-Type\"",
"assert": "startswith",
"expect": "application/json"
},
{
"check": "body.args.foo1",
"assert": "length_equals",
"expect": 5,
"msg": "check args foo1"
},
{
"check": "$varFoo1",
"assert": "length_equals",
"expect": 5,
"msg": "check args foo1"
},
{
"check": "body.args.foo2",
"assert": "equals",
"expect": "34.5",
"msg": "check args foo2"
}
]
},
{
"name": "transaction 1 end",
"transaction": {
"name": "tran1",
"type": "end"
}
},
{
"name": "post json data",
"request": {
"method": "POST",
"url": "/post",
"body": {
"foo1": "$varFoo1",
"foo2": "${max($a, $b)}"
}
},
"validate": [
{
"check": "status_code",
"assert": "equals",
"expect": 200,
"msg": "check status code"
},
{
"check": "body.json.foo1",
"assert": "length_equals",
"expect": 5,
"msg": "check args foo1"
},
{
"check": "body.json.foo2",
"assert": "equals",
"expect": 12.3,
"msg": "check args foo2"
}
]
},
{
"name": "post form data",
"request": {
"method": "POST",
"url": "/post",
"headers": {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
},
"body": {
"foo1": "$varFoo1",
"foo2": "${max($a, $b)}",
"time": "${get_timestamp()}"
}
},
"extract": {
"varTime": "body.form.time"
},
"validate": [
{
"check": "status_code",
"assert": "equals",
"expect": 200,
"msg": "check status code"
},
{
"check": "body.form.foo1",
"assert": "length_equals",
"expect": 5,
"msg": "check args foo1"
},
{
"check": "body.form.foo2",
"assert": "equals",
"expect": "12.3",
"msg": "check args foo2"
}
]
},
{
"name": "get with timestamp",
"request": {
"method": "GET",
"url": "/get",
"params": {
"time": "$varTime"
}
},
"validate": [
{
"check": "body.args.time",
"assert": "length_equals",
"expect": 13,
"msg": "check extracted var timestamp"
}
]
}
]
}

View File

@@ -24,7 +24,12 @@ teststeps:
variables:
b: 34.5
"n": 3
name: get with params
varFoo2: ${max($a, $b)}
setup_hooks:
- ${setup_hook_example($name)}
teardown_hooks:
- ${teardown_hook_example($name)}
extract:
varFoo1: body.args.foo1
validate:

View File

@@ -55,3 +55,11 @@ func Concatenate(args ...interface{}) (interface{}, error) {
}
return result, nil
}
func SetupHookExample(args string) string {
return fmt.Sprintf("step name: %v, setup...", args)
}
func TeardownHookExample(args string) string {
return fmt.Sprintf("step name: %v, teardown...", args)
}

View File

@@ -10,5 +10,7 @@ func main() {
plugin.Register("sum_two_string", SumTwoString)
plugin.Register("sum_strings", SumStrings)
plugin.Register("concatenate", Concatenate)
plugin.Register("setup_hook_example", SetupHookExample)
plugin.Register("teardown_hook_example", TeardownHookExample)
plugin.Serve()
}

View File

@@ -1,106 +1,106 @@
{
"config": {
"name": "run request with functions",
"base_url": "https://postman-echo.com",
"variables": {
"a": 12.3,
"b": 3.45,
"n": 5
},
"parameters_setting": {
"strategy": "Sequential",
"parameterIterator": [
{}
]
}
},
"teststeps": [
{
"name": "waiting for all users in the beginning",
"rendezvous": {
"name": "rendezvous0"
}
},
{
"name": "rendezvous before get",
"rendezvous": {
"name": "rendezvous1",
"number": 50,
"timeout": 3000
}
},
{
"name": "get with params",
"request": {
"method": "GET",
"url": "/get",
"params": {
"foo1": "foo1",
"foo2": "foo2"
},
"headers": {
"User-Agent": "HttpRunnerPlus"
}
},
"extract": {
"varFoo1": "body.args.foo1"
},
"validate": [
{
"check": "status_code",
"assert": "equals",
"expect": 200,
"msg": "check status code"
}
]
},
{
"name": "rendezvous before post",
"rendezvous": {
"name": "rendezvous2",
"number": 20,
"timeout": 2000
}
},
{
"name": "post json data with functions",
"request": {
"method": "POST",
"url": "/post",
"headers": {
"User-Agent": "HttpRunnerPlus"
},
"body": {
"foo1": "foo1",
"foo2": "foo2"
}
},
"validate": [
{
"check": "status_code",
"assert": "equals",
"expect": 200,
"msg": "check status code"
},
{
"check": "body.json.foo1",
"assert": "length_equals",
"expect": 4,
"msg": "check args foo1"
},
{
"check": "body.json.foo2",
"assert": "equals",
"expect": "foo2",
"msg": "check args foo2"
}
]
},
{
"name": "waiting for all users in the end",
"rendezvous": {
"name": "rendezvous3"
}
}
]
"config": {
"name": "run request with functions",
"base_url": "https://postman-echo.com",
"variables": {
"a": 12.3,
"b": 3.45,
"n": 5
},
"parameters_setting": {
"strategy": "Sequential",
"parameterIterator": [
{}
]
}
},
"teststeps": [
{
"name": "waiting for all users in the beginning",
"rendezvous": {
"name": "rendezvous0"
}
},
{
"name": "rendezvous before get",
"rendezvous": {
"name": "rendezvous1",
"number": 50,
"timeout": 3000
}
},
{
"name": "get with params",
"request": {
"method": "GET",
"url": "/get",
"params": {
"foo1": "foo1",
"foo2": "foo2"
},
"headers": {
"User-Agent": "HttpRunnerPlus"
}
},
"extract": {
"varFoo1": "body.args.foo1"
},
"validate": [
{
"check": "status_code",
"assert": "equals",
"expect": 200,
"msg": "check status code"
}
]
},
{
"name": "rendezvous before post",
"rendezvous": {
"name": "rendezvous2",
"number": 20,
"timeout": 2000
}
},
{
"name": "post json data with functions",
"request": {
"method": "POST",
"url": "/post",
"headers": {
"User-Agent": "HttpRunnerPlus"
},
"body": {
"foo1": "foo1",
"foo2": "foo2"
}
},
"validate": [
{
"check": "status_code",
"assert": "equals",
"expect": 200,
"msg": "check status code"
},
{
"check": "body.json.foo1",
"assert": "length_equals",
"expect": 4,
"msg": "check args foo1"
},
{
"check": "body.json.foo2",
"assert": "equals",
"expect": "foo2",
"msg": "check args foo2"
}
]
},
{
"name": "waiting for all users in the end",
"rendezvous": {
"name": "rendezvous3"
}
}
]
}

View File

@@ -19,8 +19,11 @@ var demoTestCase = &hrp.TestCase{
"n": 3, // inherit config level variables if not set in step level, a/varFoo1
"b": 34.5, // override config level variable if existed, n/b/varFoo2
"varFoo2": "${max($a, $b)}", // 34.5; override variable b and eval again
"name": "get with params",
}).
SetupHook("${setup_hook_example($name)}").
GET("/get").
TeardownHook("${teardown_hook_example($name)}").
WithParams(map[string]interface{}{"foo1": "$varFoo1", "foo2": "$varFoo2"}). // request with params
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}). // request with headers
Extract().
@@ -99,10 +102,20 @@ func Sum(args ...interface{}) (interface{}, error) {
return sum, nil
}
func SetupHookExample(args string) string {
return fmt.Sprintf("step name: %v, setup...", args)
}
func TeardownHookExample(args string) string {
return fmt.Sprintf("step name: %v, teardown...", args)
}
func main() {
plugin.Register("sum_ints", SumInts)
plugin.Register("sum_two_int", SumTwoInt)
plugin.Register("sum", Sum)
plugin.Register("setup_hook_example", SetupHookExample)
plugin.Register("teardown_hook_example", TeardownHookExample)
plugin.Serve()
}
`

View File

@@ -1,3 +1,3 @@
package version
const VERSION = "v0.6.1"
const VERSION = "v0.6.2"

View File

@@ -616,6 +616,14 @@ func (r *caseRunner) runStepRequest(step *TStep) (stepResult *stepData, err erro
}
sessionData := newSessionData()
// deal with setup hooks
for _, setupHook := range step.SetupHooks {
_, err = r.parser.parseData(setupHook, step.Variables)
if err != nil {
return stepResult, errors.Wrap(err, "run setup hooks failed")
}
}
// convert request struct to map
jsonRequest, _ := json.Marshal(&step.Request)
var requestMap map[string]interface{}
@@ -659,7 +667,7 @@ func (r *caseRunner) runStepRequest(step *TStep) (stepResult *stepData, err erro
if len(step.Request.Params) > 0 {
params, err := r.parser.parseData(step.Request.Params, step.Variables)
if err != nil {
return stepResult, errors.Wrap(err, "parse data failed")
return stepResult, errors.Wrap(err, "parse request params failed")
}
parsedParams := params.(map[string]interface{})
requestMap["params"] = parsedParams
@@ -682,9 +690,13 @@ func (r *caseRunner) runStepRequest(step *TStep) (stepResult *stepData, err erro
// prepare request cookies
for cookieName, cookieValue := range step.Request.Cookies {
value, err := r.parser.parseData(cookieValue, step.Variables)
if err != nil {
return stepResult, errors.Wrap(err, "parse cookie value failed")
}
req.AddCookie(&http.Cookie{
Name: cookieName,
Value: cookieValue,
Value: fmt.Sprintf("%v", value),
})
}
@@ -803,6 +815,14 @@ func (r *caseRunner) runStepRequest(step *TStep) (stepResult *stepData, err erro
}
stepResult.ContentSize = resp.ContentLength
stepResult.Data = sessionData
// deal with teardown hooks
for _, teardownHook := range step.TeardownHooks {
_, err = r.parser.parseData(teardownHook, step.Variables)
if err != nil {
return stepResult, errors.Wrap(err, "run teardown hooks failed")
}
}
return stepResult, err
}