func Test_AnIncomingMessageIsNotAllowed(t *testing.T) { ctrl, finish := testutil.NewMockCtrl(t) defer finish() wsconn, routerMock, _ := createDefaultMocks([]string{}) tam := NewMockAccessManager(ctrl) tam.EXPECT().IsAllowed(auth.READ, "testuser", protocol.Path("/foo")).Return(false) handler := NewWebSocket( testWSHandler(routerMock, tam), wsconn, "testuser", ) go func() { handler.Start() }() time.Sleep(time.Millisecond * 2) handler.sendChannel <- aTestMessage.Bytes() time.Sleep(time.Millisecond * 2) //nothing shall have been sent //now allow tam.EXPECT().IsAllowed(auth.READ, "testuser", protocol.Path("/foo")).Return(true) wsconn.EXPECT().Send(aTestMessage.Bytes()) time.Sleep(time.Millisecond * 2) handler.sendChannel <- aTestMessage.Bytes() time.Sleep(time.Millisecond * 2) }
func TestRouter_ReplacingOfRoutesMatchingAppID(t *testing.T) { a := assert.New(t) // Given a Router with a route router, _, _, _ := aStartedRouter() matcherFunc := func(route, other RouteConfig, keys ...string) bool { return route.Path == other.Path && route.Get("application_id") == other.Get("application_id") } router.Subscribe(NewRoute( RouteConfig{ RouteParams: RouteParams{"application_id": "appid01", "user_id": "user01"}, Path: protocol.Path("/blah"), Matcher: matcherFunc, }, )) // when: i add another route with the same Application Id and Same Path router.Subscribe(NewRoute( RouteConfig{ RouteParams: RouteParams{"application_id": "appid01", "user_id": "newUserId"}, Path: protocol.Path("/blah"), Matcher: matcherFunc, }, )) // then: the router only contains the new route a.Equal(1, len(router.routes)) a.Equal(1, len(router.routes["/blah"])) a.Equal("newUserId", router.routes["/blah"][0].Get("user_id")) }
func TestNexmoSender_SendWithError(t *testing.T) { a := assert.New(t) sender, err := NewNexmoSender(KEY, SECRET) a.NoError(err) sms := NexmoSms{ To: "toNumber", From: "FromNUmber", Text: "body", } d, err := json.Marshal(&sms) a.NoError(err) msg := protocol.Message{ Path: protocol.Path(SMSDefaultTopic), UserID: "samsa", ApplicationID: "sms", ID: uint64(4), Body: d, } err = sender.Send(&msg) a.Error(err) a.Equal(ErrIncompleteSMSSent, err) }
func (c *conn) initRoute() { c.route = router.NewRoute(router.RouteConfig{ Path: protocol.Path(*c.config.SMSTopic), ChannelSize: 10, FetchRequest: c.fetchRequest(), }) }
// Post creates a new subscriber func (c *connector) Post(w http.ResponseWriter, req *http.Request) { params := mux.Vars(req) log.WithField("params", params).Debug("Create subscription") topic, ok := params[TopicParam] if !ok { fmt.Fprintf(w, "Missing topic parameter.") return } delete(params, TopicParam) subscriber, err := c.manager.Create(protocol.Path("/"+topic), params) if err != nil { if err == ErrSubscriberExists { fmt.Fprintf(w, `{"error":"subscription already exists"}`) } else { http.Error(w, fmt.Sprintf(`{"error":"unknown error: %s"}`, err.Error()), http.StatusInternalServerError) } return } go c.Run(subscriber) log.WithField("topic", topic).Debug("Subscription created") fmt.Fprintf(w, `{"subscribed":"/%v"}`, topic) }
func (api *RestMessageAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } body, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, `Can not read body`, http.StatusBadRequest) return } topic, err := api.extractTopic(r.URL.Path) if err != nil { if err == errNotFound { http.NotFound(w, r) return } http.Error(w, "Server error.", http.StatusInternalServerError) return } msg := &protocol.Message{ Path: protocol.Path(topic), Body: body, UserID: q(r, `userId`), ApplicationID: xid.New().String(), OptionalID: q(r, `messageId`), HeaderJSON: headersToJSON(r.Header), NodeID: *config.Cluster.NodeID, } api.router.HandleMessage(msg) }
// Ensure the subscription is started when posting func TestConnector_PostSubscription(t *testing.T) { _, finish := testutil.NewMockCtrl(t) defer finish() a := assert.New(t) recorder := httptest.NewRecorder() conn, mocks := getTestConnector(t, Config{ Name: "test", Schema: "test", Prefix: "/connector/", URLPattern: "/{device_token}/{user_id}/{topic:.*}", }, true, false) mocks.manager.EXPECT().Load().Return(nil) mocks.manager.EXPECT().List().Return(make([]Subscriber, 0)) err := conn.Start() a.NoError(err) defer conn.Stop() subscriber := NewMockSubscriber(testutil.MockCtrl) mocks.manager.EXPECT().Create(gomock.Eq(protocol.Path("/topic1")), gomock.Eq(router.RouteParams{ "device_token": "device1", "user_id": "user1", })).Return(subscriber, nil) subscriber.EXPECT().Loop(gomock.Any(), gomock.Any()) r := router.NewRoute(router.RouteConfig{ Path: protocol.Path("topic1"), RouteParams: router.RouteParams{ "device_token": "device1", "user_id": "user1", }, }) subscriber.EXPECT().Route().Return(r) mocks.router.EXPECT().Subscribe(gomock.Eq(r)).Return(r, nil) req, err := http.NewRequest(http.MethodPost, "/connector/device1/user1/topic1", strings.NewReader("")) a.NoError(err) conn.ServeHTTP(recorder, req) a.Equal(`{"subscribed":"/topic1"}`, recorder.Body.String()) time.Sleep(100 * time.Millisecond) }
func (c *conn) fetchRequest() (fr *store.FetchRequest) { if c.LastIDSent > 0 { fr = store.NewFetchRequest( protocol.Path(*c.config.SMSTopic).Partition(), c.LastIDSent+1, 0, store.DirectionForward, -1) } return }
func aRouterRoute(unused int) (*router, *Route) { router, _, _, _ := aStartedRouter() route, _ := router.Subscribe(NewRoute( RouteConfig{ RouteParams: RouteParams{"application_id": "appid01", "user_id": "user01"}, Path: protocol.Path("/blah"), ChannelSize: chanSize, }, )) return router, route }
func (ws *WebSocket) handleCancelCmd(cmd *protocol.Cmd) { if len(cmd.Arg) == 0 { ws.sendError(protocol.ERROR_BAD_REQUEST, "- command requires a path argument, but none given") return } path := protocol.Path(cmd.Arg) rec, exist := ws.receivers[path] if exist { rec.Stop() delete(ws.receivers, path) } }
func TestRouter_SubscribeNotAllowed(t *testing.T) { ctrl, finish := testutil.NewMockCtrl(t) defer finish() a := assert.New(t) am := NewMockAccessManager(ctrl) msMock := NewMockMessageStore(ctrl) kvsMock := NewMockKVStore(ctrl) am.EXPECT().IsAllowed(auth.READ, "user01", protocol.Path("/blah")).Return(false) router := New(am, msMock, kvsMock, nil).(*router) router.Start() _, e := router.Subscribe(NewRoute( RouteConfig{ RouteParams: RouteParams{"application_id": "appid01", "user_id": "user01"}, Path: protocol.Path("/blah"), ChannelSize: chanSize, }, )) // default TestAccessManager denies all a.NotNil(e) // now add permissions am.EXPECT().IsAllowed(auth.READ, "user01", protocol.Path("/blah")).Return(true) // and user shall be allowed to subscribe _, e = router.Subscribe(NewRoute( RouteConfig{ RouteParams: RouteParams{"application_id": "appid01", "user_id": "user01"}, Path: protocol.Path("/blah"), ChannelSize: chanSize, }, )) a.Nil(e) }
func (router *router) GetSubscribersForTopic(topicPath string) ([]byte, error) { subscribers := make([]RouteParams, 0) routes, present := router.routes[protocol.Path(topicPath)] if present { for index, currRoute := range routes { logger.WithFields(log.Fields{ "index": index, "routeParams": currRoute.RouteParams, }).Debug("Added route to slice") subscribers = append(subscribers, currRoute.RouteParams) } } return json.Marshal(subscribers) }
func Test_WebSocket_SubscribeAndUnsubscribe(t *testing.T) { _, finish := testutil.NewMockCtrl(t) defer finish() a := assert.New(t) messages := []string{"+ /foo", "+ /bar", "- /foo"} wsconn, routerMock, messageStore := createDefaultMocks(messages) var wg sync.WaitGroup wg.Add(3) doneGroup := func(bytes []byte) error { wg.Done() return nil } routerMock.EXPECT().Subscribe(routeMatcher{"/foo"}).Return(nil, nil) wsconn.EXPECT(). Send([]byte("#" + protocol.SUCCESS_SUBSCRIBED_TO + " /foo")). Do(doneGroup) routerMock.EXPECT().Subscribe(routeMatcher{"/bar"}).Return(nil, nil) wsconn.EXPECT(). Send([]byte("#" + protocol.SUCCESS_SUBSCRIBED_TO + " /bar")). Do(doneGroup) routerMock.EXPECT().Unsubscribe(routeMatcher{"/foo"}) wsconn.EXPECT(). Send([]byte("#" + protocol.SUCCESS_CANCELED + " /foo")). Do(doneGroup) websocket := runNewWebSocket(wsconn, routerMock, messageStore, nil) wg.Wait() a.Equal(1, len(websocket.receivers)) a.Equal(protocol.Path("/bar"), websocket.receivers[protocol.Path("/bar")].path) }
// NewReceiverFromCmd parses the info in the command func NewReceiverFromCmd( applicationId string, cmd *protocol.Cmd, sendChannel chan []byte, router server.Router, userId string) (rec *Receiver, err error) { messageStore, err := router.MessageStore() if err != nil { return nil, err } rec = &Receiver{ applicationId: applicationId, sendC: sendChannel, router: router, messageStore: messageStore, cancelC: make(chan bool, 1), enableNotifications: true, userId: userId, } if len(cmd.Arg) == 0 || cmd.Arg[0] != '/' { return nil, fmt.Errorf("command requires at least a path argument, but non given") } args := strings.SplitN(cmd.Arg, " ", 3) rec.path = protocol.Path(args[0]) if len(args) > 1 { rec.doFetch = true rec.startId, err = strconv.ParseInt(args[1], 10, 64) if err != nil { return nil, fmt.Errorf("startid has to be empty or int, but was %q: %v", args[1], err) } } rec.doSubscription = true if len(args) > 2 { rec.doSubscription = false rec.maxCount, err = strconv.Atoi(args[2]) if err != nil { return nil, fmt.Errorf("maxCount has to be empty or int, but was %q: %v", args[1], err) } } return rec, nil }
func TestConnector_StartWithSubscriptions(t *testing.T) { _, finish := testutil.NewMockCtrl(t) defer finish() a := assert.New(t) conn, mocks := getTestConnector(t, Config{ Name: "test", Schema: "test", Prefix: "/connector/", URLPattern: "/{device_token}/{user_id}/{topic:.*}", }, false, false) entriesC := make(chan [2]string) mocks.kvstore.EXPECT().Iterate(gomock.Eq("test"), gomock.Eq("")).Return(entriesC) close(entriesC) mocks.kvstore.EXPECT().Put(gomock.Any(), gomock.Any(), gomock.Any()).Times(4) err := conn.Start() a.NoError(err) routes := make([]*router.Route, 0, 4) mocks.router.EXPECT().Subscribe(gomock.Any()).Do(func(r *router.Route) (*router.Route, error) { routes = append(routes, r) return r, nil }).Times(4) // create subscriptions createSubscriptions(t, conn, 4) time.Sleep(100 * time.Millisecond) mocks.sender.EXPECT().Send(gomock.Any()).Return(nil, nil).Times(4) // send message in route channel for i, r := range routes { r.Deliver(&protocol.Message{ ID: uint64(i), Path: protocol.Path("/topic"), Body: []byte("test body"), }) } time.Sleep(100 * time.Millisecond) err = conn.Stop() a.NoError(err) }
func TestRouter_RoutingWithSubTopics(t *testing.T) { ctrl, finish := testutil.NewMockCtrl(t) defer finish() a := assert.New(t) // Given a Router with route router, _, _, _ := aStartedRouter() msMock := NewMockMessageStore(ctrl) router.messageStore = msMock // expect a message to `blah` partition first and `blahblub` second firstStore := msMock.EXPECT(). StoreMessage(gomock.Any(), gomock.Any()). Do(func(m *protocol.Message, nodeID uint8) (int, error) { a.Equal("/blah/blub", string(m.Path)) return 0, nil }) msMock.EXPECT(). StoreMessage(gomock.Any(), gomock.Any()).After(firstStore). Do(func(m *protocol.Message, nodeID uint8) (int, error) { a.Equal("/blahblub", string(m.Path)) return 0, nil }) r, _ := router.Subscribe(NewRoute( RouteConfig{ RouteParams: RouteParams{"application_id": "appid01", "user_id": "user01"}, Path: protocol.Path("/blah"), ChannelSize: chanSize, }, )) // when i send a message to a subroute router.HandleMessage(&protocol.Message{Path: "/blah/blub", Body: aTestByteMessage}) // then I can receive the message assertChannelContainsMessage(a, r.MessagesChannel(), aTestByteMessage) // but, when i send a message to a resource, which is just a substring router.HandleMessage(&protocol.Message{Path: "/blahblub", Body: aTestByteMessage}) // then the message gets not delivered a.Equal(0, len(r.MessagesChannel())) }
// NewRoute creates a new route pointer func NewRoute(path, applicationID, userID string, c chan *MessageForRoute) *Route { queueSize := 0 route := &Route{ messagesC: c, queue: newQueue(queueSize), queueSize: queueSize, timeout: -1, closeC: make(chan struct{}), Path: protocol.Path(path), UserID: userID, ApplicationID: applicationID, logger: logger.WithFields(log.Fields{ "path": path, "applicationID": applicationID, "userID": userID, }), } return route }
func TestSender_Send(t *testing.T) { _, finish := testutil.NewMockCtrl(t) defer finish() a := assert.New(t) // given routeParams := make(map[string]string) routeParams["device_id"] = "1234" routeConfig := router.RouteConfig{ Path: protocol.Path("path"), RouteParams: routeParams, } route := router.NewRoute(routeConfig) msg := &protocol.Message{ Body: []byte("{}"), } mSubscriber := NewMockSubscriber(testutil.MockCtrl) mSubscriber.EXPECT().Route().Return(route).AnyTimes() mRequest := NewMockRequest(testutil.MockCtrl) mRequest.EXPECT().Subscriber().Return(mSubscriber).AnyTimes() mRequest.EXPECT().Message().Return(msg).AnyTimes() mPusher := NewMockPusher(testutil.MockCtrl) mPusher.EXPECT().Push(gomock.Any()).Return(nil, nil) // and s, err := NewSenderUsingPusher(mPusher, "com.myapp") a.NoError(err) // when rsp, err := s.Send(mRequest) // then a.NoError(err) a.Nil(rsp) }
func (ws *WebSocket) handleSendCmd(cmd *protocol.Cmd) { logger.WithFields(log.Fields{ "cmd": string(cmd.Bytes()), }).Debug("Sending ") if len(cmd.Arg) == 0 { ws.sendError(protocol.ERROR_BAD_REQUEST, "send command requires a path argument, but none given") return } args := strings.SplitN(cmd.Arg, " ", 2) msg := &protocol.Message{ Path: protocol.Path(args[0]), ApplicationID: ws.applicationID, UserID: ws.userID, HeaderJSON: cmd.HeaderJSON, Body: cmd.Body, } ws.router.HandleMessage(msg) ws.sendOK(protocol.SUCCESS_SEND, "") }
// ServeHTTP is an http.Handler. // It is a part of the service.endpoint implementation. func (api *RestMessageAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodHead { return } if r.Method == http.MethodGet { log.WithField("url", r.URL.Path).Debug("GET") topic, err := api.extractTopic(r.URL.Path, subscribersPrefix) if err != nil { log.WithError(err).Error("Extracting topic failed") if err == errNotFound { http.NotFound(w, r) return } http.Error(w, "Server error.", http.StatusInternalServerError) return } resp, err := api.router.GetSubscribersForTopic(topic) w.Header().Set("Content-Type", "application/json") _, err = w.Write(resp) if err != nil { log.WithField("error", err.Error()).Error("Writing to byte stream failed") http.Error(w, "Server error.", http.StatusInternalServerError) return } return } if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } body, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, "Can not read body", http.StatusBadRequest) return } topic, err := api.extractTopic(r.URL.Path, "/message") if err != nil { if err == errNotFound { http.NotFound(w, r) return } http.Error(w, "Server error.", http.StatusInternalServerError) return } msg := &protocol.Message{ Path: protocol.Path(topic), Body: body, UserID: q(r, "userId"), ApplicationID: xid.New().String(), HeaderJSON: headersToJSON(r.Header), } // add filters api.setFilters(r, msg) api.router.HandleMessage(msg) fmt.Fprintf(w, "OK") }
func TestRouter_AddAndRemoveRoutes(t *testing.T) { a := assert.New(t) // Given a Router router, _, _, _ := aStartedRouter() // when i add two routes in the same path routeBlah1, _ := router.Subscribe(NewRoute( RouteConfig{ RouteParams: RouteParams{"application_id": "appid01", "user_id": "user01"}, Path: protocol.Path("/blah"), ChannelSize: chanSize, }, )) routeBlah2, _ := router.Subscribe(NewRoute( RouteConfig{ RouteParams: RouteParams{"application_id": "appid02", "user_id": "user01"}, Path: protocol.Path("/blah"), ChannelSize: chanSize, }, )) // and one route in another path routeFoo, _ := router.Subscribe(NewRoute( RouteConfig{ RouteParams: RouteParams{"application_id": "appid01", "user_id": "user01"}, Path: protocol.Path("/foo"), ChannelSize: chanSize, }, )) // then // the routes are stored a.Equal(2, len(router.routes[protocol.Path("/blah")])) a.True(routeBlah1.Equal(router.routes[protocol.Path("/blah")][0])) a.True(routeBlah2.Equal(router.routes[protocol.Path("/blah")][1])) a.Equal(1, len(router.routes[protocol.Path("/foo")])) a.True(routeFoo.Equal(router.routes[protocol.Path("/foo")][0])) // when i remove routes router.Unsubscribe(routeBlah1) router.Unsubscribe(routeFoo) // then they are gone a.Equal(1, len(router.routes[protocol.Path("/blah")])) a.True(routeBlah2.Equal(router.routes[protocol.Path("/blah")][0])) a.Nil(router.routes[protocol.Path("/foo")]) }
// Router should handle the buffered messages also after the closing of the route func TestRouter_CleanShutdown(t *testing.T) { ctrl, finish := testutil.NewMockCtrl(t) defer finish() a := assert.New(t) var ID uint64 msMock := NewMockMessageStore(ctrl) msMock.EXPECT().Store("blah", gomock.Any(), gomock.Any()). Return(nil). Do(func(partition string, callback func(msgID uint64) []byte) error { ID++ callback(ID) return nil }). AnyTimes() router, _, _, _ := aStartedRouter() router.messageStore = msMock route, err := router.Subscribe(NewRoute( RouteConfig{ RouteParams: RouteParams{"application_id": "appid01", "user_id": "user01"}, Path: protocol.Path("/blah"), ChannelSize: 3, }, )) a.Nil(err) doneC := make(chan bool) // read the messages until done is closed go func() { for { _, ok := <-route.MessagesChannel() select { case <-doneC: return default: a.True(ok) } } }() // Send messages in the router until error go func() { for { errHandle := router.HandleMessage(&protocol.Message{ Path: protocol.Path("/blah"), Body: aTestByteMessage, }) if errHandle != nil { mse, ok := errHandle.(*ModuleStoppingError) a.True(ok) a.Equal("Router", mse.Name) return } // if doneC channel has been closed and no error then we must fail the test select { case _, ok := <-doneC: if !ok { a.Fail("Expected error from router handle message") } default: } } }() close(doneC) err = router.Stop() a.Nil(err) // wait for above goroutine to finish <-time.After(100 * time.Millisecond) }
func TestRouteConfig_Equal(t *testing.T) { a := assert.New(t) testcases := map[string]struct { // first route definition first routeConfig // second route definition second routeConfig Matcher Matcher // keys to pass on matching keys []string // expected result result bool }{ "full equal": { first: routeConfig{ path: "/path", fields: map[string]string{ "field1": "value1", "field2": "value2", }, }, second: routeConfig{ path: "/path", fields: map[string]string{ "field1": "value1", "field2": "value2", }, }, result: true, }, "full equal with matcher": { first: routeConfig{ path: "/path", fields: map[string]string{ "field1": "value1", "field2": "value2", }, }, second: routeConfig{ path: "/path", fields: map[string]string{ "field1": "value1", "field2": "value2", }, }, Matcher: func(config RouteConfig, other RouteConfig, keys ...string) bool { return config.Path == other.Path }, result: true, }, "make sure matcher is called": { first: routeConfig{ path: "/path", fields: map[string]string{ "field1": "value1", "field2": "value2", }, }, second: routeConfig{ path: "/incorrect-path", fields: map[string]string{ "field1": "value1", "field2": "value2", }, }, Matcher: func(config RouteConfig, other RouteConfig, keys ...string) bool { return true }, result: true, }, "partial match": { first: routeConfig{ path: "/path", fields: map[string]string{ "field1": "value1", "field2": "value2", }, }, second: routeConfig{ path: "/path", fields: map[string]string{ "field1": "value1", "field3": "value3", }, }, keys: []string{"field1"}, result: true, }, "unequal path with keys": { first: routeConfig{ path: "/path", fields: map[string]string{ "field1": "value1", "field2": "value2", }, }, second: routeConfig{ path: "/different-path", fields: map[string]string{ "field1": "value1", "field3": "value3", }, }, keys: []string{"field1"}, result: false, }, } for name, c := range testcases { first := RouteConfig{ Path: protocol.Path(c.first.path), RouteParams: RouteParams(c.first.fields), Matcher: c.Matcher, } second := RouteConfig{ Path: protocol.Path(c.second.path), RouteParams: RouteParams(c.second.fields), Matcher: c.Matcher, } a.Equal(c.result, first.Equal(second, c.keys...), "Failed forward check for case: "+name) a.Equal(c.result, second.Equal(first, c.keys...), "Failed backwards check for case: "+name) } }
func getPathFromRawMessage(raw []byte) protocol.Path { i := strings.Index(string(raw), ",") return protocol.Path(raw[:i]) }
func Test_Receiver_Fetch_Subscribe_Fetch_Subscribe(t *testing.T) { _, finish := testutil.NewMockCtrl(t) defer finish() a := assert.New(t) rec, msgChannel, routerMock, messageStore, err := aMockedReceiver("/foo 0") a.NoError(err) // fetch first, starting at 0 fetchFirst1 := messageStore.EXPECT().Fetch(gomock.Any()).Do(func(r *store.FetchRequest) { go func() { a.Equal("foo", r.Partition) a.Equal(store.DirectionForward, r.Direction) a.Equal(uint64(0), r.StartID) a.Equal(int(math.MaxInt32), r.Count) r.StartC <- 2 r.MessageC <- &store.FetchedMessage{ID: uint64(1), Message: []byte("fetch_first1-a")} r.MessageC <- &store.FetchedMessage{ID: uint64(2), Message: []byte("fetch_first1-b")} close(r.MessageC) }() }) // there is a gap between fetched and max id messageID1 := messageStore.EXPECT().DoInTx(gomock.Any(), gomock.Any()). Do(func(partition string, callback func(maxMessageId uint64) error) { callback(uint64(3)) }).Return(errUnreadMsgsAvailable) messageID1.After(fetchFirst1) // fetch again, starting at 3, because, there is still a gap fetchFirst2 := messageStore.EXPECT().Fetch(gomock.Any()).Do(func(r *store.FetchRequest) { go func() { a.Equal("foo", r.Partition) a.Equal(store.DirectionForward, r.Direction) a.Equal(uint64(3), r.StartID) a.Equal(int(math.MaxInt32), r.Count) r.StartC <- 1 r.MessageC <- &store.FetchedMessage{ID: uint64(3), Message: []byte("fetch_first2-a")} close(r.MessageC) }() }) fetchFirst2.After(messageID1) // the gap is closed messageID2 := messageStore.EXPECT().DoInTx(gomock.Any(), gomock.Any()). Do(func(partition string, callback func(maxMessageId uint64) error) { callback(uint64(3)) }) messageID2.After(fetchFirst2) // subscribe subscribe := routerMock.EXPECT().Subscribe(gomock.Any()).Do(func(r *router.Route) { a.Equal(r.Path, protocol.Path("/foo")) r.Deliver(&protocol.Message{ID: uint64(4), Body: []byte("router-a"), Time: 1405544146}) r.Deliver(&protocol.Message{ID: uint64(5), Body: []byte("router-b"), Time: 1405544146}) r.Close() // emulate router close }) subscribe.After(messageID2) // router closed, so we fetch again, starting at 6 (after meesages from subscribe) fetchAfter := messageStore.EXPECT().Fetch(gomock.Any()).Do(func(r *store.FetchRequest) { go func() { a.Equal(uint64(6), r.StartID) a.Equal(int(math.MaxInt32), r.Count) r.StartC <- 1 r.MessageC <- &store.FetchedMessage{ID: uint64(6), Message: []byte("fetch_after-a")} close(r.MessageC) }() }) fetchAfter.After(subscribe) // no gap messageID3 := messageStore.EXPECT().DoInTx(gomock.Any(), gomock.Any()). Do(func(partition string, callback func(maxMessageId uint64) error) { callback(uint64(6)) }) messageID3.After(fetchAfter) // subscribe and don't send messages, // so the client has to wait until we stop subscribe2 := routerMock.EXPECT().Subscribe(gomock.Any()) subscribe2.After(messageID3) subscriptionLoopDone := make(chan bool) go func() { rec.subscriptionLoop() subscriptionLoopDone <- true }() expectMessages(a, msgChannel, "#"+protocol.SUCCESS_FETCH_START+" /foo 2", "fetch_first1-a", "fetch_first1-b", "#"+protocol.SUCCESS_FETCH_END+" /foo", "#"+protocol.SUCCESS_FETCH_START+" /foo 1", "fetch_first2-a", "#"+protocol.SUCCESS_FETCH_END+" /foo", "#"+protocol.SUCCESS_SUBSCRIBED_TO+" /foo", ",4,,,,1405544146,0\n\nrouter-a", ",5,,,,1405544146,0\n\nrouter-b", "#"+protocol.SUCCESS_FETCH_START+" /foo 1", "fetch_after-a", "#"+protocol.SUCCESS_FETCH_END+" /foo", "#"+protocol.SUCCESS_SUBSCRIBED_TO+" /foo", ) time.Sleep(time.Millisecond) routerMock.EXPECT().Unsubscribe(gomock.Any()) rec.Stop() expectMessages(a, msgChannel, "#"+protocol.SUCCESS_CANCELED+" /foo", ) testutil.ExpectDone(a, subscriptionLoopDone) }
func Test_Cluster_Integration(t *testing.T) { testutil.SkipIfShort(t) defer testutil.ResetDefaultRegistryHealthCheck() a := assert.New(t) node1 := newTestClusterNode(t, testClusterNodeConfig{ HttpListen: "localhost:8092", NodeID: 1, NodePort: 11002, Remotes: "localhost:11002", }) a.NotNil(node1) defer node1.cleanup(true) node2 := newTestClusterNode(t, testClusterNodeConfig{ HttpListen: "localhost:8093", NodeID: 2, NodePort: 11003, Remotes: "localhost:11002", }) a.NotNil(node2) defer node2.cleanup(true) client1, err := node1.client("user1", 10, false) a.NoError(err) client2, err := node2.client("user2", 10, false) a.NoError(err) err = client2.Subscribe("/testTopic/m") a.NoError(err) client3, err := node1.client("user3", 10, false) a.NoError(err) numSent := 3 for i := 0; i < numSent; i++ { err := client1.Send("/testTopic/m", "body", "{jsonHeader:1}") a.NoError(err) err = client3.Send("/testTopic/m", "body", "{jsonHeader:4}") a.NoError(err) } breakTimer := time.After(3 * time.Second) numReceived := 0 idReceived := make(map[uint64]bool) // see if the correct number of messages arrived at the other client, before timeout is reached WAIT: for { select { case incomingMessage := <-client2.Messages(): numReceived++ logger.WithFields(log.Fields{ "nodeID": incomingMessage.NodeID, "path": incomingMessage.Path, "incomingMsgUserId": incomingMessage.UserID, "headerJson": incomingMessage.HeaderJSON, "body": incomingMessage.BodyAsString(), "numReceived": numReceived, }).Info("Client2 received a message") a.Equal(protocol.Path("/testTopic/m"), incomingMessage.Path) a.Equal("body", incomingMessage.BodyAsString()) a.True(incomingMessage.ID > 0) idReceived[incomingMessage.ID] = true if 2*numReceived == numSent { break WAIT } case <-breakTimer: break WAIT } } }
func Test_Restart(t *testing.T) { ctrl, finish := testutil.NewMockCtrl(t) defer finish() defer testutil.EnableDebugForMethod()() a := assert.New(t) mockSmsSender := NewMockSender(ctrl) kvStore := kvstore.NewMemoryKVStore() a.NotNil(kvStore) routerMock := NewMockRouter(testutil.MockCtrl) routerMock.EXPECT().KVStore().AnyTimes().Return(kvStore, nil) msgStore := NewMockMessageStore(ctrl) routerMock.EXPECT().MessageStore().AnyTimes().Return(msgStore, nil) topic := "/sms" worker := 1 config := Config{ Workers: &worker, SMSTopic: &topic, Name: "test_gateway", Schema: SMSSchema, } routerMock.EXPECT().Subscribe(gomock.Any()).Do(func(r *router.Route) (*router.Route, error) { a.Equal(strings.Split(topic, "/")[1], r.Path.Partition()) return r, nil }).Times(2) gw, err := New(routerMock, mockSmsSender, config) a.NoError(err) err = gw.Start() a.NoError(err) sms := NexmoSms{ To: "toNumber", From: "FromNUmber", Text: "body", } d, err := json.Marshal(&sms) a.NoError(err) msg := protocol.Message{ Path: protocol.Path(topic), UserID: "samsa", ApplicationID: "sms", ID: uint64(4), Body: d, } //TODO MARIAN FIX THIS TEST //msgStore.EXPECT().MaxMessageID(gomock.Eq(gw.route.Path.Partition())).Return(uint64(0), nil) //msgStore.EXPECT().MaxMessageID(gomock.Eq(gw.route.Path.Partition())).Return(uint64(4), nil) //msgStore.EXPECT().MaxMessageID(gomock.Eq(gw.route.Path.Partition())).Return(uint64(4), nil) mockSmsSender.EXPECT().Send(gomock.Eq(&msg)).Times(1).Return(ErrNoSMSSent) //routerMock.EXPECT().Fetch(gomock.Any()).Do(func(r *store.FetchRequest) { // go func() { // // logger.WithField("r.Partition", r.Partition).Info("----") // // a.Equal(strings.Split(topic, "/")[1], r.Partition) // // r.StartC <- 1 // // r.MessageC <- &store.FetchedMessage{ID: uint64(4), Message: msg.Bytes()} // close(r.MessageC) // }() //}) doneC := make(chan bool) routerMock.EXPECT().Done().AnyTimes().Return(doneC) // //mockSmsSender.EXPECT().Send(gomock.Eq(&msg)).Return(nil) a.NotNil(gw.route) gw.route.Deliver(&msg) time.Sleep(100 * time.Millisecond) }
func Test_SendOneSms(t *testing.T) { ctrl, finish := testutil.NewMockCtrl(t) defer finish() defer testutil.EnableDebugForMethod()() a := assert.New(t) mockSmsSender := NewMockSender(ctrl) kvStore := kvstore.NewMemoryKVStore() a.NotNil(kvStore) routerMock := NewMockRouter(testutil.MockCtrl) routerMock.EXPECT().KVStore().AnyTimes().Return(kvStore, nil) msgStore := dummystore.New(kvStore) routerMock.EXPECT().MessageStore().AnyTimes().Return(msgStore, nil) topic := "/sms" worker := 1 config := Config{ Workers: &worker, SMSTopic: &topic, Name: "test_gateway", Schema: SMSSchema, } routerMock.EXPECT().Subscribe(gomock.Any()).Do(func(r *router.Route) (*router.Route, error) { a.Equal(topic, string(r.Path)) return r, nil }) gw, err := New(routerMock, mockSmsSender, config) a.NoError(err) err = gw.Start() a.NoError(err) sms := NexmoSms{ To: "toNumber", From: "FromNUmber", Text: "body", } d, err := json.Marshal(&sms) a.NoError(err) msg := protocol.Message{ Path: protocol.Path(topic), ID: uint64(4), Body: d, } mockSmsSender.EXPECT().Send(gomock.Eq(&msg)).Return(nil) a.NotNil(gw.route) gw.route.Deliver(&msg) time.Sleep(100 * time.Millisecond) err = gw.Stop() a.NoError(err) err = gw.ReadLastID() a.NoError(err) time.Sleep(100 * time.Millisecond) }