diff --git a/Makefile b/Makefile index b59b9bb..33e2f02 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ build: mkdir -p bin - go build -o bin/atest cmd/*.go + rm -rf bin/atest + go build -o bin/atest main.go copy: build cp bin/atest /usr/local/bin/ diff --git a/go.mod b/go.mod index ee1d26b..a8302c5 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,11 @@ module github.com/linuxsuren/api-testing go 1.17 require ( + github.com/Masterminds/sprig/v3 v3.2.3 github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 + github.com/antonmedv/expr v1.12.1 + github.com/h2non/gock v1.2.0 + github.com/linuxsuren/unstructured v0.0.1 github.com/spf13/cobra v1.4.0 github.com/stretchr/testify v1.8.2 gopkg.in/yaml.v2 v2.4.0 @@ -12,16 +16,12 @@ require ( require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect - github.com/Masterminds/sprig/v3 v3.2.3 // indirect - github.com/antonmedv/expr v1.12.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/uuid v1.1.1 // indirect - github.com/h2non/gock v1.2.0 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.11 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/linuxsuren/unstructured v0.0.1 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/go.sum b/go.sum index cf3d7d4..3db7b66 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,7 @@ github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMK github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -53,9 +54,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= diff --git a/pkg/runner/simple.go b/pkg/runner/simple.go index 19303d4..6fbf5c9 100644 --- a/pkg/runner/simple.go +++ b/pkg/runner/simple.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "mime/multipart" "net/http" "os" "reflect" @@ -50,6 +51,20 @@ func RunTestCase(testcase *testing.TestCase, ctx interface{}) (output interface{ return } + if len(testcase.Request.Form) > 0 { + if testcase.Request.Header["Content-Type"] == "multipart/form-data" { + multiBody := &bytes.Buffer{} + writer := multipart.NewWriter(multiBody) + for key, val := range testcase.Request.Form { + writer.WriteField(key, val) + } + + _ = writer.Close() + requestBody = multiBody + testcase.Request.Header["Content-Type"] = writer.FormDataContentType() + } + } + var request *http.Request if request, err = http.NewRequest(testcase.Request.Method, testcase.Request.API, requestBody); err != nil { return @@ -68,8 +83,14 @@ func RunTestCase(testcase *testing.TestCase, ctx interface{}) (output interface{ return } + var responseBodyData []byte + if responseBodyData, err = io.ReadAll(resp.Body); err != nil { + return + } + if testcase.Expect.StatusCode != 0 { if err = expectInt(testcase.Name, testcase.Expect.StatusCode, resp.StatusCode); err != nil { + err = fmt.Errorf("error is: %v\n%s", err, string(responseBodyData)) return } } @@ -81,10 +102,6 @@ func RunTestCase(testcase *testing.TestCase, ctx interface{}) (output interface{ } } - var responseBodyData []byte - if responseBodyData, err = io.ReadAll(resp.Body); err != nil { - return - } if testcase.Expect.Body != "" { if string(responseBodyData) != strings.TrimSpace(testcase.Expect.Body) { err = fmt.Errorf("case: %s, got different response body, diff: \n%s", testcase.Name, diff --git a/pkg/runner/simple_test.go b/pkg/runner/simple_test.go index 9ecf8f7..c7bbdb3 100644 --- a/pkg/runner/simple_test.go +++ b/pkg/runner/simple_test.go @@ -6,6 +6,7 @@ import ( "testing" _ "embed" + "github.com/h2non/gock" atest "github.com/linuxsuren/api-testing/pkg/testing" "github.com/stretchr/testify/assert" @@ -314,6 +315,27 @@ func TestTestCase(t *testing.T) { assert.NotNil(t, err) assert.Contains(t, err.Error(), "template: api:1:") }, + }, { + name: "form request", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: "http://localhost/foo", + Method: http.MethodPost, + Header: map[string]string{ + "Content-Type": "multipart/form-data", + }, + Form: map[string]string{ + "key": "value", + }, + }, + }, + prepare: func() { + gock.New("http://localhost"). + Post("/foo").Reply(http.StatusOK).BodyString(`{"items":[]}`) + }, + verify: func(t *testing.T, output interface{}, err error) { + assert.Nil(t, err) + }, }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/testing/case.go b/pkg/testing/case.go index b8a95f8..4ff287f 100644 --- a/pkg/testing/case.go +++ b/pkg/testing/case.go @@ -27,6 +27,7 @@ type Request struct { Method string `yaml:"method"` Query map[string]string `yaml:"query"` Header map[string]string `yaml:"header"` + Form map[string]string `yaml:"form"` Body string `yaml:"body"` BodyFromFile string `yaml:"bodyFromFile"` } diff --git a/pkg/testing/parser.go b/pkg/testing/parser.go index 8cbc268..8b49f68 100644 --- a/pkg/testing/parser.go +++ b/pkg/testing/parser.go @@ -49,5 +49,15 @@ func (r *Request) Render(ctx interface{}) (err error) { r.Body = buf.String() } } + + // template the form + for key, val := range r.Form { + if tpl, err = template.New("form").Funcs(sprig.FuncMap()).Parse(val); err == nil { + buf = new(bytes.Buffer) + if err = tpl.Execute(buf, ctx); err == nil { + r.Form[key] = buf.String() + } + } + } return } diff --git a/pkg/testing/parser_test.go b/pkg/testing/parser_test.go index d52c567..69ca996 100644 --- a/pkg/testing/parser_test.go +++ b/pkg/testing/parser_test.go @@ -92,6 +92,18 @@ func TestRender(t *testing.T) { }, ctx: TestCase{}, hasErr: true, + }, { + name: "form render", + request: &Request{ + Form: map[string]string{ + "key": "{{.Name}}", + }, + }, + ctx: TestCase{Name: "linuxsuren"}, + verify: func(t *testing.T, req *Request) { + assert.Equal(t, "linuxsuren", req.Form["key"]) + }, + hasErr: false, }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {