// TestErrors verifies that an error sent from a handler is correctly returned by a client func (suite *clientServerSuite) TestErrors() { suite.server.AddEndpoints(server.Endpoint{ Name: "error", Request: new(testproto.DummyRequest), Response: new(testproto.DummyResponse), Handler: func(req mercury.Request) (mercury.Response, error) { return nil, terrors.BadRequest("", "naughty naughty", nil) }}) cl := client.NewClient(). SetMiddleware(DefaultClientMiddleware()). Add( client.Call{ Uid: "call", Service: testServiceName, Endpoint: "error", Body: &testproto.DummyRequest{}, Response: &testproto.DummyResponse{}, }). SetTransport(suite.trans). SetTimeout(time.Second). Execute() suite.Assert().True(cl.Errors().Any()) err := cl.Errors().ForUid("call") suite.Assert().NotNil(err) suite.Assert().Equal(terrors.ErrBadRequest, err.Code) }
func (suite *errorSetSuite) TestMultiErrorPriority() { br := terrors.BadRequest("missing_param", "foo bar", nil) is := terrors.InternalService("something_broke", "hello world", nil) suite.Assert().True(higherPriority(is.Code, br.Code)) se := terrors.New("something_else", "baz", nil) suite.Assert().True(higherPriority(is.Code, se.Code)) suite.Assert().True(higherPriority(br.Code, se.Code)) es := ErrorSet{se, is, br} suite.Assert().Equal(is.Code, es.Combined().(*terrors.Error).Code) }
func (suite *clientSuite) SetupSuite() { trans := suite.TransF() select { case <-trans.Ready(): case <-time.After(2 * time.Second): panic("transport not ready") } suite.trans = trans // Add a listener that responds blindly to all messages inboundChan := make(chan tmsg.Request, 10) trans.Listen(testServiceName, inboundChan) go func() { for { select { case _req := <-inboundChan: req := mercury.FromTyphonRequest(_req) switch req.Endpoint() { case "timeout": continue case "invalid-payload": // Wrong proto here rsp := req.Response(nil) rsp.SetPayload([]byte("†HÎß ßHøܬ∂ÑT ∑ø®K")) suite.Require().NoError(trans.Respond(req, rsp)) case "error": err := terrors.BadRequest("", "foo bar", nil) rsp := req.Response(terrors.Marshal(err)) rsp.SetHeaders(req.Headers()) rsp.SetIsError(true) suite.Require().NoError(trans.Respond(req, rsp)) case "bulls--t": rsp := req.Response(map[string]string{}) rsp.SetHeaders(req.Headers()) rsp.SetHeader(marshaling.ContentTypeHeader, "application/bulls--t") suite.Require().NoError(trans.Respond(req, rsp)) default: rsp := req.Response(&testproto.DummyResponse{ Pong: "Pong"}) rsp.SetHeaders(req.Headers()) suite.Require().NoError(tmsg.ProtoMarshaler().MarshalBody(rsp)) suite.Require().NoError(trans.Respond(req, rsp)) } case <-trans.Tomb().Dying(): return } } }() }
"github.com/mondough/mercury" "github.com/mondough/mercury/transport" terrors "github.com/mondough/typhon/errors" tmsg "github.com/mondough/typhon/message" ttrans "github.com/mondough/typhon/transport" ) const ( connectTimeout = 30 * time.Second ) var ( ErrAlreadyRunning error = terrors.InternalService("", "Server is already running", nil) // empty dotted code so impl details don't leak outside ErrTransportClosed error = terrors.InternalService("", "Transport closed", nil) errEndpointNotFound = terrors.BadRequest("endpoint_not_found", "Endpoint not found", nil) defaultMiddleware []ServerMiddleware defaultMiddlewareM sync.RWMutex ) func NewServer(name string) Server { defaultMiddlewareM.RLock() middleware := defaultMiddleware defaultMiddlewareM.RUnlock() return &server{ name: name, middleware: middleware, } }
"github.com/mondough/typhon/message" "github.com/mondough/typhon/transport" ) const ( DirectReplyQueue = "amq.rabbitmq.reply-to" connectTimeout = 30 * time.Second chanSendTimeout = 10 * time.Second respondTimeout = 10 * time.Second ) var ( ErrCouldntConnect = errors.InternalService("", "Could not connect to RabbitMQ", nil) ErrDeliveriesClosed = errors.InternalService("", "Delivery channel closed", nil) ErrNoReplyTo = errors.BadRequest("", "Request does not have appropriate X-Rabbit-ReplyTo header", nil) ) type rabbitTransport struct { tomb *tomb.Tomb connM sync.RWMutex // protects conn + connReady conn *RabbitConnection // underlying connection connReady chan struct{} // swapped along with conn (reconnecting) replyQueue string // message reply queue name inflightReqs map[string]chan<- message.Response // correlation id: response chan inflightReqsM sync.Mutex // protects inflightReqs listeners map[string]*tomb.Tomb // service name: tomb listenersM sync.RWMutex // protects listeners } // run starts the asynchronous run-loop connecting to RabbitMQ