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 # install hrp with one shell command
# bash -c "$(curl -ksSL https://httprunner.oss-cn-beijing.aliyuncs.com/install.sh)" # 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 set -e

View File

@@ -1,20 +1,26 @@
# Release History # 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: omit pseudo header names for HTTP/1, e.g. :authority
- fix: generate `headers.\"Content-Type\"` in har2case - 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: 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: omit print request/response body for non-text content
- fix: parse data for request cookie value
## v0.6.1 (2022-02-17) ## 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: set request Content-Type for posting json only when not specified
- fix: failed to generate API test report when data is null - fix: failed to generate API test report when data is null
- fix: panic when assertion function not exists - 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 - fix: broadcast to all rendezvous at once when spawn done
## v0.6.0 (2022-02-08) ## v0.6.0 (2022-02-08)

View File

@@ -33,4 +33,4 @@ Copyright 2021 debugtalk
* [hrp run](hrp_run.md) - run API test * [hrp run](hrp_run.md) - run API test
* [hrp startproject](hrp_startproject.md) - create a scaffold project * [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. * [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 -h, --help help for har2case
-d, --output-dir string specify output directory, default to the same dir with har file -d, --output-dir string specify output directory, default to the same dir with har file
-j, --to-json convert to JSON format (default) -j, --to-json convert to JSON format (default true)
-y, --to-yaml convert to JSON format -y, --to-yaml convert to YAML format
``` ```
### SEE ALSO ### SEE ALSO
* [hrp](hrp.md) - One-stop solution for HTTP(S) testing. * [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. * [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. * [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": { "config": {
"name": "demo with complex mechanisms", "name": "demo with complex mechanisms",
"base_url": "https://postman-echo.com", "base_url": "https://postman-echo.com",
"variables": { "variables": {
"a": "${sum(10, 2.3)}", "a": "${sum(10, 2.3)}",
"b": 3.45, "b": 3.45,
"n": "${sum_ints(1, 2, 2)}", "n": "${sum_ints(1, 2, 2)}",
"varFoo1": "${gen_random_string($n)}", "varFoo1": "${gen_random_string($n)}",
"varFoo2": "${max($a, $b)}" "varFoo2": "${max($a, $b)}"
} }
}, },
"teststeps": [ "teststeps": [
{ {
"name": "transaction 1 start", "name": "transaction 1 start",
"transaction": { "transaction": {
"name": "tran1", "name": "tran1",
"type": "start" "type": "start"
} }
}, },
{ {
"name": "get with params", "name": "get with params",
"request": { "request": {
"method": "GET", "method": "GET",
"url": "/get", "url": "/get",
"params": { "params": {
"foo1": "$varFoo1", "foo1": "$varFoo1",
"foo2": "$varFoo2" "foo2": "$varFoo2"
}, },
"headers": { "headers": {
"User-Agent": "HttpRunnerPlus" "User-Agent": "HttpRunnerPlus"
} }
}, },
"variables": { "variables": {
"b": 34.5, "b": 34.5,
"n": 3, "n": 3,
"varFoo2": "${max($a, $b)}" "name": "get with params",
}, "varFoo2": "${max($a, $b)}"
"extract": { },
"varFoo1": "body.args.foo1" "setup_hooks": [
}, "${setup_hook_example($name)}"
"validate": [ ],
{ "teardown_hooks": [
"check": "status_code", "${teardown_hook_example($name)}"
"assert": "equals", ],
"expect": 200, "extract": {
"msg": "check response status code" "varFoo1": "body.args.foo1"
}, },
{ "validate": [
"check": "headers.\"Content-Type\"", {
"assert": "startswith", "check": "status_code",
"expect": "application/json" "assert": "equals",
}, "expect": 200,
{ "msg": "check response status code"
"check": "body.args.foo1", },
"assert": "length_equals", {
"expect": 5, "check": "headers.\"Content-Type\"",
"msg": "check args foo1" "assert": "startswith",
}, "expect": "application/json"
{ },
"check": "$varFoo1", {
"assert": "length_equals", "check": "body.args.foo1",
"expect": 5, "assert": "length_equals",
"msg": "check args foo1" "expect": 5,
}, "msg": "check args foo1"
{ },
"check": "body.args.foo2", {
"assert": "equals", "check": "$varFoo1",
"expect": "34.5", "assert": "length_equals",
"msg": "check args foo2" "expect": 5,
} "msg": "check args foo1"
] },
}, {
{ "check": "body.args.foo2",
"name": "transaction 1 end", "assert": "equals",
"transaction": { "expect": "34.5",
"name": "tran1", "msg": "check args foo2"
"type": "end" }
} ]
}, },
{ {
"name": "post json data", "name": "transaction 1 end",
"request": { "transaction": {
"method": "POST", "name": "tran1",
"url": "/post", "type": "end"
"body": { }
"foo1": "$varFoo1", },
"foo2": "${max($a, $b)}" {
} "name": "post json data",
}, "request": {
"validate": [ "method": "POST",
{ "url": "/post",
"check": "status_code", "body": {
"assert": "equals", "foo1": "$varFoo1",
"expect": 200, "foo2": "${max($a, $b)}"
"msg": "check status code" }
}, },
{ "validate": [
"check": "body.json.foo1", {
"assert": "length_equals", "check": "status_code",
"expect": 5, "assert": "equals",
"msg": "check args foo1" "expect": 200,
}, "msg": "check status code"
{ },
"check": "body.json.foo2", {
"assert": "equals", "check": "body.json.foo1",
"expect": 12.3, "assert": "length_equals",
"msg": "check args foo2" "expect": 5,
} "msg": "check args foo1"
] },
}, {
{ "check": "body.json.foo2",
"name": "post form data", "assert": "equals",
"request": { "expect": 12.3,
"method": "POST", "msg": "check args foo2"
"url": "/post", }
"headers": { ]
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
}, {
"body": { "name": "post form data",
"foo1": "$varFoo1", "request": {
"foo2": "${max($a, $b)}", "method": "POST",
"time": "${get_timestamp()}" "url": "/post",
} "headers": {
}, "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
"extract": { },
"varTime": "body.form.time" "body": {
}, "foo1": "$varFoo1",
"validate": [ "foo2": "${max($a, $b)}",
{ "time": "${get_timestamp()}"
"check": "status_code", }
"assert": "equals", },
"expect": 200, "extract": {
"msg": "check status code" "varTime": "body.form.time"
}, },
{ "validate": [
"check": "body.form.foo1", {
"assert": "length_equals", "check": "status_code",
"expect": 5, "assert": "equals",
"msg": "check args foo1" "expect": 200,
}, "msg": "check status code"
{ },
"check": "body.form.foo2", {
"assert": "equals", "check": "body.form.foo1",
"expect": "12.3", "assert": "length_equals",
"msg": "check args foo2" "expect": 5,
} "msg": "check args foo1"
] },
}, {
{ "check": "body.form.foo2",
"name": "get with timestamp", "assert": "equals",
"request": { "expect": "12.3",
"method": "GET", "msg": "check args foo2"
"url": "/get", }
"params": { ]
"time": "$varTime" },
} {
}, "name": "get with timestamp",
"validate": [ "request": {
{ "method": "GET",
"check": "body.args.time", "url": "/get",
"assert": "length_equals", "params": {
"expect": 13, "time": "$varTime"
"msg": "check extracted var timestamp" }
} },
] "validate": [
} {
] "check": "body.args.time",
"assert": "length_equals",
"expect": 13,
"msg": "check extracted var timestamp"
}
]
}
]
} }

View File

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

View File

@@ -55,3 +55,11 @@ func Concatenate(args ...interface{}) (interface{}, error) {
} }
return result, nil 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_two_string", SumTwoString)
plugin.Register("sum_strings", SumStrings) plugin.Register("sum_strings", SumStrings)
plugin.Register("concatenate", Concatenate) plugin.Register("concatenate", Concatenate)
plugin.Register("setup_hook_example", SetupHookExample)
plugin.Register("teardown_hook_example", TeardownHookExample)
plugin.Serve() plugin.Serve()
} }

View File

@@ -1,106 +1,106 @@
{ {
"config": { "config": {
"name": "run request with functions", "name": "run request with functions",
"base_url": "https://postman-echo.com", "base_url": "https://postman-echo.com",
"variables": { "variables": {
"a": 12.3, "a": 12.3,
"b": 3.45, "b": 3.45,
"n": 5 "n": 5
}, },
"parameters_setting": { "parameters_setting": {
"strategy": "Sequential", "strategy": "Sequential",
"parameterIterator": [ "parameterIterator": [
{} {}
] ]
} }
}, },
"teststeps": [ "teststeps": [
{ {
"name": "waiting for all users in the beginning", "name": "waiting for all users in the beginning",
"rendezvous": { "rendezvous": {
"name": "rendezvous0" "name": "rendezvous0"
} }
}, },
{ {
"name": "rendezvous before get", "name": "rendezvous before get",
"rendezvous": { "rendezvous": {
"name": "rendezvous1", "name": "rendezvous1",
"number": 50, "number": 50,
"timeout": 3000 "timeout": 3000
} }
}, },
{ {
"name": "get with params", "name": "get with params",
"request": { "request": {
"method": "GET", "method": "GET",
"url": "/get", "url": "/get",
"params": { "params": {
"foo1": "foo1", "foo1": "foo1",
"foo2": "foo2" "foo2": "foo2"
}, },
"headers": { "headers": {
"User-Agent": "HttpRunnerPlus" "User-Agent": "HttpRunnerPlus"
} }
}, },
"extract": { "extract": {
"varFoo1": "body.args.foo1" "varFoo1": "body.args.foo1"
}, },
"validate": [ "validate": [
{ {
"check": "status_code", "check": "status_code",
"assert": "equals", "assert": "equals",
"expect": 200, "expect": 200,
"msg": "check status code" "msg": "check status code"
} }
] ]
}, },
{ {
"name": "rendezvous before post", "name": "rendezvous before post",
"rendezvous": { "rendezvous": {
"name": "rendezvous2", "name": "rendezvous2",
"number": 20, "number": 20,
"timeout": 2000 "timeout": 2000
} }
}, },
{ {
"name": "post json data with functions", "name": "post json data with functions",
"request": { "request": {
"method": "POST", "method": "POST",
"url": "/post", "url": "/post",
"headers": { "headers": {
"User-Agent": "HttpRunnerPlus" "User-Agent": "HttpRunnerPlus"
}, },
"body": { "body": {
"foo1": "foo1", "foo1": "foo1",
"foo2": "foo2" "foo2": "foo2"
} }
}, },
"validate": [ "validate": [
{ {
"check": "status_code", "check": "status_code",
"assert": "equals", "assert": "equals",
"expect": 200, "expect": 200,
"msg": "check status code" "msg": "check status code"
}, },
{ {
"check": "body.json.foo1", "check": "body.json.foo1",
"assert": "length_equals", "assert": "length_equals",
"expect": 4, "expect": 4,
"msg": "check args foo1" "msg": "check args foo1"
}, },
{ {
"check": "body.json.foo2", "check": "body.json.foo2",
"assert": "equals", "assert": "equals",
"expect": "foo2", "expect": "foo2",
"msg": "check args foo2" "msg": "check args foo2"
} }
] ]
}, },
{ {
"name": "waiting for all users in the end", "name": "waiting for all users in the end",
"rendezvous": { "rendezvous": {
"name": "rendezvous3" "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 "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 "b": 34.5, // override config level variable if existed, n/b/varFoo2
"varFoo2": "${max($a, $b)}", // 34.5; override variable b and eval again "varFoo2": "${max($a, $b)}", // 34.5; override variable b and eval again
"name": "get with params",
}). }).
SetupHook("${setup_hook_example($name)}").
GET("/get"). GET("/get").
TeardownHook("${teardown_hook_example($name)}").
WithParams(map[string]interface{}{"foo1": "$varFoo1", "foo2": "$varFoo2"}). // request with params WithParams(map[string]interface{}{"foo1": "$varFoo1", "foo2": "$varFoo2"}). // request with params
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}). // request with headers WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}). // request with headers
Extract(). Extract().
@@ -99,10 +102,20 @@ func Sum(args ...interface{}) (interface{}, error) {
return sum, nil 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() { func main() {
plugin.Register("sum_ints", SumInts) plugin.Register("sum_ints", SumInts)
plugin.Register("sum_two_int", SumTwoInt) plugin.Register("sum_two_int", SumTwoInt)
plugin.Register("sum", Sum) plugin.Register("sum", Sum)
plugin.Register("setup_hook_example", SetupHookExample)
plugin.Register("teardown_hook_example", TeardownHookExample)
plugin.Serve() plugin.Serve()
} }
` `

View File

@@ -1,3 +1,3 @@
package version 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() 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 // convert request struct to map
jsonRequest, _ := json.Marshal(&step.Request) jsonRequest, _ := json.Marshal(&step.Request)
var requestMap map[string]interface{} 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 { if len(step.Request.Params) > 0 {
params, err := r.parser.parseData(step.Request.Params, step.Variables) params, err := r.parser.parseData(step.Request.Params, step.Variables)
if err != nil { 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{}) parsedParams := params.(map[string]interface{})
requestMap["params"] = parsedParams requestMap["params"] = parsedParams
@@ -682,9 +690,13 @@ func (r *caseRunner) runStepRequest(step *TStep) (stepResult *stepData, err erro
// prepare request cookies // prepare request cookies
for cookieName, cookieValue := range step.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{ req.AddCookie(&http.Cookie{
Name: cookieName, 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.ContentSize = resp.ContentLength
stepResult.Data = sessionData 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 return stepResult, err
} }