mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-10 17:43:00 +08:00
feat: postman2case
This commit is contained in:
@@ -108,7 +108,7 @@ Use "hrp [command] --help" for more information about a command.
|
||||
|
||||
关注 HttpRunner 的微信公众号,第一时间获得最新资讯。
|
||||
|
||||
<img src="docs/assets/qrcode.jpg" alt="HttpRunner" width="200">
|
||||
<img src="https://httprunner.com/image/qrcode.png" alt="HttpRunner" width="400">
|
||||
|
||||
如果你期望加入 HttpRunner 核心用户群,请填写[用户调研问卷][survey]并留下你的联系方式,作者将拉你进群。
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ HttpRunner is in Sentry Sponsored plan.
|
||||
|
||||
关注 HttpRunner 的微信公众号,第一时间获得最新资讯。
|
||||
|
||||
<img src="https://httprunner.com/image/qrcode.jpg" alt="HttpRunner" width="200">
|
||||
<img src="https://httprunner.com/image/qrcode.png" alt="HttpRunner" width="400">
|
||||
|
||||
如果你期望加入 HttpRunner 核心用户群,请填写[用户调研问卷][survey]并留下你的联系方式,作者将拉你进群。
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
- fix: step request elapsed timing should contain ContentTransfer part
|
||||
- fix #1288: unable to go get httprunner v4
|
||||
- feat: support converting Postman collection to HttpRunner testcase
|
||||
|
||||
**python version**
|
||||
|
||||
|
||||
346
examples/data/postman2case/postman_collection.json
Normal file
346
examples/data/postman2case/postman_collection.json
Normal file
@@ -0,0 +1,346 @@
|
||||
{
|
||||
"info": {
|
||||
"_postman_id": "0417a445-b206-4ea2-b1d2-5441afd6c6b9",
|
||||
"name": "postman collection demo",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
},
|
||||
"item": [
|
||||
{
|
||||
"name": "folder1",
|
||||
"item": [
|
||||
{
|
||||
"name": "folder2",
|
||||
"item": [
|
||||
{
|
||||
"name": "Get with params",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "https://postman-echo.com/:path?k1=v1&k2=v2",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"postman-echo",
|
||||
"com"
|
||||
],
|
||||
"path": [
|
||||
":path"
|
||||
],
|
||||
"query": [
|
||||
{
|
||||
"key": "k1",
|
||||
"value": "v1"
|
||||
},
|
||||
{
|
||||
"key": "k2",
|
||||
"value": "v2"
|
||||
},
|
||||
{
|
||||
"key": "k3",
|
||||
"value": "v3",
|
||||
"disabled": true
|
||||
}
|
||||
],
|
||||
"variable": [
|
||||
{
|
||||
"key": "path",
|
||||
"value": "get"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"name": "Get with params",
|
||||
"originalRequest": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "https://postman-echo.com/:path?k1=v1&k2=v2",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"postman-echo",
|
||||
"com"
|
||||
],
|
||||
"path": [
|
||||
":path"
|
||||
],
|
||||
"query": [
|
||||
{
|
||||
"key": "k1",
|
||||
"value": "v1"
|
||||
},
|
||||
{
|
||||
"key": "k2",
|
||||
"value": "v2"
|
||||
},
|
||||
{
|
||||
"key": "k3",
|
||||
"value": "v3",
|
||||
"disabled": true
|
||||
}
|
||||
],
|
||||
"variable": [
|
||||
{
|
||||
"key": "path",
|
||||
"value": "get"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"_postman_previewlanguage": "json",
|
||||
"header": null,
|
||||
"cookie": [],
|
||||
"body": "{\n \"args\": {\n \"k1\": \"v1\",\n \"k2\": \"v2\"\n },\n \"headers\": {\n \"x-forwarded-proto\": \"https\",\n \"x-forwarded-port\": \"443\",\n \"host\": \"postman-echo.com\",\n \"user-agent\": \"PostmanRuntime/7.29.0\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate, br\"\n },\n \"url\": \"https://postman-echo.com/get?k1=v1&k2=v2\"\n}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "folder3",
|
||||
"item": [
|
||||
{
|
||||
"name": "Post form-data",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "formdata",
|
||||
"formdata": [
|
||||
{
|
||||
"key": "k1",
|
||||
"value": "v1",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "k2",
|
||||
"value": "v2",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "k3",
|
||||
"value": "v3",
|
||||
"type": "text",
|
||||
"disabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"raw": "https://postman-echo.com/:path",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"postman-echo",
|
||||
"com"
|
||||
],
|
||||
"path": [
|
||||
":path"
|
||||
],
|
||||
"variable": [
|
||||
{
|
||||
"key": "path",
|
||||
"value": "post"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "Post x-www-form-urlencoded",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "urlencoded",
|
||||
"urlencoded": [
|
||||
{
|
||||
"key": "k1",
|
||||
"value": "v1",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "k2",
|
||||
"value": "v2",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "k3",
|
||||
"value": "v3",
|
||||
"type": "text",
|
||||
"disabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"raw": "https://postman-echo.com/:path",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"postman-echo",
|
||||
"com"
|
||||
],
|
||||
"path": [
|
||||
":path"
|
||||
],
|
||||
"variable": [
|
||||
{
|
||||
"key": "path",
|
||||
"value": "post"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "Post raw json",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"k1\": \"v1\",\n \"k2\": \"v2\"\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "https://postman-echo.com/:path",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"postman-echo",
|
||||
"com"
|
||||
],
|
||||
"path": [
|
||||
":path"
|
||||
],
|
||||
"variable": [
|
||||
{
|
||||
"key": "path",
|
||||
"value": "post"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "Post raw text",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "have a nice day",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "https://postman-echo.com/:path",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"postman-echo",
|
||||
"com"
|
||||
],
|
||||
"path": [
|
||||
":path"
|
||||
],
|
||||
"variable": [
|
||||
{
|
||||
"key": "path",
|
||||
"value": "post"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Get request headers",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [
|
||||
{
|
||||
"key": "User-Agent",
|
||||
"value": "HttpRunner",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "User-Name",
|
||||
"value": "bbx",
|
||||
"type": "text",
|
||||
"disabled": true
|
||||
}
|
||||
],
|
||||
"url": {
|
||||
"raw": "https://postman-echo.com/:path",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"postman-echo",
|
||||
"com"
|
||||
],
|
||||
"path": [
|
||||
":path"
|
||||
],
|
||||
"variable": [
|
||||
{
|
||||
"key": "path",
|
||||
"value": "headers"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"name": "Get request headers",
|
||||
"originalRequest": {
|
||||
"method": "GET",
|
||||
"header": [
|
||||
{
|
||||
"key": "User-Agent",
|
||||
"value": "HttpRunner",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "User-Name",
|
||||
"value": "bbx",
|
||||
"type": "text",
|
||||
"disabled": true
|
||||
}
|
||||
],
|
||||
"url": {
|
||||
"raw": "https://postman-echo.com/:path",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"postman-echo",
|
||||
"com"
|
||||
],
|
||||
"path": [
|
||||
":path"
|
||||
],
|
||||
"variable": [
|
||||
{
|
||||
"key": "path",
|
||||
"value": "headers"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"_postman_previewlanguage": "json",
|
||||
"header": null,
|
||||
"cookie": [],
|
||||
"body": "{\n \"headers\": {\n \"x-forwarded-proto\": \"https\",\n \"x-forwarded-port\": \"443\",\n \"host\": \"postman-echo.com\",\n \"user-agent\": \"HttpRunner\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate, br\"\n }\n}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
65
hrp/cmd/postman2case.go
Normal file
65
hrp/cmd/postman2case.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/postman2case"
|
||||
)
|
||||
|
||||
// postman2caseCmd represents the postman2case command
|
||||
var postman2caseCmd = &cobra.Command{
|
||||
Use: "postman2case $postman_path...",
|
||||
Short: "convert postman collection to json/yaml testcase files",
|
||||
Long: `convert postman collection to json/yaml testcase files`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
setLogLevel(logLevel)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var outputFiles []string
|
||||
for _, arg := range args {
|
||||
// must choose one
|
||||
if !postman2JSONFlag && !postman2YAMLFlag {
|
||||
return errors.New("please select convert format type")
|
||||
}
|
||||
var outputPath string
|
||||
var err error
|
||||
|
||||
postman := postman2case.NewCollection(arg)
|
||||
|
||||
// specify output dir
|
||||
if postman2Dir != "" {
|
||||
postman.SetOutputDir(postman2Dir)
|
||||
}
|
||||
|
||||
// generate json/yaml files
|
||||
if genYAMLFlag {
|
||||
outputPath, err = postman.GenYAML()
|
||||
} else {
|
||||
outputPath, err = postman.GenJSON() // default
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outputFiles = append(outputFiles, outputPath)
|
||||
}
|
||||
log.Info().Strs("output", outputFiles).Msg("convert testcase success")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
postman2JSONFlag bool
|
||||
postman2YAMLFlag bool
|
||||
postman2Dir string
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(postman2caseCmd)
|
||||
postman2caseCmd.Flags().BoolVarP(&postman2JSONFlag, "to-json", "j", true, "convert to JSON format")
|
||||
postman2caseCmd.Flags().BoolVarP(&postman2YAMLFlag, "to-yaml", "y", false, "convert to YAML format")
|
||||
postman2caseCmd.Flags().StringVarP(&postman2Dir, "output-dir", "d", "", "specify output directory, default to the same dir with postman collection file")
|
||||
}
|
||||
74
hrp/internal/postman2case/collection.go
Normal file
74
hrp/internal/postman2case/collection.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package postman2case
|
||||
|
||||
/*
|
||||
Postman Collection format reference:
|
||||
https://schema.postman.com/json/collection/v2.0.0/collection.json
|
||||
https://schema.postman.com/json/collection/v2.1.0/collection.json
|
||||
*/
|
||||
|
||||
// TCollection represents the postman exported file
|
||||
type TCollection struct {
|
||||
Info TInfo `json:"info"`
|
||||
Items []TItem `json:"item"`
|
||||
}
|
||||
|
||||
// TInfo gives information about the collection
|
||||
type TInfo struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Schema string `json:"schema"`
|
||||
}
|
||||
|
||||
// TItem contains the detail information of request and expected responses
|
||||
// item could be defined recursively
|
||||
type TItem struct {
|
||||
Items []TItem `json:"item"`
|
||||
Name string `json:"name"`
|
||||
Request TRequest `json:"request"`
|
||||
Responses []TResponse `json:"response"`
|
||||
}
|
||||
|
||||
type TRequest struct {
|
||||
Method string `json:"method"`
|
||||
Headers []TField `json:"header"`
|
||||
Body TBody `json:"body"`
|
||||
URL TUrl `json:"url"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type TResponse struct {
|
||||
Name string `json:"name"`
|
||||
OriginalRequest TRequest `json:"originalRequest"`
|
||||
Status string `json:"status"`
|
||||
Code int `json:"code"`
|
||||
Headers []TField `json:"headers"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
type TUrl struct {
|
||||
Raw string `json:"raw"`
|
||||
Protocol string `json:"protocol"`
|
||||
Path []string `json:"path"`
|
||||
Description string `json:"description"`
|
||||
Query []TField `json:"query"`
|
||||
Variable []TField `json:"variable"`
|
||||
}
|
||||
|
||||
type TField struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
Src string `json:"src"`
|
||||
Description string `json:"description"`
|
||||
Type string `json:"type"`
|
||||
Disabled bool `json:"disabled"`
|
||||
Enable bool `json:"enable"`
|
||||
}
|
||||
|
||||
type TBody struct {
|
||||
Mode string `json:"mode"`
|
||||
FormData []TField `json:"formdata"`
|
||||
URLEncoded []TField `json:"urlencoded"`
|
||||
Raw string `json:"raw"`
|
||||
Disabled bool `json:"disabled"`
|
||||
Options interface{} `json:"options"`
|
||||
}
|
||||
364
hrp/internal/postman2case/core.go
Normal file
364
hrp/internal/postman2case/core.go
Normal file
@@ -0,0 +1,364 @@
|
||||
package postman2case
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/json"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
||||
)
|
||||
|
||||
const (
|
||||
enumBodyRaw = "raw"
|
||||
enumBodyUrlEncoded = "urlencoded"
|
||||
enumBodyFormData = "formdata"
|
||||
enumBodyFile = "file"
|
||||
enumBodyGraphQL = "graphql"
|
||||
)
|
||||
|
||||
const (
|
||||
enumFieldTypeText = "text"
|
||||
enumFieldTypeFile = "file"
|
||||
)
|
||||
|
||||
const (
|
||||
suffixName = ".converted"
|
||||
extensionJSON = ".json"
|
||||
extensionYAML = ".yaml"
|
||||
)
|
||||
|
||||
var contentTypeMap = map[string]string{
|
||||
"text": "text/plain",
|
||||
"javascript": "application/javascript",
|
||||
"json": "application/json",
|
||||
"html": "text/html",
|
||||
"xml": "application/xml",
|
||||
}
|
||||
|
||||
func NewCollection(path string) *collection {
|
||||
return &collection{
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
|
||||
type collection struct {
|
||||
path string
|
||||
outputDir string
|
||||
}
|
||||
|
||||
func (c *collection) SetOutputDir(dir string) {
|
||||
log.Info().Str("dir", dir).Msg("set output directory")
|
||||
c.outputDir = dir
|
||||
}
|
||||
|
||||
func (c *collection) GenJSON() (jsonPath string, err error) {
|
||||
testCase, err := c.makeTestCase()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
jsonPath = c.genOutputPath(extensionJSON)
|
||||
err = builtin.Dump2JSON(testCase, jsonPath)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *collection) GenYAML() (yamlPath string, err error) {
|
||||
testCase, err := c.makeTestCase()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
yamlPath = c.genOutputPath(extensionYAML)
|
||||
err = builtin.Dump2YAML(testCase, yamlPath)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *collection) genOutputPath(suffix string) string {
|
||||
file := getFilenameWithoutExtension(c.path) + suffix
|
||||
if c.outputDir != "" {
|
||||
return filepath.Join(c.outputDir, file)
|
||||
} else {
|
||||
return filepath.Join(filepath.Dir(c.path), file)
|
||||
}
|
||||
}
|
||||
|
||||
func getFilenameWithoutExtension(path string) string {
|
||||
base := filepath.Base(path)
|
||||
ext := filepath.Ext(base)
|
||||
return base[0:len(base)-len(ext)] + suffixName
|
||||
}
|
||||
|
||||
func (c *collection) makeTestCase() (*hrp.TCase, error) {
|
||||
tCollection, err := c.load()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
teststeps, err := c.prepareTestSteps(tCollection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tCase := &hrp.TCase{
|
||||
Config: c.prepareConfig(tCollection),
|
||||
TestSteps: teststeps,
|
||||
}
|
||||
return tCase, nil
|
||||
}
|
||||
|
||||
func (c *collection) load() (*TCollection, error) {
|
||||
collection := &TCollection{}
|
||||
err := builtin.LoadFile(c.path, collection)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "load postman collection failed")
|
||||
}
|
||||
return collection, nil
|
||||
}
|
||||
|
||||
func (c *collection) prepareConfig(tCollection *TCollection) *hrp.TConfig {
|
||||
return hrp.NewConfig(tCollection.Info.Name).
|
||||
SetVerifySSL(false)
|
||||
}
|
||||
|
||||
func (c *collection) prepareTestSteps(tCollection *TCollection) ([]*hrp.TStep, error) {
|
||||
// recursively convert collection items into a list
|
||||
var itemList []TItem
|
||||
for _, item := range tCollection.Items {
|
||||
extractItemList(item, &itemList)
|
||||
}
|
||||
|
||||
var steps []*hrp.TStep
|
||||
for _, item := range itemList {
|
||||
step, err := c.prepareTestStep(&item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
steps = append(steps, step)
|
||||
}
|
||||
return steps, nil
|
||||
}
|
||||
|
||||
func extractItemList(item TItem, itemList *[]TItem) {
|
||||
// current item contains no other items and request is not empty
|
||||
if len(item.Items) == 0 {
|
||||
if !reflect.DeepEqual(item.Request, TRequest{}) {
|
||||
*itemList = append(*itemList, item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// look up all items inside
|
||||
for _, i := range item.Items {
|
||||
// append item name
|
||||
i.Name = fmt.Sprintf("%s - %s", item.Name, i.Name)
|
||||
extractItemList(i, itemList)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *collection) prepareTestStep(item *TItem) (*hrp.TStep, error) {
|
||||
log.Info().
|
||||
Str("method", item.Request.Method).
|
||||
Str("url", item.Request.URL.Raw).
|
||||
Msg("convert teststep")
|
||||
|
||||
step := &tStep{
|
||||
hrp.TStep{
|
||||
Request: &hrp.Request{},
|
||||
Validators: make([]interface{}, 0),
|
||||
},
|
||||
}
|
||||
if err := step.makeRequestName(item); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := step.makeRequestMethod(item); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := step.makeRequestURL(item); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := step.makeRequestParams(item); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := step.makeRequestHeadersAndCookies(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
|
||||
}
|
||||
|
||||
// makeRequestName indicates the step name the same as item name
|
||||
func (s *tStep) makeRequestName(item *TItem) error {
|
||||
s.Name = item.Name
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *tStep) makeRequestMethod(item *TItem) error {
|
||||
s.Request.Method = hrp.HTTPMethod(item.Request.Method)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *tStep) makeRequestURL(item *TItem) error {
|
||||
rawUrl := item.Request.URL.Raw
|
||||
// parse path variables like ":path" in https://postman-echo.com/:path?k1=v1&k2=v2
|
||||
for _, field := range item.Request.URL.Variable {
|
||||
pathVar := ":" + field.Key
|
||||
rawUrl = strings.Replace(rawUrl, pathVar, field.Value, -1)
|
||||
}
|
||||
u, err := url.Parse(rawUrl)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "parse URL error")
|
||||
}
|
||||
s.Request.URL = fmt.Sprintf("%s://%s", u.Scheme, u.Host+u.Path)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *tStep) makeRequestParams(item *TItem) error {
|
||||
s.Request.Params = make(map[string]interface{})
|
||||
for _, field := range item.Request.URL.Query {
|
||||
if field.Disabled {
|
||||
continue
|
||||
}
|
||||
s.Request.Params[field.Key] = field.Value
|
||||
}
|
||||
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
|
||||
}
|
||||
if strings.EqualFold(field.Key, "cookie") {
|
||||
s.Request.Cookies[field.Key] = field.Value
|
||||
continue
|
||||
}
|
||||
s.Request.Headers[field.Key] = field.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *tStep) makeRequestBody(item *TItem) error {
|
||||
mode := item.Request.Body.Mode
|
||||
if mode == "" {
|
||||
return nil
|
||||
}
|
||||
switch mode {
|
||||
case enumBodyRaw:
|
||||
return s.makeRequestBodyRaw(item)
|
||||
case enumBodyFormData:
|
||||
return s.makeRequestBodyFormData(item)
|
||||
case enumBodyUrlEncoded:
|
||||
return s.makeRequestBodyUrlEncoded(item)
|
||||
case enumBodyFile, enumBodyGraphQL:
|
||||
return errors.New("not supported body type")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *tStep) makeRequestBodyRaw(item *TItem) (err error) {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
err = fmt.Errorf("make request body raw failed: %v", p)
|
||||
}
|
||||
}()
|
||||
|
||||
// extract language type
|
||||
iOptions := item.Request.Body.Options
|
||||
iLanguage := iOptions.(map[string]interface{})["raw"]
|
||||
languageType := iLanguage.(map[string]interface{})["language"].(string)
|
||||
|
||||
// make request body and indicate Content-Type
|
||||
rawBody := item.Request.Body.Raw
|
||||
if languageType == "json" {
|
||||
var iBody interface{}
|
||||
err = json.Unmarshal([]byte(rawBody), &iBody)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "make request body raw failed")
|
||||
}
|
||||
s.Request.Body = iBody
|
||||
} else {
|
||||
s.Request.Body = rawBody
|
||||
}
|
||||
s.Request.Headers["Content-Type"] = contentTypeMap[languageType]
|
||||
return
|
||||
}
|
||||
|
||||
func (s *tStep) makeRequestBodyFormData(item *TItem) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "make request body form-data failed")
|
||||
}
|
||||
}()
|
||||
payload := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(payload)
|
||||
for _, field := range item.Request.Body.FormData {
|
||||
if field.Disabled {
|
||||
continue
|
||||
}
|
||||
// form data could be text or file
|
||||
if field.Type == enumFieldTypeText {
|
||||
err = writer.WriteField(field.Key, field.Value)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else if field.Type == enumFieldTypeFile {
|
||||
err = writeFormDataFile(writer, &field)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
err = writer.Close()
|
||||
s.Request.Body = payload.String()
|
||||
s.Request.Headers["Content-Type"] = writer.FormDataContentType()
|
||||
return
|
||||
}
|
||||
|
||||
func writeFormDataFile(writer *multipart.Writer, field *TField) error {
|
||||
file, err := os.Open(field.Src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
formFile, err := writer.CreateFormFile(field.Key, filepath.Base(field.Src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(formFile, file)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *tStep) makeRequestBodyUrlEncoded(item *TItem) error {
|
||||
payloadMap := make(map[string]string)
|
||||
for _, field := range item.Request.Body.URLEncoded {
|
||||
if field.Disabled {
|
||||
continue
|
||||
}
|
||||
payloadMap[field.Key] = field.Value
|
||||
}
|
||||
s.Request.Body = payloadMap
|
||||
s.Request.Headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO makeValidate from example response
|
||||
func (s *tStep) makeValidate(item *TItem) error {
|
||||
return nil
|
||||
}
|
||||
39
hrp/internal/postman2case/core_test.go
Normal file
39
hrp/internal/postman2case/core_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user