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 }