fix: the code smells from sonarqube (#43)

This commit is contained in:
Rick 2023-04-18 12:25:55 +08:00 committed by GitHub
parent f6d271b7eb
commit 6aed29e0b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 149 additions and 137 deletions

5
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"recommendations": [
"linuxsuren.api-testing"
]
}

View File

@ -1,7 +1,15 @@
FROM golang:1.18 as builder
FROM golang:1.18 AS builder
WORKDIR /workspace
COPY . .
COPY cmd/ cmd/
COPY pkg/ pkg/
COPY sample/ sample/
COPY go.mod go.mod
COPY go.sum go.sum
COPY main.go main.go
COPY README.md README.md
COPY LICENSE LICENSE
RUN go mod download
RUN CGO_ENABLE=0 go build -ldflags "-w -s" -o atest .
@ -19,5 +27,7 @@ LABEL "maintainer"="Rick <linuxsuren@gmail.com>"
LABEL "Name"="API testing"
COPY --from=builder /workspace/atest /usr/local/bin/atest
COPY --from=builder /workspace/LICENSE /LICENSE
COPY --from=builder /workspace/README.md /README.md
CMD ["atest", "server"]

View File

@ -9,7 +9,7 @@ import (
exec "github.com/linuxsuren/go-fake-runtime"
)
func Test_setRelativeDir(t *testing.T) {
func TestSetRelativeDir(t *testing.T) {
type args struct {
configFile string
testcase *atesting.TestCase

View File

@ -21,9 +21,9 @@ func TestRunSuite(t *testing.T) {
hasError bool
}{{
name: "simple",
suiteFile: "testdata/simple-suite.yaml",
suiteFile: simpleSuite,
prepare: func() {
gock.New("http://foo").
gock.New(urlFoo).
Get("/bar").
Reply(http.StatusOK).
JSON("{}")
@ -31,9 +31,9 @@ func TestRunSuite(t *testing.T) {
hasError: false,
}, {
name: "response is not JSON",
suiteFile: "testdata/simple-suite.yaml",
suiteFile: simpleSuite,
prepare: func() {
gock.New("http://foo").
gock.New(urlFoo).
Get("/bar").
Reply(http.StatusOK)
},
@ -69,9 +69,9 @@ func TestRunCommand(t *testing.T) {
hasErr bool
}{{
name: "status code is not match",
args: []string{"-p", "testdata/simple-suite.yaml"},
args: []string{"-p", simpleSuite},
prepare: func() {
gock.New("http://foo").Get("/bar")
gock.New(urlFoo).Get("/bar")
},
hasErr: true,
}, {
@ -81,9 +81,9 @@ func TestRunCommand(t *testing.T) {
hasErr: false,
}, {
name: "normal case",
args: []string{"-p", "testdata/simple-suite.yaml"},
args: []string{"-p", simpleSuite},
prepare: func() {
gock.New("http://foo").Get("/bar").Reply(http.StatusOK).JSON("{}")
gock.New(urlFoo).Get("/bar").Reply(http.StatusOK).JSON("{}")
},
hasErr: false,
}}
@ -177,3 +177,6 @@ func TestPreRunE(t *testing.T) {
})
}
}
const urlFoo = "http://foo"
const simpleSuite = "testdata/simple-suite.yaml"

View File

@ -12,7 +12,7 @@ import (
)
func TestKubernetesValidatorFunc(t *testing.T) {
os.Setenv("KUBERNETES_SERVER", "http://foo")
os.Setenv("KUBERNETES_SERVER", urlFoo)
os.Setenv("KUBERNETES_TOKEN", "token")
gock.InterceptClient(kubernetes.GetClient())
defer gock.RestoreClient(http.DefaultClient)
@ -92,44 +92,49 @@ func TestKubernetesValidatorFunc(t *testing.T) {
}
}
func emptyPrepare() {}
func emptyPrepare() {
// only for testing
}
func preparePod() {
gock.New("http://foo").
gock.New(urlFoo).
Get("/api/v1/namespaces/ns/pods/foo").
MatchHeader("Authorization", "Bearer token").
MatchHeader("Authorization", defaultToken).
Reply(http.StatusOK).
JSON(`{"kind":"pod"}`)
}
func prepareDeploy() {
gock.New("http://foo").
gock.New(urlFoo).
Get("/apis/apps/v1/namespaces/ns/deployments/foo").
MatchHeader("Authorization", "Bearer token").
MatchHeader("Authorization", defaultToken).
Reply(http.StatusOK).
JSON(`{"kind":"deploy"}`)
}
func prepareStatefulset() {
gock.New("http://foo").
gock.New(urlFoo).
Get("/apis/apps/v1/namespaces/ns/statefulsets/foo").
MatchHeader("Authorization", "Bearer token").
MatchHeader("Authorization", defaultToken).
Reply(http.StatusOK).
JSON(`{"kind":"statefulset"}`)
}
func prepareDaemonset() {
gock.New("http://foo").
gock.New(urlFoo).
Get("/apis/apps/v1/namespaces/ns/daemonsets/foo").
MatchHeader("Authorization", "Bearer token").
MatchHeader("Authorization", defaultToken).
Reply(http.StatusOK).
JSON(`{"kind":"daemonset"}`)
}
func prepareCRDVM() {
gock.New("http://foo").
gock.New(urlFoo).
Get("/apis/bar/v2/namespaces/ns/vms/foo").
MatchHeader("Authorization", "Bearer token").
MatchHeader("Authorization", defaultToken).
Reply(http.StatusOK).
JSON(`{"kind":"vm"}`)
}
const urlFoo = "http://foo"
const defaultToken = "Bearer token"

View File

@ -9,7 +9,9 @@ func NewDiscardTestReporter() TestReporter {
}
// PutRecord does nothing
func (r *discardTestReporter) PutRecord(*ReportRecord) {}
func (r *discardTestReporter) PutRecord(*ReportRecord) {
// Do nothing which is the design purpose
}
// GetAllRecords does nothing
func (r *discardTestReporter) GetAllRecords() []*ReportRecord {

View File

@ -34,6 +34,17 @@ func (r *memoryTestReporter) GetAllRecords() []*ReportRecord {
return r.records
}
func getMaxAndMin(max, min, duration time.Duration) (time.Duration, time.Duration) {
if max < duration {
max = duration
}
if min > duration {
min = duration
}
return max, min
}
// ExportAllReportResults exports all the report results
func (r *memoryTestReporter) ExportAllReportResults() (result ReportResultSlice, err error) {
resultWithTotal := map[string]*ReportResultWithTotal{}
@ -42,13 +53,7 @@ func (r *memoryTestReporter) ExportAllReportResults() (result ReportResultSlice,
duration := record.Duration()
if item, ok := resultWithTotal[api]; ok {
if item.Max < duration {
item.Max = duration
}
if item.Min > duration {
item.Min = duration
}
item.Max, item.Min = getMaxAndMin(item.Max, item.Min, duration)
item.Error += record.ErrorCount()
item.Total += duration
item.Count += 1

View File

@ -10,6 +10,10 @@ import (
"github.com/stretchr/testify/assert"
)
const urlFoo = "http://foo"
const urlBar = "http://bar"
const urlFake = "http://fake"
func TestExportAllReportResults(t *testing.T) {
now := time.Now()
@ -24,28 +28,28 @@ func TestExportAllReportResults(t *testing.T) {
}, {
name: "normal",
records: []*runner.ReportRecord{{
API: "http://foo",
API: urlFoo,
Method: http.MethodGet,
BeginTime: now,
EndTime: now.Add(time.Second * 3),
}, {
API: "http://foo",
API: urlFoo,
Method: http.MethodGet,
BeginTime: now,
EndTime: now.Add(time.Second * 4),
Error: errors.New("fake"),
}, {
API: "http://foo",
API: urlFoo,
Method: http.MethodGet,
BeginTime: now,
EndTime: now.Add(time.Second * 2),
}, {
API: "http://bar",
API: urlBar,
Method: http.MethodGet,
BeginTime: now,
EndTime: now.Add(time.Second),
}, {
API: "http://fake",
API: urlFake,
Method: http.MethodGet,
BeginTime: now,
EndTime: now.Add(time.Second * 5),

View File

@ -217,7 +217,7 @@ func (r *simpleTestCaseRunner) RunTestCase(testcase *testing.TestCase, dataConte
}
if len(testcase.Request.Form) > 0 {
if testcase.Request.Header["Content-Type"] == "multipart/form-data" {
if testcase.Request.Header[contentType] == "multipart/form-data" {
multiBody := &bytes.Buffer{}
writer := multipart.NewWriter(multiBody)
for key, val := range testcase.Request.Form {
@ -226,8 +226,8 @@ func (r *simpleTestCaseRunner) RunTestCase(testcase *testing.TestCase, dataConte
_ = writer.Close()
requestBody = multiBody
testcase.Request.Header["Content-Type"] = writer.FormDataContentType()
} else if testcase.Request.Header["Content-Type"] == "application/x-www-form-urlencoded" {
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)
@ -436,3 +436,5 @@ func jsonSchemaValidation(schema string, body []byte) (err error) {
}
return
}
const contentType = "Content-Type"

View File

@ -17,6 +17,10 @@ import (
)
func TestTestCase(t *testing.T) {
fooRequst := atest.Request{
API: urlFoo,
}
tests := []struct {
name string
execer fakeruntime.Execer
@ -32,14 +36,11 @@ func TestTestCase(t *testing.T) {
},
},
execer: fakeruntime.FakeExecer{ExpectError: errors.New("fake")},
verify: func(t *testing.T, output interface{}, err error) {
assert.NotNil(t, err)
},
}, {
name: "normal, response is map",
testCase: &atest.TestCase{
Request: atest.Request{
API: "http://localhost/foo",
API: urlFoo,
Header: map[string]string{
"key": "value",
},
@ -67,7 +68,7 @@ func TestTestCase(t *testing.T) {
},
execer: fakeruntime.FakeExecer{},
prepare: func() {
gock.New("http://localhost").
gock.New(urlLocalhost).
Get("/foo").
MatchHeader("key", "value").
Reply(http.StatusOK).
@ -81,16 +82,14 @@ func TestTestCase(t *testing.T) {
}, {
name: "normal, response is slice",
testCase: &atest.TestCase{
Request: atest.Request{
API: "http://localhost/foo",
},
Request: fooRequst,
Expect: atest.Response{
StatusCode: http.StatusOK,
Body: `["foo", "bar"]`,
},
},
prepare: func() {
gock.New("http://localhost").
gock.New(urlLocalhost).
Get("/foo").
Reply(http.StatusOK).
BodyString(`["foo", "bar"]`)
@ -103,7 +102,7 @@ func TestTestCase(t *testing.T) {
name: "normal, response from file",
testCase: &atest.TestCase{
Request: atest.Request{
API: "http://localhost/foo",
API: urlFoo,
Method: http.MethodPost,
BodyFromFile: "testdata/generic_response.json",
},
@ -112,79 +111,56 @@ func TestTestCase(t *testing.T) {
},
},
prepare: func() {
gock.New("http://localhost").
gock.New(urlLocalhost).
Post("/foo").BodyString(genericBody).
Reply(http.StatusOK).BodyString("123")
},
verify: func(t *testing.T, output interface{}, err error) {
assert.NotNil(t, err)
},
}, {
name: "response from a not found file",
testCase: &atest.TestCase{
Request: atest.Request{
API: "http://localhost/foo",
API: urlFoo,
Method: http.MethodPost,
BodyFromFile: "testdata/fake.json",
},
},
verify: func(t *testing.T, output interface{}, err error) {
assert.NotNil(t, err)
},
}, {
name: "bad request",
testCase: &atest.TestCase{
Request: atest.Request{
API: "http://localhost/foo",
},
Request: fooRequst,
Expect: atest.Response{
StatusCode: http.StatusOK,
},
},
prepare: func() {
gock.New("http://localhost").
gock.New(urlLocalhost).
Get("/foo").Reply(http.StatusBadRequest)
},
verify: func(t *testing.T, output interface{}, err error) {
assert.NotNil(t, err)
},
}, {
name: "error with request",
testCase: &atest.TestCase{
Request: atest.Request{
API: "http://localhost/foo",
},
Request: fooRequst,
},
prepare: func() {
gock.New("http://localhost").
gock.New(urlLocalhost).
Get("/foo").ReplyError(errors.New("error"))
},
verify: func(t *testing.T, output interface{}, err error) {
assert.NotNil(t, err)
},
}, {
name: "not match with body",
testCase: &atest.TestCase{
Request: atest.Request{
API: "http://localhost/foo",
},
Request: fooRequst,
Expect: atest.Response{
Body: "bar",
},
},
prepare: func() {
gock.New("http://localhost").
gock.New(urlLocalhost).
Get("/foo").Reply(http.StatusOK).BodyString("foo")
},
verify: func(t *testing.T, output interface{}, err error) {
assert.NotNil(t, err)
},
}, {
name: "not match with header",
testCase: &atest.TestCase{
Request: atest.Request{
API: "http://localhost/foo",
},
Request: fooRequst,
Expect: atest.Response{
Header: map[string]string{
"foo": "bar",
@ -192,56 +168,35 @@ func TestTestCase(t *testing.T) {
},
},
prepare: func() {
gock.New("http://localhost").
gock.New(urlLocalhost).
Get("/foo").Reply(http.StatusOK).SetHeader("foo", "value")
},
verify: func(t *testing.T, output interface{}, err error) {
assert.NotNil(t, err)
},
}, {
name: "not found from fields",
testCase: &atest.TestCase{
Request: atest.Request{
API: "http://localhost/foo",
},
Request: fooRequst,
Expect: atest.Response{
BodyFieldsExpect: map[string]interface{}{
"foo": "bar",
},
},
},
prepare: func() {
gock.New("http://localhost").
Get("/foo").Reply(http.StatusOK).BodyString(genericBody)
},
verify: func(t *testing.T, output interface{}, err error) {
assert.NotNil(t, err)
},
prepare: prepareForFoo,
}, {
name: "body filed not match",
testCase: &atest.TestCase{
Request: atest.Request{
API: "http://localhost/foo",
},
Request: fooRequst,
Expect: atest.Response{
BodyFieldsExpect: map[string]interface{}{
"name": "bar",
},
},
},
prepare: func() {
gock.New("http://localhost").
Get("/foo").Reply(http.StatusOK).BodyString(genericBody)
},
verify: func(t *testing.T, output interface{}, err error) {
assert.NotNil(t, err)
},
prepare: prepareForFoo,
}, {
name: "invalid filed finding",
testCase: &atest.TestCase{
Request: atest.Request{
API: "http://localhost/foo",
},
Request: fooRequst,
Expect: atest.Response{
BodyFieldsExpect: map[string]interface{}{
"items[1]": "bar",
@ -249,7 +204,7 @@ func TestTestCase(t *testing.T) {
},
},
prepare: func() {
gock.New("http://localhost").
gock.New(urlLocalhost).
Get("/foo").Reply(http.StatusOK).BodyString(`{"items":[]}`)
},
verify: func(t *testing.T, output interface{}, err error) {
@ -261,7 +216,7 @@ func TestTestCase(t *testing.T) {
// name: "verify failed",
// testCase: &atest.TestCase{
// Request: atest.Request{
// API: "http://localhost/foo",
// API: urlFoo,
// },
// Expect: atest.Response{
// Verify: []string{
@ -270,7 +225,7 @@ func TestTestCase(t *testing.T) {
// },
// },
// prepare: func() {
// gock.New("http://localhost").
// gock.New(urlLocalhost).
// Get("/foo").Reply(http.StatusOK).BodyString(`{"items":[]}`)
// },
// verify: func(t *testing.T, output interface{}, err error) {
@ -282,9 +237,7 @@ func TestTestCase(t *testing.T) {
{
name: "failed to compile",
testCase: &atest.TestCase{
Request: atest.Request{
API: "http://localhost/foo",
},
Request: fooRequst,
Expect: atest.Response{
Verify: []string{
`println("12")`,
@ -292,7 +245,7 @@ func TestTestCase(t *testing.T) {
},
},
prepare: func() {
gock.New("http://localhost").
gock.New(urlLocalhost).
Get("/foo").Reply(http.StatusOK).BodyString(`{"items":[]}`)
},
verify: func(t *testing.T, output interface{}, err error) {
@ -302,9 +255,7 @@ func TestTestCase(t *testing.T) {
}, {
name: "failed to compile",
testCase: &atest.TestCase{
Request: atest.Request{
API: "http://localhost/foo",
},
Request: fooRequst,
Expect: atest.Response{
Verify: []string{
`1 + 1`,
@ -312,7 +263,7 @@ func TestTestCase(t *testing.T) {
},
},
prepare: func() {
gock.New("http://localhost").
gock.New(urlLocalhost).
Get("/foo").Reply(http.StatusOK).BodyString(`{"items":[]}`)
},
verify: func(t *testing.T, output interface{}, err error) {
@ -346,10 +297,10 @@ func TestTestCase(t *testing.T) {
name: "multipart form request",
testCase: &atest.TestCase{
Request: atest.Request{
API: "http://localhost/foo",
API: urlFoo,
Method: http.MethodPost,
Header: map[string]string{
"Content-Type": "multipart/form-data",
contentType: "multipart/form-data",
},
Form: map[string]string{
"key": "value",
@ -357,20 +308,18 @@ func TestTestCase(t *testing.T) {
},
},
prepare: func() {
gock.New("http://localhost").
gock.New(urlLocalhost).
Post("/foo").Reply(http.StatusOK).BodyString(`{"items":[]}`)
},
verify: func(t *testing.T, output interface{}, err error) {
assert.Nil(t, err)
},
verify: noError,
}, {
name: "normal form request",
testCase: &atest.TestCase{
Request: atest.Request{
API: "http://localhost/foo",
API: urlFoo,
Method: http.MethodPost,
Header: map[string]string{
"Content-Type": "application/x-www-form-urlencoded",
contentType: "application/x-www-form-urlencoded",
},
Form: map[string]string{
"key": "value",
@ -378,12 +327,10 @@ func TestTestCase(t *testing.T) {
},
},
prepare: func() {
gock.New("http://localhost").
gock.New(urlLocalhost).
Post("/foo").Reply(http.StatusOK).BodyString(`{"items":[]}`)
},
verify: func(t *testing.T, output interface{}, err error) {
assert.Nil(t, err)
},
verify: noError,
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -391,6 +338,9 @@ func TestTestCase(t *testing.T) {
if tt.prepare != nil {
tt.prepare()
}
if tt.verify == nil {
tt.verify = hasError
}
runner := NewSimpleTestCaseRunner().WithOutputWriter(os.Stdout)
if tt.execer != nil {
runner.WithExecer(tt.execer)
@ -465,5 +415,21 @@ const defaultSchemaForTest = `{"properties": {
"type":"object"
}`
func hasError(t *testing.T, output interface{}, err error) {
assert.NotNil(t, err)
}
func noError(t *testing.T, output interface{}, err error) {
assert.Nil(t, err)
}
func prepareForFoo() {
gock.New(urlLocalhost).
Get("/foo").Reply(http.StatusOK).BodyString(genericBody)
}
//go:embed testdata/generic_response.json
var genericBody string
const urlFoo = "http://localhost/foo"
const urlLocalhost = "http://localhost"

View File

@ -24,16 +24,19 @@ func NewRemoteServer() RunnerServer {
return &server{}
}
func withDefaultValue(old, defVal any) any {
if old == "" || old == nil {
old = defVal
}
return old
}
// Run start to run the test task
func (s *server) Run(ctx context.Context, task *TestTask) (reply *HelloReply, err error) {
if task.Level == "" {
task.Level = "info"
}
task.Level = withDefaultValue(task.Level, "info").(string)
task.Env = withDefaultValue(task.Env, map[string]string{}).(map[string]string)
var suite *testing.TestSuite
if task.Env == nil {
task.Env = map[string]string{}
}
// TODO may not safe in multiple threads
oldEnv := map[string]string{}

View File

@ -159,6 +159,13 @@ func TestUniqueSlice(t *testing.T) {
assert.Equal(t, []string{"a", "b"}, uniqueSlice.GetAll())
}
func TestWithDefaultValue(t *testing.T) {
assert.Equal(t, withDefaultValue("a", "b"), "a")
assert.Equal(t, withDefaultValue("", "b"), "b")
assert.Equal(t, withDefaultValue(nil, map[string]string{"key": "val"}), map[string]string{"key": "val"})
assert.Equal(t, withDefaultValue(map[string]string{"key": "val"}, map[string]string{"key": "value"}), map[string]string{"key": "val"})
}
//go:embed testdata/simple.yaml
var simpleSuite string