feat: support detailed mock server (#377)

* feat: support detailed mock server

* make the constant be private

---------

Co-authored-by: rick <linuxsuren@users.noreply.github.com>
This commit is contained in:
Rick 2024-04-21 17:49:44 +08:00 committed by GitHub
parent c17b3e7c89
commit 062f96f965
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 73 additions and 2 deletions

2
go.mod
View File

@ -10,6 +10,7 @@ require (
github.com/expr-lang/expr v1.15.6
github.com/flopp/go-findfont v0.1.0
github.com/ghodss/yaml v1.0.0
github.com/gorilla/mux v1.8.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0
github.com/h2non/gock v1.2.0
github.com/invopop/jsonschema v0.7.0
@ -42,7 +43,6 @@ require (
github.com/gofrs/uuid v4.2.0+incompatible // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-memdb v1.3.2 // indirect

View File

@ -27,6 +27,7 @@ items:
path: /v1/teams/{team}/repos
method: GET
response:
statusCode: 200
body: |
{
"status": 0,
@ -40,12 +41,16 @@ items:
request:
path: /v1/repos/{repo}/prs
response:
headers:
- key: server
value: mock
body: |
{
"count": 1,
"items": [{
"title": "fix: there is a bug on page one",
"title": "fix: there is a bug on page {{ randEnum "one" }}",
"number": 123,
"message": "{{index (index .Header "Accept-Encoding") 0}}",
"author": "someone",
"status": "success"
}]

View File

@ -21,6 +21,7 @@ import (
"github.com/gorilla/mux"
"github.com/linuxsuren/api-testing/pkg/render"
"github.com/linuxsuren/api-testing/pkg/util"
"github.com/linuxsuren/api-testing/pkg/version"
"io"
"log"
"net"
@ -57,6 +58,11 @@ func (s *inMemoryServer) Start(reader Reader) (err error) {
s.initObjectData(obj)
}
log.Println("start to run all the APIs from items")
for _, item := range server.Items {
s.startItem(item)
}
s.listener, err = net.Listen("tcp", fmt.Sprintf(":%d", s.port))
go func() {
err = http.Serve(s.listener, s.mux)
@ -201,6 +207,25 @@ func (s *inMemoryServer) startObject(obj Object) {
return
}
func (s *inMemoryServer) startItem(item Item) {
s.mux.HandleFunc(item.Request.Path, func(w http.ResponseWriter, req *http.Request) {
item.Response.Headers = append(item.Response.Headers, Header{
Key: headerMockServer,
Value: fmt.Sprintf("api-testing: %s", version.GetVersion()),
})
for _, header := range item.Response.Headers {
w.Header().Set(header.Key, header.Value)
}
body, err := render.Render("start-item", item.Response.Body, req)
if err == nil {
w.Write([]byte(body))
} else {
w.Write([]byte(err.Error()))
}
w.WriteHeader(util.ZeroThenDefault(item.Response.StatusCode, http.StatusOK))
}).Methods(util.EmptyThenDefault(item.Request.Method, http.MethodGet))
}
func (s *inMemoryServer) initObjectData(obj Object) {
if obj.Sample == "" {
return

View File

@ -20,6 +20,7 @@ import (
"github.com/stretchr/testify/assert"
"io"
"net/http"
"strings"
"testing"
)
@ -116,6 +117,19 @@ func TestInMemoryServer(t *testing.T) {
assert.Equal(t, http.StatusMethodNotAllowed, resp.StatusCode)
})
t.Run("mock item", func(t *testing.T) {
resp, err := http.Get(api + "/v1/repos/test/prs")
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "mock", resp.Header.Get("server"))
assert.NotEmpty(t, resp.Header.Get(headerMockServer))
data, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
assert.True(t, strings.Contains(string(data), `"message": "gzip"`), string(data))
})
t.Run("not found config file", func(t *testing.T) {
server := NewInMemoryServer(0)
err := server.Start(NewLocalFileReader("fake"))

View File

@ -20,3 +20,7 @@ type DynamicServer interface {
Stop() error
GetPort() string
}
const (
headerMockServer = "MockServer"
)

View File

@ -27,6 +27,29 @@ type Field struct {
Kind string `yaml:"kind"`
}
type Item struct {
Name string `yaml:"name"`
Request Request `yaml:"request"`
Response Response `yaml:"response"`
}
type Request struct {
Path string `yaml:"path"`
Method string `yaml:"method"`
}
type Response struct {
Body string `yaml:"body"`
Headers []Header `yaml:"headers"`
StatusCode int `yaml:"statusCode"`
}
type Header struct {
Key string `yaml:"key"`
Value string `yaml:"value"`
}
type Server struct {
Objects []Object `yaml:"objects"`
Items []Item `yaml:"items"`
}