mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-10 17:43:00 +08:00
231 lines
5.3 KiB
Go
231 lines
5.3 KiB
Go
package convert
|
|
|
|
import (
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
hrp "github.com/httprunner/httprunner/v5"
|
|
"github.com/httprunner/httprunner/v5/internal/builtin"
|
|
"github.com/httprunner/httprunner/v5/internal/sdk"
|
|
)
|
|
|
|
// target testcase format extensions
|
|
const (
|
|
suffixJSON = ".json"
|
|
suffixYAML = ".yaml"
|
|
suffixGoTest = ".go"
|
|
suffixPyTest = ".py"
|
|
suffixHAR = ".har"
|
|
)
|
|
|
|
type FromType int
|
|
|
|
const (
|
|
FromTypeJSON FromType = iota
|
|
FromTypeYAML
|
|
FromTypeHAR
|
|
FromTypePostman
|
|
FromTypeCurl
|
|
FromTypeSwagger
|
|
FromTypePyest
|
|
FromTypeGotest
|
|
)
|
|
|
|
func (fromType FromType) String() string {
|
|
switch fromType {
|
|
case FromTypeYAML:
|
|
return "yaml"
|
|
case FromTypeHAR:
|
|
return "har"
|
|
case FromTypePostman:
|
|
return "postman"
|
|
case FromTypeSwagger:
|
|
return "swagger"
|
|
case FromTypeCurl:
|
|
return "curl"
|
|
case FromTypeGotest:
|
|
return "gotest"
|
|
case FromTypePyest:
|
|
return "pytest"
|
|
default:
|
|
return "json"
|
|
}
|
|
}
|
|
|
|
func (fromType FromType) Extensions() []string {
|
|
switch fromType {
|
|
case FromTypeYAML:
|
|
return []string{suffixYAML, ".yml"}
|
|
case FromTypeHAR:
|
|
return []string{suffixHAR}
|
|
case FromTypePostman, FromTypeSwagger:
|
|
return []string{suffixJSON}
|
|
case FromTypeCurl:
|
|
return []string{".txt", ".curl"}
|
|
case FromTypeGotest:
|
|
return []string{suffixGoTest}
|
|
case FromTypePyest:
|
|
return []string{suffixPyTest}
|
|
default:
|
|
return []string{suffixJSON}
|
|
}
|
|
}
|
|
|
|
type OutputType int
|
|
|
|
const (
|
|
OutputTypeJSON OutputType = iota // default output type: JSON
|
|
OutputTypeYAML
|
|
OutputTypeGoTest
|
|
OutputTypePyTest
|
|
)
|
|
|
|
func (outputType OutputType) String() string {
|
|
switch outputType {
|
|
case OutputTypeYAML:
|
|
return "yaml"
|
|
case OutputTypeGoTest:
|
|
return "gotest"
|
|
case OutputTypePyTest:
|
|
return "pytest"
|
|
default:
|
|
return "json"
|
|
}
|
|
}
|
|
|
|
// Profile is used to override or update(create if not existed) original headers and cookies
|
|
type Profile struct {
|
|
Override bool `json:"override" yaml:"override"`
|
|
Headers map[string]string `json:"headers" yaml:"headers"`
|
|
Cookies map[string]string `json:"cookies" yaml:"cookies"`
|
|
}
|
|
|
|
func NewConverter(outputDir, profilePath string) *TCaseConverter {
|
|
return &TCaseConverter{
|
|
profilePath: profilePath,
|
|
outputDir: outputDir,
|
|
}
|
|
}
|
|
|
|
// TCaseConverter holds the common properties of case converter
|
|
type TCaseConverter struct {
|
|
fromFile string
|
|
profilePath string
|
|
outputDir string
|
|
tCase *hrp.TestCaseDef
|
|
}
|
|
|
|
// LoadCase loads source file and convert to TCase type
|
|
func (c *TCaseConverter) loadCase(casePath string, fromType FromType) error {
|
|
c.fromFile = casePath
|
|
var err error
|
|
switch fromType {
|
|
case FromTypeJSON:
|
|
c.tCase, err = LoadJSONCase(casePath)
|
|
case FromTypeYAML:
|
|
c.tCase, err = LoadYAMLCase(casePath)
|
|
case FromTypeHAR:
|
|
c.tCase, err = LoadHARCase(casePath)
|
|
case FromTypePostman:
|
|
c.tCase, err = LoadPostmanCase(casePath)
|
|
case FromTypeSwagger:
|
|
c.tCase, err = LoadSwaggerCase(casePath)
|
|
case FromTypeCurl:
|
|
c.tCase, err = LoadCurlCase(casePath)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (c *TCaseConverter) Convert(casePath string, fromType FromType, outputType OutputType) (err error) {
|
|
// report GA event
|
|
startTime := time.Now()
|
|
defer func() {
|
|
sdk.SendGA4Event("hrp_convert", map[string]interface{}{
|
|
"from": fromType.String(),
|
|
"to": outputType.String(),
|
|
"success": err == nil,
|
|
"engagement_time_msec": time.Since(startTime).Milliseconds(),
|
|
})
|
|
}()
|
|
|
|
log.Info().Str("path", casePath).
|
|
Str("fromType", fromType.String()).
|
|
Str("outputType", outputType.String()).
|
|
Msg("convert testcase")
|
|
|
|
// load source file
|
|
err = c.loadCase(casePath, fromType)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// override TCase with profile
|
|
if c.profilePath != "" {
|
|
c.overrideWithProfile(c.profilePath)
|
|
}
|
|
|
|
// convert to target format
|
|
var outputFile string
|
|
switch outputType {
|
|
case OutputTypeYAML:
|
|
outputFile, err = c.toYAML()
|
|
case OutputTypeGoTest:
|
|
outputFile, err = c.toGoTest()
|
|
case OutputTypePyTest:
|
|
outputFile, err = c.toPyTest()
|
|
default:
|
|
outputFile, err = c.toJSON()
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Info().Str("outputFile", outputFile).Msg("conversion completed")
|
|
return nil
|
|
}
|
|
|
|
func (c *TCaseConverter) genOutputPath(suffix string) string {
|
|
outFileFullName := builtin.GetFileNameWithoutExtension(c.fromFile) + "_test" + suffix
|
|
if c.outputDir != "" {
|
|
return filepath.Join(c.outputDir, outFileFullName)
|
|
} else {
|
|
return filepath.Join(filepath.Dir(c.fromFile), outFileFullName)
|
|
}
|
|
}
|
|
|
|
func (c *TCaseConverter) overrideWithProfile(path string) error {
|
|
log.Info().Str("path", path).Msg("load profile")
|
|
profile := new(Profile)
|
|
err := hrp.LoadFileObject(path, profile)
|
|
if err != nil {
|
|
log.Warn().Str("path", path).
|
|
Msg("failed to load profile, ignore!")
|
|
return err
|
|
}
|
|
|
|
log.Info().Interface("profile", profile).Msg("override with profile")
|
|
for _, step := range c.tCase.Steps {
|
|
// override original headers and cookies
|
|
if profile.Override {
|
|
step.Request.Headers = make(map[string]string)
|
|
step.Request.Cookies = make(map[string]string)
|
|
}
|
|
// update (create if not existed) original headers and cookies
|
|
if step.Request.Headers == nil {
|
|
step.Request.Headers = make(map[string]string)
|
|
}
|
|
if step.Request.Cookies == nil {
|
|
step.Request.Cookies = make(map[string]string)
|
|
}
|
|
for k, v := range profile.Headers {
|
|
step.Request.Headers[k] = v
|
|
}
|
|
for k, v := range profile.Cookies {
|
|
step.Request.Cookies[k] = v
|
|
}
|
|
}
|
|
return nil
|
|
}
|