fix the duplicates (#44)

* fix the duplicates

* improve the function RunTestCase
This commit is contained in:
Rick 2023-04-21 08:41:56 +08:00 committed by GitHub
parent f663b697bd
commit 49441573e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 171 additions and 61 deletions

View File

@ -8,6 +8,7 @@ import (
"github.com/h2non/gock" "github.com/h2non/gock"
"github.com/linuxsuren/api-testing/pkg/limit" "github.com/linuxsuren/api-testing/pkg/limit"
"github.com/linuxsuren/api-testing/pkg/util"
fakeruntime "github.com/linuxsuren/go-fake-runtime" fakeruntime "github.com/linuxsuren/go-fake-runtime"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -41,14 +42,12 @@ func TestRunSuite(t *testing.T) {
}, { }, {
name: "not found file", name: "not found file",
suiteFile: "testdata/fake.yaml", suiteFile: "testdata/fake.yaml",
prepare: func() {},
hasError: true, hasError: true,
}} }}
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
defer gock.Clean() defer gock.Clean()
util.MakeSureNotNil(tt.prepare)()
tt.prepare()
ctx := getDefaultContext() ctx := getDefaultContext()
opt := newDiskCardRunOption() opt := newDiskCardRunOption()
opt.requestTimeout = 30 * time.Second opt.requestTimeout = 30 * time.Second
@ -75,10 +74,9 @@ func TestRunCommand(t *testing.T) {
}, },
hasErr: true, hasErr: true,
}, { }, {
name: "file not found", name: "file not found",
args: []string{"--pattern", "fake"}, args: []string{"--pattern", "fake"},
prepare: func() {}, hasErr: false,
hasErr: false,
}, { }, {
name: "normal case", name: "normal case",
args: []string{"-p", simpleSuite}, args: []string{"-p", simpleSuite},
@ -90,8 +88,7 @@ func TestRunCommand(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
defer gock.Clean() defer gock.Clean()
tt.prepare() util.MakeSureNotNil(tt.prepare)()
root := &cobra.Command{Use: "root"} root := &cobra.Command{Use: "root"}
root.AddCommand(createRunCommand()) root.AddCommand(createRunCommand())

View File

@ -71,4 +71,5 @@ func (s *fakeGRPCServer) Serve(net.Listener) error {
// RegisterService is a fake method // RegisterService is a fake method
func (s *fakeGRPCServer) RegisterService(desc *grpc.ServiceDesc, impl interface{}) { func (s *fakeGRPCServer) RegisterService(desc *grpc.ServiceDesc, impl interface{}) {
// Do nothing due to this is a fake method
} }

View File

@ -16,7 +16,7 @@ func TestService(t *testing.T) {
assert.NotNil(t, err) assert.NotNil(t, err)
notLinux := NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "fake"}, NewFakeGRPCServer()) notLinux := NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "fake"}, NewFakeGRPCServer())
notLinux.SetArgs([]string{"service", "--action", "install"}) notLinux.SetArgs([]string{"service", paramAction, "install"})
err = notLinux.Execute() err = notLinux.Execute()
assert.NotNil(t, err) assert.NotNil(t, err)
@ -27,7 +27,7 @@ func TestService(t *testing.T) {
}() }()
targetScript := NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "linux"}, NewFakeGRPCServer()) targetScript := NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "linux"}, NewFakeGRPCServer())
targetScript.SetArgs([]string{"service", "--action", "install", "--script-path", tmpFile.Name()}) targetScript.SetArgs([]string{"service", paramAction, "install", "--script-path", tmpFile.Name()})
err = targetScript.Execute() err = targetScript.Execute()
assert.Nil(t, err) assert.Nil(t, err)
data, err := os.ReadFile(tmpFile.Name()) data, err := os.ReadFile(tmpFile.Name())
@ -67,3 +67,5 @@ func TestService(t *testing.T) {
}) })
} }
} }
const paramAction = "--action"

View File

@ -27,7 +27,7 @@ func TestGetPod(t *testing.T) {
name: "fake", name: "fake",
}, },
prepare: func() { prepare: func() {
gock.New("http://foo"). gock.New(urlFoo).
Get("/api/v1/namespaces/ns/pods/fake"). Get("/api/v1/namespaces/ns/pods/fake").
Reply(http.StatusOK). Reply(http.StatusOK).
JSON(`{"kind":"pod"}`) JSON(`{"kind":"pod"}`)
@ -46,7 +46,7 @@ func TestGetPod(t *testing.T) {
name: "fake", name: "fake",
}, },
prepare: func() { prepare: func() {
gock.New("http://foo"). gock.New(urlFoo).
Get("/apis/apps/v1/namespaces/ns/deployments/fake"). Get("/apis/apps/v1/namespaces/ns/deployments/fake").
Reply(http.StatusOK). Reply(http.StatusOK).
JSON(`{"kind":"deployment"}`) JSON(`{"kind":"deployment"}`)
@ -60,7 +60,7 @@ func TestGetPod(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
defer gock.Clean() defer gock.Clean()
tt.prepare() tt.prepare()
reader := kubernetes.NewDefaultReader("http://foo", "") reader := kubernetes.NewDefaultReader(urlFoo, "")
result, err := reader.GetResource(tt.group, tt.kind, tt.version, tt.namespacedName.namespace, tt.namespacedName.name) result, err := reader.GetResource(tt.group, tt.kind, tt.version, tt.namespacedName.namespace, tt.namespacedName.name)
assert.Equal(t, tt.expect, result) assert.Equal(t, tt.expect, result)
assert.Nil(t, err) assert.Nil(t, err)

View File

@ -8,6 +8,7 @@ import (
"github.com/antonmedv/expr" "github.com/antonmedv/expr"
"github.com/h2non/gock" "github.com/h2non/gock"
"github.com/linuxsuren/api-testing/pkg/runner/kubernetes" "github.com/linuxsuren/api-testing/pkg/runner/kubernetes"
"github.com/linuxsuren/api-testing/pkg/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -47,7 +48,6 @@ func TestKubernetesValidatorFunc(t *testing.T) {
}, { }, {
name: "no enough params", name: "no enough params",
expression: `k8s('crd')`, expression: `k8s('crd')`,
prepare: emptyPrepare,
expectBool: false, expectBool: false,
expectErr: true, expectErr: true,
}, { }, {
@ -73,11 +73,11 @@ func TestKubernetesValidatorFunc(t *testing.T) {
}, { }, {
name: "no kind", name: "no kind",
expression: `k8s({"foo": "bar"}, "ns", "foo").Exist()`, expression: `k8s({"foo": "bar"}, "ns", "foo").Exist()`,
prepare: emptyPrepare,
expectErr: true, expectErr: true,
}} }}
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
tt.prepare = util.MakeSureNotNil(tt.prepare)
tt.prepare() tt.prepare()
vm, err := expr.Compile(tt.expression, kubernetes.KubernetesValidatorFunc(), vm, err := expr.Compile(tt.expression, kubernetes.KubernetesValidatorFunc(),
kubernetes.PodValidatorFunc()) kubernetes.PodValidatorFunc())
@ -92,10 +92,6 @@ func TestKubernetesValidatorFunc(t *testing.T) {
} }
} }
func emptyPrepare() {
// only for testing
}
func preparePod() { func preparePod() {
gock.New(urlFoo). gock.New(urlFoo).
Get("/api/v1/namespaces/ns/pods/foo"). Get("/api/v1/namespaces/ns/pods/foo").

View File

@ -1,16 +1,12 @@
package runner package runner
import ( import (
"bytes"
"context" "context"
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"mime/multipart"
"net/http" "net/http"
"net/url"
"os"
"reflect" "reflect"
"strings" "strings"
"time" "time"
@ -202,40 +198,14 @@ func (r *simpleTestCaseRunner) RunTestCase(testcase *testing.TestCase, dataConte
} }
var requestBody io.Reader var requestBody io.Reader
if testcase.Request.Body != "" { if requestBody, err = testcase.Request.GetBody(); err != nil {
requestBody = bytes.NewBufferString(testcase.Request.Body) return
} else if testcase.Request.BodyFromFile != "" {
var data []byte
if data, err = os.ReadFile(testcase.Request.BodyFromFile); err != nil {
return
}
requestBody = bytes.NewBufferString(string(data))
} }
if err = testcase.Request.Render(dataContext); err != nil { if err = testcase.Request.Render(dataContext); err != nil {
return return
} }
if len(testcase.Request.Form) > 0 {
if testcase.Request.Header[contentType] == "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[contentType] = writer.FormDataContentType()
} else if testcase.Request.Header[contentType] == "application/x-www-form-urlencoded" {
data := url.Values{}
for key, val := range testcase.Request.Form {
data.Set(key, val)
}
requestBody = strings.NewReader(data.Encode())
}
}
var request *http.Request var request *http.Request
if request, err = http.NewRequestWithContext(ctx, testcase.Request.Method, testcase.Request.API, requestBody); err != nil { if request, err = http.NewRequestWithContext(ctx, testcase.Request.Method, testcase.Request.API, requestBody); err != nil {
return return
@ -436,5 +406,3 @@ func jsonSchemaValidation(schema string, body []byte) (err error) {
} }
return return
} }
const contentType = "Content-Type"

View File

@ -12,6 +12,7 @@ import (
"github.com/h2non/gock" "github.com/h2non/gock"
atest "github.com/linuxsuren/api-testing/pkg/testing" atest "github.com/linuxsuren/api-testing/pkg/testing"
"github.com/linuxsuren/api-testing/pkg/util"
fakeruntime "github.com/linuxsuren/go-fake-runtime" fakeruntime "github.com/linuxsuren/go-fake-runtime"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -300,7 +301,7 @@ func TestTestCase(t *testing.T) {
API: urlFoo, API: urlFoo,
Method: http.MethodPost, Method: http.MethodPost,
Header: map[string]string{ Header: map[string]string{
contentType: "multipart/form-data", util.ContentType: "multipart/form-data",
}, },
Form: map[string]string{ Form: map[string]string{
"key": "value", "key": "value",
@ -319,7 +320,7 @@ func TestTestCase(t *testing.T) {
API: urlFoo, API: urlFoo,
Method: http.MethodPost, Method: http.MethodPost,
Header: map[string]string{ Header: map[string]string{
contentType: "application/x-www-form-urlencoded", util.ContentType: "application/x-www-form-urlencoded",
}, },
Form: map[string]string{ Form: map[string]string{
"key": "value", "key": "value",

View File

@ -19,22 +19,22 @@ func TestRemoteServer(t *testing.T) {
}) })
assert.NotNil(t, err) assert.NotNil(t, err)
gock.New("http://foo").Get("/").Reply(http.StatusOK).JSON(&server) gock.New(urlFoo).Get("/").Reply(http.StatusOK).JSON(&server)
gock.New("http://foo").Get("/").Reply(http.StatusOK).JSON(&server) gock.New(urlFoo).Get("/").Reply(http.StatusOK).JSON(&server)
_, err = server.Run(context.TODO(), &TestTask{ _, err = server.Run(context.TODO(), &TestTask{
Kind: "suite", Kind: "suite",
Data: simpleSuite, Data: simpleSuite,
}) })
assert.Nil(t, err) assert.Nil(t, err)
gock.New("http://bar").Get("/").Reply(http.StatusOK).JSON(&server) gock.New(urlFoo).Get("/").Reply(http.StatusOK).JSON(&server)
_, err = server.Run(context.TODO(), &TestTask{ _, err = server.Run(context.TODO(), &TestTask{
Kind: "testcase", Kind: "testcase",
Data: simpleTestCase, Data: simpleTestCase,
}) })
assert.Nil(t, err) assert.Nil(t, err)
gock.New("http://foo").Get("/").Reply(http.StatusOK).JSON(&server) gock.New(urlFoo).Get("/").Reply(http.StatusOK).JSON(&server)
_, err = server.Run(context.TODO(), &TestTask{ _, err = server.Run(context.TODO(), &TestTask{
Kind: "testcaseInSuite", Kind: "testcaseInSuite",
Data: simpleSuite, Data: simpleSuite,
@ -42,7 +42,7 @@ func TestRemoteServer(t *testing.T) {
}) })
assert.Nil(t, err) assert.Nil(t, err)
gock.New("http://foo").Get("/").Reply(http.StatusOK).JSON(&server) gock.New(urlFoo).Get("/").Reply(http.StatusOK).JSON(&server)
_, err = server.Run(context.TODO(), &TestTask{ _, err = server.Run(context.TODO(), &TestTask{
Kind: "testcaseInSuite", Kind: "testcaseInSuite",
Data: simpleSuite, Data: simpleSuite,
@ -171,3 +171,5 @@ var simpleSuite string
//go:embed testdata/simple_testcase.yaml //go:embed testdata/simple_testcase.yaml
var simpleTestCase string var simpleTestCase string
const urlFoo = "http://foo"

View File

@ -1,12 +1,17 @@
package testing package testing
import ( import (
"bytes"
"fmt" "fmt"
"io"
"mime/multipart"
"net/http" "net/http"
"net/url"
"os" "os"
"strings" "strings"
"github.com/linuxsuren/api-testing/pkg/render" "github.com/linuxsuren/api-testing/pkg/render"
"github.com/linuxsuren/api-testing/pkg/util"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
@ -83,6 +88,37 @@ func (r *Request) Render(ctx interface{}) (err error) {
return return
} }
// GetBody returns the request body
func (r *Request) GetBody() (reader io.Reader, err error) {
if len(r.Form) > 0 {
if r.Header[util.ContentType] == util.MultiPartFormData {
multiBody := &bytes.Buffer{}
writer := multipart.NewWriter(multiBody)
for key, val := range r.Form {
writer.WriteField(key, val)
}
_ = writer.Close()
reader = multiBody
r.Header[util.ContentType] = writer.FormDataContentType()
} else if r.Header[util.ContentType] == util.Form {
data := url.Values{}
for key, val := range r.Form {
data.Set(key, val)
}
reader = strings.NewReader(data.Encode())
}
} else if r.Body != "" {
reader = bytes.NewBufferString(r.Body)
} else if r.BodyFromFile != "" {
var data []byte
if data, err = os.ReadFile(r.BodyFromFile); err == nil {
reader = bytes.NewBufferString(string(data))
}
}
return
}
// Render renders the response // Render renders the response
func (r *Response) Render(ctx interface{}) (err error) { func (r *Response) Render(ctx interface{}) (err error) {
r.StatusCode = zeroThenDefault(r.StatusCode, http.StatusOK) r.StatusCode = zeroThenDefault(r.StatusCode, http.StatusOK)

View File

@ -1,11 +1,13 @@
package testing package testing
import ( import (
"io"
"net/http" "net/http"
"testing" "testing"
_ "embed" _ "embed"
"github.com/linuxsuren/api-testing/pkg/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -212,5 +214,64 @@ func TestTestCase(t *testing.T) {
}, testCase) }, testCase)
} }
func TestGetBody(t *testing.T) {
defaultBody := "fake body"
tests := []struct {
name string
req *Request
expectBody string
containBody string
expectErr bool
}{{
name: "normal body",
req: &Request{Body: defaultBody},
expectBody: defaultBody,
}, {
name: "body from file",
req: &Request{BodyFromFile: "testdata/testcase.yaml"},
expectBody: testCaseContent,
}, {
name: "multipart form data",
req: &Request{
Header: map[string]string{
util.ContentType: util.MultiPartFormData,
},
Form: map[string]string{
"key": "value",
},
},
containBody: "name=\"key\"\r\n\r\nvalue\r\n",
}, {
name: "normal form",
req: &Request{
Header: map[string]string{
util.ContentType: util.Form,
},
Form: map[string]string{
"name": "linuxsuren",
},
},
expectBody: "name=linuxsuren",
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reader, err := tt.req.GetBody()
if tt.expectErr {
assert.NotNil(t, err)
} else {
assert.NotNil(t, reader)
data, err := io.ReadAll(reader)
assert.Nil(t, err)
if tt.expectBody != "" {
assert.Equal(t, tt.expectBody, string(data))
} else {
assert.Contains(t, string(data), tt.containBody)
}
}
})
}
}
//go:embed testdata/testcase.yaml //go:embed testdata/testcase.yaml
var testCaseContent string var testCaseContent string

28
pkg/util/default.go Normal file
View File

@ -0,0 +1,28 @@
// Package util provides a set of common functions
package util
// MakeSureNotNil makes sure the parameter is not nil
func MakeSureNotNil[T any](inter T) T {
switch val := any(inter).(type) {
case func():
if val == nil {
val = func() {
// only making sure this is not nil
}
return any(val).(T)
}
case map[string]string:
if val == nil {
val = map[string]string{}
return any(val).(T)
}
}
return inter
}
// ContentType is the HTTP header key
const (
ContentType = "Content-Type"
MultiPartFormData = "multipart/form-data"
Form = "application/x-www-form-urlencoded"
)

18
pkg/util/default_test.go Normal file
View File

@ -0,0 +1,18 @@
package util_test
import (
"testing"
"github.com/linuxsuren/api-testing/pkg/util"
"github.com/stretchr/testify/assert"
)
func TestMakeSureNotNil(t *testing.T) {
var fun func()
var mapStruct map[string]string
assert.NotNil(t, util.MakeSureNotNil(fun))
assert.NotNil(t, util.MakeSureNotNil(TestMakeSureNotNil))
assert.NotNil(t, util.MakeSureNotNil(mapStruct))
assert.NotNil(t, util.MakeSureNotNil(map[string]string{}))
}