First stab at mock client
This commit is contained in:
		
							
								
								
									
										16
									
								
								client/mock/context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								client/mock/context.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | package mock | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"golang.org/x/net/context" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type responseKey struct{} | ||||||
|  |  | ||||||
|  | func fromContext(ctx context.Context) (map[string][]MockResponse, bool) { | ||||||
|  | 	r, ok := ctx.Value(responseKey{}).(map[string][]MockResponse) | ||||||
|  | 	return r, ok | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newContext(ctx context.Context, r map[string][]MockResponse) context.Context { | ||||||
|  | 	return context.WithValue(ctx, responseKey{}, r) | ||||||
|  | } | ||||||
							
								
								
									
										174
									
								
								client/mock/mock.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								client/mock/mock.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | |||||||
|  | package mock | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"reflect" | ||||||
|  | 	"sync" | ||||||
|  |  | ||||||
|  | 	"github.com/micro/go-micro/client" | ||||||
|  | 	"github.com/micro/go-micro/errors" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/net/context" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	_ client.Client = NewClient() | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type MockResponse struct { | ||||||
|  | 	Method   string | ||||||
|  | 	Response interface{} | ||||||
|  | 	Error    error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type MockClient struct { | ||||||
|  | 	Client client.Client | ||||||
|  | 	Opts   client.Options | ||||||
|  |  | ||||||
|  | 	sync.Mutex | ||||||
|  | 	Response map[string][]MockResponse | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *MockClient) Init(opts ...client.Option) error { | ||||||
|  | 	m.Lock() | ||||||
|  | 	defer m.Unlock() | ||||||
|  |  | ||||||
|  | 	for _, opt := range opts { | ||||||
|  | 		opt(&m.Opts) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	r, ok := fromContext(m.Opts.Context) | ||||||
|  | 	if !ok { | ||||||
|  | 		r = make(map[string][]MockResponse) | ||||||
|  | 	} | ||||||
|  | 	m.Response = r | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *MockClient) Options() client.Options { | ||||||
|  | 	return m.Opts | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *MockClient) NewPublication(topic string, msg interface{}) client.Publication { | ||||||
|  | 	return m.Client.NewPublication(topic, msg) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *MockClient) NewRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request { | ||||||
|  | 	return m.Client.NewRequest(service, method, req, reqOpts...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *MockClient) NewProtoRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request { | ||||||
|  | 	return m.Client.NewProtoRequest(service, method, req, reqOpts...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *MockClient) NewJsonRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request { | ||||||
|  | 	return m.Client.NewJsonRequest(service, method, req, reqOpts...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *MockClient) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { | ||||||
|  | 	m.Lock() | ||||||
|  | 	defer m.Unlock() | ||||||
|  |  | ||||||
|  | 	response, ok := m.Response[req.Service()] | ||||||
|  | 	if !ok { | ||||||
|  | 		return errors.NotFound("go.micro.client.mock", "service not found") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, r := range response { | ||||||
|  | 		if r.Method != req.Method() { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if r.Error != nil { | ||||||
|  | 			return r.Error | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		v := reflect.ValueOf(rsp) | ||||||
|  |  | ||||||
|  | 		if t := reflect.TypeOf(rsp); t.Kind() == reflect.Ptr { | ||||||
|  | 			v = reflect.Indirect(v) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		v.Set(reflect.ValueOf(r.Response)) | ||||||
|  |  | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return fmt.Errorf("rpc: can't find service %s", req.Method()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *MockClient) CallRemote(ctx context.Context, addr string, req client.Request, rsp interface{}, opts ...client.CallOption) error { | ||||||
|  | 	m.Lock() | ||||||
|  | 	defer m.Unlock() | ||||||
|  |  | ||||||
|  | 	response, ok := m.Response[req.Service()] | ||||||
|  | 	if !ok { | ||||||
|  | 		return errors.NotFound("go.micro.client.mock", "service not found") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, r := range response { | ||||||
|  | 		if r.Method != req.Method() { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if r.Error != nil { | ||||||
|  | 			return r.Error | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		v := reflect.ValueOf(rsp) | ||||||
|  |  | ||||||
|  | 		if t := reflect.TypeOf(rsp); t.Kind() == reflect.Ptr { | ||||||
|  | 			v = reflect.Indirect(v) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		v.Set(reflect.ValueOf(r.Response)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return fmt.Errorf("rpc: can't find service %s", req.Method()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *MockClient) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Streamer, error) { | ||||||
|  | 	m.Lock() | ||||||
|  | 	defer m.Unlock() | ||||||
|  |  | ||||||
|  | 	// TODO: mock stream | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *MockClient) StreamRemote(ctx context.Context, addr string, req client.Request, opts ...client.CallOption) (client.Streamer, error) { | ||||||
|  | 	m.Lock() | ||||||
|  | 	defer m.Unlock() | ||||||
|  |  | ||||||
|  | 	// TODO: mock stream | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *MockClient) Publish(ctx context.Context, p client.Publication, opts ...client.PublishOption) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *MockClient) String() string { | ||||||
|  | 	return "mock" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewClient(opts ...client.Option) *MockClient { | ||||||
|  | 	options := client.Options{ | ||||||
|  | 		Context: context.TODO(), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, opt := range opts { | ||||||
|  | 		opt(&options) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	r, ok := fromContext(options.Context) | ||||||
|  | 	if !ok { | ||||||
|  | 		r = make(map[string][]MockResponse) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &MockClient{ | ||||||
|  | 		Client:   client.DefaultClient, | ||||||
|  | 		Opts:     options, | ||||||
|  | 		Response: r, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								client/mock/mock_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								client/mock/mock_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | package mock | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/micro/go-micro/errors" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/net/context" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestClient(t *testing.T) { | ||||||
|  | 	response := []MockResponse{ | ||||||
|  | 		{Method: "Foo.Bar", Response: map[string]interface{}{"foo": "bar"}}, | ||||||
|  | 		{Method: "Foo.Fail", Error: errors.InternalServerError("go.mock", "failed")}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	c := NewClient(Response("go.mock", response)) | ||||||
|  |  | ||||||
|  | 	for _, r := range response { | ||||||
|  | 		req := c.NewJsonRequest("go.mock", r.Method, map[string]interface{}{"foo": "bar"}) | ||||||
|  | 		var rsp map[string]interface{} | ||||||
|  |  | ||||||
|  | 		err := c.Call(context.TODO(), req, &rsp) | ||||||
|  |  | ||||||
|  | 		if err != r.Error { | ||||||
|  | 			t.Fatalf("Expecter error %v got %v", r.Error, err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		t.Log(rsp) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										17
									
								
								client/mock/options.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								client/mock/options.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | package mock | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/micro/go-micro/client" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Response sets the response methods for a service | ||||||
|  | func Response(service string, response []MockResponse) client.Option { | ||||||
|  | 	return func(o *client.Options) { | ||||||
|  | 		r, ok := fromContext(o.Context) | ||||||
|  | 		if !ok { | ||||||
|  | 			r = make(map[string][]MockResponse) | ||||||
|  | 		} | ||||||
|  | 		r[service] = response | ||||||
|  | 		o.Context = newContext(o.Context, r) | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user