refactor: move hrp/ to root folder

This commit is contained in:
lilong.129
2025-02-06 10:52:08 +08:00
parent 9376692b71
commit 1f063dd6f7
221 changed files with 206 additions and 211 deletions

View File

@@ -0,0 +1,34 @@
package scaffold
import (
"path/filepath"
"testing"
)
func TestGenDemoExamples(t *testing.T) {
dir := "../../../examples/demo-with-go-plugin"
err := CreateScaffold(dir, Go, "", true)
if err != nil {
t.Fatal(err)
}
dir = "../../../examples/demo-with-py-plugin"
venv := filepath.Join(dir, ".venv")
_ = CreateScaffold(dir, Py, venv, true)
// FIXME
// if err != nil {
// t.Fatal(err)
// }
dir = "../../../examples/demo-without-plugin"
err = CreateScaffold(dir, Ignore, "", true)
if err != nil {
t.Fatal(err)
}
dir = "../../../examples/demo-empty-project"
err = CreateScaffold(dir, Empty, "", true)
if err != nil {
t.Fatal(err)
}
}

220
internal/scaffold/main.go Normal file
View File

@@ -0,0 +1,220 @@
package scaffold
import (
"embed"
"fmt"
"os"
"path/filepath"
"time"
"github.com/httprunner/funplugin/myexec"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/config"
"github.com/httprunner/httprunner/v5/internal/sdk"
"github.com/httprunner/httprunner/v5/internal/version"
)
type PluginType string
const (
Empty PluginType = "empty"
Ignore PluginType = "ignore"
Py PluginType = "py"
Go PluginType = "go"
)
type ProjectInfo struct {
ProjectName string `json:"project_name,omitempty" yaml:"project_name,omitempty"`
CreateTime time.Time `json:"create_time,omitempty" yaml:"create_time,omitempty"`
Version string `json:"hrp_version,omitempty" yaml:"hrp_version,omitempty"`
}
//go:embed templates/*
var templatesDir embed.FS
// CopyFile copies a file from templates dir to scaffold project
func CopyFile(templateFile, targetFile string) error {
log.Info().Str("path", targetFile).Msg("create file")
content, err := templatesDir.ReadFile(templateFile)
if err != nil {
return errors.Wrap(err, "template file not found")
}
err = os.WriteFile(targetFile, content, 0o644)
if err != nil {
log.Error().Err(err).Msg("create file failed")
return err
}
return nil
}
func CreateScaffold(projectName string, pluginType PluginType, venv string, force bool) error {
// report GA event
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_startproject", map[string]interface{}{
"pluginType": string(pluginType),
"force": force,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
log.Info().
Str("projectName", projectName).
Str("pluginType", string(pluginType)).
Bool("force", force).
Msg("create new scaffold project")
// check if projectName exists
if _, err := os.Stat(projectName); err == nil {
if !force {
log.Warn().Str("projectName", projectName).
Msg("project name already exists, please specify a new one.")
return fmt.Errorf("project name already exists")
}
log.Warn().Str("projectName", projectName).
Msg("project name already exists, remove first !!!")
os.RemoveAll(projectName)
}
// create project folders
if err := builtin.CreateFolder(projectName); err != nil {
return err
}
if err := builtin.CreateFolder(filepath.Join(projectName, "har")); err != nil {
return err
}
if err := builtin.CreateFile(filepath.Join(projectName, "har", ".keep"), ""); err != nil {
return err
}
if err := builtin.CreateFolder(filepath.Join(projectName, "testcases")); err != nil {
return err
}
if err := builtin.CreateFolder(filepath.Join(projectName, config.ResultsDirName)); err != nil {
return err
}
if err := builtin.CreateFile(filepath.Join(projectName, config.ResultsDirName, ".keep"), ""); err != nil {
return err
}
projectInfo := &ProjectInfo{
ProjectName: filepath.Base(projectName),
CreateTime: time.Now(),
Version: version.VERSION,
}
// dump project information to file
err := builtin.Dump2JSON(projectInfo, filepath.Join(projectName, "proj.json"))
if err != nil {
return err
}
// create .gitignore
err = CopyFile("templates/gitignore", filepath.Join(projectName, ".gitignore"))
if err != nil {
return err
}
// create .env
err = CopyFile("templates/env", filepath.Join(projectName, ".env"))
if err != nil {
return err
}
// create project testcases
if pluginType == Empty {
// create empty project
err := CopyFile("templates/testcases/demo_empty_request.json",
filepath.Join(projectName, "testcases", "requests.json"))
if err != nil {
return err
}
return nil
} else if pluginType == Ignore {
// create project without funplugin
err := CopyFile("templates/testcases/demo_without_funplugin.json",
filepath.Join(projectName, "testcases", "requests.json"))
if err != nil {
return err
}
log.Info().Msg("skip creating function plugin")
return nil
}
// create project with funplugin
err = CopyFile("templates/testcases/demo_with_funplugin.json",
filepath.Join(projectName, "testcases", "demo.json"))
if err != nil {
return err
}
err = CopyFile("templates/testcases/demo_requests.json",
filepath.Join(projectName, "testcases", "requests.json"))
if err != nil {
return err
}
err = CopyFile("templates/testcases/demo_requests.yml",
filepath.Join(projectName, "testcases", "requests.yml"))
if err != nil {
return err
}
err = CopyFile("templates/testcases/demo_ref_testcase.yml",
filepath.Join(projectName, "testcases", "ref_testcase.yml"))
if err != nil {
return err
}
// create debugtalk function plugin
switch pluginType {
case Py:
return createPythonPlugin(projectName, venv)
case Go:
return createGoPlugin(projectName)
}
return nil
}
func createGoPlugin(projectName string) error {
log.Info().Msg("start to create hashicorp go plugin")
// check go sdk
if err := myexec.RunCommand("go", "version"); err != nil {
return errors.Wrap(err, "go sdk not installed")
}
// create debugtalk.go
pluginDir := filepath.Join(projectName, "plugin")
if err := builtin.CreateFolder(pluginDir); err != nil {
return err
}
err := CopyFile("templates/plugin/debugtalk.go",
filepath.Join(projectName, "plugin", hrp.PluginGoSourceFile))
if err != nil {
return errors.Wrap(err, "copy debugtalk.go failed")
}
return nil
}
func createPythonPlugin(projectName, venv string) error {
log.Info().Msg("start to create hashicorp python plugin")
// create debugtalk.py
pluginFile := filepath.Join(projectName, hrp.PluginPySourceFile)
err := CopyFile("templates/plugin/debugtalk.py", pluginFile)
if err != nil {
return errors.Wrap(err, "copy file failed")
}
packages := []string{"funppy", "httprunner"}
_, err = myexec.EnsurePython3Venv(venv, packages...)
if err != nil {
return errors.Wrap(code.InvalidPython3Venv, err.Error())
}
return nil
}

View File

@@ -0,0 +1,34 @@
{
"name": "",
"request": {
"method": "GET",
"url": "/get",
"params": {
"foo1": "bar1",
"foo2": "bar2"
},
"headers": {
"Postman-Token": "ea19464c-ddd4-4724-abe9-5e2b254c2723"
}
},
"validate": [
{
"check": "status_code",
"assert": "equals",
"expect": 200,
"msg": "assert response status code"
},
{
"check": "headers.\"Content-Type\"",
"assert": "equals",
"expect": "application/json; charset=utf-8",
"msg": "assert response header Content-Type"
},
{
"check": "body.url",
"assert": "equals",
"expect": "https://postman-echo.com/get?foo1=bar1&foo2=bar2",
"msg": "assert response body url"
}
]
}

View File

@@ -0,0 +1,22 @@
name: ""
request:
method: GET
url: /get
params:
foo1: bar1
foo2: bar2
headers:
Postman-Token: ea19464c-ddd4-4724-abe9-5e2b254c2723
validate:
- check: status_code
assert: equals
expect: 200
msg: assert response status code
- check: headers."Content-Type"
assert: equals
expect: application/json; charset=utf-8
msg: assert response header Content-Type
- check: body.url
assert: equals
expect: https://postman-echo.com/get?foo1=bar1&foo2=bar2
msg: assert response body url

View File

@@ -0,0 +1,45 @@
{
"name": "",
"request": {
"method": "POST",
"url": "/post",
"headers": {
"Content-Length": "58",
"Content-Type": "text/plain",
"Postman-Token": "$session_token"
},
"body": "This is expected to be sent back as part of response body."
},
"validate": [
{
"check": "status_code",
"assert": "equals",
"expect": 200,
"msg": "assert response status code"
},
{
"check": "headers.\"Content-Type\"",
"assert": "equals",
"expect": "application/json; charset=utf-8",
"msg": "assert response header Content-Type"
},
{
"check": "body.data",
"assert": "equals",
"expect": "This is expected to be sent back as part of response body.",
"msg": "assert response body data"
},
{
"check": "body.json",
"assert": "equals",
"expect": null,
"msg": "assert response body json"
},
{
"check": "body.url",
"assert": "equals",
"expect": "https://postman-echo.com/post/",
"msg": "assert response body url"
}
]
}

View File

@@ -0,0 +1,30 @@
name: ""
request:
method: POST
url: /post
headers:
Content-Length: "58"
Content-Type: text/plain
Postman-Token: $session_token
body: This is expected to be sent back as part of response body.
validate:
- check: status_code
assert: equals
expect: 200
msg: assert response status code
- check: headers."Content-Type"
assert: equals
expect: application/json; charset=utf-8
msg: assert response header Content-Type
- check: body.data
assert: equals
expect: This is expected to be sent back as part of response body.
msg: assert response body data
- check: body.json
assert: equals
expect: null
msg: assert response body json
- check: body.url
assert: equals
expect: https://postman-echo.com/post/
msg: assert response body url

View File

@@ -0,0 +1,45 @@
{
"name": "",
"request": {
"method": "PUT",
"url": "/put",
"headers": {
"Content-Length": "58",
"Content-Type": "text/plain",
"Postman-Token": "5d357b2b-0f10-4ded-bc9a-299ebef7a2d5"
},
"body": "This is expected to be sent back as part of response body."
},
"validate": [
{
"check": "status_code",
"assert": "equals",
"expect": 200,
"msg": "assert response status code"
},
{
"check": "headers.\"Content-Type\"",
"assert": "equals",
"expect": "application/json; charset=utf-8",
"msg": "assert response header Content-Type"
},
{
"check": "body.data",
"assert": "equals",
"expect": "This is expected to be sent back as part of response body.",
"msg": "assert response body data"
},
{
"check": "body.json",
"assert": "equals",
"expect": null,
"msg": "assert response body json"
},
{
"check": "body.url",
"assert": "equals",
"expect": "https://postman-echo.com/put/",
"msg": "assert response body url"
}
]
}

View File

@@ -0,0 +1,30 @@
name: ""
request:
method: PUT
url: /put
headers:
Content-Length: "58"
Content-Type: text/plain
Postman-Token: 5d357b2b-0f10-4ded-bc9a-299ebef7a2d5
body: This is expected to be sent back as part of response body.
validate:
- check: status_code
assert: equals
expect: 200
msg: assert response status code
- check: headers."Content-Type"
assert: equals
expect: application/json; charset=utf-8
msg: assert response header Content-Type
- check: body.data
assert: equals
expect: This is expected to be sent back as part of response body.
msg: assert response body data
- check: body.json
assert: equals
expect: null
msg: assert response body json
- check: body.url
assert: equals
expect: https://postman-echo.com/put/
msg: assert response body url

View File

@@ -0,0 +1,3 @@
base_url=https://postman-echo.com
USERNAME=debugtalk
PASSWORD=123456

View File

@@ -0,0 +1,14 @@
reports/
*.so
.vscode/
.idea/
.DS_Store
output/
__pycache__/
*.pyc
.python-version
logs/
# plugin
debugtalk.bin
debugtalk.so

View File

@@ -0,0 +1,24 @@
# NOTE: Generated By hrp v4.3.6, DO NOT EDIT!
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from debugtalk import *
if __name__ == "__main__":
import funppy
funppy.register("get_user_agent", get_user_agent)
funppy.register("sleep", sleep)
funppy.register("sum", sum)
funppy.register("sum_ints", sum_ints)
funppy.register("sum_two_int", sum_two_int)
funppy.register("sum_two_string", sum_two_string)
funppy.register("sum_strings", sum_strings)
funppy.register("concatenate", concatenate)
funppy.register("setup_hook_example", setup_hook_example)
funppy.register("teardown_hook_example", teardown_hook_example)
funppy.serve()

View File

@@ -0,0 +1,44 @@
package main
import (
"fmt"
)
func SumTwoInt(a, b int) int {
return a + b
}
func SumInts(args ...int) int {
var sum int
for _, arg := range args {
sum += arg
}
return sum
}
func Sum(args ...interface{}) (interface{}, error) {
var sum float64
for _, arg := range args {
switch v := arg.(type) {
case int:
sum += float64(v)
case float64:
sum += v
default:
return nil, fmt.Errorf("unexpected type: %T", arg)
}
}
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 GetUserAgent() string {
return "hrp/fungo"
}

View File

@@ -0,0 +1,62 @@
import logging
import time
from typing import List
# commented out function will be filtered
# def get_headers():
# return {"User-Agent": "hrp"}
def get_user_agent():
return "hrp/funppy"
def sleep(n_secs):
time.sleep(n_secs)
def sum(*args):
result = 0
for arg in args:
result += arg
return result
def sum_ints(*args: List[int]) -> int:
result = 0
for arg in args:
result += arg
return result
def sum_two_int(a: int, b: int) -> int:
return a + b
def sum_two_string(a: str, b: str) -> str:
return a + b
def sum_strings(*args: List[str]) -> str:
result = ""
for arg in args:
result += arg
return result
def concatenate(*args: List[str]) -> str:
result = ""
for arg in args:
result += str(arg)
return result
def setup_hook_example(name):
logging.warning("setup_hook_example")
return f"setup_hook_example: {name}"
def teardown_hook_example(name):
logging.warning("teardown_hook_example")
return f"teardown_hook_example: {name}"

View File

@@ -0,0 +1,13 @@
// NOTE: Generated By hrp {{ .Version }}, DO NOT EDIT!
package main
import (
"github.com/httprunner/funplugin/fungo"
)
func main() {
{{- range $functionName := .FunctionNames }}
fungo.Register("{{ $functionName }}", {{ $functionName }})
{{- end }}
fungo.Serve()
}

View File

@@ -0,0 +1,16 @@
# NOTE: Generated By hrp {{ .Version }}, DO NOT EDIT!
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from debugtalk import *
if __name__ == "__main__":
import funppy
{{- range $functionName := .FunctionNames }}
funppy.register("{{ $functionName }}", {{ $functionName }})
{{- end }}
funppy.serve()

View File

@@ -0,0 +1,16 @@
// NOTE: Generated By hrp v4.3.6, DO NOT EDIT!
package main
import (
"github.com/httprunner/funplugin/fungo"
)
func main() {
fungo.Register("SumTwoInt", SumTwoInt)
fungo.Register("SumInts", SumInts)
fungo.Register("Sum", Sum)
fungo.Register("SetupHookExample", SetupHookExample)
fungo.Register("TeardownHookExample", TeardownHookExample)
fungo.Register("GetUserAgent", GetUserAgent)
fungo.Serve()
}

View File

@@ -0,0 +1,6 @@
[pytest]
addopts = -s
# https://docs.pytest.org/en/latest/how-to/output.html
junit_logging = all
junit_duration_report = total
log_cli = False

View File

@@ -0,0 +1,359 @@
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TestReport</title>
<style>
body {
background-color: #f2f2f2;
color: #333;
margin: 0 auto;
width: 960px;
}
#summary {
width: 960px;
margin-bottom: 20px;
}
#summary th {
background-color: skyblue;
padding: 5px 12px;
}
#summary td {
background-color: lightblue;
text-align: center;
padding: 4px 8px;
}
.details {
width: 960px;
margin-bottom: 20px;
}
.details th {
background-color: skyblue;
padding: 5px 12px;
}
.details tr .passed {
background-color: lightgreen;
}
.details tr .failed {
background-color: red;
}
.details tr .unchecked {
background-color: gray;
}
.details td {
background-color: lightblue;
padding: 5px 12px;
}
.details .detail {
background-color: lightgrey;
font-size: smaller;
padding: 5px 10px;
line-height: 20px;
text-align: left;
}
.details .success {
background-color: greenyellow;
}
.details .error {
background-color: red;
}
.details .failure {
background-color: salmon;
}
.details .skipped {
background-color: gray;
}
.button {
font-size: 1em;
padding: 6px;
width: 4em;
text-align: center;
background-color: #06d85f;
border-radius: 20px/50px;
cursor: pointer;
transition: all 0.3s ease-out;
}
a.button {
color: gray;
text-decoration: none;
display: inline-block;
}
.button:hover {
background: #2cffbd;
}
.overlay {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.7);
transition: opacity 500ms;
visibility: hidden;
opacity: 0;
line-height: 25px;
}
.overlay:target {
visibility: visible;
opacity: 1;
}
.popup {
margin: 70px auto;
padding: 20px;
background: #fff;
border-radius: 10px;
width: 50%;
position: relative;
transition: all 3s ease-in-out;
}
.popup h2 {
margin-top: 0;
color: #333;
font-family: Tahoma, Arial, sans-serif;
}
.popup .close {
position: absolute;
top: 20px;
right: 30px;
transition: all 200ms;
font-size: 30px;
font-weight: bold;
text-decoration: none;
color: #333;
}
.popup .close:hover {
color: #06d85f;
}
.popup .content {
max-height: 80%;
overflow: auto;
text-align: left;
}
.popup .separator {
color: royalblue
}
@media screen and (max-width: 700px) {
.box {
width: 70%;
}
.popup {
width: 70%;
}
}
</style>
</head>
<body>
<h1>API Test Report</h1>
<h2>Summary</h2>
<table id="summary">
<tr>
<th>START AT</th>
<td colspan="4">{{.Time.StartAt}}</td>
</tr>
<tr>
<th>DURATION</th>
<td colspan="4">{{ .Time.Duration }} seconds</td>
</tr>
<tr>
<th>PLATFORM</th>
<td>HttpRunnerPlus {{ .Platform.HttprunnerVersion }}</td>
<td>{{ .Platform.GoVersion }}</td>
<td colspan="2">{{ .Platform.Platform }}</td>
</tr>
<tr>
<th>STAT</th>
<th colspan="2">TESTCASES (success/fail)</th>
<th colspan="2">TESTSTEPS (success/fail/error/skip)</th>
</tr>
<tr>
<td>total (details) =></td>
<td colspan="2">{{.Stat.TestCases.Total}} ({{.Stat.TestCases.Success}}/{{.Stat.TestCases.Fail}})</td>
<td colspan="2">{{.Stat.TestSteps.Total}} ({{.Stat.TestSteps.Successes}}/0/{{.Stat.TestSteps.Failures}}/0)</td>
</tr>
</table>
<h2>Details</h2>
{{ range $suite_index, $detail := .Details }}
<h3>{{.Name}}</h3>
<table id="suite_{{$suite_index}}" class="details">
<tr>
<td>TOTAL: {{.Stat.Total}}</td>
<td>SUCCESS: {{.Stat.Successes}}</td>
<td>FAILED: 0</td>
<td>ERROR: {{.Stat.Failures}}</td>
<td>SKIPPED: 0</td>
</tr>
<tr>
<th>Status</th>
<th colspan="2">Name</th>
<th>Response Time</th>
<th>Detail</th>
</tr>
{{- range $loop_index, $record := .Records }}
{{- with $record}}
{{- $status := "error"}}
{{- if .Success }} {{ $status = "success" }} {{ end }}
<tr id="record_{{$suite_index}}_{{$loop_index}}">
<th class={{$status}} style="width:5em;">{{$status}}</th>
<td colspan="2">{{.Name}}</td>
<td style="text-align:center;width:6em;">{{ .Elapsed }} ms</td>
<td class="detail">
<a class="button" href="#popup_log_{{$suite_index}}_{{$loop_index}}">log</a>
<div id="popup_log_{{$suite_index}}_{{$loop_index}}" class="overlay">
<div class="popup">
<h2>Request and Response data</h2>
<a class="close" href="#record_{{$suite_index}}_{{$loop_index}}">&times;</a>
<div class="content">
<h3>Name: {{ .Name }}</h3>
{{- if .Data}}
<h3>Request:</h3>
<div style="overflow: auto">
<table>
{{- range $key, $value := .Data.ReqResps.Request}}
<tr>
<th>{{$key}}</th>
<td align="left">
{{- if eq $key "headers" }}
{{- range $k, $v := $value }}
<pre>{{$k}}: {{$v}}</pre>
{{- end -}}
{{- else if eq $key "params" }}
{{- range $k, $v := $value }}
<pre>{{$k}}: {{$v}}</pre>
{{- end -}}
{{- else if eq $key "cookies" }}
{{- range $k, $v := $value }}
<pre>{{$k}}: {{$v}}</pre>
{{- end -}}
{{- else }}
<pre>{{$value}}</pre>
{{- end }}
</td>
</tr>
{{- end }}
</table>
</div>
<h3>Response:</h3>
<div style="overflow: auto">
<table>
{{- range $key, $value := .Data.ReqResps.Response}}
<tr>
<th>{{$key}}</th>
<td align="left">
{{- if eq $key "headers" }}
{{- range $k, $v := $value}}
<pre>{{$k}}: {{$v}}</pre>
{{- end -}}
{{- else if eq $key "cookies" }}
{{- range $k, $v := $value }}
<pre>{{$k}}: {{$v}}</pre>
{{- end -}}
{{- else }}
<pre>{{ $value }}</pre>
{{- end }}
</td>
</tr>
{{- end }}
</table>
</div>
<h3>Validators:</h3>
<div style="overflow: auto">
{{- if .Data.Validators }}
<table>
<tr>
<th>check</th>
<th>comparator</th>
<th>expect value</th>
<th>actual value</th>
</tr>
{{- range $validator := .Data.Validators }}
<tr>
{{- if eq $validator.CheckResult "pass" }}
<td class="passed">
{{- else if eq $validator.CheckResult "fail" }}
<td class="failed">
{{- else if eq $validator.CheckResult "unchecked" }}
<td class="unchecked">
{{- end }}
{{$validator.Check}}
</td>
<td>{{$validator.Assert}}</td>
<td>{{$validator.Expect}}</td>
<td>{{$validator.CheckValue}}</td>
</tr>
{{- end }}
</table>
{{- end }}
<h3>Statistics:</h3>
<div style="overflow: auto">
<table>
<tr>
<th>content_size(bytes)</th>
<td>{{ .ContentSize }}</td>
</tr>
<tr>
<th>response_time(ms)</th>
<td>{{ .Elapsed }}</td>
</tr>
<tr>
<th>elapsed(ms)</th>
<td>{{ .Elapsed }}</td>
</tr>
</table>
</div>
</div>
{{- end }}
</div>
</div>
</div>
{{ if .Attachments }}
<a class="button" href="#popup_attachment_{{$suite_index}}_{{$loop_index}}">traceback</a>
<div id="popup_attachment_{{$suite_index}}_{{$loop_index}}" class="overlay">
<div class="popup">
<h2>Traceback Message</h2>
<a class="close" href="#record_{{$suite_index}}_{{$loop_index}}">&times;</a>
<div class="content">
<pre>{{ .Attachments }}</pre>
</div>
</div>
</div>
{{- end }}
</td>
</tr>
{{- end }}
{{- end }}
</table>
{{- end }}
</body>

View File

@@ -0,0 +1 @@
# NOTICE: Generated By HttpRunner. DO NOT EDIT!

View File

@@ -0,0 +1,25 @@
{
"config": {
"name": "request methods testcase: empty testcase",
"variables": null,
"verify": false
},
"teststeps": [
{
"name": "",
"variables": null,
"request": {
"method": "GET",
"url": "https://"
},
"validate": [
{
"check": "status_code",
"assert": "equal",
"expect": 200,
"msg": "check status_code"
}
]
}
]
}

View File

@@ -0,0 +1,13 @@
config:
name: "request methods testcase: empty testcase"
variables:
verify: False
teststeps:
- name:
variables:
request:
method: GET
url: "https://"
validate:
- eq: ["status_code", 200]

View File

@@ -0,0 +1,76 @@
{
"config": {
"name": "api test demo",
"variables": {
"user_agent": "iOS/10.3",
"device_sn": "TESTCASE_SETUP_XXX",
"os_platform": "ios",
"app_version": "2.8.6"
},
"base_url": "https://postman-echo.com",
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Host": "postman-echo.com",
"User-Agent": "PostmanRuntime/7.28.4"
},
"verify": false,
"export": [
"session_token"
]
},
"teststeps": [
{
"name": "test api /get",
"api": "api/get.json",
"variables": {
"user_agent": "iOS/10.4",
"device_sn": "$device_sn",
"os_platform": "ios",
"app_version": "2.8.7"
},
"extract": {
"session_token": "body.headers.\"postman-token\""
}
},
{
"name": "test api /post",
"api": "api/post.json",
"variables": {
"user_agent": "iOS/10.5",
"device_sn": "$device_sn",
"os_platform": "ios",
"app_version": "2.8.9"
},
"validate": [
{
"check": "status_code",
"assert": "equal",
"expect": 200,
"msg": "check status_code"
},
{
"check": "body.headers.\"postman-token\"",
"assert": "equal",
"expect": "ea19464c-ddd4-4724-abe9-5e2b254c2723",
"msg": "check body.headers.postman-token"
}
]
},
{
"name": "test api /put",
"api": "api/put.json",
"variables": {
"user_agent": "iOS/10.6",
"device_sn": "$device_sn",
"os_platform": "ios",
"app_version": "2.8.10"
},
"extract": {
"session_token": "body.headers.\"postman-token\""
}
}
]
}

View File

@@ -0,0 +1,33 @@
config:
name: "request methods testcase: reference testcase"
variables:
foo1: testsuite_config_bar1
expect_foo1: testsuite_config_bar1
expect_foo2: config_bar2
base_url: "https://postman-echo.com"
verify: False
teststeps:
-
name: request with functions
variables:
foo1: testcase_ref_bar1
expect_foo1: testcase_ref_bar1
testcase: testcases/requests.yml
export:
- foo3
-
name: post form data
variables:
foo1: bar1
request:
method: POST
url: /post
headers:
User-Agent: ${get_user_agent()}
Content-Type: "application/x-www-form-urlencoded"
body: "foo1=$foo1&foo2=$foo3"
validate:
- eq: ["status_code", 200]
- eq: ["body.form.foo1", "bar1"]
- eq: ["body.form.foo2", "bar21"]

View File

@@ -0,0 +1,60 @@
# NOTE: Generated By HttpRunner v4.0.0
# FROM: testcases/ref_testcase.yml
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
from testcases.demo_requests_test import TestCaseDemoRequests as DemoRequests
class TestCaseDemoRefTestcase(HttpRunner):
config = (
Config("request methods testcase: reference testcase")
.variables(
**{
"foo1": "testsuite_config_bar1",
"expect_foo1": "testsuite_config_bar1",
"expect_foo2": "config_bar2",
}
)
.base_url("https://postman-echo.com")
.verify(False)
)
teststeps = [
Step(
RunTestCase("request with functions")
.with_variables(
**{"foo1": "testcase_ref_bar1", "expect_foo1": "testcase_ref_bar1"}
)
.call(DemoRequests)
.export(*["foo3"])
),
Step(
RunRequest("post form data")
.with_variables(**{"foo1": "bar1"})
.post("/post")
.with_headers(
**{
"User-Agent": "${get_user_agent()}",
"Content-Type": "application/x-www-form-urlencoded",
}
)
.with_data("foo1=$foo1&foo2=$foo3")
.validate()
.assert_equal("status_code", 200)
.assert_equal("body.form.foo1", "bar1")
.assert_equal("body.form.foo2", "bar21")
),
]
if __name__ == "__main__":
TestCaseDemoRefTestcase().test_start()

View File

@@ -0,0 +1,136 @@
{
"config": {
"name": "request methods testcase with functions",
"variables": {
"foo1": "config_bar1",
"foo2": "config_bar2",
"expect_foo1": "config_bar1",
"expect_foo2": "config_bar2"
},
"headers": {
"User-Agent": "${get_user_agent()}"
},
"base_url": "https://postman-echo.com",
"verify": false,
"export": [
"foo3"
]
},
"teststeps": [
{
"name": "get with params",
"variables": {
"foo1": "${ENV(USERNAME)}",
"foo2": "bar21",
"sum_v": "${sum_two_int(10000000, 20000000)}"
},
"request": {
"method": "GET",
"url": "/get",
"params": {
"foo1": "$foo1",
"foo2": "$foo2",
"sum_v": "$sum_v"
}
},
"extract": {
"foo3": "body.args.foo2"
},
"validate": [
{
"check": "status_code",
"assert": "equal",
"expect": 200,
"msg": "check status_code"
},
{
"check": "body.args.foo1",
"assert": "equal",
"expect": "debugtalk",
"msg": "check body.args.foo1"
},
{
"check": "body.args.sum_v",
"assert": "equal",
"expect": "30000000",
"msg": "check body.args.sum_v"
},
{
"check": "body.args.foo2",
"assert": "equal",
"expect": "bar21",
"msg": "check body.args.foo2"
}
]
},
{
"name": "post raw text",
"variables": {
"foo1": "bar12",
"foo3": "bar32"
},
"request": {
"method": "POST",
"url": "/post",
"headers": {
"Content-Type": "text/plain"
},
"body": "This is expected to be sent back as part of response body: $foo1-$foo2-$foo3."
},
"validate": [
{
"check": "status_code",
"assert": "equal",
"expect": 200,
"msg": "check status_code"
},
{
"check": "body.data",
"assert": "equal",
"expect": "This is expected to be sent back as part of response body: bar12-$expect_foo2-bar32.",
"msg": "check body.data"
}
]
},
{
"name": "post form data",
"variables": {
"foo2": "bar23"
},
"request": {
"method": "POST",
"url": "/post",
"headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"body": "foo1=$foo1&foo2=$foo2&foo3=$foo3"
},
"validate": [
{
"check": "status_code",
"assert": "equal",
"expect": 200,
"msg": "check status_code"
},
{
"check": "body.form.foo1",
"assert": "equal",
"expect": "$expect_foo1",
"msg": "check body.form.foo1"
},
{
"check": "body.form.foo2",
"assert": "equal",
"expect": "bar23",
"msg": "check body.form.foo2"
},
{
"check": "body.form.foo3",
"assert": "equal",
"expect": "bar21",
"msg": "check body.form.foo3"
}
]
}
]
}

View File

@@ -0,0 +1,62 @@
config:
name: "request methods testcase with functions"
variables:
foo1: config_bar1
foo2: config_bar2
expect_foo1: config_bar1
expect_foo2: config_bar2
headers:
User-Agent: ${get_user_agent()}
verify: False
export: ["foo3"]
teststeps:
-
name: get with params
variables:
foo1: ${ENV(USERNAME)}
foo2: bar21
sum_v: "${sum_two_int(10000000, 20000000)}"
request:
method: GET
url: $base_url/get
params:
foo1: $foo1
foo2: $foo2
sum_v: $sum_v
extract:
foo3: "body.args.foo2"
validate:
- eq: ["status_code", 200]
- eq: ["body.args.foo1", "debugtalk"]
- eq: ["body.args.sum_v", "30000000"]
- eq: ["body.args.foo2", "bar21"]
-
name: post raw text
variables:
foo1: "bar12"
foo3: "bar32"
request:
method: POST
url: $base_url/post
headers:
Content-Type: "text/plain"
body: "This is expected to be sent back as part of response body: $foo1-$foo2-$foo3."
validate:
- eq: ["status_code", 200]
- eq: ["body.data", "This is expected to be sent back as part of response body: bar12-$expect_foo2-bar32."]
-
name: post form data
variables:
foo2: bar23
request:
method: POST
url: $base_url/post
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "foo1=$foo1&foo2=$foo2&foo3=$foo3"
validate:
- eq: ["status_code", 200]
- eq: ["body.form.foo1", "$expect_foo1"]
- eq: ["body.form.foo2", "bar23"]
- eq: ["body.form.foo3", "bar21"]

View File

@@ -0,0 +1,83 @@
# NOTE: Generated By HttpRunner v4.0.0
# FROM: testcases/requests.yml
from httprunner import HttpRunner, Config, Step, RunRequest
class TestCaseDemoRequests(HttpRunner):
config = (
Config("request methods testcase with functions")
.variables(
**{
"foo1": "config_bar1",
"foo2": "config_bar2",
"expect_foo1": "config_bar1",
"expect_foo2": "config_bar2",
}
)
.base_url("https://postman-echo.com")
.verify(False)
.export(*["foo3"])
)
teststeps = [
Step(
RunRequest("get with params")
.with_variables(
**{"foo1": "bar11", "foo2": "bar21", "sum_v": "${sum_two_int(10000000, 20000000)}"}
)
.get("/get")
.with_params(**{"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"})
.with_headers(**{"User-Agent": "${get_user_agent()}"})
.extract()
.with_jmespath("body.args.foo2", "foo3")
.validate()
.assert_equal("status_code", 200)
.assert_equal("body.args.foo1", "bar11")
.assert_equal("body.args.sum_v", "30000000")
.assert_equal("body.args.foo2", "bar21")
),
Step(
RunRequest("post raw text")
.with_variables(**{"foo1": "bar12", "foo3": "bar32"})
.post("/post")
.with_headers(
**{
"User-Agent": "${get_user_agent()}",
"Content-Type": "text/plain",
}
)
.with_data(
"This is expected to be sent back as part of response body: $foo1-$foo2-$foo3."
)
.validate()
.assert_equal("status_code", 200)
.assert_equal(
"body.data",
"This is expected to be sent back as part of response body: bar12-$expect_foo2-bar32.",
)
),
Step(
RunRequest("post form data")
.with_variables(**{"foo2": "bar23"})
.post("/post")
.with_headers(
**{
"User-Agent": "${get_user_agent()}",
"Content-Type": "application/x-www-form-urlencoded",
}
)
.with_data("foo1=$foo1&foo2=$foo2&foo3=$foo3")
.validate()
.assert_equal("status_code", 200)
.assert_equal("body.form.foo1", "$expect_foo1")
.assert_equal("body.form.foo2", "bar23")
.assert_equal("body.form.foo3", "bar21")
),
]
if __name__ == "__main__":
TestCaseDemoRequests().test_start()

View File

@@ -0,0 +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,
"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

@@ -0,0 +1,114 @@
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

@@ -0,0 +1,170 @@
{
"config": {
"name": "demo without custom function plugin",
"base_url": "https://postman-echo.com",
"variables": {
"a": 12.3,
"b": 3.45,
"n": 5,
"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)}"
},
"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

@@ -0,0 +1,110 @@
config:
name: demo without custom function plugin
base_url: https://postman-echo.com
variables:
a: 12.3
b: 3.45
"n": 5
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)}
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