func TestMockCallerWithOneStubResponse(t *testing.T) { req, _ := client.NewRequest(mockFooService, mockHealthEndpoint, &hcproto.Request{}) stub := &Stub{ Service: mockFooService, Endpoint: mockHealthEndpoint, Response: &hcproto.Response{}, } mock := NewMock().Stub(stub) caller := mock.Caller() rsp := &hcproto.Response{} e := caller(req, rsp) assert.Nil(t, e, "Expecting our mocked call to be intercepted and stubbed response returned, got err: %v", e) // ensure stub has what we expect assert.Len(t, stub.matched, 1, "Expecting 1 match payload to be stored after execution") assert.Equal(t, stub.CountCalls(), 1, "CountCalls should return 1 too") // try something else that _shouldn't_ match req, _ = client.NewRequest(mockFooService, "baz", &hcproto.Request{}) rsp = &hcproto.Response{} e = caller(req, rsp) assert.NotNil(t, e, "Expecting different endpoint name NOT to match") assert.Equal(t, e.Code(), "mock.notfound", "Expecting code of mock caller to be mock.notfound") }
// ScopedRequest returns a client request, prepared with any scoping information from _this_ inbound server request (in // other words we are forwarding all the scope information such as trace ID, session ID etc.. with our new client // request). This includes the scope of the service _making_ the call. func (self *Request) ScopedRequest(service, endpoint string, payload proto.Message) (*client.Request, error) { if self == nil { return nil, fmt.Errorf("Cannot build scoped request from nil Request") } r, err := client.NewRequest(service, endpoint, payload) if err != nil { return nil, err } // load in scope if self.SessionID() != "" { r.SetSessionID(self.SessionID()) } else { // double check Auth() scope if self.Auth().IsAuth() { r.SetSessionID(self.Auth().AuthUser().SessId) } } r.SetTraceID(self.TraceID()) r.SetTraceShouldPersist(self.TraceShouldPersist()) r.SetParentMessageID(self.MessageID()) // scope -- who WE are (not who sent it to us) r.SetFrom(Name) r.SetFromEndpoint(self.Endpoint()) // set whether the request has already been authorised r.SetAuthorised(self.Auth().Authorised()) return r, nil }
// AddScopedReq adds a server-scoped request (from the server request `from`) to our multi-client // with the `uid` that uniquely identifies the request within the group (for getting response from `Outcome`) func (c *defClient) AddScopedReq(sr *ScopedReq) MultiClient { c.Lock() defer c.Unlock() if _, exists := c.requests[sr.Uid]; exists { panic(fmt.Sprintf("Cannot add scoped request with UID '%v' - already exists within this MultiClient", sr.Uid)) } from := sr.From if from == nil { from = c.defaultFromScope } var clientReq *client.Request var err error // if no from, just use normal client request if from == nil { clientReq, err = client.NewRequest(sr.Service, sr.Endpoint, sr.Req) } else { clientReq, err = from.ScopedRequest(sr.Service, sr.Endpoint, sr.Req) } c.requests[sr.Uid] = clientReq c.responses[sr.Uid] = sr.Rsp if err != nil { c.errors.set(sr.Uid, clientReq, errors.InternalServerError("com.hailocab.kernel.multirequest.badrequest", err.Error()), from) } else { clientReq.SetOptions(sr.Options) } return c }
func TestFluentStubbingSequence(t *testing.T) { mock := NewMock() mock. On(mockFooService, mockHealthEndpoint). Return(NewDummy("pong-1")). Once() mock. On(mockFooService, mockHealthEndpoint). Return(NewDummy("pong-2")). Once() caller := mock.Caller() req, _ := client.NewRequest(mockFooService, mockHealthEndpoint, NewDummy("ping")) rsp := &Dummy{} // Succeed 1st call assert.Nil(t, caller(req, rsp)) assert.Equal(t, *rsp.Id, "pong-1") // Succeed 2nd call with different response assert.Nil(t, caller(req, rsp)) assert.Equal(t, *rsp.Id, "pong-2") }
// ScopedRequest to satisfy Scoper func (es *explicitScoper) ScopedRequest(service, endpoint string, payload proto.Message) (*client.Request, error) { req, err := client.NewRequest(service, endpoint, payload) if err != nil { return nil, err } req.SetSessionID(es.SessionId) req.SetTraceID(es.TraceId) return req, nil }
func TestFluentStubbingWithPayload(t *testing.T) { mock := NewMock() mock. On(mockFooService, mockHealthEndpoint). Payload(NewDummy("ping")). Return(NewDummy("pong")) caller := mock.Caller() // Fail as payload does not match req, _ := client.NewRequest(mockFooService, mockHealthEndpoint, NewDummy("pong")) rsp := &Dummy{} err := caller(req, rsp) assert.NotNil(t, err) assert.Equal(t, err.Code(), "mock.notfound") // Succeed as payload matches req, _ = client.NewRequest(mockFooService, mockHealthEndpoint, NewDummy("ping")) assert.Nil(t, caller(req, rsp)) assert.Equal(t, *rsp.Id, "pong") }
// ScopedRequest returns a client request, prepared with scoping information // of the service _making_ the call - such that service-to-service auth can work func ScopedRequest(service, endpoint string, payload proto.Message) (*client.Request, error) { r, err := client.NewRequest(service, endpoint, payload) if err != nil { return nil, err } // scope -- who WE are r.SetFrom(Name) // set request as already authorised r.SetAuthorised(true) return r, nil }
// load config via login service func (s *serviceToService) load() error { svc := s.getService() if svc == "" { log.Debug("[Auth] Skipping loading service-to-service auth rules (no service defined)") return nil } log.Tracef("[Auth] Loading service-to-service auth rules for %s", svc) reqProto := &endpointauth.Request{ Service: proto.String(svc), } req, err := client.NewRequest("com.hailocab.service.login", "endpointauth", reqProto) if err != nil { return err } // scope it req.SetFrom(svc) rsp := &endpointauth.Response{} if err := client.Req(req, rsp); err != nil { return err } newEndpoints := make(map[string]grantedServices) for _, ep := range rsp.GetEndpoints() { name := ep.GetEndpoint() if _, ok := newEndpoints[name]; !ok { newEndpoints[name] = make(grantedServices) } // add in the granted services to this endpoint for _, gs := range ep.GetGranted() { newEndpoints[name][gs.GetName()] = role(gs.GetRole()) } } // check if changed - to avoid locking/changing/logging if not if hashEndpoints(newEndpoints) == s.hash() { return nil } // switch in config s.Lock() defer s.Unlock() s.endpoints = newEndpoints log.Debugf("[Auth] Loaded service-to-service auth rules: %#v", s.endpoints) return nil }
func TestFluentStubbingWithError(t *testing.T) { mock := NewMock() mock. On(mockFooService, mockHealthEndpoint). Fail(errors.BadRequest("code", "description")) req, _ := client.NewRequest(mockFooService, mockHealthEndpoint, NewDummy("ping")) rsp := &Dummy{} // Fail with given error err := mock.Caller()(req, rsp) assert.NotNil(t, err) assert.Equal(t, err.Code(), "code") assert.Equal(t, err.Description(), "description") }
func TestResponder(t *testing.T) { stub := &Stub{ Service: mockFooService, Endpoint: mockHealthEndpoint, Responder: func(invocation int, req *client.Request) (proto.Message, errors.Error) { if invocation == 1 { return &hcproto.Response{ Healthchecks: []*hcproto.HealthCheck{ &hcproto.HealthCheck{ Timestamp: proto.Int64(1403629015), ServiceName: proto.String("foo"), ServiceVersion: proto.Uint64(1403629015), Hostname: proto.String("localhost"), InstanceId: proto.String("foobar"), HealthCheckId: proto.String("boom"), IsHealthy: proto.Bool(true), }, }, }, nil } return nil, errors.InternalServerError("only.one.allowed", "First call only works") }, } mock := NewMock().Stub(stub) caller := mock.Caller() req, _ := client.NewRequest(mockFooService, mockHealthEndpoint, &hcproto.Request{}) rsp := &hcproto.Response{} e := caller(req, rsp) assert.Nil(t, e, "Expecting our mocked call to be intercepted and stubbed response returned, got err: %v", e) assert.Len(t, rsp.GetHealthchecks(), 1, "Response does not contain our mocked content: no healthchecks") // now repeat, and we SHOULD get an error e = caller(req, rsp) assert.NotNil(t, e, "Expecting our mocked call to be intercepted and error response returned on 2nd call") assert.Equal(t, e.Code(), "only.one.allowed", "Expecting code 'only.one.allowed', got '%s'", e.Code()) }
func TestFluentStubbingWithTimes(t *testing.T) { mock := NewMock() mock. On(mockFooService, mockHealthEndpoint). Return(NewDummy("pong")). Once() caller := mock.Caller() req, _ := client.NewRequest(mockFooService, mockHealthEndpoint, NewDummy("ping")) rsp := &Dummy{} // Succeed 1st call assert.Nil(t, caller(req, rsp)) assert.Equal(t, *rsp.Id, "pong") // Fail 2nd call err := caller(req, rsp) assert.NotNil(t, err) assert.Equal(t, err.Code(), "mock.notfound") }
func TestFluentStubbingUnlimited(t *testing.T) { mock := NewMock() mock. On(mockFooService, mockHealthEndpoint). Return(NewDummy("pong")) caller := mock.Caller() req, _ := client.NewRequest(mockFooService, mockHealthEndpoint, NewDummy("ping")) rsp := &Dummy{} // Succeed 1st call err := caller(req, rsp) assert.Nil(t, err) assert.Equal(t, *rsp.Id, "pong") // Succeed 2nd call err = caller(req, rsp) assert.Nil(t, err) assert.Equal(t, *rsp.Id, "pong") }
func TestProtoHttpCallThatFails(t *testing.T) { // discovery service is NOT open to the world, because it's a kernel service req, err := client.NewRequest("com.hailocab.kernel.discovery", "services", &servicesproto.Request{}) if err != nil { t.Fatalf("Error creating request: %v", err) } rsp := &servicesproto.Response{} caller := HttpCaller("https://api2-staging.elasticride.com") perr := caller(req, rsp) if perr == nil { t.Fatal("We are EXPECTING as error executing request") } if perr.Code() != "com.hailocab.api.rpc.auth" { t.Errorf("Expecting code 'com.hailocab.api.rpc.auth' got '%s'", perr.Code()) } if perr.Type() != "FORBIDDEN" { t.Errorf("Expecting type 'FORBIDDEN' got '%s'", perr.Type()) } }
func TestMockCallerPopulatesResponse(t *testing.T) { req, _ := client.NewRequest(mockFooService, mockHealthEndpoint, &hcproto.Request{}) stub := &Stub{ Service: mockFooService, Endpoint: mockHealthEndpoint, Response: &hcproto.Response{ Healthchecks: []*hcproto.HealthCheck{ &hcproto.HealthCheck{ Timestamp: proto.Int64(1403629015), ServiceName: proto.String("foo"), ServiceVersion: proto.Uint64(1403629015), Hostname: proto.String("localhost"), InstanceId: proto.String("foobar"), HealthCheckId: proto.String("boom"), IsHealthy: proto.Bool(true), }, }, }, } mock := NewMock().Stub(stub) caller := mock.Caller() rsp := &hcproto.Response{} e := caller(req, rsp) assert.Nil(t, e, "Expecting our mocked call to be intercepted and stubbed response returned, got err: %v", e) // ensure stub has what we expect assert.Len(t, stub.matched, 1, "Expecting 1 match payload to be stored after execution") assert.Equal(t, stub.CountCalls(), 1, "CountCalls should return 1 too") assert.Len(t, rsp.GetHealthchecks(), 1, "Response does not contain our mocked content: no healthchecks") }
func TestProtoHttpCall(t *testing.T) { // zoning service is OpenToTheWorld and should have _some_ zone surrounding Somerset House req, err := client.NewRequest("com.hailocab.service.zoning", "search", &searchproto.Request{ Location: &searchproto.LatLng{ Lat: proto.Float64(51.510761), Lng: proto.Float64(-0.1174437), }, }) if err != nil { t.Fatalf("Error creating request: %v", err) } rsp := &searchproto.Response{} caller := HttpCaller("https://api2-staging.elasticride.com") perr := caller(req, rsp) if perr != nil { t.Fatalf("Error executing request: %v", perr) } if len(rsp.GetZones()) == 0 { t.Error("Expecting > 0 zones, got back 0") } }