Merge pull request #52 from httprunner/scaffold

- feat: create scaffold project
- fix: report GA event with value to indicate failure
This commit is contained in:
debugtalk
2022-01-08 22:58:21 +08:00
committed by GitHub
27 changed files with 274 additions and 89 deletions

View File

@@ -27,7 +27,7 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
project_path: "./hrp" # go build ./hrp/main.go
project_path: "./cli/hrp" # go build ./cli/hrp/main.go
binary_name: "hrp"
ldflags: "-s -w"
extra_files: LICENSE README.md docs/CHANGELOG.md

28
.github/workflows/scaffold.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Run scaffold
on:
pull_request:
types: [synchronize]
jobs:
scaffold:
strategy:
fail-fast: true
matrix:
go-version:
- 1.17.x
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Build hrp binary
run: make build
- name: Run start project
run: ./output/hrp startproject demo
- name: Run demo tests
run: ./output/hrp run demo/testcases/demo.json demo/testcases/demo.yaml

View File

@@ -1,7 +1,6 @@
name: Run unittests
on:
push:
pull_request:
types: [synchronize]
schedule:

View File

@@ -11,7 +11,7 @@ test: ## run unit tests
.PHONY: build
build: ## build hrp cli tool
@echo "[info] build hrp cli tool"
@. hrp/scripts/build.sh
@. cli/scripts/build.sh
.PHONY: help
help: ## print make commands

View File

@@ -29,13 +29,13 @@ See [CHANGELOG].
You can install `hrp` with one shell command, which will download the latest version's released binary and install to the current system.
```bash
$ curl -sL https://raw.githubusercontent.com/httprunner/hrp/main/hrp/scripts/install.sh | bash
$ curl -sL https://raw.githubusercontent.com/httprunner/hrp/main/cli/scripts/install.sh | bash
```
If you are a golang developer, you can also install `hrp` with `go get`.
```bash
$ go get -u github.com/httprunner/hrp/hrp
$ go get -u github.com/httprunner/hrp/cli/hrp
```
Since installed, you will get a `hrp` command with multiple sub-commands.
@@ -62,11 +62,12 @@ Usage:
hrp [command]
Available Commands:
boom run load test with boomer
completion generate the autocompletion script for the specified shell
har2case Convert HAR to json/yaml testcase files
help Help about any command
run run API test
boom run load test with boomer
completion generate the autocompletion script for the specified shell
har2case convert HAR to json/yaml testcase files
help Help about any command
run run API test
startproject create a scaffold project
Flags:
-h, --help help for hrp

View File

@@ -55,7 +55,7 @@ var (
)
func init() {
RootCmd.AddCommand(boomCmd)
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.")

View File

@@ -4,13 +4,11 @@ import (
"testing"
"github.com/spf13/cobra/doc"
"github.com/httprunner/hrp/hrp/cmd"
)
// run this test to generate markdown docs
// run this test to generate markdown docs for hrp command
func TestGenMarkdownTree(t *testing.T) {
err := doc.GenMarkdownTree(cmd.RootCmd, "./")
err := doc.GenMarkdownTree(rootCmd, "../../../docs/cmd")
if err != nil {
t.Fatal(err)
}

View File

@@ -9,9 +9,9 @@ import (
// har2caseCmd represents the har2case command
var har2caseCmd = &cobra.Command{
Use: "har2case harPath...",
Short: "Convert HAR to json/yaml testcase files",
Long: `Convert HAR to json/yaml testcase files`,
Use: "har2case $har_path...",
Short: "convert HAR to json/yaml testcase files",
Long: `convert HAR to json/yaml testcase files`,
Args: cobra.MinimumNArgs(1),
PreRun: func(cmd *cobra.Command, args []string) {
setLogLevel(logLevel)
@@ -52,7 +52,7 @@ var (
)
func init() {
RootCmd.AddCommand(har2caseCmd)
rootCmd.AddCommand(har2caseCmd)
har2caseCmd.Flags().BoolVarP(&genJSONFlag, "to-json", "j", false, "convert to JSON format (default)")
har2caseCmd.Flags().BoolVarP(&genYAMLFlag, "to-yaml", "y", false, "convert to JSON format")
har2caseCmd.Flags().StringVarP(&outputDir, "output-dir", "d", "", "specify output directory, default to the same dir with har file")

View File

@@ -11,8 +11,8 @@ import (
"github.com/httprunner/hrp/internal/version"
)
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "hrp",
Short: "One-stop solution for HTTP(S) testing.",
Long: `
@@ -47,10 +47,10 @@ var (
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
RootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "l", "INFO", "set log level")
RootCmd.PersistentFlags().BoolVar(&logJSON, "log-json", false, "set log to json format")
rootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "l", "INFO", "set log level")
rootCmd.PersistentFlags().BoolVar(&logJSON, "log-json", false, "set log to json format")
if err := RootCmd.Execute(); err != nil {
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}

View File

@@ -10,7 +10,7 @@ import (
// runCmd represents the run command
var runCmd = &cobra.Command{
Use: "run path...",
Use: "run $path...",
Short: "run API test",
Long: `run yaml/json testcase files for API test`,
Example: ` $ hrp run demo.json # run specified json testcase file
@@ -45,7 +45,7 @@ var (
)
func init() {
RootCmd.AddCommand(runCmd)
rootCmd.AddCommand(runCmd)
runCmd.Flags().BoolVar(&continueOnFailure, "continue-on-failure", false, "continue running next step when failure occurs")
runCmd.Flags().BoolVarP(&silentFlag, "silent", "s", false, "disable logging request & response details")
runCmd.Flags().StringVarP(&proxyUrl, "proxy-url", "p", "", "set proxy url")

28
cli/hrp/cmd/scaffold.go Normal file
View File

@@ -0,0 +1,28 @@
package cmd
import (
"os"
"github.com/spf13/cobra"
"github.com/httprunner/hrp/internal/scaffold"
)
var scaffoldCmd = &cobra.Command{
Use: "startproject $project_name",
Short: "create a scaffold project",
Args: cobra.ExactValidArgs(1),
PreRun: func(cmd *cobra.Command, args []string) {
setLogLevel(logLevel)
},
Run: func(cmd *cobra.Command, args []string) {
err := scaffold.CreateScaffold(args[0])
if err != nil {
os.Exit(1)
}
},
}
func init() {
rootCmd.AddCommand(scaffoldCmd)
}

View File

@@ -1,7 +1,7 @@
package main
import (
"github.com/httprunner/hrp/hrp/cmd"
"github.com/httprunner/hrp/cli/hrp/cmd"
"github.com/httprunner/hrp/internal/sentry"
)

View File

@@ -5,7 +5,7 @@
# Usage:
# $ make build
# or
# $ bash hrp/scripts/build.sh
# $ bash cli/scripts/build.sh
set -e
set -x
@@ -15,7 +15,7 @@ mkdir -p "output"
bin_path="output/hrp"
# build
go build -ldflags '-s -w' -o "$bin_path" hrp/main.go
go build -ldflags '-s -w' -o "$bin_path" cli/hrp/main.go
# check output and version
ls -lh "$bin_path"

View File

@@ -1,6 +1,6 @@
#!/bin/bash
# install hrp with one shell command
# curl -sL https://raw.githubusercontent.com/httprunner/hrp/main/hrp/scripts/install.sh | bash
# curl -sL https://raw.githubusercontent.com/httprunner/hrp/main/cli/scripts/install.sh | bash
set -e

View File

@@ -1,9 +1,10 @@
# Release History
## v0.5.0 (2022-01-06)
## v0.5.0 (2022-01-08)
- feat: support creating and calling custom functions with [go plugin](https://pkg.go.dev/plugin)
- feat: install hrp with one shell command
- feat: add `startproject` sub-command for creating scaffold project
- feat: report GA event for loading go plugin
## v0.4.0 (2022-01-05)

View File

@@ -31,5 +31,6 @@ Copyright 2021 debugtalk
* [hrp boom](hrp_boom.md) - run load test with boomer
* [hrp har2case](hrp_har2case.md) - Convert HAR to json/yaml testcase files
* [hrp run](hrp_run.md) - run API test
* [hrp startproject](hrp_startproject.md) - Create a scaffold project
###### Auto generated by spf13/cobra on 7-Jan-2022
###### Auto generated by spf13/cobra on 8-Jan-2022

View File

@@ -38,4 +38,4 @@ hrp boom [flags]
* [hrp](hrp.md) - One-stop solution for HTTP(S) testing.
###### Auto generated by spf13/cobra on 7-Jan-2022
###### Auto generated by spf13/cobra on 8-Jan-2022

View File

@@ -7,7 +7,7 @@ Convert HAR to json/yaml testcase files
Convert HAR to json/yaml testcase files
```
hrp har2case harPath... [flags]
hrp har2case $har_path... [flags]
```
### Options
@@ -23,4 +23,4 @@ hrp har2case harPath... [flags]
* [hrp](hrp.md) - One-stop solution for HTTP(S) testing.
###### Auto generated by spf13/cobra on 7-Jan-2022
###### Auto generated by spf13/cobra on 8-Jan-2022

View File

@@ -7,7 +7,7 @@ run API test
run yaml/json testcase files for API test
```
hrp run path... [flags]
hrp run $path... [flags]
```
### Examples
@@ -31,4 +31,4 @@ hrp run path... [flags]
* [hrp](hrp.md) - One-stop solution for HTTP(S) testing.
###### Auto generated by spf13/cobra on 7-Jan-2022
###### Auto generated by spf13/cobra on 8-Jan-2022

View File

@@ -0,0 +1,19 @@
## hrp startproject
Create a scaffold project
```
hrp startproject $project_name [flags]
```
### Options
```
-h, --help help for startproject
```
### SEE ALSO
* [hrp](hrp.md) - One-stop solution for HTTP(S) testing.
###### Auto generated by spf13/cobra on 8-Jan-2022

View File

@@ -8,7 +8,7 @@ func TestSendEvents(t *testing.T) {
event := EventTracking{
Category: "unittest",
Action: "SendEvents",
Value: "123",
Value: 123,
}
err := gaClient.SendEvent(event)
if err != nil {
@@ -21,7 +21,7 @@ func TestStructToUrlValues(t *testing.T) {
Category: "unittest",
Action: "convert",
Label: "v0.3.0",
Value: "123",
Value: 123,
}
val := structToUrlValues(event)
if val.Encode() != "ea=convert&ec=unittest&el=v0.3.0&ev=123" {

View File

@@ -17,7 +17,7 @@ type EventTracking struct {
Category string `form:"ec"` // Required. Event Category.
Action string `form:"ea"` // Required. Event Action.
Label string `form:"el"` // Optional. Event label, used as version.
Value string `form:"ev"` // Optional. Event value, must be digits, "123"
Value int `form:"ev"` // Optional. Event value, must be non-negative integer
}
func (e EventTracking) StartTiming(variable string) UserTimingTracking {

View File

@@ -1,11 +1,6 @@
package examples
package scaffold
import (
"fmt"
"testing"
"github.com/httprunner/hrp"
)
import "github.com/httprunner/hrp"
var demoTestCase = &hrp.TestCase{
Config: hrp.NewConfig("demo with complex mechanisms").
@@ -61,42 +56,17 @@ var demoTestCase = &hrp.TestCase{
},
}
var (
demoTestCaseJSONPath = "demo.json"
demoTestCaseYAMLPath = "demo.yaml"
)
// .gitignore
var demoIgnoreContent = `.env
reports/*
*.so
.vscode/
.idea/
.DS_Store
output/
`
func TestGenDemoTestCase(t *testing.T) {
tCase, _ := demoTestCase.ToTCase()
err := tCase.Dump2JSON(demoTestCaseJSONPath)
if err != nil {
t.Fail()
}
err = tCase.Dump2YAML(demoTestCaseYAMLPath)
if err != nil {
t.Fail()
}
}
func Example_demo() {
err := hrp.NewRunner(nil).Run(demoTestCase) // hrp.Run(demoTestCase)
fmt.Println(err)
// Output:
// <nil>
}
func Example_jsonDemo() {
testCase := &hrp.TestCasePath{Path: demoTestCaseJSONPath}
err := hrp.NewRunner(nil).Run(testCase) // hrp.Run(testCase)
fmt.Println(err)
// Output:
// <nil>
}
func Example_yamlDemo() {
testCase := &hrp.TestCasePath{Path: demoTestCaseYAMLPath}
err := hrp.NewRunner(nil).Run(testCase) // hrp.Run(testCase)
fmt.Println(err)
// Output:
// <nil>
}
// .env
var demoEnvContent = `USERNAME=debugtalk
"PASSWORD=123456
`

View File

@@ -0,0 +1,48 @@
package scaffold
import (
"fmt"
"testing"
"github.com/httprunner/hrp"
)
var (
demoTestCaseJSONPath = "../../examples/demo.json"
demoTestCaseYAMLPath = "../../examples/demo.yaml"
)
func TestGenDemoTestCase(t *testing.T) {
tCase, _ := demoTestCase.ToTCase()
err := tCase.Dump2JSON(demoTestCaseJSONPath)
if err != nil {
t.Fail()
}
err = tCase.Dump2YAML(demoTestCaseYAMLPath)
if err != nil {
t.Fail()
}
}
func Example_demo() {
err := hrp.NewRunner(nil).Run(demoTestCase) // hrp.Run(demoTestCase)
fmt.Println(err)
// Output:
// <nil>
}
func Example_jsonDemo() {
testCase := &hrp.TestCasePath{Path: demoTestCaseJSONPath}
err := hrp.NewRunner(nil).Run(testCase) // hrp.Run(testCase)
fmt.Println(err)
// Output:
// <nil>
}
func Example_yamlDemo() {
testCase := &hrp.TestCasePath{Path: demoTestCaseYAMLPath}
err := hrp.NewRunner(nil).Run(testCase) // hrp.Run(testCase)
fmt.Println(err)
// Output:
// <nil>
}

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

@@ -0,0 +1,86 @@
package scaffold
import (
"fmt"
"io/ioutil"
"os"
"path"
"github.com/httprunner/hrp/internal/ga"
"github.com/rs/zerolog/log"
)
func CreateScaffold(projectName string) error {
// report event
ga.SendEvent(ga.EventTracking{
Category: "Scaffold",
Action: "hrp startproject",
})
// check if projectName exists
if _, err := os.Stat(projectName); err == nil {
log.Warn().Str("projectName", projectName).
Msg("project name already exists, please specify a new one.")
return fmt.Errorf("project name already exists")
}
log.Info().Str("projectName", projectName).Msg("create new scaffold project")
// create project folders
if err := createFolder(projectName); err != nil {
return err
}
if err := createFolder(path.Join(projectName, "har")); err != nil {
return err
}
if err := createFolder(path.Join(projectName, "testcases")); err != nil {
return err
}
if err := createFolder(path.Join(projectName, "reports")); err != nil {
return err
}
// create demo testcases
tCase, _ := demoTestCase.ToTCase()
err := tCase.Dump2JSON(path.Join(projectName, "testcases", "demo.json"))
if err != nil {
log.Error().Err(err).Msg("create demo.json testcase failed")
return err
}
err = tCase.Dump2YAML(path.Join(projectName, "testcases", "demo.yaml"))
if err != nil {
log.Error().Err(err).Msg("create demo.yml testcase failed")
return err
}
// create .gitignore
if err := createFile(path.Join(projectName, ".gitignore"), demoIgnoreContent); err != nil {
return err
}
// create .env
if err := createFile(path.Join(projectName, ".env"), demoEnvContent); err != nil {
return err
}
return nil
}
func createFolder(folderPath string) error {
log.Info().Str("path", folderPath).Msg("create folder")
err := os.MkdirAll(folderPath, os.ModePerm)
if err != nil {
log.Error().Err(err).Msg("create folder failed")
return err
}
return nil
}
func createFile(filePath string, data string) error {
log.Info().Str("path", filePath).Msg("create file")
err := ioutil.WriteFile(filePath, []byte(data), 0o644)
if err != nil {
log.Error().Err(err).Msg("create file failed")
return err
}
return nil
}

View File

@@ -35,10 +35,16 @@ func (p *parser) loadPlugin(path string) error {
}
// report event for loading go plugin
go ga.SendEvent(ga.EventTracking{
Category: "LoadGoPlugin",
Action: "plugin.Open",
})
defer func() {
event := ga.EventTracking{
Category: "LoadGoPlugin",
Action: "plugin.Open",
}
if err != nil {
event.Value = 1 // failed
}
go ga.SendEvent(event)
}()
// load plugin
plugins, err := plugin.Open(pluginPath)