feat: support combine mock and core server together (#390)
Co-authored-by: rick <linuxsuren@users.noreply.github.com>
This commit is contained in:
parent
1943e7d82e
commit
f4f1d4c312
|
@ -37,6 +37,7 @@ import (
|
||||||
|
|
||||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||||
"github.com/linuxsuren/api-testing/pkg/logging"
|
"github.com/linuxsuren/api-testing/pkg/logging"
|
||||||
|
"github.com/linuxsuren/api-testing/pkg/mock"
|
||||||
"github.com/linuxsuren/api-testing/pkg/oauth"
|
"github.com/linuxsuren/api-testing/pkg/oauth"
|
||||||
template "github.com/linuxsuren/api-testing/pkg/render"
|
template "github.com/linuxsuren/api-testing/pkg/render"
|
||||||
"github.com/linuxsuren/api-testing/pkg/server"
|
"github.com/linuxsuren/api-testing/pkg/server"
|
||||||
|
@ -88,6 +89,7 @@ func createServerCmd(execer fakeruntime.Execer, httpServer server.HTTPServer) (c
|
||||||
flags.StringVarP(&opt.clientID, "client-id", "", os.Getenv("OAUTH_CLIENT_ID"), "ClientID is the application's ID")
|
flags.StringVarP(&opt.clientID, "client-id", "", os.Getenv("OAUTH_CLIENT_ID"), "ClientID is the application's ID")
|
||||||
flags.StringVarP(&opt.clientSecret, "client-secret", "", os.Getenv("OAUTH_CLIENT_SECRET"), "ClientSecret is the application's secret")
|
flags.StringVarP(&opt.clientSecret, "client-secret", "", os.Getenv("OAUTH_CLIENT_SECRET"), "ClientSecret is the application's secret")
|
||||||
flags.BoolVarP(&opt.dryRun, "dry-run", "", false, "Do not really start a gRPC server")
|
flags.BoolVarP(&opt.dryRun, "dry-run", "", false, "Do not really start a gRPC server")
|
||||||
|
flags.StringArrayVarP(&opt.mockConfig, "mock-config", "", nil, "The mock config files")
|
||||||
|
|
||||||
// gc related flags
|
// gc related flags
|
||||||
flags.IntVarP(&opt.gcPercent, "gc-percent", "", 100, "The GC percent of Go")
|
flags.IntVarP(&opt.gcPercent, "gc-percent", "", 100, "The GC percent of Go")
|
||||||
|
@ -121,6 +123,8 @@ type serverOption struct {
|
||||||
oauthSkipTls bool
|
oauthSkipTls bool
|
||||||
oauthGroup []string
|
oauthGroup []string
|
||||||
|
|
||||||
|
mockConfig []string
|
||||||
|
|
||||||
gcPercent int
|
gcPercent int
|
||||||
|
|
||||||
dryRun bool
|
dryRun bool
|
||||||
|
@ -283,10 +287,24 @@ func (o *serverOption) runE(cmd *cobra.Command, args []string) (err error) {
|
||||||
promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: reg}).ServeHTTP(w, r)
|
promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: reg}).ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
combineHandlers := server.NewDefaultCombineHandler()
|
||||||
|
combineHandlers.PutHandler("", mux)
|
||||||
|
|
||||||
|
if len(o.mockConfig) > 0 {
|
||||||
|
cmd.Println("currently only one mock config is supported, will take the first one")
|
||||||
|
var mockServerHandler http.Handler
|
||||||
|
if mockServerHandler, err = mock.NewInMemoryServer(0).
|
||||||
|
SetupHandler(mock.NewLocalFileReader(o.mockConfig[0])); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
combineHandlers.PutHandler("/mock", mockServerHandler)
|
||||||
|
}
|
||||||
|
|
||||||
debugHandler(mux, remoteServer)
|
debugHandler(mux, remoteServer)
|
||||||
o.httpServer.WithHandler(mux)
|
o.httpServer.WithHandler(combineHandlers.GetHandler())
|
||||||
serverLogger.Info("HTTP server listening at", "addr", httplis.Addr())
|
serverLogger.Info("HTTP server listening at", "addr", httplis.Addr())
|
||||||
serverLogger.Info("Server is running.")
|
serverLogger.Info("Server is running.")
|
||||||
|
|
||||||
err = o.httpServer.Serve(httplis)
|
err = o.httpServer.Serve(httplis)
|
||||||
err = util.IgnoreErrServerClosed(err)
|
err = util.IgnoreErrServerClosed(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,18 @@ func TestPrintProto(t *testing.T) {
|
||||||
verify: func(t *testing.T, buf *bytes.Buffer, err error) {
|
verify: func(t *testing.T, buf *bytes.Buffer, err error) {
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
name: "mock server, not found config",
|
||||||
|
args: []string{"server", "--mock-config=fake", "-p=0", "--http-port=0"},
|
||||||
|
verify: func(t *testing.T, buffer *bytes.Buffer, err error) {
|
||||||
|
assert.Error(t, err)
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
name: "mock server, normal",
|
||||||
|
args: []string{"server", "--mock-config=testdata/invalid-api.yaml", "-p=0", "--http-port=0"},
|
||||||
|
verify: func(t *testing.T, buffer *bytes.Buffer, err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -14,8 +14,6 @@ require (
|
||||||
github.com/go-logr/zapr v1.3.0
|
github.com/go-logr/zapr v1.3.0
|
||||||
github.com/gorilla/mux v1.8.1
|
github.com/gorilla/mux v1.8.1
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.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/h2non/gock v1.2.0
|
||||||
github.com/invopop/jsonschema v0.7.0
|
github.com/invopop/jsonschema v0.7.0
|
||||||
github.com/jhump/protoreflect v1.15.3
|
github.com/jhump/protoreflect v1.15.3
|
||||||
|
@ -48,8 +46,6 @@ require (
|
||||||
github.com/gofrs/uuid v4.2.0+incompatible // indirect
|
github.com/gofrs/uuid v4.2.0+incompatible // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
|
||||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // 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-immutable-radix v1.3.1 // indirect
|
||||||
github.com/hashicorp/go-memdb v1.3.2 // indirect
|
github.com/hashicorp/go-memdb v1.3.2 // indirect
|
||||||
|
|
|
@ -49,7 +49,7 @@ func NewInMemoryServer(port int) DynamicServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryServer) Start(reader Reader) (err error) {
|
func (s *inMemoryServer) SetupHandler(reader Reader) (handler http.Handler, err error) {
|
||||||
var server *Server
|
var server *Server
|
||||||
if server, err = reader.Parse(); err != nil {
|
if server, err = reader.Parse(); err != nil {
|
||||||
return
|
return
|
||||||
|
@ -57,7 +57,8 @@ func (s *inMemoryServer) Start(reader Reader) (err error) {
|
||||||
|
|
||||||
// init the data
|
// init the data
|
||||||
s.data = make(map[string][]map[string]interface{})
|
s.data = make(map[string][]map[string]interface{})
|
||||||
s.mux = mux.NewRouter()
|
s.mux = mux.NewRouter().PathPrefix("/mock").Subrouter()
|
||||||
|
handler = s.mux
|
||||||
|
|
||||||
memLogger.Info("start to run all the APIs from objects")
|
memLogger.Info("start to run all the APIs from objects")
|
||||||
for _, obj := range server.Objects {
|
for _, obj := range server.Objects {
|
||||||
|
@ -69,10 +70,18 @@ func (s *inMemoryServer) Start(reader Reader) (err error) {
|
||||||
for _, item := range server.Items {
|
for _, item := range server.Items {
|
||||||
s.startItem(item)
|
s.startItem(item)
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *inMemoryServer) Start(reader Reader) (err error) {
|
||||||
|
var handler http.Handler
|
||||||
|
if handler, err = s.SetupHandler(reader); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
s.listener, err = net.Listen("tcp", fmt.Sprintf(":%d", s.port))
|
s.listener, err = net.Listen("tcp", fmt.Sprintf(":%d", s.port))
|
||||||
go func() {
|
go func() {
|
||||||
err = http.Serve(s.listener, s.mux)
|
err = http.Serve(s.listener, handler)
|
||||||
}()
|
}()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ func TestInMemoryServer(t *testing.T) {
|
||||||
server.Stop()
|
server.Stop()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
api := "http://localhost:" + server.GetPort()
|
api := "http://localhost:" + server.GetPort() + "/mock"
|
||||||
|
|
||||||
_, err = http.Post(api+"/team", "", bytes.NewBufferString(`{
|
_, err = http.Post(api+"/team", "", bytes.NewBufferString(`{
|
||||||
"name": "test",
|
"name": "test",
|
||||||
|
|
|
@ -15,8 +15,11 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
package mock
|
package mock
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
type DynamicServer interface {
|
type DynamicServer interface {
|
||||||
Start(reader Reader) error
|
Start(reader Reader) error
|
||||||
|
SetupHandler(reader Reader) (http.Handler, error)
|
||||||
Stop() error
|
Stop() error
|
||||||
GetPort() string
|
GetPort() string
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,25 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024 API Testing Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
context "context"
|
context "context"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTPServer is an interface for serving HTTP requests
|
// HTTPServer is an interface for serving HTTP requests
|
||||||
|
@ -13,6 +29,11 @@ type HTTPServer interface {
|
||||||
Shutdown(ctx context.Context) error
|
Shutdown(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CombineHandler interface {
|
||||||
|
PutHandler(string, http.Handler)
|
||||||
|
GetHandler() http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
type defaultHTTPServer struct {
|
type defaultHTTPServer struct {
|
||||||
server *http.Server
|
server *http.Server
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
|
@ -37,6 +58,42 @@ func (s *defaultHTTPServer) Shutdown(ctx context.Context) error {
|
||||||
return s.server.Shutdown(ctx)
|
return s.server.Shutdown(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type defaultCombineHandler struct {
|
||||||
|
handlerMapping map[string]http.Handler
|
||||||
|
defaultHandler http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultCombineHandler() CombineHandler {
|
||||||
|
return &defaultCombineHandler{
|
||||||
|
handlerMapping: make(map[string]http.Handler),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *defaultCombineHandler) PutHandler(name string, handler http.Handler) {
|
||||||
|
if name == "" {
|
||||||
|
s.defaultHandler = handler
|
||||||
|
} else {
|
||||||
|
s.handlerMapping[name] = handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *defaultCombineHandler) GetHandler() http.Handler {
|
||||||
|
if len(s.handlerMapping) == 0 {
|
||||||
|
return s.defaultHandler
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *defaultCombineHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
for prefix, handler := range s.handlerMapping {
|
||||||
|
if strings.HasPrefix(r.URL.Path, prefix) {
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.defaultHandler.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
type fakeHandler struct{}
|
type fakeHandler struct{}
|
||||||
|
|
||||||
// NewFakeHTTPServer creates a fake HTTP server
|
// NewFakeHTTPServer creates a fake HTTP server
|
||||||
|
|
|
@ -1,7 +1,23 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024 API Testing Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
package server_test
|
package server_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/linuxsuren/api-testing/pkg/server"
|
"github.com/linuxsuren/api-testing/pkg/server"
|
||||||
|
@ -19,3 +35,28 @@ func TestHTTPServer(t *testing.T) {
|
||||||
defaultHTTPServer := server.NewDefaultHTTPServer()
|
defaultHTTPServer := server.NewDefaultHTTPServer()
|
||||||
defaultHTTPServer.WithHandler(nil)
|
defaultHTTPServer.WithHandler(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCombineHandler(t *testing.T) {
|
||||||
|
defaultHandler := http.NewServeMux()
|
||||||
|
fakeHandler := http.NewServeMux()
|
||||||
|
|
||||||
|
t.Run("correct default handler", func(t *testing.T) {
|
||||||
|
combineHandler := server.NewDefaultCombineHandler()
|
||||||
|
|
||||||
|
combineHandler.PutHandler("", defaultHandler)
|
||||||
|
combineHandler.PutHandler("/fake", fakeHandler)
|
||||||
|
|
||||||
|
assert.NotEqual(t, defaultHandler, combineHandler.GetHandler())
|
||||||
|
|
||||||
|
fakeServer := server.NewFakeHTTPServer()
|
||||||
|
assert.Nil(t, fakeServer.Shutdown(nil))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("only one default handler", func(t *testing.T) {
|
||||||
|
combineHandler := server.NewDefaultCombineHandler()
|
||||||
|
|
||||||
|
combineHandler.PutHandler("", defaultHandler)
|
||||||
|
|
||||||
|
assert.Equal(t, defaultHandler, combineHandler.GetHandler())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue