mirror of
https://github.com/httprunner/httprunner.git
synced 2026-06-06 08:19:45 +08:00
Merge branch 'master' of github.com:httprunner/httprunner
This commit is contained in:
@@ -1,12 +1,15 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/boomer"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
||||
)
|
||||
|
||||
// boomCmd represents the boom command
|
||||
@@ -28,57 +31,70 @@ var boomCmd = &cobra.Command{
|
||||
path := hrp.TestCasePath(arg)
|
||||
paths = append(paths, &path)
|
||||
}
|
||||
hrpBoomer := hrp.NewBoomer(spawnCount, spawnRate)
|
||||
hrpBoomer.SetRateLimiter(maxRPS, requestIncreaseRate)
|
||||
if loopCount > 0 {
|
||||
hrpBoomer.SetLoopCount(loopCount)
|
||||
// if set profile, the priority is higher than the other commands
|
||||
if boomArgs.profile != "" {
|
||||
err := builtin.LoadFile(boomArgs.profile, &boomArgs)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to load profile")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
if !disableConsoleOutput {
|
||||
|
||||
hrpBoomer := hrp.NewBoomer(boomArgs.SpawnCount, boomArgs.SpawnRate)
|
||||
hrpBoomer.SetRateLimiter(boomArgs.MaxRPS, boomArgs.RequestIncreaseRate)
|
||||
if boomArgs.LoopCount > 0 {
|
||||
hrpBoomer.SetLoopCount(boomArgs.LoopCount)
|
||||
}
|
||||
if !boomArgs.DisableConsoleOutput {
|
||||
hrpBoomer.AddOutput(boomer.NewConsoleOutput())
|
||||
}
|
||||
if prometheusPushgatewayURL != "" {
|
||||
hrpBoomer.AddOutput(boomer.NewPrometheusPusherOutput(prometheusPushgatewayURL, "hrp", hrpBoomer.GetMode()))
|
||||
if boomArgs.PrometheusPushgatewayURL != "" {
|
||||
hrpBoomer.AddOutput(boomer.NewPrometheusPusherOutput(boomArgs.PrometheusPushgatewayURL, "hrp", hrpBoomer.GetMode()))
|
||||
}
|
||||
hrpBoomer.SetDisableKeepAlive(disableKeepalive)
|
||||
hrpBoomer.SetDisableCompression(disableCompression)
|
||||
hrpBoomer.SetDisableKeepAlive(boomArgs.DisableKeepalive)
|
||||
hrpBoomer.SetDisableCompression(boomArgs.DisableCompression)
|
||||
hrpBoomer.SetClientTransport()
|
||||
hrpBoomer.EnableCPUProfile(cpuProfile, cpuProfileDuration)
|
||||
hrpBoomer.EnableMemoryProfile(memoryProfile, memoryProfileDuration)
|
||||
hrpBoomer.EnableCPUProfile(boomArgs.CPUProfile, boomArgs.CPUProfileDuration)
|
||||
hrpBoomer.EnableMemoryProfile(boomArgs.MemoryProfile, boomArgs.MemoryProfileDuration)
|
||||
hrpBoomer.EnableGracefulQuit()
|
||||
hrpBoomer.Run(paths...)
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
spawnCount int
|
||||
spawnRate float64
|
||||
maxRPS int64
|
||||
loopCount int64
|
||||
requestIncreaseRate string
|
||||
memoryProfile string
|
||||
memoryProfileDuration time.Duration
|
||||
cpuProfile string
|
||||
cpuProfileDuration time.Duration
|
||||
prometheusPushgatewayURL string
|
||||
disableConsoleOutput bool
|
||||
disableCompression bool
|
||||
disableKeepalive bool
|
||||
)
|
||||
type BoomArgs struct {
|
||||
SpawnCount int `json:"spawn-count,omitempty" yaml:"spawn-count,omitempty"`
|
||||
SpawnRate float64 `json:"spawn-rate,omitempty" yaml:"spawn-rate,omitempty"`
|
||||
MaxRPS int64 `json:"max-rps,omitempty" yaml:"max-rps,omitempty"`
|
||||
LoopCount int64 `json:"loop-count,omitempty" yaml:"loop-count,omitempty"`
|
||||
RequestIncreaseRate string `json:"request-increase-rate,omitempty" yaml:"request-increase-rate,omitempty"`
|
||||
MemoryProfile string `json:"memory-profile,omitempty" yaml:"memory-profile,omitempty"`
|
||||
MemoryProfileDuration time.Duration `json:"memory-profile-duration" yaml:"memory-profile-duration"`
|
||||
CPUProfile string `json:"cpu-profile,omitempty" yaml:"cpu-profile,omitempty"`
|
||||
CPUProfileDuration time.Duration `json:"cpu-profile-duration,omitempty" yaml:"cpu-profile-duration,omitempty"`
|
||||
PrometheusPushgatewayURL string `json:"prometheus-gateway,omitempty" yaml:"prometheus-gateway,omitempty"`
|
||||
DisableConsoleOutput bool `json:"disable-console-output,omitempty" yaml:"disable-console-output,omitempty"`
|
||||
DisableCompression bool `json:"disable-compression,omitempty" yaml:"disable-compression,omitempty"`
|
||||
DisableKeepalive bool `json:"disable-keepalive,omitempty" yaml:"disable-keepalive,omitempty"`
|
||||
profile string
|
||||
}
|
||||
|
||||
var boomArgs BoomArgs
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(boomCmd)
|
||||
|
||||
boomCmd.Flags().Int64Var(&maxRPS, "max-rps", 0, "Max RPS that boomer can generate, disabled by default.")
|
||||
boomCmd.Flags().StringVar(&requestIncreaseRate, "request-increase-rate", "-1", "Request increase rate, disabled by default.")
|
||||
boomCmd.Flags().IntVar(&spawnCount, "spawn-count", 1, "The number of users to spawn for load testing")
|
||||
boomCmd.Flags().Float64Var(&spawnRate, "spawn-rate", 1, "The rate for spawning users")
|
||||
boomCmd.Flags().Int64Var(&loopCount, "loop-count", -1, "The specify running cycles for load testing")
|
||||
boomCmd.Flags().StringVar(&memoryProfile, "mem-profile", "", "Enable memory profiling.")
|
||||
boomCmd.Flags().DurationVar(&memoryProfileDuration, "mem-profile-duration", 30*time.Second, "Memory profile duration.")
|
||||
boomCmd.Flags().StringVar(&cpuProfile, "cpu-profile", "", "Enable CPU profiling.")
|
||||
boomCmd.Flags().DurationVar(&cpuProfileDuration, "cpu-profile-duration", 30*time.Second, "CPU profile duration.")
|
||||
boomCmd.Flags().StringVar(&prometheusPushgatewayURL, "prometheus-gateway", "", "Prometheus Pushgateway url.")
|
||||
boomCmd.Flags().BoolVar(&disableConsoleOutput, "disable-console-output", false, "Disable console output.")
|
||||
boomCmd.Flags().BoolVar(&disableCompression, "disable-compression", false, "Disable compression")
|
||||
boomCmd.Flags().BoolVar(&disableKeepalive, "disable-keepalive", false, "Disable keepalive")
|
||||
boomCmd.Flags().Int64Var(&boomArgs.MaxRPS, "max-rps", 0, "Max RPS that boomer can generate, disabled by default.")
|
||||
boomCmd.Flags().StringVar(&boomArgs.RequestIncreaseRate, "request-increase-rate", "-1", "Request increase rate, disabled by default.")
|
||||
boomCmd.Flags().IntVar(&boomArgs.SpawnCount, "spawn-count", 1, "The number of users to spawn for load testing")
|
||||
boomCmd.Flags().Float64Var(&boomArgs.SpawnRate, "spawn-rate", 1, "The rate for spawning users")
|
||||
boomCmd.Flags().Int64Var(&boomArgs.LoopCount, "loop-count", -1, "The specify running cycles for load testing")
|
||||
boomCmd.Flags().StringVar(&boomArgs.MemoryProfile, "mem-profile", "", "Enable memory profiling.")
|
||||
boomCmd.Flags().DurationVar(&boomArgs.MemoryProfileDuration, "mem-profile-duration", 30*time.Second, "Memory profile duration.")
|
||||
boomCmd.Flags().StringVar(&boomArgs.CPUProfile, "cpu-profile", "", "Enable CPU profiling.")
|
||||
boomCmd.Flags().DurationVar(&boomArgs.CPUProfileDuration, "cpu-profile-duration", 30*time.Second, "CPU profile duration.")
|
||||
boomCmd.Flags().StringVar(&boomArgs.PrometheusPushgatewayURL, "prometheus-gateway", "", "Prometheus Pushgateway url.")
|
||||
boomCmd.Flags().BoolVar(&boomArgs.DisableConsoleOutput, "disable-console-output", false, "Disable console output.")
|
||||
boomCmd.Flags().BoolVar(&boomArgs.DisableCompression, "disable-compression", false, "Disable compression")
|
||||
boomCmd.Flags().BoolVar(&boomArgs.DisableKeepalive, "disable-keepalive", false, "Disable keepalive")
|
||||
boomCmd.Flags().StringVar(&boomArgs.profile, "profile", "", "profile for load testing")
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@ import (
|
||||
)
|
||||
|
||||
var scaffoldCmd = &cobra.Command{
|
||||
Use: "startproject $project_name",
|
||||
Short: "create a scaffold project",
|
||||
Args: cobra.ExactValidArgs(1),
|
||||
Use: "startproject $project_name",
|
||||
Aliases: []string{"scaffold"},
|
||||
Short: "create a scaffold project",
|
||||
Args: cobra.ExactValidArgs(1),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
setLogLevel(logLevel)
|
||||
},
|
||||
|
||||
23
hrp/cmd/wiki.go
Normal file
23
hrp/cmd/wiki.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/wiki"
|
||||
)
|
||||
|
||||
var wikiCmd = &cobra.Command{
|
||||
Use: "wiki",
|
||||
Aliases: []string{"info", "docs", "doc"},
|
||||
Short: "visit https://httprunner.com",
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
setLogLevel(logLevel)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return wiki.OpenWiki()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(wikiCmd)
|
||||
}
|
||||
@@ -39,11 +39,11 @@ const (
|
||||
)
|
||||
|
||||
func fmta(d time.Duration) string {
|
||||
return color.BlueString("%7dms", int(d.Milliseconds()))
|
||||
return color.YellowString("%7dms", int(d.Milliseconds()))
|
||||
}
|
||||
|
||||
func fmtb(d time.Duration) string {
|
||||
return color.MagentaString("%-9s", strconv.Itoa(int(d.Milliseconds()))+"ms")
|
||||
return color.RedString("%-9s", strconv.Itoa(int(d.Milliseconds()))+"ms")
|
||||
}
|
||||
|
||||
func grayscale(code color.Attribute) func(string, ...interface{}) string {
|
||||
@@ -137,7 +137,7 @@ func (s *Stat) Print() {
|
||||
if s.network != "" && s.addr != "" {
|
||||
printf("\n%s %s: %s\n",
|
||||
color.CyanString("Connected to"),
|
||||
color.YellowString(s.network),
|
||||
color.MagentaString(s.network),
|
||||
color.BlueString(s.addr),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/httprunner/funplugin/shared"
|
||||
"github.com/pkg/errors"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/version"
|
||||
)
|
||||
|
||||
type PluginType string
|
||||
@@ -23,6 +25,13 @@ const (
|
||||
Go PluginType = "go"
|
||||
)
|
||||
|
||||
type ProjectInfo struct {
|
||||
ProjectName string `json:"project_name,omitempty" yaml:"project_name,omitempty"`
|
||||
ProjectPath string `json:"project_path,omitempty" yaml:"project_path,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
|
||||
|
||||
@@ -68,6 +77,12 @@ func CreateScaffold(projectName string, pluginType PluginType, force bool) error
|
||||
os.RemoveAll(projectName)
|
||||
}
|
||||
|
||||
// get project abs path
|
||||
projectPath, err := filepath.Abs(projectName)
|
||||
if err != nil {
|
||||
projectPath = projectName
|
||||
}
|
||||
|
||||
// create project folders
|
||||
if err := builtin.CreateFolder(projectName); err != nil {
|
||||
return err
|
||||
@@ -88,8 +103,21 @@ func CreateScaffold(projectName string, pluginType PluginType, force bool) error
|
||||
return err
|
||||
}
|
||||
|
||||
projectInfo := &ProjectInfo{
|
||||
ProjectName: projectName,
|
||||
ProjectPath: projectPath,
|
||||
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"))
|
||||
err = CopyFile("templates/gitignore", filepath.Join(projectName, ".gitignore"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -102,7 +130,7 @@ func CreateScaffold(projectName string, pluginType PluginType, force bool) error
|
||||
// create demo testcases
|
||||
if pluginType == Ignore {
|
||||
err := CopyFile("templates/testcases/demo_without_funplugin.json",
|
||||
filepath.Join(projectName, "testcases", "demo_without_funplugin.json"))
|
||||
filepath.Join(projectName, "testcases", "requests.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -111,17 +139,22 @@ func CreateScaffold(projectName string, pluginType PluginType, force bool) error
|
||||
}
|
||||
|
||||
err = CopyFile("templates/testcases/demo_with_funplugin.json",
|
||||
filepath.Join(projectName, "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", "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", "demo_ref_testcase.yml"))
|
||||
filepath.Join(projectName, "testcases", "ref_testcase.yml"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ teststeps:
|
||||
variables:
|
||||
foo1: testcase_ref_bar1
|
||||
expect_foo1: testcase_ref_bar1
|
||||
testcase: testcases/demo_requests.yml
|
||||
testcase: testcases/requests.yml
|
||||
export:
|
||||
- foo3
|
||||
-
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# NOTE: Generated By HttpRunner v4.0.0
|
||||
# FROM: testcases/demo_ref_testcase.yml
|
||||
# FROM: testcases/ref_testcase.yml
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
138
hrp/internal/scaffold/templates/testcases/demo_requests.json
Normal file
138
hrp/internal/scaffold/templates/testcases/demo_requests.json
Normal file
@@ -0,0 +1,138 @@
|
||||
{
|
||||
"config": {
|
||||
"name": "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": [
|
||||
{
|
||||
"name": "get with params",
|
||||
"variables": {
|
||||
"foo1": "bar11",
|
||||
"foo2": "bar21",
|
||||
"sum_v": "${sum_two_int(1, 2)}"
|
||||
},
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "/get",
|
||||
"params": {
|
||||
"foo1": "$foo1",
|
||||
"foo2": "$foo2",
|
||||
"sum_v": "$sum_v"
|
||||
},
|
||||
"headers": {
|
||||
"User-Agent": "funplugin/${get_version()}"
|
||||
}
|
||||
},
|
||||
"extract": {
|
||||
"foo3": "body.args.foo2"
|
||||
},
|
||||
"validate": [
|
||||
{
|
||||
"eq": [
|
||||
"status_code",
|
||||
200
|
||||
]
|
||||
},
|
||||
{
|
||||
"eq": [
|
||||
"body.args.foo1",
|
||||
"bar11"
|
||||
]
|
||||
},
|
||||
{
|
||||
"eq": [
|
||||
"body.args.sum_v",
|
||||
"3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"eq": [
|
||||
"body.args.foo2",
|
||||
"bar21"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "post raw text",
|
||||
"variables": {
|
||||
"foo1": "bar12",
|
||||
"foo3": "bar32"
|
||||
},
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "/post",
|
||||
"headers": {
|
||||
"User-Agent": "funplugin/${get_version()}",
|
||||
"Content-Type": "text/plain"
|
||||
},
|
||||
"data": "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": "/post",
|
||||
"headers": {
|
||||
"User-Agent": "funplugin/${get_version()}",
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
},
|
||||
"data": "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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
# NOTE: Generated By HttpRunner v4.0.0
|
||||
# FROM: testcases/demo_requests.yml
|
||||
# FROM: testcases/requests.yml
|
||||
|
||||
|
||||
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
|
||||
from httprunner import HttpRunner, Config, Step, RunRequest
|
||||
|
||||
|
||||
class TestCaseDemoRequests(HttpRunner):
|
||||
|
||||
@@ -1 +1 @@
|
||||
v4.1.0-alpha
|
||||
v4.1.0-beta
|
||||
12
hrp/internal/wiki/main.go
Normal file
12
hrp/internal/wiki/main.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package wiki
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func OpenWiki() error {
|
||||
log.Info().Msgf("%s https://httprunner.com", openCmd)
|
||||
return exec.Command(openCmd, "https://httprunner.com").Run()
|
||||
}
|
||||
3
hrp/internal/wiki/open_darwin.go
Normal file
3
hrp/internal/wiki/open_darwin.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package wiki
|
||||
|
||||
const openCmd = "open"
|
||||
3
hrp/internal/wiki/open_linux.go
Normal file
3
hrp/internal/wiki/open_linux.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package wiki
|
||||
|
||||
const openCmd = "xdg-open"
|
||||
3
hrp/internal/wiki/open_windows.go
Normal file
3
hrp/internal/wiki/open_windows.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package wiki
|
||||
|
||||
const openCmd = "explorer"
|
||||
@@ -12,17 +12,17 @@ import (
|
||||
)
|
||||
|
||||
type TParamsConfig struct {
|
||||
Strategy iteratorStrategy `json:"strategy,omitempty" yaml:"strategy,omitempty"` // overall strategy
|
||||
PickOrder iteratorPickOrder `json:"strategy,omitempty" yaml:"strategy,omitempty"` // overall pick-order strategy
|
||||
Strategies map[string]iteratorStrategy `json:"strategies,omitempty" yaml:"strategies,omitempty"` // individual strategies for each parameters
|
||||
Limit int `json:"limit,omitempty" yaml:"limit,omitempty"`
|
||||
}
|
||||
|
||||
type iteratorStrategy string
|
||||
type iteratorPickOrder string
|
||||
|
||||
const (
|
||||
strategySequential iteratorStrategy = "sequential"
|
||||
strategyRandom iteratorStrategy = "random"
|
||||
strategyUnique iteratorStrategy = "unique"
|
||||
pickOrderSequential iteratorPickOrder = "sequential"
|
||||
pickOrderRandom iteratorPickOrder = "random"
|
||||
pickOrderUnique iteratorPickOrder = "unique"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -33,6 +33,11 @@ const (
|
||||
*/
|
||||
type Parameters []map[string]interface{}
|
||||
|
||||
type iteratorStrategy struct {
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
PickOrder iteratorPickOrder `json:"pick_order,omitempty" yaml:"pick_order,omitempty"`
|
||||
}
|
||||
|
||||
func initParametersIterator(cfg *TConfig) (*ParametersIterator, error) {
|
||||
parameters, err := loadParameters(cfg.Parameters, cfg.Variables)
|
||||
if err != nil {
|
||||
@@ -62,15 +67,15 @@ func newParametersIterator(parameters map[string]Parameters, config *TParamsConf
|
||||
|
||||
parametersList := make([]Parameters, 0)
|
||||
for paramName := range parameters {
|
||||
// check parameter individual strategy
|
||||
// check parameter individual pick order strategy
|
||||
strategy, ok := config.Strategies[paramName]
|
||||
if !ok {
|
||||
// default to overall strategy
|
||||
strategy = config.Strategy
|
||||
// default to overall pick order strategy
|
||||
strategy.PickOrder = config.PickOrder
|
||||
}
|
||||
|
||||
// group parameters by strategy
|
||||
if strategy == strategyRandom {
|
||||
// group parameters by pick order strategy
|
||||
if strategy.PickOrder == pickOrderRandom {
|
||||
iterator.randomParameterNames = append(iterator.randomParameterNames, paramName)
|
||||
} else {
|
||||
parametersList = append(parametersList, parameters[paramName])
|
||||
|
||||
@@ -137,25 +137,25 @@ func TestInitParametersIteratorCount(t *testing.T) {
|
||||
},
|
||||
6, // 3 * 2 * 1
|
||||
},
|
||||
// default equals to set overall parameters strategy to "sequential"
|
||||
// default equals to set overall parameters pick-order to "sequential"
|
||||
{
|
||||
&TConfig{
|
||||
Parameters: configParameters,
|
||||
ParametersSetting: &TParamsConfig{
|
||||
Strategy: "sequential",
|
||||
PickOrder: "sequential",
|
||||
},
|
||||
},
|
||||
6, // 3 * 2 * 1
|
||||
},
|
||||
// default equals to set each individual parameters strategy to "sequential"
|
||||
// default equals to set each individual parameters pick-order to "sequential"
|
||||
{
|
||||
&TConfig{
|
||||
Parameters: configParameters,
|
||||
ParametersSetting: &TParamsConfig{
|
||||
Strategies: map[string]iteratorStrategy{
|
||||
"username-password": "sequential",
|
||||
"user_agent": "sequential",
|
||||
"app_version": "sequential",
|
||||
"username-password": {Name: "user-info", PickOrder: "sequential"},
|
||||
"user_agent": {Name: "user-identity", PickOrder: "sequential"},
|
||||
"app_version": {Name: "app-version", PickOrder: "sequential"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -166,33 +166,33 @@ func TestInitParametersIteratorCount(t *testing.T) {
|
||||
Parameters: configParameters,
|
||||
ParametersSetting: &TParamsConfig{
|
||||
Strategies: map[string]iteratorStrategy{
|
||||
"user_agent": "sequential",
|
||||
"app_version": "sequential",
|
||||
"user_agent": {Name: "user-identity", PickOrder: "sequential"},
|
||||
"app_version": {Name: "app-version", PickOrder: "sequential"},
|
||||
},
|
||||
},
|
||||
},
|
||||
6, // 3 * 2 * 1
|
||||
},
|
||||
|
||||
// set overall parameters overall strategy to "random"
|
||||
// set overall parameters overall pick-order to "random"
|
||||
// each random parameters only select one item
|
||||
{
|
||||
&TConfig{
|
||||
Parameters: configParameters,
|
||||
ParametersSetting: &TParamsConfig{
|
||||
Strategy: "random",
|
||||
PickOrder: "random",
|
||||
},
|
||||
},
|
||||
1, // 1 * 1 * 1
|
||||
},
|
||||
// set some individual parameters strategy to "random"
|
||||
// set some individual parameters pick-order to "random"
|
||||
// this will override overall strategy
|
||||
{
|
||||
&TConfig{
|
||||
Parameters: configParameters,
|
||||
ParametersSetting: &TParamsConfig{
|
||||
Strategies: map[string]iteratorStrategy{
|
||||
"user_agent": "random",
|
||||
"user_agent": {Name: "user-identity", PickOrder: "random"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -203,7 +203,7 @@ func TestInitParametersIteratorCount(t *testing.T) {
|
||||
Parameters: configParameters,
|
||||
ParametersSetting: &TParamsConfig{
|
||||
Strategies: map[string]iteratorStrategy{
|
||||
"username-password": "random",
|
||||
"username-password": {Name: "user-info", PickOrder: "random"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -349,7 +349,7 @@ func TestInitParametersIteratorContent(t *testing.T) {
|
||||
ParametersSetting: &TParamsConfig{
|
||||
Limit: 5, // limit could also be greater than total
|
||||
Strategies: map[string]iteratorStrategy{
|
||||
"username-password": "random",
|
||||
"username-password": {Name: "user-info", PickOrder: "random"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -78,11 +78,11 @@ func locatePlugin(path string) (pluginPath string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Warn().Err(err).Str("path", path).Msg("plugin file not found")
|
||||
return "", fmt.Errorf("plugin file not found")
|
||||
}
|
||||
|
||||
// locateFile searches destFile upward recursively until current
|
||||
// working directory or system root dir.
|
||||
// locateFile searches destFile upward recursively until system root dir
|
||||
func locateFile(startPath string, destFile string) (string, error) {
|
||||
stat, err := os.Stat(startPath)
|
||||
if os.IsNotExist(err) {
|
||||
@@ -103,12 +103,6 @@ func locateFile(startPath string, destFile string) (string, error) {
|
||||
return pluginPath, nil
|
||||
}
|
||||
|
||||
// current working directory
|
||||
cwd, _ := os.Getwd()
|
||||
if startDir == cwd {
|
||||
return "", fmt.Errorf("searched to CWD, plugin file not found")
|
||||
}
|
||||
|
||||
// system root dir
|
||||
parentDir, _ := filepath.Abs(filepath.Dir(startDir))
|
||||
if parentDir == startDir {
|
||||
@@ -118,7 +112,7 @@ func locateFile(startPath string, destFile string) (string, error) {
|
||||
return locateFile(parentDir, destFile)
|
||||
}
|
||||
|
||||
func getProjectRootDirPath(path string) (rootDir string, err error) {
|
||||
func GetProjectRootDirPath(path string) (rootDir string, err error) {
|
||||
pluginPath, err := locatePlugin(path)
|
||||
if err == nil {
|
||||
rootDir = filepath.Dir(pluginPath)
|
||||
|
||||
@@ -207,7 +207,7 @@ func TestLoadTestCases(t *testing.T) {
|
||||
if !assert.Nil(t, err) {
|
||||
t.Fatal()
|
||||
}
|
||||
if !assert.Equal(t, len(testCases), 3) {
|
||||
if !assert.Equal(t, 4, len(testCases)) {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ func TestLoadTestCases(t *testing.T) {
|
||||
if !assert.Nil(t, err) {
|
||||
t.Fatal()
|
||||
}
|
||||
if !assert.Equal(t, len(testCases), 3) {
|
||||
if !assert.Equal(t, len(testCases), 4) {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ func (path *TestCasePath) ToTestCase() (*TestCase, error) {
|
||||
}
|
||||
|
||||
// locate project root dir by plugin path
|
||||
projectRootDir, err := getProjectRootDirPath(casePath)
|
||||
projectRootDir, err := GetProjectRootDirPath(casePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get project root dir")
|
||||
}
|
||||
@@ -361,8 +361,8 @@ func LoadTestCases(iTestCases ...ITestCase) ([]*TestCase, error) {
|
||||
testCasePath := TestCasePath(path)
|
||||
tc, err := testCasePath.ToTestCase()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("path", path).Msg("load testcase failed")
|
||||
return errors.Wrap(err, "load testcase failed")
|
||||
log.Warn().Err(err).Str("path", path).Msg("load testcase failed")
|
||||
return nil
|
||||
}
|
||||
testCases = append(testCases, tc)
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user