func TestSubscribersIntegration(t *testing.T) { defer testutil.ResetDefaultRegistryHealthCheck() defer testutil.EnableDebugForMethod()() a := assert.New(t) s, cleanup := serviceSetUp(t) defer cleanup() subscribeMultipleClients(t, s, 4) a.Nil(nil) restClient := restclient.New(fmt.Sprintf("http://%s/api", s.WebServer().GetAddr())) content, err := restClient.GetSubscribers(testTopic) a.NoError(err) routeParams := make([]*Subscriber, 0) err = json.Unmarshal(content, &routeParams) a.Equal(4, len(routeParams), "Should have 4 subscribers") for i, rp := range routeParams { a.Equal(fmt.Sprintf("gcmId%d", i), rp.DeviceToken) a.Equal(fmt.Sprintf("user%d", i), rp.UserID) } a.NoError(err) }
func TestStartServiceModules(t *testing.T) { defer testutil.ResetDefaultRegistryHealthCheck() a := assert.New(t) // when starting a simple valid service *Config.KVS = "memory" *Config.MS = "file" *Config.FCM.Enabled = false *Config.APNS.Enabled = false // using an available port for http testHttpPort++ logger.WithField("port", testHttpPort).Debug("trying to use HTTP Port") *Config.HttpListen = fmt.Sprintf(":%d", testHttpPort) s := StartService() // then the number and ordering of modules should be correct a.Equal(6, len(s.ModulesSortedByStartOrder())) var moduleNames []string for _, iface := range s.ModulesSortedByStartOrder() { name := reflect.TypeOf(iface).String() moduleNames = append(moduleNames, name) } a.Equal("*kvstore.MemoryKVStore *filestore.FileMessageStore *router.router *webserver.WebServer *websocket.WSHandler *rest.RestMessageAPI", strings.Join(moduleNames, " ")) }
func TestHealthUp(t *testing.T) { _, finish := testutil.NewMockCtrl(t) defer finish() defer testutil.ResetDefaultRegistryHealthCheck() a := assert.New(t) // given: service, _, _, _ := aMockedServiceWithMockedRouterStandalone() service = service.HealthEndpoint("/health_url") a.Equal(2, len(service.ModulesSortedByStartOrder())) // when starting the service defer service.Stop() service.Start() time.Sleep(time.Millisecond * 10) // and when I call the health URL url := fmt.Sprintf("http://%s/health_url", service.WebServer().GetAddr()) result, err := http.Get(url) // then I get status 200 and JSON: {} a.NoError(err) body, err := ioutil.ReadAll(result.Body) a.NoError(err) a.Equal("{}", string(body)) }
func TestHealthDown(t *testing.T) { ctrl, finish := testutil.NewMockCtrl(t) defer finish() defer testutil.ResetDefaultRegistryHealthCheck() a := assert.New(t) // given: service, _, _, _ := aMockedServiceWithMockedRouterStandalone() service = service.HealthEndpoint("/health_url") mockChecker := NewMockChecker(ctrl) mockChecker.EXPECT().Check().Return(errors.New("sick")).AnyTimes() // when starting the service with a short frequency defer service.Stop() service.healthFrequency = time.Millisecond * 3 service.RegisterModules(0, 0, mockChecker) a.Equal(3, len(service.ModulesSortedByStartOrder())) service.Start() time.Sleep(time.Millisecond * 10) // and when I can call the health URL url := fmt.Sprintf("http://%s/health_url", service.WebServer().GetAddr()) result, err := http.Get(url) // then I receive status 503 and a JSON error message a.NoError(err) a.Equal(503, result.StatusCode) body, err := ioutil.ReadAll(result.Body) a.NoError(err) a.Equal("{\"*service.MockChecker\":\"sick\"}", string(body)) }
func Benchmark_E2E_Fetch_HelloWorld_Messages(b *testing.B) { defer testutil.ResetDefaultRegistryHealthCheck() a := assert.New(b) dir, _ := ioutil.TempDir("", "guble_benchmarking_fetch_test") defer os.RemoveAll(dir) *Config.HttpListen = "localhost:0" *Config.KVS = "memory" *Config.MS = "file" *Config.StoragePath = dir service := StartService() defer service.Stop() time.Sleep(time.Millisecond * 10) // fill the topic location := "ws://" + service.WebServer().GetAddr() + "/stream/user/xy" c, err := client.Open(location, "http://localhost/", 1000, true) a.NoError(err) for i := 1; i <= b.N; i++ { a.NoError(c.Send("/hello", fmt.Sprintf("Hello %v", i), "")) select { case <-c.StatusMessages(): // wait for, but ignore case <-time.After(time.Millisecond * 100): a.Fail("timeout on send notification") return } } start := time.Now() b.ResetTimer() c.WriteRawMessage([]byte("+ /hello 0 1000000")) for i := 1; i <= b.N; i++ { select { case msg := <-c.Messages(): a.Equal(fmt.Sprintf("Hello %v", i), msg.BodyAsString()) case e := <-c.Errors(): a.Fail(string(e.Bytes())) return case <-time.After(time.Second): a.Fail("timeout on message: " + strconv.Itoa(i)) return } } b.StopTimer() end := time.Now() throughput := float64(b.N) / end.Sub(start).Seconds() fmt.Printf("\n\tThroughput: %v/sec (%v message in %v)\n", int(throughput), b.N, end.Sub(start)) }
func TestStoppingOfModules(t *testing.T) { defer testutil.ResetDefaultRegistryHealthCheck() var p interface{} func() { defer func() { p = recover() }() service, _, _, _ := aMockedServiceWithMockedRouterStandalone() service.RegisterModules(0, 0, &testStopable{}) service.Stop() }() assert.NotNil(t, p) }
func TestStartingOfModules(t *testing.T) { defer testutil.ResetDefaultRegistryHealthCheck() a := assert.New(t) var p interface{} func() { defer func() { p = recover() }() service, _, _, _ := aMockedServiceWithMockedRouterStandalone() service.RegisterModules(0, 0, &testStartable{}) a.Equal(3, len(service.ModulesSortedByStartOrder())) service.Start() }() a.NotNil(p) }
func TestEndpointRegisterAndServing(t *testing.T) { _, finish := testutil.NewMockCtrl(t) defer finish() defer testutil.ResetDefaultRegistryHealthCheck() a := assert.New(t) // given: service, _, _, _ := aMockedServiceWithMockedRouterStandalone() // when I register an endpoint at path /foo service.RegisterModules(0, 0, &testEndpoint{}) a.Equal(3, len(service.ModulesSortedByStartOrder())) service.Start() defer service.Stop() time.Sleep(time.Millisecond * 10) // then I can call the handler url := fmt.Sprintf("http://%s/foo", service.WebServer().GetAddr()) result, err := http.Get(url) a.NoError(err) body := make([]byte, 3) result.Body.Read(body) a.Equal("bar", string(body)) }
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 TestThroughput(t *testing.T) { //testutil.EnableDebugForMethod() defer testutil.ResetDefaultRegistryHealthCheck() dir, _ := ioutil.TempDir("", "guble_benchmarking_test") defer os.RemoveAll(dir) *config.HttpListen = "localhost:0" *config.KVS = "memory" *config.MS = "file" *config.StoragePath = dir service := StartService() defer func() { service.Stop() }() time.Sleep(time.Millisecond * 10) testgroupCount := 4 messagesPerGroup := 100 log.Printf("init the %v testgroups", testgroupCount) testgroups := make([]*testgroup, testgroupCount, testgroupCount) for i := range testgroups { testgroups[i] = newTestgroup(t, i, service.WebServer().GetAddr(), messagesPerGroup) } // init test log.Print("init the testgroups") for i := range testgroups { testgroups[i].Init() } defer func() { // cleanup tests log.Print("cleanup the testgroups") for i := range testgroups { testgroups[i].Clean() } }() // start test log.Print("start the testgroups") start := time.Now() for i := range testgroups { go testgroups[i].Start() } log.Print("wait for finishing") timeout := time.After(time.Second * 60) for i, test := range testgroups { //fmt.Printf("wating for test %v\n", i) select { case successFlag := <-test.done: if !successFlag { t.Logf("testgroup %v returned with error", i) t.FailNow() return } case <-timeout: t.Log("timeout. testgroups not ready before timeout") t.Fail() return } } end := time.Now() totalMessages := testgroupCount * messagesPerGroup throughput := float64(totalMessages) / end.Sub(start).Seconds() log.Printf("finished! Throughput: %v/sec (%v message in %v)", int(throughput), totalMessages, end.Sub(start)) }
func throughputSend(b *testing.B, nWorkers int, sampleSend func(c client.Client) error) float64 { //testutil.EnableDebugForMethod() fmt.Printf("b.N=%v\n", b.N) defer testutil.ResetDefaultRegistryHealthCheck() a := assert.New(b) dir, errTempDir := ioutil.TempDir("", "guble_benchmarking_gcm_test") defer func() { errRemove := os.RemoveAll(dir) if errRemove != nil { log.WithFields(log.Fields{ "module": "testing", "err": errRemove, }).Error("Could not remove directory") } }() a.NoError(errTempDir) *config.HttpListen = "localhost:0" *config.KVS = "memory" *config.MS = "file" *config.StoragePath = dir *config.GCM.Enabled = true *config.GCM.APIKey = "WILL BE OVERWRITTEN" *config.GCM.Workers = nWorkers service := StartService() log.WithFields(log.Fields{ "module": "testing", }).Debug("Overwriting the GCM Sender with a MocK") gcmConnector, ok := service.Modules()[4].(*gcm.GCMConnector) a.True(ok, "Modules[4] should be of type GCMConnector") gcmConnector.Sender = testutil.CreateGcmSender( testutil.CreateRoundTripperWithJsonResponse(http.StatusOK, testutil.CorrectGcmResponseMessageJSON, nil)) gomaxprocs := runtime.GOMAXPROCS(0) clients := make([]client.Client, 0, gomaxprocs) for clientID := 0; clientID < gomaxprocs; clientID++ { location := "ws://" + service.WebServer().GetAddr() + "/stream/user/" + strconv.Itoa(clientID) client, err := client.Open(location, "http://localhost/", 1000, true) a.NoError(err) clients = append(clients, client) } // create a topic url := fmt.Sprintf("http://%s/gcm/0/gcmId0/subscribe/topic", service.WebServer().GetAddr()) response, errPost := http.Post(url, "text/plain", bytes.NewBufferString("")) a.NoError(errPost) a.Equal(response.StatusCode, 200) body, errReadAll := ioutil.ReadAll(response.Body) a.NoError(errReadAll) a.Equal("registered: /topic\n", string(body)) log.WithFields(log.Fields{ "module": "testing", }).Debug("Overwriting the GCM Sender with a MocK") start := time.Now() b.ResetTimer() log.WithFields(log.Fields{ "module": "testing", }).Debug("Sending multiple messages from each client in separate goroutines") var wg sync.WaitGroup wg.Add(gomaxprocs) for _, c := range clients { go func(c client.Client) { defer wg.Done() for i := 0; i < b.N; i++ { a.NoError(sampleSend(c)) } }(c) } wg.Wait() // stop service (and wait for all the messages to be processed during the given grace period) err := service.Stop() a.Nil(err) end := time.Now() b.StopTimer() return float64(b.N*gomaxprocs) / end.Sub(start).Seconds() }
func (params *benchParams) throughputFCM() { defer testutil.ResetDefaultRegistryHealthCheck() a := assert.New(params) dir, errTempDir := ioutil.TempDir("", "guble_benchmarking_fcm_test") a.NoError(errTempDir) *Config.HttpListen = "localhost:0" *Config.KVS = "memory" *Config.MS = "file" *Config.StoragePath = dir *Config.FCM.Enabled = true *Config.FCM.APIKey = "WILL BE OVERWRITTEN" *Config.FCM.Workers = params.workers params.service = StartService() var fcmConn connector.ResponsiveConnector var ok bool for _, iface := range params.service.ModulesSortedByStartOrder() { fcmConn, ok = iface.(connector.ResponsiveConnector) if ok { break } } if fcmConn == nil { a.FailNow("There should be a module of type: FCM Connector") } params.receiveC = make(chan bool) sender, err := fcm.CreateFcmSender(fcm.SuccessFCMResponse, params.receiveC, params.timeout) a.NoError(err) fcmConn.SetSender(sender) urlFormat := fmt.Sprintf("http://%s/fcm/%%d/gcmId%%d/subscribe/%%s", params.service.WebServer().GetAddr()) for i := 1; i <= params.subscriptions; i++ { // create FCM subscription response, errPost := http.Post( fmt.Sprintf(urlFormat, i, i, strings.TrimPrefix(testTopic, "/")), "text/plain", bytes.NewBufferString(""), ) a.NoError(errPost) a.Equal(response.StatusCode, 200) body, errReadAll := ioutil.ReadAll(response.Body) a.NoError(errReadAll) a.Equal("{\"subscribed\":\"/topic\"}", string(body)) } clients := params.createClients() // Report allocations also params.ReportAllocs() expectedMessagesNumber := params.N * params.clients * params.subscriptions logger.WithFields(log.Fields{ "expectedMessagesNumber": expectedMessagesNumber, "N": params.N, }).Info("Expecting messages") params.wg.Add(expectedMessagesNumber) // start the receive loop (a select on receiveC and doneC) params.doneC = make(chan struct{}) params.receiveLoop() params.ResetTimer() // send all messages, or fail on any error for _, cl := range clients { go func(cl client.Client) { for i := 0; i < params.N; i++ { err := params.sender(cl) if err != nil { a.FailNow("Message could not be sent") } params.sent++ } }(cl) } // wait to receive all messages params.wg.Wait() // stop timer after the actual test params.StopTimer() close(params.doneC) a.NoError(params.service.Stop()) params.service = nil close(params.receiveC) errRemove := os.RemoveAll(dir) if errRemove != nil { logger.WithError(errRemove).WithField("module", "testing").Error("Could not remove directory") } }
func TestThroughput(t *testing.T) { // TODO: We disabled this test because the receiver implementation of fetching messages // should be reimplemented according to the new message store testutil.SkipIfDisabled(t) testutil.SkipIfShort(t) defer testutil.ResetDefaultRegistryHealthCheck() dir, _ := ioutil.TempDir("", "guble_benchmarking_test") *Config.HttpListen = "localhost:0" *Config.KVS = "memory" *Config.MS = "file" *Config.StoragePath = dir service := StartService() testgroupCount := 4 messagesPerGroup := 100 log.Printf("init the %v testgroups", testgroupCount) testgroups := make([]*testgroup, testgroupCount, testgroupCount) for i := range testgroups { testgroups[i] = newTestgroup(t, i, service.WebServer().GetAddr(), messagesPerGroup) } // init test log.Print("init the testgroups") for i := range testgroups { testgroups[i].Init() } defer func() { // cleanup tests log.Print("cleanup the testgroups") for i := range testgroups { testgroups[i].Clean() } service.Stop() os.RemoveAll(dir) }() // start test log.Print("start the testgroups") start := time.Now() for i := range testgroups { go testgroups[i].Start() } log.Print("wait for finishing") for i, test := range testgroups { select { case successFlag := <-test.done: if !successFlag { t.Logf("testgroup %v returned with error", i) t.FailNow() return } case <-time.After(time.Second * 20): t.Log("timeout. testgroups not ready before timeout") t.Fail() return } } end := time.Now() totalMessages := testgroupCount * messagesPerGroup throughput := float64(totalMessages) / end.Sub(start).Seconds() log.Printf("finished! Throughput: %v/sec (%v message in %v)", int(throughput), totalMessages, end.Sub(start)) time.Sleep(time.Second * 1) }
// Test that restarting the service continues to fetch messages from store for a subscription from lastID func TestFCMRestart(t *testing.T) { // defer testutil.EnableDebugForMethod()() defer testutil.ResetDefaultRegistryHealthCheck() a := assert.New(t) receiveC := make(chan bool) s, cleanup := serviceSetUp(t) defer cleanup() assertMetrics(a, s, expectedValues{true, 0, 0, 0}) var fcmConn connector.ResponsiveConnector var ok bool for _, iface := range s.ModulesSortedByStartOrder() { fcmConn, ok = iface.(connector.ResponsiveConnector) if ok { break } } a.True(ok, "There should be a module of type FCMConnector") // add a high timeout so the messages are processed slow sender, err := fcm.CreateFcmSender(fcm.SuccessFCMResponse, receiveC, 10*time.Millisecond) a.NoError(err) fcmConn.SetSender(sender) // create subscription on topic subscriptionSetUp(t, s) assertMetrics(a, s, expectedValues{true, 0, 1, 1}) c := clientSetUp(t, s) // send 3 messages in the router but read only one and close the service for i := 0; i < 3; i++ { c.Send(testTopic, "dummy body", "{dummy: value}") } // receive one message only from FCM select { case <-receiveC: case <-time.After(timeoutForOneMessage): a.Fail("Initial FCM message not received") } assertMetrics(a, s, expectedValues{false, 1, 1, 1}) close(receiveC) // restart the service a.NoError(s.Stop()) // remake the sender receiveC = make(chan bool) sender, err = fcm.CreateFcmSender(fcm.SuccessFCMResponse, receiveC, 10*time.Millisecond) a.NoError(err) fcmConn.SetSender(sender) time.Sleep(50 * time.Millisecond) testutil.ResetDefaultRegistryHealthCheck() a.NoError(s.Start()) //TODO Cosmin Bogdan add 2 calls to assertMetrics before and after the next block // read the other 2 messages for i := 0; i < 1; i++ { select { case <-receiveC: case <-time.After(2 * timeoutForOneMessage): a.Fail("FCM message not received") } } }
func (params *benchParams) throughputAPNS() { defer testutil.EnableDebugForMethod()() _, finish := testutil.NewMockBenchmarkCtrl(params.B) defer finish() defer testutil.ResetDefaultRegistryHealthCheck() a := assert.New(params) dir, errTempDir := ioutil.TempDir("", "guble_benchmarking_apns_test") a.NoError(errTempDir) *Config.HttpListen = "localhost:0" *Config.KVS = "memory" *Config.MS = "file" *Config.StoragePath = dir *Config.APNS.Enabled = true *Config.APNS.AppTopic = "app.topic" *Config.APNS.Prefix = "/apns/" params.receiveC = make(chan bool) CreateModules = createModulesWebsocketAndMockAPNSPusher(params.receiveC, params.timeout) params.service = StartService() var apnsConn connector.ResponsiveConnector var ok bool for _, iface := range params.service.ModulesSortedByStartOrder() { apnsConn, ok = iface.(connector.ResponsiveConnector) if ok { break } } if apnsConn == nil { a.FailNow("There should be a module of type: APNS Connector") } urlFormat := fmt.Sprintf("http://%s/apns/apns-%%d/%%d/%%s", params.service.WebServer().GetAddr()) for i := 1; i <= params.subscriptions; i++ { // create APNS subscription response, errPost := http.Post( fmt.Sprintf(urlFormat, i, i, strings.TrimPrefix(testTopic, "/")), "text/plain", bytes.NewBufferString(""), ) a.NoError(errPost) a.Equal(response.StatusCode, 200) body, errReadAll := ioutil.ReadAll(response.Body) a.NoError(errReadAll) a.Equal("{\"subscribed\":\"/topic\"}", string(body)) } clients := params.createClients() // Report allocations also params.ReportAllocs() expectedMessagesNumber := params.N * params.clients * params.subscriptions logger.WithFields(log.Fields{ "expectedMessagesNumber": expectedMessagesNumber, "b.N": params.N, }).Info("Expecting messages") params.wg.Add(expectedMessagesNumber) // start the receive loop (a select on receiveC and doneC) params.doneC = make(chan struct{}) params.receiveLoop() params.ResetTimer() // send all messages, or fail on any error for _, cl := range clients { go func(cl client.Client) { for i := 0; i < params.N; i++ { err := params.sender(cl) if err != nil { a.FailNow("Message could not be sent") } params.sent++ } }(cl) } // wait to receive all messages params.wg.Wait() // stop timer after the actual test params.StopTimer() close(params.doneC) a.NoError(params.service.Stop()) params.service = nil close(params.receiveC) errRemove := os.RemoveAll(dir) if errRemove != nil { logger.WithError(errRemove).WithField("module", "testing").Error("Could not remove directory") } }