github.com/vmware/transport-go@v1.3.4/stompserver/server_test.go (about)

     1  // Copyright 2019-2020 VMware, Inc.
     2  // SPDX-License-Identifier: BSD-2-Clause
     3  
     4  package stompserver
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"github.com/go-stomp/stomp/v3/frame"
    10  	"github.com/stretchr/testify/assert"
    11  	"strconv"
    12  	"sync"
    13  	"testing"
    14  )
    15  
    16  type MockRawConnectionListener struct {
    17  	connected           bool
    18  	incomingConnections chan interface{}
    19  }
    20  
    21  func NewMockRawConnectionListener() *MockRawConnectionListener {
    22  	return &MockRawConnectionListener{
    23  		incomingConnections: make(chan interface{}),
    24  		connected:           true,
    25  	}
    26  }
    27  
    28  func (cl *MockRawConnectionListener) Accept() (RawConnection, error) {
    29  	obj := <-cl.incomingConnections
    30  	mockConn, ok := obj.(*MockRawConnection)
    31  	if ok {
    32  		return mockConn, nil
    33  	}
    34  
    35  	return nil, obj.(error)
    36  }
    37  
    38  func (cl *MockRawConnectionListener) Close() error {
    39  	cl.connected = false
    40  	return nil
    41  }
    42  
    43  func newTestStompServer(config StompConfig) (*stompServer, *MockRawConnectionListener) {
    44  	listener := NewMockRawConnectionListener()
    45  	return NewStompServer(listener, config).(*stompServer), listener
    46  
    47  }
    48  
    49  func TestStompServer_NewSubscription(t *testing.T) {
    50  	server, conListener := newTestStompServer(NewStompConfig(0, []string{"/pub"}))
    51  
    52  	go server.Start()
    53  
    54  	wg := sync.WaitGroup{}
    55  	wg.Add(1)
    56  
    57  	server.OnSubscribeEvent(
    58  		func(conId string, subId string, destination string, frame *frame.Frame) {
    59  			assert.Equal(t, subId, "sub-id-1")
    60  			wg.Done()
    61  		})
    62  
    63  	mockRawConn := NewMockRawConnection()
    64  	conListener.incomingConnections <- mockRawConn
    65  
    66  	mockRawConn.SendConnectFrame()
    67  	mockRawConn.incomingFrames <- frame.New(frame.SUBSCRIBE,
    68  		frame.Destination, "/topic/destination",
    69  		frame.Id, "sub-id-1")
    70  
    71  	wg.Wait()
    72  }
    73  
    74  func TestStompServer_OnApplicationRequest(t *testing.T) {
    75  	appPrefixes := []string{"/pub", "/pub2", "/pub3/"}
    76  	server, _ := newTestStompServer(NewStompConfig(0, appPrefixes))
    77  
    78  	assert.Equal(t, server.config.AppDestinationPrefix(), []string{"/pub/", "/pub2/", "/pub3/"})
    79  	assert.Equal(t, appPrefixes, []string{"/pub", "/pub2", "/pub3/"})
    80  	fmt.Println(appPrefixes)
    81  
    82  	go server.Start()
    83  
    84  	wg := sync.WaitGroup{}
    85  
    86  	wg.Add(2)
    87  	server.OnApplicationRequest(func(destination string, message []byte, connectionId string) {
    88  		if destination == "/pub/testRequest1" {
    89  			assert.Equal(t, string(message), "request1-payload")
    90  			assert.Equal(t, connectionId, "con1")
    91  			wg.Done()
    92  		} else if destination == "/pub2/testRequest2" {
    93  			assert.Equal(t, string(message), "request2-payload")
    94  			assert.Equal(t, connectionId, "con2")
    95  			wg.Done()
    96  		} else {
    97  			assert.Fail(t, "unexpected request")
    98  		}
    99  	})
   100  
   101  	f1 := frame.New(frame.MESSAGE, frame.Destination, "/pub/testRequest1")
   102  	f1.Body = []byte("request1-payload")
   103  
   104  	f2 := frame.New(frame.MESSAGE, frame.Destination, "/pub2/testRequest2")
   105  	f2.Body = []byte("request2-payload")
   106  
   107  	server.connectionEvents <- &ConnEvent{
   108  		ConnId:    "con1",
   109  		eventType: IncomingMessage,
   110  		conn: &stompConn{
   111  			id: "con1",
   112  		},
   113  		destination: "/pub/testRequest1",
   114  		frame:       f1,
   115  	}
   116  	server.connectionEvents <- &ConnEvent{
   117  		ConnId:    "con2",
   118  		eventType: IncomingMessage,
   119  		conn: &stompConn{
   120  			id: "con2",
   121  		},
   122  		destination: "/pub2/testRequest2",
   123  		frame:       f2,
   124  	}
   125  	server.connectionEvents <- &ConnEvent{
   126  		ConnId:    "con1",
   127  		eventType: IncomingMessage,
   128  		conn: &stompConn{
   129  			id: "con1",
   130  		},
   131  		destination: "/pub4/testRequest3",
   132  		frame:       frame.New(frame.MESSAGE, frame.Destination, "/pub3/testRequest3"),
   133  	}
   134  
   135  	wg.Wait()
   136  }
   137  
   138  func TestStompServer_SendMessage(t *testing.T) {
   139  	server, listener := newTestStompServer(NewStompConfig(0, []string{"/pub/"}))
   140  	go server.Start()
   141  
   142  	mockRwConn1 := NewMockRawConnection()
   143  	mockRwConn2 := NewMockRawConnection()
   144  	mockRwConn3 := NewMockRawConnection()
   145  
   146  	listener.incomingConnections <- mockRwConn1
   147  	listener.incomingConnections <- mockRwConn2
   148  	listener.incomingConnections <- mockRwConn3
   149  
   150  	mockRwConn1.SendConnectFrame()
   151  	mockRwConn2.SendConnectFrame()
   152  	mockRwConn3.SendConnectFrame()
   153  
   154  	wg := sync.WaitGroup{}
   155  	wg.Add(6)
   156  	server.OnSubscribeEvent(func(conId string, subId string, destination string, f *frame.Frame) {
   157  		wg.Done()
   158  	})
   159  
   160  	subscribeMockConToTopic(mockRwConn1, "/topic/test-topic1", "/topic/test-topic1", "/topic/test-topic2")
   161  	subscribeMockConToTopic(mockRwConn2, "/topic/test-topic1", "/topic/test-topic3")
   162  	subscribeMockConToTopic(mockRwConn3, "/topic/test-topic3")
   163  
   164  	wg.Wait()
   165  
   166  	mockRwConn1.writeWg = &wg
   167  	mockRwConn2.writeWg = &wg
   168  	mockRwConn3.writeWg = &wg
   169  
   170  	wg.Add(3)
   171  	server.SendMessage("/topic/test-topic1", []byte("test-message"))
   172  	wg.Wait()
   173  
   174  	f := mockRwConn1.LastSentFrame()
   175  	assert.Equal(t, len(mockRwConn1.sentFrames), 3)
   176  	verifyFrame(t, f, frame.New(
   177  		frame.MESSAGE, frame.Destination, "/topic/test-topic1"), false)
   178  	assert.Equal(t, string(f.Body), "test-message")
   179  
   180  	f = mockRwConn2.LastSentFrame()
   181  	assert.Equal(t, len(mockRwConn2.sentFrames), 2)
   182  	verifyFrame(t, f, frame.New(frame.MESSAGE,
   183  		frame.Destination, "/topic/test-topic1",
   184  		frame.Subscription, "/topic/test-topic1-0"), false)
   185  	assert.Equal(t, string(f.Body), "test-message")
   186  
   187  	assert.Equal(t, len(mockRwConn3.sentFrames), 1)
   188  
   189  	wg.Add(2)
   190  	server.SendMessage("/topic/test-topic3", []byte("test-message2"))
   191  	wg.Wait()
   192  
   193  	assert.Equal(t, len(mockRwConn1.sentFrames), 3)
   194  
   195  	assert.Equal(t, len(mockRwConn2.sentFrames), 3)
   196  	assert.Equal(t, string(mockRwConn2.LastSentFrame().Body), "test-message2")
   197  
   198  	assert.Equal(t, len(mockRwConn3.sentFrames), 2)
   199  	assert.Equal(t, string(mockRwConn3.LastSentFrame().Body), "test-message2")
   200  
   201  	wg.Add(1)
   202  	server.SendMessage("/topic/test-topic2", []byte("test-message3"))
   203  	wg.Wait()
   204  
   205  	assert.Equal(t, len(mockRwConn1.sentFrames), 4)
   206  	assert.Equal(t, string(mockRwConn1.LastSentFrame().Body), "test-message3")
   207  	assert.Equal(t, len(mockRwConn2.sentFrames), 3)
   208  	assert.Equal(t, len(mockRwConn3.sentFrames), 2)
   209  
   210  	server.OnUnsubscribeEvent(func(conId string, subId string, destination string) {
   211  		wg.Done()
   212  	})
   213  
   214  	wg.Add(1)
   215  	mockRwConn1.incomingFrames <- frame.New(
   216  		frame.UNSUBSCRIBE, frame.Id, "/topic/test-topic1-0")
   217  	wg.Wait()
   218  
   219  	wg.Add(2)
   220  	server.SendMessage("/topic/test-topic1", []byte("test-message4"))
   221  	wg.Wait()
   222  
   223  	assert.Equal(t, len(mockRwConn1.sentFrames), 5)
   224  	assert.Equal(t, string(mockRwConn1.LastSentFrame().Body), "test-message4")
   225  	assert.Equal(t, len(mockRwConn2.sentFrames), 4)
   226  	assert.Equal(t, string(mockRwConn2.LastSentFrame().Body), "test-message4")
   227  
   228  	wg.Add(1)
   229  	mockRwConn1.incomingFrames <- frame.New(
   230  		frame.UNSUBSCRIBE, frame.Id, "/topic/test-topic1-1")
   231  	wg.Wait()
   232  
   233  	wg.Add(1)
   234  	server.SendMessage("/topic/test-topic1", []byte("test-message5"))
   235  	wg.Wait()
   236  
   237  	assert.Equal(t, len(mockRwConn1.sentFrames), 5)
   238  	assert.Equal(t, len(mockRwConn2.sentFrames), 5)
   239  	assert.Equal(t, string(mockRwConn2.LastSentFrame().Body), "test-message5")
   240  	assert.Equal(t, len(mockRwConn3.sentFrames), 2)
   241  
   242  	wg.Add(2)
   243  	mockRwConn2.incomingFrames <- frame.New(frame.DISCONNECT)
   244  	wg.Wait()
   245  
   246  	wg.Add(1)
   247  	server.SendMessage("/topic/test-topic3", []byte("test-message6"))
   248  	wg.Wait()
   249  
   250  	assert.Equal(t, len(mockRwConn1.sentFrames), 5)
   251  	assert.Equal(t, len(mockRwConn2.sentFrames), 5)
   252  	assert.Equal(t, len(mockRwConn3.sentFrames), 3)
   253  	assert.Equal(t, string(mockRwConn3.LastSentFrame().Body), "test-message6")
   254  
   255  	wg.Add(2)
   256  	server.OnUnsubscribeEvent(func(conId string, subId string, destination string) {
   257  		assert.Equal(t, subId, "/topic/test-topic3-0")
   258  		assert.Equal(t, destination, "/topic/test-topic3")
   259  		wg.Done()
   260  	})
   261  	mockRwConn3.incomingFrames <- frame.New(frame.DISCONNECT)
   262  	wg.Wait()
   263  }
   264  
   265  func TestStompServer_SetConnectionEventCallback_ConnectionStarting(t *testing.T) {
   266  	wg := sync.WaitGroup{}
   267  	server, listener := newTestStompServer(NewStompConfig(0, []string{"/pub/"}))
   268  	server.SetConnectionEventCallback(ConnectionStarting, func(connEvent *ConnEvent) {
   269  		assert.Equal(t, ConnectionStarting, connEvent.eventType)
   270  		wg.Done()
   271  	})
   272  
   273  	go server.Start()
   274  
   275  	wg.Add(1)
   276  	mockRwConn1 := NewMockRawConnection()
   277  	listener.incomingConnections <- mockRwConn1
   278  
   279  	// should trigger ConnectionStarting callback
   280  	mockRwConn1.SendConnectFrame()
   281  	wg.Wait()
   282  }
   283  
   284  func TestStompServer_SetConnectionEventCallback_SubscribeToTopic(t *testing.T) {
   285  	wg := sync.WaitGroup{}
   286  	server, listener := newTestStompServer(NewStompConfig(0, []string{"/pub/"}))
   287  	server.SetConnectionEventCallback(SubscribeToTopic, func(connEvent *ConnEvent) {
   288  		assert.Equal(t, SubscribeToTopic, connEvent.eventType)
   289  		wg.Done()
   290  	})
   291  
   292  	go server.Start()
   293  
   294  	mockRwConn1 := NewMockRawConnection()
   295  	listener.incomingConnections <- mockRwConn1
   296  
   297  	// connect first
   298  	mockRwConn1.SendConnectFrame()
   299  
   300  	// should trigger SubscribeToTopic callback
   301  	wg.Add(1)
   302  	subscribeMockConToTopic(mockRwConn1, "/topic/test-topic1")
   303  	wg.Wait()
   304  }
   305  
   306  func TestStompServer_SetConnectionEventCallback_IncomingMessage(t *testing.T) {
   307  	wg := sync.WaitGroup{}
   308  	server, listener := newTestStompServer(NewStompConfig(0, []string{"/pub/"}))
   309  	server.SetConnectionEventCallback(IncomingMessage, func(connEvent *ConnEvent) {
   310  		assert.Equal(t, IncomingMessage, connEvent.eventType)
   311  		wg.Done()
   312  	})
   313  
   314  	go server.Start()
   315  
   316  	mockRwConn1 := NewMockRawConnection()
   317  	listener.incomingConnections <- mockRwConn1
   318  
   319  	// connect first
   320  	mockRwConn1.SendConnectFrame()
   321  
   322  	// only decerement wg after subscription has been established
   323  	server.OnSubscribeEvent(func(conId string, subId string, destination string, f *frame.Frame) {
   324  		wg.Done()
   325  	})
   326  
   327  	// subscribe to a topic
   328  	wg.Add(1)
   329  	subscribeMockConToTopic(mockRwConn1, "/topic/test-topic1")
   330  	wg.Wait()
   331  
   332  	// should trigger IncomingMessage callback
   333  	wg.Add(1)
   334  	server.connectionEvents <- &ConnEvent{
   335  		ConnId:    "con1",
   336  		eventType: IncomingMessage,
   337  		conn: &stompConn{
   338  			id: "con1",
   339  		},
   340  		destination: "/pub/test-topic1",
   341  		frame:       frame.New(frame.SEND),
   342  	}
   343  	wg.Wait()
   344  }
   345  
   346  func TestStompServer_SetConnectionEventCallback_Unsubscribe(t *testing.T) {
   347  	wg := sync.WaitGroup{}
   348  	server, listener := newTestStompServer(NewStompConfig(0, []string{"/pub/"}))
   349  	server.SetConnectionEventCallback(UnsubscribeFromTopic, func(connEvent *ConnEvent) {
   350  		assert.Equal(t, UnsubscribeFromTopic, connEvent.eventType)
   351  		wg.Done()
   352  	})
   353  
   354  	go server.Start()
   355  
   356  	mockRwConn1 := NewMockRawConnection()
   357  	listener.incomingConnections <- mockRwConn1
   358  
   359  	// should trigger ConnectionStarting callback
   360  	mockRwConn1.SendConnectFrame()
   361  
   362  	server.OnSubscribeEvent(func(conId string, subId string, destination string, f *frame.Frame) {
   363  		wg.Done()
   364  	})
   365  
   366  	// subscribe to a channel
   367  	wg.Add(1)
   368  	subscribeMockConToTopic(mockRwConn1, "/topic/test-topic1")
   369  
   370  	// should trigger UnsubscribeFromTopic callback
   371  	wg.Add(1)
   372  	mockRwConn1.incomingFrames <- frame.New(frame.UNSUBSCRIBE, frame.Id, "/topic/test-topic1-0")
   373  	wg.Wait()
   374  }
   375  
   376  func TestStompServer_SetConnectionEventCallback_Disconnect(t *testing.T) {
   377  	wg := sync.WaitGroup{}
   378  	server, listener := newTestStompServer(NewStompConfig(0, []string{"/pub/"}))
   379  	server.SetConnectionEventCallback(ConnectionClosed, func(connEvent *ConnEvent) {
   380  		assert.Equal(t, ConnectionClosed, connEvent.eventType)
   381  		wg.Done()
   382  	})
   383  
   384  	go server.Start()
   385  
   386  	mockRwConn1 := NewMockRawConnection()
   387  	listener.incomingConnections <- mockRwConn1
   388  
   389  	// connect
   390  	mockRwConn1.SendConnectFrame()
   391  
   392  	// should trigger ConnectionClosed callback
   393  	wg.Add(1)
   394  	mockRwConn1.incomingFrames <- frame.New(frame.DISCONNECT)
   395  	wg.Wait()
   396  }
   397  
   398  func TestStompServer_SendMessageToClient(t *testing.T) {
   399  	server, listener := newTestStompServer(NewStompConfig(0, []string{"/pub/"}))
   400  	go server.Start()
   401  
   402  	mockRwConn1 := NewMockRawConnection()
   403  	mockRwConn2 := NewMockRawConnection()
   404  
   405  	listener.incomingConnections <- mockRwConn1
   406  	listener.incomingConnections <- mockRwConn2
   407  
   408  	mockRwConn1.SendConnectFrame()
   409  	mockRwConn2.SendConnectFrame()
   410  
   411  	wg := sync.WaitGroup{}
   412  	wg.Add(5)
   413  	server.OnSubscribeEvent(func(conId string, subId string, destination string, f *frame.Frame) {
   414  		wg.Done()
   415  	})
   416  
   417  	subscribeMockConToTopic(mockRwConn1, "/topic/test-topic1", "/topic/test-topic1", "/topic/test-topic2")
   418  	subscribeMockConToTopic(mockRwConn2, "/topic/test-topic1", "/topic/test-topic3")
   419  
   420  	wg.Wait()
   421  
   422  	connMap := make(map[*MockRawConnection]*stompConn)
   423  	for _, conn := range server.connectionsMap {
   424  		if conn.(*stompConn).rawConnection == mockRwConn1 {
   425  			connMap[mockRwConn1] = conn.(*stompConn)
   426  		} else if conn.(*stompConn).rawConnection == mockRwConn2 {
   427  			connMap[mockRwConn2] = conn.(*stompConn)
   428  		}
   429  	}
   430  
   431  	mockRwConn1.writeWg = &wg
   432  	mockRwConn2.writeWg = &wg
   433  
   434  	wg.Add(2)
   435  	server.SendMessageToClient(connMap[mockRwConn1].id, "/topic/test-topic1", []byte("test-message"))
   436  	wg.Wait()
   437  
   438  	f := mockRwConn1.LastSentFrame()
   439  	assert.Equal(t, len(mockRwConn1.sentFrames), 3)
   440  	verifyFrame(t, f, frame.New(
   441  		frame.MESSAGE, frame.Destination, "/topic/test-topic1"), false)
   442  	assert.Equal(t, string(f.Body), "test-message")
   443  
   444  	assert.Equal(t, len(mockRwConn2.sentFrames), 1)
   445  
   446  	server.SendMessageToClient(connMap[mockRwConn2].id, "/topic/invalid-topic", []byte("invalid-message"))
   447  
   448  	server.SendMessageToClient("invalid-connection-id", "/topic/invalid-topic", []byte("invalid-message"))
   449  
   450  	wg.Add(1)
   451  	server.SendMessageToClient(connMap[mockRwConn2].id, "/topic/test-topic1", []byte("test-message2"))
   452  	wg.Wait()
   453  
   454  	assert.Equal(t, len(mockRwConn1.sentFrames), 3)
   455  	assert.Equal(t, len(mockRwConn2.sentFrames), 2)
   456  	assert.Equal(t, string(mockRwConn2.LastSentFrame().Body), "test-message2")
   457  
   458  }
   459  
   460  func TestStompServer_Stop(t *testing.T) {
   461  	server, listener := newTestStompServer(NewStompConfig(0, []string{"/pub"}))
   462  
   463  	wg := sync.WaitGroup{}
   464  
   465  	go func() {
   466  		server.Start()
   467  		wg.Done()
   468  	}()
   469  
   470  	mockRwConn1 := NewMockRawConnection()
   471  	mockRwConn2 := NewMockRawConnection()
   472  	listener.incomingConnections <- mockRwConn1
   473  	listener.incomingConnections <- mockRwConn2
   474  
   475  	wg.Add(2)
   476  	server.OnSubscribeEvent(func(conId string, subId string, destination string, frame *frame.Frame) {
   477  		wg.Done()
   478  	})
   479  
   480  	mockRwConn1.SendConnectFrame()
   481  	mockRwConn2.SendConnectFrame()
   482  	subscribeMockConToTopic(mockRwConn1, "/topic1")
   483  	subscribeMockConToTopic(mockRwConn1, "/topic2")
   484  
   485  	wg.Wait()
   486  
   487  	// calling start on started server doesn't do anything and doesn't block
   488  	server.Start()
   489  
   490  	wg.Add(1)
   491  	server.Stop()
   492  	wg.Wait()
   493  
   494  	assert.Equal(t, mockRwConn1.connected, false)
   495  	assert.Equal(t, mockRwConn2.connected, false)
   496  	assert.Equal(t, listener.connected, false)
   497  }
   498  
   499  func TestStompServer_ConnectionErrors(t *testing.T) {
   500  	server, listener := newTestStompServer(NewStompConfig(0, []string{"/pub"}))
   501  
   502  	go server.Start()
   503  
   504  	// simulate 10 errors
   505  	for i := 0; i < 10; i++ {
   506  		listener.incomingConnections <- errors.New("connection-error")
   507  	}
   508  
   509  	assert.Equal(t, len(server.connectionsMap), 0)
   510  
   511  	// verify that we can still connect after the errors
   512  	for i := 0; i < 5; i++ {
   513  		listener.incomingConnections <- NewMockRawConnection()
   514  	}
   515  
   516  	server.callbackLock.Lock()
   517  	defer server.callbackLock.Unlock()
   518  	assert.True(t, len(server.connectionsMap) > 0)
   519  }
   520  
   521  func subscribeMockConToTopic(conn *MockRawConnection, topics ...string) {
   522  	for index, topic := range topics {
   523  		conn.incomingFrames <- frame.New(frame.SUBSCRIBE,
   524  			frame.Destination, topic,
   525  			frame.Id, topic+"-"+strconv.Itoa(index))
   526  	}
   527  }