Skip to content

Commit

Permalink
feat: support to load test suite from URL
Browse files Browse the repository at this point in the history
  • Loading branch information
LinuxSuRen committed Aug 19, 2023
1 parent 9088297 commit ced3a6b
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 10 deletions.
20 changes: 20 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ k3s kubectl apply -k sample/kubernetes/default
kustomize build sample/kubernetes/docker.io/ | k3s kubectl apply -f -
```

## Run your test cases
The test suite file could be in local, or in the HTTP server. See the following different ways:

* `atest run -p your-local-file.yaml`
* `atest run -p https://gitee.com/linuxsuren/api-testing/raw/master/sample/testsuite-gitee.yaml`
* `atest run -p http://localhost:8080/server.Runner/ConvertTestSuite?suite=sample`

For the last one, it represents the API Testing server.

## Convert to JMeter
[JMeter](https://jmeter.apache.org/) is a load test tool. You can run the following commands from the root directory of this repository:

```shell
atest convert --converter jmeter -p sample/testsuite-gitee.yaml --target bin/gitee.jmx

jmeter -n -t bin/gitee.jmx
```

Please feel free to bring more test tool converters.

## Storage
There are multiple storage backends supported. See the status from the list:

Expand Down
18 changes: 10 additions & 8 deletions pkg/generator/converter_jmeter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,18 @@ func TestJmeterConvert(t *testing.T) {
assert.NotNil(t, jmeterConvert)

converters := GetTestSuiteConverters()
assert.Equal(t, 1, len(converters))
assert.Equal(t, 2, len(converters))
})

testSuite := &atest.TestSuite{
converter := &jmeterConverter{}
output, err := converter.Convert(createTestSuiteForTest())
assert.NoError(t, err)

assert.Equal(t, expectedJmeter, output, output)
}

func createTestSuiteForTest() *atest.TestSuite {
return &atest.TestSuite{
Name: "API Testing",
API: "http://localhost:8080",
Items: []atest.TestCase{{
Expand All @@ -53,12 +61,6 @@ func TestJmeterConvert(t *testing.T) {
},
}},
}

converter := &jmeterConverter{}
output, err := converter.Convert(testSuite)
assert.NoError(t, err)

assert.Equal(t, expectedJmeter, output, output)
}

//go:embed testdata/expected_jmeter.jmx
Expand Down
51 changes: 51 additions & 0 deletions pkg/generator/converter_raw.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
MIT License
Copyright (c) 2023 API Testing Authors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package generator

import (
"github.com/linuxsuren/api-testing/pkg/testing"
"gopkg.in/yaml.v3"
)

type rawConverter struct {
}

func init() {
RegisterTestSuiteConverter("raw", &rawConverter{})
}

func (c *rawConverter) Convert(testSuite *testing.TestSuite) (result string, err error) {
if err = testSuite.Render(make(map[string]interface{})); err == nil {
for _, item := range testSuite.Items {
item.Request.RenderAPI(testSuite.API)
}

var data []byte
if data, err = yaml.Marshal(testSuite); err == nil {
result = string(data)
}
}
return
}
45 changes: 45 additions & 0 deletions pkg/generator/converter_raw_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
MIT License
Copyright (c) 2023 API Testing Authors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package generator

import (
"testing"

_ "embed"

"github.com/stretchr/testify/assert"
)

func TestRawConvert(t *testing.T) {
rawConvert := GetTestSuiteConverter("raw")
assert.NotNil(t, rawConvert)

output, err := rawConvert.Convert(createTestSuiteForTest())
assert.NoError(t, err)
assert.Equal(t, expectedTestSuite, output)
}

//go:embed testdata/expected_testsuite.yaml
var expectedTestSuite string
7 changes: 7 additions & 0 deletions pkg/generator/testdata/expected_testsuite.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: API Testing
api: http://localhost:8080
items:
- name: hello-jmeter
request:
api: /GetSuites
method: POST
2 changes: 1 addition & 1 deletion pkg/server/remote_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ func TestCodeGenerator(t *testing.T) {
t.Run("ListConverter", func(t *testing.T) {
list, err := server.ListConverter(ctx, &Empty{})
assert.NoError(t, err)
assert.Equal(t, 1, len(list.Data))
assert.Equal(t, 2, len(list.Data))
})

t.Run("ConvertTestSuite no converter given", func(t *testing.T) {
Expand Down
60 changes: 59 additions & 1 deletion pkg/testing/loader_file.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package testing

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
"strings"
"sync"

"github.com/linuxsuren/api-testing/pkg/util"
Expand Down Expand Up @@ -35,7 +41,54 @@ func (l *fileLoader) HasMore() bool {

// Load returns the test case content
func (l *fileLoader) Load() (data []byte, err error) {
data, err = os.ReadFile(l.paths[l.index])
targetFile := l.paths[l.index]
if strings.HasPrefix(targetFile, "http://") || strings.HasPrefix(targetFile, "https://") {
var ok bool
data, ok, err = gRPCCompitableRequest(targetFile)
if !ok && err == nil {
var resp *http.Response
if resp, err = http.Get(targetFile); err == nil {
data, err = io.ReadAll(resp.Body)
}
}
} else {
data, err = os.ReadFile(targetFile)
}
return
}

func gRPCCompitableRequest(targetURLStr string) (data []byte, ok bool, err error) {
if !strings.Contains(targetURLStr, "server.Runner/ConvertTestSuite") {
return
}

var targetURL *url.URL
if targetURL, err = url.Parse(targetURLStr); err != nil {
return
}

suite := targetURL.Query().Get("suite")
if suite == "" {
err = fmt.Errorf("suite is required")
return
}

payload := new(bytes.Buffer)
payload.WriteString(fmt.Sprintf(`{"TestSuite":"%s", "Generator":"raw"}`, suite))

var resp *http.Response
if resp, err = http.Post(targetURLStr, "", payload); err == nil {
if data, err = io.ReadAll(resp.Body); err != nil {
return
}

var gRPCData map[string]interface{}
if err = json.Unmarshal(data, &gRPCData); err == nil {
var obj interface{}
obj, ok = gRPCData["message"]
data = []byte(fmt.Sprintf("%v", obj))
}
}
return
}

Expand All @@ -48,6 +101,11 @@ func (l *fileLoader) Put(item string) (err error) {
l.parent = path.Dir(item)
}

if strings.HasPrefix(item, "http://") || strings.HasPrefix(item, "https://") {
l.paths = append(l.paths, item)
return
}

for _, pattern := range util.Expand(item) {
var files []string
if files, err = filepath.Glob(pattern); err == nil {
Expand Down
57 changes: 57 additions & 0 deletions pkg/testing/loader_file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"testing"

"github.com/h2non/gock"
atest "github.com/linuxsuren/api-testing/pkg/testing"
atesting "github.com/linuxsuren/api-testing/pkg/testing"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -44,6 +45,62 @@ func TestFileLoader(t *testing.T) {
}
}

func TestURLLoader(t *testing.T) {
const json = `{"key": "value", "message": "sample"}`

t.Run("normal HTTP GET request", func(t *testing.T) {
loader := atest.NewFileLoader()
defer gock.Off()

err := loader.Put(urlFake)
assert.NoError(t, err)

gock.New(urlFake).Get("/").Reply(http.StatusOK).BodyString(json)

assert.True(t, loader.HasMore())
var data []byte
data, err = loader.Load()
assert.NoError(t, err)
assert.Equal(t, json, string(data))
})

t.Run("HTTP POST request, lack of suite name", func(t *testing.T) {
loader := atest.NewFileLoader()
defer gock.Off()
const api = "/server.Runner/ConvertTestSuite"
const reqURL = urlFake + api

err := loader.Put(reqURL)
assert.NoError(t, err)

gock.New(urlFake).Get(api).Reply(http.StatusOK).BodyString(json)

assert.True(t, loader.HasMore())
_, err = loader.Load()
assert.Error(t, err)
})

t.Run("HTTP POST request", func(t *testing.T) {
loader := atest.NewFileLoader()
defer gock.Off()
const api = "/server.Runner/ConvertTestSuite"
const reqURL = urlFake + api + "?suite=sample"

err := loader.Put(reqURL)
assert.NoError(t, err)

gock.New(urlFake).Post(api).BodyString(`{"TestSuite":"sample", "Generator":"raw"}`).
Reply(http.StatusOK).BodyString(json)

assert.True(t, loader.HasMore())

var data []byte
data, err = loader.Load()
assert.NoError(t, err)
assert.Equal(t, "sample", string(data))
})
}

func defaultVerify(t *testing.T, loader atest.Loader) {
assert.True(t, loader.HasMore())
data, err := loader.Load()
Expand Down

0 comments on commit ced3a6b

Please sign in to comment.