add unittest; add --patch options

This commit is contained in:
buyuxiang
2022-05-17 16:03:45 +08:00
parent ac0fc8b4e4
commit 115b5cbc3f
18 changed files with 604 additions and 248 deletions

View File

@@ -1,4 +1,4 @@
package convert
package case2script
import (
_ "embed"

View File

@@ -22,6 +22,13 @@ const (
suffixYAML = ".yaml"
)
const (
configProfile = "profile"
configPatch = "patch"
keyHeaders = "headers"
keyCookies = "cookies"
)
func NewHAR(path string) *har {
return &har{
path: path,
@@ -33,6 +40,7 @@ type har struct {
filterStr string
excludeStr string
profile map[string]interface{}
patch map[string]interface{}
outputDir string
}
@@ -46,6 +54,16 @@ func (h *har) SetProfile(path string) {
}
}
func (h *har) SetPatch(path string) {
log.Info().Str("path", path).Msg("set patch")
h.patch = make(map[string]interface{})
err := builtin.LoadFile(path, h.patch)
if err != nil {
log.Warn().Str("path", path).
Msg("invalid patch format, ignore!")
}
}
func (h *har) SetOutputDir(dir string) {
log.Info().Str("dir", dir).Msg("set output directory")
h.outputDir = dir
@@ -146,6 +164,7 @@ func (h *har) prepareTestStep(entry *Entry) (*hrp.TStep, error) {
Validators: make([]interface{}, 0),
},
profile: h.profile,
patch: h.patch,
}
if err := step.makeRequestMethod(entry); err != nil {
return nil, err
@@ -174,6 +193,7 @@ func (h *har) prepareTestStep(entry *Entry) (*hrp.TStep, error) {
type tStep struct {
hrp.TStep
profile map[string]interface{}
patch map[string]interface{}
}
func (s *tStep) makeRequestMethod(entry *Entry) error {
@@ -199,43 +219,59 @@ func (s *tStep) makeRequestParams(entry *Entry) error {
return nil
}
func (s *tStep) updateRequestInfo(config string, key string) bool {
var m map[string]interface{}
switch config {
case configProfile:
m = s.profile
case configPatch:
m = s.patch
default:
return false
}
iRequestMap, existed := m[key]
if existed {
requestMap, ok := iRequestMap.(map[string]interface{})
if ok {
for k, v := range requestMap {
switch key {
case keyHeaders:
s.Request.Headers[k] = fmt.Sprintf("%v", v)
case keyCookies:
s.Request.Cookies[k] = fmt.Sprintf("%v", v)
}
}
return true
}
log.Warn().Interface(key, iRequestMap).Msgf("%v from %v is not a map, ignore!", key, config)
}
return false
}
func (s *tStep) makeRequestCookies(entry *Entry) error {
s.Request.Cookies = make(map[string]string)
cookies, ok := s.profile["cookies"]
if ok {
// use cookies from profile
cookies, ok := cookies.(map[string]interface{})
if ok {
for k, v := range cookies {
s.Request.Cookies[k] = fmt.Sprintf("%v", v)
}
return nil
}
log.Warn().Interface("cookies", cookies).
Msg("cookies from profile is not a map, ignore!")
// override all cookies according to the profile
if s.updateRequestInfo(configProfile, keyCookies) {
return nil
}
// use cookies from har
for _, cookie := range entry.Request.Cookies {
s.Request.Cookies[cookie.Name] = cookie.Value
}
// create or update the cookies indicated in the patch
s.updateRequestInfo(configPatch, keyCookies)
return nil
}
func (s *tStep) makeRequestHeaders(entry *Entry) error {
s.Request.Headers = make(map[string]string)
headers, ok := s.profile["headers"]
if ok {
// use headers from profile
cookies, ok := headers.(map[string]interface{})
if ok {
for k, v := range cookies {
s.Request.Headers[k] = fmt.Sprintf("%v", v)
}
return nil
}
log.Warn().Interface("headers", headers).
Msg("headers from profile is not a map, ignore!")
// override all headers according to the profile
if s.updateRequestInfo(configProfile, keyHeaders) {
return nil
}
// use headers from har
@@ -245,6 +281,9 @@ func (s *tStep) makeRequestHeaders(entry *Entry) error {
}
s.Request.Headers[header.Name] = header.Value
}
// create or update the headers indicated in the patch
s.updateRequestInfo(configPatch, keyHeaders)
return nil
}

View File

@@ -1,6 +1,7 @@
package har2case
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
@@ -9,9 +10,9 @@ import (
)
var (
harPath = "../../../examples/data/har/demo.har"
harPath2 = "../../../examples/data/har/postman-echo.har"
profilePath = "../../../examples/data/har/profile.yml"
harPath = "../../../../examples/data/har/demo.har"
harPath2 = "../../../../examples/data/har/postman-echo.har"
profilePath = "../../../../examples/data/har/profile.yml"
)
func TestGenJSON(t *testing.T) {
@@ -381,3 +382,32 @@ func TestMakeValidate(t *testing.T) {
t.Fatal()
}
}
func Test_tStep_makeRequestCookies(t *testing.T) {
type fields struct {
TStep hrp.TStep
profile map[string]interface{}
patch map[string]interface{}
}
type args struct {
entry *Entry
}
tests := []struct {
name string
fields fields
args args
wantErr assert.ErrorAssertionFunc
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &tStep{
TStep: tt.fields.TStep,
profile: tt.fields.profile,
patch: tt.fields.patch,
}
tt.wantErr(t, s.makeRequestCookies(tt.args.entry), fmt.Sprintf("makeRequestCookies(%v)", tt.args.entry))
})
}
}

View File

@@ -3,7 +3,6 @@ package postman2case
import (
"bytes"
"fmt"
"github.com/httprunner/httprunner/v4/hrp/internal/json"
"io"
"mime/multipart"
"net/url"
@@ -17,6 +16,7 @@ import (
"github.com/httprunner/httprunner/v4/hrp"
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
"github.com/httprunner/httprunner/v4/hrp/internal/json"
)
const (
@@ -33,11 +33,18 @@ const (
)
const (
suffixName = ".converted"
suffixName = ".converted" // distinguish the converted json(testcase) from the origin json(collection)
extensionJSON = ".json"
extensionYAML = ".yaml"
)
const (
configProfile = "profile"
configPatch = "patch"
keyHeaders = "headers"
keyCookies = "cookies"
)
var contentTypeMap = map[string]string{
"text": "text/plain",
"javascript": "application/javascript",
@@ -54,9 +61,31 @@ func NewCollection(path string) *collection {
type collection struct {
path string
profile map[string]interface{}
patch map[string]interface{}
outputDir string
}
func (c *collection) SetProfile(path string) {
log.Info().Str("path", path).Msg("set profile")
c.profile = make(map[string]interface{})
err := builtin.LoadFile(path, c.profile)
if err != nil {
log.Warn().Str("path", path).
Msg("invalid profile format, ignore!")
}
}
func (c *collection) SetPatch(path string) {
log.Info().Str("path", path).Msg("set patch")
c.patch = make(map[string]interface{})
err := builtin.LoadFile(path, c.patch)
if err != nil {
log.Warn().Str("path", path).
Msg("invalid patch format, ignore!")
}
}
func (c *collection) SetOutputDir(dir string) {
log.Info().Str("dir", dir).Msg("set output directory")
c.outputDir = dir
@@ -169,10 +198,12 @@ func (c *collection) prepareTestStep(item *TItem) (*hrp.TStep, error) {
Msg("convert teststep")
step := &tStep{
hrp.TStep{
TStep: hrp.TStep{
Request: &hrp.Request{},
Validators: make([]interface{}, 0),
},
profile: c.profile,
patch: c.patch,
}
if err := step.makeRequestName(item); err != nil {
return nil, err
@@ -186,20 +217,22 @@ func (c *collection) prepareTestStep(item *TItem) (*hrp.TStep, error) {
if err := step.makeRequestParams(item); err != nil {
return nil, err
}
if err := step.makeRequestHeadersAndCookies(item); err != nil {
if err := step.makeRequestHeaders(item); err != nil {
return nil, err
}
if err := step.makeRequestCookies(item); err != nil {
return nil, err
}
if err := step.makeRequestBody(item); err != nil {
return nil, err
}
if err := step.makeValidate(item); err != nil {
return nil, err
}
return &step.TStep, nil
}
type tStep struct {
hrp.TStep
profile map[string]interface{}
patch map[string]interface{}
}
// makeRequestName indicates the step name the same as item name
@@ -239,21 +272,89 @@ func (s *tStep) makeRequestParams(item *TItem) error {
return nil
}
func (s *tStep) makeRequestHeadersAndCookies(item *TItem) error {
s.Request.Headers = make(map[string]string)
for _, field := range item.Request.Headers {
if field.Disabled {
continue
func (s *tStep) updateRequestInfo(config string, key string) bool {
var m map[string]interface{}
switch config {
case configProfile:
m = s.profile
case configPatch:
m = s.patch
default:
return false
}
iRequestMap, existed := m[key]
if existed {
requestMap, ok := iRequestMap.(map[string]interface{})
if ok {
for k, v := range requestMap {
switch key {
case keyHeaders:
s.Request.Headers[k] = fmt.Sprintf("%v", v)
case keyCookies:
s.Request.Cookies[k] = fmt.Sprintf("%v", v)
}
}
return true
}
if strings.EqualFold(field.Key, "cookie") {
s.Request.Cookies[field.Key] = field.Value
log.Warn().Interface(key, iRequestMap).Msgf("%v from %v is not a map, ignore!", key, config)
}
return false
}
func (s *tStep) makeRequestHeaders(item *TItem) error {
s.Request.Headers = make(map[string]string)
// override all headers according to the profile
if s.updateRequestInfo(configProfile, keyHeaders) {
return nil
}
// headers defined in postman collection
for _, field := range item.Request.Headers {
if field.Disabled || strings.EqualFold(field.Key, "cookie") {
continue
}
s.Request.Headers[field.Key] = field.Value
}
// create or update the headers indicated in the patch
s.updateRequestInfo(configPatch, keyHeaders)
return nil
}
func (s *tStep) makeRequestCookies(item *TItem) error {
s.Request.Cookies = make(map[string]string)
// override all cookies according to the profile
if s.updateRequestInfo(configProfile, keyCookies) {
return nil
}
// cookies defined in postman collection
for _, field := range item.Request.Headers {
if field.Disabled || !strings.EqualFold(field.Key, "cookie") {
continue
}
s.parseRequestCookiesMap(field.Value)
}
// create or update the cookies indicated in the patch
s.updateRequestInfo(configPatch, keyCookies)
return nil
}
func (s *tStep) parseRequestCookiesMap(cookies string) {
for _, cookie := range strings.Split(cookies, ";") {
cookie = strings.TrimSpace(cookie)
index := strings.Index(cookie, "=")
if index == -1 {
log.Warn().Str("cookie", cookie).Msg("cookie format invalid")
continue
}
s.Request.Cookies[cookie[0:index]] = cookie[index+1:]
}
}
func (s *tStep) makeRequestBody(item *TItem) error {
mode := item.Request.Body.Mode
if mode == "" {
@@ -267,7 +368,7 @@ func (s *tStep) makeRequestBody(item *TItem) error {
case enumBodyUrlEncoded:
return s.makeRequestBodyUrlEncoded(item)
case enumBodyFile, enumBodyGraphQL:
return errors.New("not supported body type")
return errors.Errorf("unsupported body type: %v", mode)
}
return nil
}

View File

@@ -0,0 +1,155 @@
package postman2case
import (
"testing"
"github.com/stretchr/testify/assert"
)
var (
collectionPath = "../../../../examples/data/postman2case/demo.json"
profilePath = "../../../../examples/data/postman2case/profile.yml"
patchPath = "../../../../examples/data/postman2case/patch.yml"
)
func TestGenJSON(t *testing.T) {
jsonPath, err := NewCollection(collectionPath).GenJSON()
if !assert.NoError(t, err) {
t.Fatal()
}
if !assert.NotEmpty(t, jsonPath) {
t.Fatal()
}
}
func TestGenYAML(t *testing.T) {
yamlPath, err := NewCollection(collectionPath).GenYAML()
if !assert.NoError(t, err) {
t.Fatal()
}
if !assert.NotEmpty(t, yamlPath) {
t.Fatal()
}
}
func TestLoadCollection(t *testing.T) {
tCollection, err := NewCollection(collectionPath).load()
if !assert.NoError(t, err) {
t.Fatal(err)
}
if !assert.Equal(t, "postman collection demo", tCollection.Info.Name) {
t.Fatal()
}
}
func TestMakeTestCase(t *testing.T) {
tCase, err := NewCollection(collectionPath).makeTestCase()
if !assert.NoError(t, err) {
t.Fatal()
}
// check name
if !assert.Equal(t, "postman collection demo", tCase.Config.Name) {
t.Fatal()
}
// check method
if !assert.EqualValues(t, "GET", tCase.TestSteps[0].Request.Method) {
t.Fatal()
}
if !assert.EqualValues(t, "POST", tCase.TestSteps[1].Request.Method) {
t.Fatal()
}
// check url
if !assert.Equal(t, "https://postman-echo.com/get", tCase.TestSteps[0].Request.URL) {
t.Fatal()
}
if !assert.Equal(t, "https://postman-echo.com/post", tCase.TestSteps[1].Request.URL) {
t.Fatal()
}
// check params
if !assert.Equal(t, "v1", tCase.TestSteps[0].Request.Params["k1"]) {
t.Fatal()
}
// check cookies (pass, postman collection doesn't contains cookies)
// check headers
if !assert.Contains(t, tCase.TestSteps[1].Request.Headers["Content-Type"], "multipart/form-data") {
t.Fatal()
}
if !assert.Equal(t, "application/x-www-form-urlencoded", tCase.TestSteps[2].Request.Headers["Content-Type"]) {
t.Fatal()
}
if !assert.Equal(t, "application/json", tCase.TestSteps[3].Request.Headers["Content-Type"]) {
t.Fatal()
}
if !assert.Equal(t, "text/plain", tCase.TestSteps[4].Request.Headers["Content-Type"]) {
t.Fatal()
}
if !assert.Equal(t, "HttpRunner", tCase.TestSteps[5].Request.Headers["User-Agent"]) {
t.Fatal()
}
// check body
if !assert.Equal(t, nil, tCase.TestSteps[0].Request.Body) {
t.Fatal()
}
if !assert.NotEmpty(t, tCase.TestSteps[1].Request.Body) {
t.Fatal()
}
if !assert.Equal(t, map[string]string{"k1": "v1", "k2": "v2"}, tCase.TestSteps[2].Request.Body) {
t.Fatal()
}
if !assert.Equal(t, map[string]interface{}{"k1": "v1", "k2": "v2"}, tCase.TestSteps[3].Request.Body) {
t.Fatal()
}
if !assert.Equal(t, "have a nice day", tCase.TestSteps[4].Request.Body) {
t.Fatal()
}
if !assert.Equal(t, nil, tCase.TestSteps[5].Request.Body) {
t.Fatal()
}
}
func TestMakeTestCaseWithProfile(t *testing.T) {
c := NewCollection(collectionPath)
c.SetProfile(profilePath)
tCase, err := c.makeTestCase()
if !assert.NoError(t, err) {
t.Fatal()
}
for _, step := range tCase.TestSteps {
if step.Request.Method == "GET" && !assert.Len(t, step.Request.Headers, 1) {
t.Fatal()
}
if step.Request.Method == "POST" && !assert.Len(t, step.Request.Headers, 2) {
t.Fatal()
}
if !assert.Equal(t, "all original headers will be overridden", step.Request.Headers["Header1"]) {
t.Fatal()
}
if !assert.Len(t, step.Request.Cookies, 1) {
t.Fatal()
}
if !assert.Equal(t, "all original cookies will be overridden", step.Request.Cookies["Cookie1"]) {
t.Fatal()
}
}
}
func TestMakeTestCaseWithPatch(t *testing.T) {
c := NewCollection(collectionPath)
c.SetPatch(patchPath)
tCase, err := c.makeTestCase()
if !assert.NoError(t, err) {
t.Fatal()
}
// create cookies Cookie1 indicated in patch
if !assert.Equal(t, "this cookie will be created or updated", tCase.TestSteps[0].Request.Cookies["Cookie1"]) {
t.Fatal()
}
// update header User-Agent indicated in patch
if !assert.Equal(t, "this header will be created or updated", tCase.TestSteps[5].Request.Headers["User-Agent"]) {
t.Fatal()
}
// pass header Connection which is not indicated in patch
if !assert.Equal(t, "close", tCase.TestSteps[5].Request.Headers["Connection"]) {
t.Fatal()
}
}

View File

@@ -1,39 +0,0 @@
package postman2case
import (
"testing"
"github.com/stretchr/testify/assert"
)
var collectionPath = "../../../examples/data/postman2case/postman_collection.json"
func TestLoadPostmanCollection(t *testing.T) {
c, err := NewCollection(collectionPath).load()
if !assert.NoError(t, err) {
t.Fatal(err)
}
if !assert.Equal(t, "postman collection demo", c.Info.Name) {
t.Fatal()
}
}
func TestGenJSON(t *testing.T) {
jsonPath, err := NewCollection(collectionPath).GenJSON()
if !assert.NoError(t, err) {
t.Fatal()
}
if !assert.NotEmpty(t, jsonPath) {
t.Fatal()
}
}
func TestGenYAML(t *testing.T) {
yamlPath, err := NewCollection(collectionPath).GenYAML()
if !assert.NoError(t, err) {
t.Fatal()
}
if !assert.NotEmpty(t, yamlPath) {
t.Fatal()
}
}