From 9baa5913ffbceb3d065d9f9b21e191dd20d5285c Mon Sep 17 00:00:00 2001 From: Rick Date: Sun, 5 Mar 2023 16:14:18 +0800 Subject: [PATCH] test: add unit tests --- entrypoint.sh | 2 +- pkg/runner/simple.go | 17 ++- pkg/runner/simple_test.go | 272 +++++++++++++++++++++++++++++++++++++- pkg/testing/parser.go | 3 +- 4 files changed, 280 insertions(+), 14 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 6db88a13..fcc9d6c2 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -3,5 +3,5 @@ k3d cluster create k3d cluster list -atest init -k $2 --wait-namespace $3 --wait-resource $4 +atest init -k "$2" --wait-namespace "$3" --wait-resource "$4" atest run -p "$1" diff --git a/pkg/runner/simple.go b/pkg/runner/simple.go index bacbef56..19303d44 100644 --- a/pkg/runner/simple.go +++ b/pkg/runner/simple.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "os" "reflect" @@ -83,7 +82,7 @@ func RunTestCase(testcase *testing.TestCase, ctx interface{}) (output interface{ } var responseBodyData []byte - if responseBodyData, err = ioutil.ReadAll(resp.Body); err != nil { + if responseBodyData, err = io.ReadAll(resp.Body); err != nil { return } if testcase.Expect.Body != "" { @@ -100,13 +99,13 @@ func RunTestCase(testcase *testing.TestCase, ctx interface{}) (output interface{ case *json.UnmarshalTypeError: if b.Value != "array" { return - } else { - arrayOutput := []interface{}{} - if err = json.Unmarshal(responseBodyData, &arrayOutput); err != nil { - return - } - output = arrayOutput } + + var arrayOutput []interface{} + if err = json.Unmarshal(responseBodyData, &arrayOutput); err != nil { + return + } + output = arrayOutput default: return } @@ -141,7 +140,7 @@ func RunTestCase(testcase *testing.TestCase, ctx interface{}) (output interface{ } if !result.(bool) { - err = fmt.Errorf("faild to verify: %s", verify) + err = fmt.Errorf("failed to verify: %s", verify) break } } diff --git a/pkg/runner/simple_test.go b/pkg/runner/simple_test.go index f0084127..9ecf8f77 100644 --- a/pkg/runner/simple_test.go +++ b/pkg/runner/simple_test.go @@ -1,9 +1,11 @@ package runner import ( + "errors" "net/http" "testing" + _ "embed" "github.com/h2non/gock" atest "github.com/linuxsuren/api-testing/pkg/testing" "github.com/stretchr/testify/assert" @@ -17,7 +19,7 @@ func TestTestCase(t *testing.T) { prepare func() verify func(t *testing.T, output interface{}, err error) }{{ - name: "normal", + name: "normal, response is map", testCase: &atest.TestCase{ Request: atest.Request{ API: "http://localhost/foo", @@ -51,13 +53,279 @@ func TestTestCase(t *testing.T) { assert.Nil(t, err) assert.Equal(t, map[string]interface{}{"name": "linuxsuren"}, output) }, + }, { + name: "normal, response is slice", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: "http://localhost/foo", + }, + Expect: atest.Response{ + StatusCode: http.StatusOK, + Body: `["foo", "bar"]`, + }, + }, + prepare: func() { + gock.New("http://localhost"). + Get("/foo"). + Reply(http.StatusOK). + BodyString(`["foo", "bar"]`) + }, + verify: func(t *testing.T, output interface{}, err error) { + assert.Nil(t, err) + assert.Equal(t, []interface{}{"foo", "bar"}, output) + }, + }, { + name: "normal, response from file", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: "http://localhost/foo", + Method: http.MethodPost, + BodyFromFile: "testdata/generic_response.json", + }, + Expect: atest.Response{ + StatusCode: http.StatusOK, + }, + }, + prepare: func() { + gock.New("http://localhost"). + 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", + 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", + }, + Expect: atest.Response{ + StatusCode: http.StatusOK, + }, + }, + prepare: func() { + gock.New("http://localhost"). + 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", + }, + }, + prepare: func() { + gock.New("http://localhost"). + 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", + }, + Expect: atest.Response{ + Body: "bar", + }, + }, + prepare: func() { + gock.New("http://localhost"). + 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", + }, + Expect: atest.Response{ + Header: map[string]string{ + "foo": "bar", + }, + }, + }, + prepare: func() { + gock.New("http://localhost"). + 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", + }, + Expect: atest.Response{ + BodyFieldsExpect: map[string]string{ + "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) + }, + }, { + name: "body filed not match", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: "http://localhost/foo", + }, + Expect: atest.Response{ + BodyFieldsExpect: map[string]string{ + "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) + }, + }, { + name: "invalid filed finding", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: "http://localhost/foo", + }, + Expect: atest.Response{ + BodyFieldsExpect: map[string]string{ + "items[1]": "bar", + }, + }, + }, + prepare: func() { + gock.New("http://localhost"). + Get("/foo").Reply(http.StatusOK).BodyString(`{"items":[]}`) + }, + verify: func(t *testing.T, output interface{}, err error) { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "failed to get field") + }, + }, { + name: "verify failed", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: "http://localhost/foo", + }, + Expect: atest.Response{ + Verify: []string{ + "len(items) > 0", + }, + }, + }, + prepare: func() { + gock.New("http://localhost"). + Get("/foo").Reply(http.StatusOK).BodyString(`{"items":[]}`) + }, + verify: func(t *testing.T, output interface{}, err error) { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "failed to verify") + }, + }, { + name: "failed to compile", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: "http://localhost/foo", + }, + Expect: atest.Response{ + Verify: []string{ + `println("12")`, + }, + }, + }, + prepare: func() { + gock.New("http://localhost"). + Get("/foo").Reply(http.StatusOK).BodyString(`{"items":[]}`) + }, + verify: func(t *testing.T, output interface{}, err error) { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "unknown name println") + }, + }, { + name: "failed to compile", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: "http://localhost/foo", + }, + Expect: atest.Response{ + Verify: []string{ + `1 + 1`, + }, + }, + }, + prepare: func() { + gock.New("http://localhost"). + Get("/foo").Reply(http.StatusOK).BodyString(`{"items":[]}`) + }, + verify: func(t *testing.T, output interface{}, err error) { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "expected bool, but got int") + }, + }, { + name: "wrong API format", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: "ssh://localhost/foo", + Method: "fake,fake", + }, + }, + verify: func(t *testing.T, output interface{}, err error) { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "invalid method") + }, + }, { + name: "failed to render API", + testCase: &atest.TestCase{ + Request: atest.Request{ + API: "http://localhost/foo/{{.abc}", + }, + }, + verify: func(t *testing.T, output interface{}, err error) { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "template: api:1:") + }, }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { defer gock.Clean() - tt.prepare() + if tt.prepare != nil { + tt.prepare() + } output, err := RunTestCase(tt.testCase, tt.ctx) tt.verify(t, output, err) }) } } + +//go:embed testdata/generic_response.json +var genericBody string diff --git a/pkg/testing/parser.go b/pkg/testing/parser.go index 12dae291..8cbc2688 100644 --- a/pkg/testing/parser.go +++ b/pkg/testing/parser.go @@ -30,9 +30,8 @@ func (r *Request) Render(ctx interface{}) (err error) { buf := new(bytes.Buffer) if err = tpl.Execute(buf, ctx); err != nil { return - } else { - r.API = buf.String() } + r.API = buf.String() // read body from file if r.BodyFromFile != "" {