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)
}
Beispiel #2
0
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"))
}
Beispiel #3
0
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)
}
Beispiel #4
0
func (c *conn) initRoute() {
	c.route = router.NewRoute(router.RouteConfig{
		Path:         protocol.Path(*c.config.SMSTopic),
		ChannelSize:  10,
		FetchRequest: c.fetchRequest(),
	})
}
Beispiel #5
0
// 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)
}
Beispiel #6
0
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)
}
Beispiel #7
0
// 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)
}
Beispiel #8
0
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
}
Beispiel #9
0
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
}
Beispiel #10
0
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)
	}
}
Beispiel #11
0
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)
}
Beispiel #12
0
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)
}
Beispiel #14
0
// 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
}
Beispiel #15
0
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)
}
Beispiel #16
0
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()))
}
Beispiel #17
0
// 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
}
Beispiel #18
0
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)
}
Beispiel #19
0
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, "")
}
Beispiel #20
0
// 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")
}
Beispiel #21
0
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")])
}
Beispiel #22
0
// 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)
}
Beispiel #23
0
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)
	}
}
Beispiel #24
0
func getPathFromRawMessage(raw []byte) protocol.Path {
	i := strings.Index(string(raw), ",")
	return protocol.Path(raw[:i])
}
Beispiel #25
0
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
		}
	}
}
Beispiel #27
0
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)
}
Beispiel #28
0
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)
}