github.com/anycable/anycable-go@v1.5.1/node/node_test.go (about)

     1  package node
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"testing"
     7  
     8  	"github.com/anycable/anycable-go/common"
     9  	"github.com/anycable/anycable-go/encoders"
    10  	"github.com/anycable/anycable-go/mocks"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/mock"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  func TestAuthenticate(t *testing.T) {
    17  	node := NewMockNode()
    18  	go node.hub.Run()
    19  	defer node.hub.Shutdown()
    20  
    21  	t.Run("Successful authentication", func(t *testing.T) {
    22  		session := NewMockSessionWithEnv("1", node, "/cable", &map[string]string{"id": "test_id"})
    23  		_, err := node.Authenticate(session)
    24  		defer node.hub.RemoveSession(session)
    25  
    26  		assert.Nil(t, err, "Error must be nil")
    27  		assert.Equal(t, true, session.Connected, "Session must be marked as connected")
    28  		assert.Equalf(t, "test_id", session.GetIdentifiers(), "Identifiers must be equal to %s", "test_id")
    29  
    30  		msg, err := session.conn.Read()
    31  		assert.Nil(t, err)
    32  
    33  		assert.Equalf(t, []byte("welcome"), msg, "Sent message is invalid: %s", msg)
    34  
    35  		assert.Equal(t, 1, node.hub.Size())
    36  	})
    37  
    38  	t.Run("Failed authentication", func(t *testing.T) {
    39  		session := NewMockSessionWithEnv("1", node, "/failure", &map[string]string{"id": "test_id"})
    40  
    41  		_, err := node.Authenticate(session)
    42  
    43  		assert.Nil(t, err, "Error must be nil")
    44  
    45  		msg, err := session.conn.Read()
    46  		assert.Nil(t, err)
    47  
    48  		assert.Equalf(t, []byte("unauthorized"), msg, "Sent message is invalid: %s", msg)
    49  		assert.Equal(t, 0, node.hub.Size())
    50  	})
    51  
    52  	t.Run("Error during authentication", func(t *testing.T) {
    53  		session := NewMockSessionWithEnv("1", node, "/error", &map[string]string{"id": "test_id"})
    54  
    55  		_, err := node.Authenticate(session)
    56  
    57  		assert.NotNil(t, err, "Error must not be nil")
    58  		assert.Equal(t, 0, node.hub.Size())
    59  	})
    60  
    61  	t.Run("With connection state", func(t *testing.T) {
    62  		session := NewMockSessionWithEnv("1", node, "/cable", &map[string]string{"x-session-test": "my_session", "id": "session_id"})
    63  		defer node.hub.RemoveSession(session)
    64  
    65  		_, err := node.Authenticate(session)
    66  
    67  		assert.Nil(t, err, "Error must be nil")
    68  		assert.Equal(t, true, session.Connected, "Session must be marked as connected")
    69  
    70  		assert.Len(t, *session.env.ConnectionState, 1)
    71  		assert.Equal(t, "my_session", (*session.env.ConnectionState)["_s_"])
    72  
    73  		assert.Equal(t, 1, node.hub.Size())
    74  	})
    75  }
    76  
    77  func TestSubscribe(t *testing.T) {
    78  	node := NewMockNode()
    79  	session := NewMockSession("14", node)
    80  
    81  	node.hub.AddSession(session)
    82  	defer node.hub.RemoveSession(session)
    83  
    84  	go node.hub.Run()
    85  	defer node.hub.Shutdown()
    86  
    87  	t.Run("Successful subscription", func(t *testing.T) {
    88  		_, err := node.Subscribe(session, &common.Message{Identifier: "test_channel"})
    89  		assert.Nil(t, err, "Error must be nil")
    90  
    91  		// Adds subscription to session
    92  		assert.Truef(t, session.subscriptions.HasChannel("test_channel"), "Session subscription must be set")
    93  
    94  		msg, err := session.conn.Read()
    95  		assert.Nil(t, err)
    96  
    97  		assert.Equalf(t, []byte("14"), msg, "Sent message is invalid: %s", msg)
    98  	})
    99  
   100  	t.Run("Subscription with a stream", func(t *testing.T) {
   101  		_, err := node.Subscribe(session, &common.Message{Identifier: "with_stream"})
   102  		assert.Nil(t, err, "Error must be nil")
   103  
   104  		// Adds subscription and stream to session
   105  		assert.Truef(t, session.subscriptions.HasChannel("with_stream"), "Session subsription must be set")
   106  		assert.Equal(t, []string{"stream"}, session.subscriptions.StreamsFor("with_stream"))
   107  
   108  		msg, err := session.conn.Read()
   109  		assert.Nil(t, err)
   110  
   111  		assert.Equalf(t, "14", string(msg), "Sent message is invalid: %s", msg)
   112  
   113  		// Make sure session is subscribed
   114  		node.hub.BroadcastMessage(&common.StreamMessage{Stream: "stream", Data: "41"})
   115  
   116  		msg, err = session.conn.Read()
   117  		assert.Nil(t, err)
   118  
   119  		assert.Equalf(t, "{\"identifier\":\"with_stream\",\"message\":41}", string(msg), "Broadcasted message is invalid: %s", msg)
   120  	})
   121  
   122  	t.Run("Error during subscription", func(t *testing.T) {
   123  		_, err := node.Subscribe(session, &common.Message{Identifier: "error"})
   124  		assert.Error(t, err)
   125  	})
   126  
   127  	t.Run("Rejected subscription", func(t *testing.T) {
   128  		session := NewMockSession("15", node)
   129  
   130  		node.hub.AddSession(session)
   131  		defer node.hub.RemoveSession(session)
   132  
   133  		res, err := node.Subscribe(session, &common.Message{Identifier: "failure"})
   134  
   135  		assert.Equal(t, common.FAILURE, res.Status)
   136  		assert.Equal(t, 0, len(session.subscriptions.Channels()))
   137  		assert.NoError(t, err)
   138  	})
   139  }
   140  
   141  func TestUnsubscribe(t *testing.T) {
   142  	node := NewMockNode()
   143  	session := NewMockSession("14", node)
   144  
   145  	node.hub.AddSession(session)
   146  	defer node.hub.RemoveSession(session)
   147  
   148  	go node.hub.Run()
   149  	defer node.hub.Shutdown()
   150  
   151  	t.Run("Successful unsubscribe", func(t *testing.T) {
   152  		session.subscriptions.AddChannel("test_channel")
   153  		node.hub.SubscribeSession(session, "streamo", "test_channel")
   154  
   155  		node.hub.Broadcast("streamo", `"before"`)
   156  		msg, err := session.conn.Read()
   157  		require.NoError(t, err)
   158  
   159  		assert.Equalf(t, `{"identifier":"test_channel","message":"before"}`, string(msg), "Broadcasted message is invalid: %s", msg)
   160  
   161  		_, err = node.Unsubscribe(session, &common.Message{Identifier: "test_channel"})
   162  		assert.Nil(t, err, "Error must be nil")
   163  
   164  		// Removes subscription from session
   165  		assert.Falsef(t, session.subscriptions.HasChannel("test_channel"), "Shouldn't contain test_channel")
   166  
   167  		node.hub.BroadcastMessage(&common.StreamMessage{Stream: "streamo", Data: "41"})
   168  
   169  		msg, err = session.conn.Read()
   170  		assert.Nil(t, msg)
   171  		assert.Error(t, err, "Session hasn't received any messages")
   172  	})
   173  
   174  	t.Run("Error during unsubscription", func(t *testing.T) {
   175  		session.subscriptions.AddChannel("failure")
   176  
   177  		_, err := node.Unsubscribe(session, &common.Message{Identifier: "failure"})
   178  		assert.Error(t, err)
   179  	})
   180  }
   181  
   182  func TestPerform(t *testing.T) {
   183  	node := NewMockNode()
   184  	session := NewMockSession("14", node)
   185  
   186  	node.hub.AddSession(session)
   187  	defer node.hub.RemoveSession(session)
   188  
   189  	session.subscriptions.AddChannel("test_channel")
   190  
   191  	go node.hub.Run()
   192  	defer node.hub.Shutdown()
   193  
   194  	t.Run("Successful perform", func(t *testing.T) {
   195  		_, err := node.Perform(session, &common.Message{Identifier: "test_channel", Data: "action"})
   196  		assert.Nil(t, err)
   197  
   198  		msg, err := session.conn.Read()
   199  		assert.Nil(t, err)
   200  
   201  		assert.Equalf(t, []byte("action"), msg, "Sent message is invalid: %s", msg)
   202  	})
   203  
   204  	t.Run("With connection state", func(t *testing.T) {
   205  		_, err := node.Perform(session, &common.Message{Identifier: "test_channel", Data: "session"})
   206  		assert.Nil(t, err)
   207  
   208  		_, err = session.conn.Read()
   209  		assert.Nil(t, err)
   210  
   211  		assert.Len(t, *session.env.ConnectionState, 1)
   212  		assert.Equal(t, "performed", (*session.env.ConnectionState)["_s_"])
   213  	})
   214  
   215  	t.Run("Error during perform", func(t *testing.T) {
   216  		session.subscriptions.AddChannel("failure")
   217  
   218  		_, err := node.Perform(session, &common.Message{Identifier: "failure", Data: "test"})
   219  		assert.NotNil(t, err, "Error must not be nil")
   220  	})
   221  
   222  	t.Run("With stopped streams", func(t *testing.T) {
   223  		session.subscriptions.AddChannelStream("test_channel", "stop_stream")
   224  		node.hub.SubscribeSession(session, "stop_stream", "test_channel")
   225  
   226  		node.hub.BroadcastMessage(&common.StreamMessage{Stream: "stop_stream", Data: "40"})
   227  
   228  		msg, _ := session.conn.Read()
   229  		assert.NotNil(t, msg)
   230  
   231  		_, err := node.Perform(session, &common.Message{Identifier: "test_channel", Data: "stop_stream"})
   232  		assert.Nil(t, err)
   233  
   234  		assert.Empty(t, session.subscriptions.StreamsFor("test_channel"))
   235  
   236  		_, err = node.Perform(session, &common.Message{Identifier: "test_channel", Data: "stop_stream"})
   237  		assert.Nil(t, err)
   238  
   239  		node.hub.BroadcastMessage(&common.StreamMessage{Stream: "stop_stream", Data: "41"})
   240  
   241  		msg, err = session.conn.Read()
   242  		assert.Nil(t, msg)
   243  		assert.Error(t, err, "Session hasn't received any messages")
   244  	})
   245  
   246  	t.Run("With channel state", func(t *testing.T) {
   247  		assert.Len(t, *session.env.ChannelStates, 0)
   248  
   249  		_, err := node.Perform(session, &common.Message{Identifier: "test_channel", Data: "channel_state"})
   250  		assert.Nil(t, err)
   251  
   252  		_, err = session.conn.Read()
   253  		assert.Nil(t, err)
   254  
   255  		assert.Len(t, *session.env.ChannelStates, 1)
   256  		assert.Len(t, (*session.env.ChannelStates)["test_channel"], 1)
   257  		assert.Equal(t, "performed", (*session.env.ChannelStates)["test_channel"]["_c_"])
   258  	})
   259  }
   260  
   261  func TestWhisper(t *testing.T) {
   262  	node := NewMockNode()
   263  	session := NewMockSession("14", node)
   264  	session2 := NewMockSession("15", node)
   265  
   266  	// Subscribe using different identifiers to make sure whisper is working
   267  	// per stream name, not per identifier
   268  	defer subscribeSessionToStream(session, node, "test_channel", "test_whisper")()
   269  	defer subscribeSessionToStream(session2, node, "test_channel_2", "test_whisper")()
   270  
   271  	go node.hub.Run()
   272  	defer node.hub.Shutdown()
   273  
   274  	t.Run("When whispering stream is configured for sending subscription", func(t *testing.T) {
   275  		session.env.MergeChannelState("test_channel", &map[string]string{common.WHISPER_STREAM_STATE: "test_whisper"})
   276  
   277  		err := node.Whisper(session, &common.Message{Identifier: "test_channel", Data: "tshh... it's a secret"})
   278  		assert.Nil(t, err)
   279  
   280  		expected := `{"identifier":"test_channel_2","message":"tshh... it's a secret"}`
   281  
   282  		msg, err := session2.conn.Read()
   283  		assert.NoError(t, err)
   284  		assert.Equal(t, expected, string(msg))
   285  
   286  		// Sender do not receive the message
   287  		msg, err = session.conn.Read()
   288  		assert.Nil(t, msg)
   289  		assert.Error(t, err, "Session hasn't received any messages")
   290  	})
   291  
   292  	t.Run("When whispering stream is not configured", func(t *testing.T) {
   293  		session.env.RemoveChannelState("test_channel")
   294  
   295  		err := node.Whisper(session, &common.Message{Identifier: "test_channel", Data: "tshh... it's a secret"})
   296  		assert.Nil(t, err)
   297  
   298  		msg, err := session2.conn.Read()
   299  		assert.Error(t, err)
   300  		assert.Nil(t, msg)
   301  
   302  		// Sender do not receive the message
   303  		msg, err = session.conn.Read()
   304  		assert.Nil(t, msg)
   305  		assert.Error(t, err, "Session hasn't received any messages")
   306  	})
   307  }
   308  
   309  func TestStreamSubscriptionRaceConditions(t *testing.T) {
   310  	node := NewMockNode()
   311  	session := NewMockSession("14", node)
   312  
   313  	node.hub.AddSession(session)
   314  	session.subscriptions.AddChannel("test_channel")
   315  
   316  	// We need a real hub here to catch a race condition
   317  	go node.hub.Run()
   318  	defer node.hub.Shutdown()
   319  
   320  	t.Run("stop and start streams race conditions", func(t *testing.T) {
   321  		_, err := node.Perform(session, &common.Message{Identifier: "test_channel", Data: "stop_and_start_streams"})
   322  		assert.Nil(t, err)
   323  
   324  		// Make sure session is subscribed to the stream
   325  		node.hub.Broadcast("all", "2022")
   326  
   327  		msg, err := session.conn.Read()
   328  		require.NoError(t, err)
   329  
   330  		assert.Equalf(t, "{\"identifier\":\"test_channel\",\"message\":2022}", string(msg), "Broadcasted message is invalid: %s", msg)
   331  	})
   332  }
   333  
   334  func TestDisconnect(t *testing.T) {
   335  	node := NewMockNode()
   336  	go node.hub.Run()
   337  	defer node.hub.Shutdown()
   338  
   339  	t.Run("Disconnectable session", func(t *testing.T) {
   340  		session := NewMockSessionWithEnv("1", node, "/cable", &map[string]string{"id": "test_id"})
   341  		// Authenticate via controller marks session as disconnectable automatically
   342  		_, err := node.Authenticate(session)
   343  		require.NoError(t, err)
   344  
   345  		assert.True(t, session.IsDisconnectable())
   346  
   347  		assert.Nil(t, node.Disconnect(session))
   348  
   349  		assert.Equal(t, 1, node.disconnector.Size(), "Expected disconnect to have 1 task in a queue")
   350  
   351  		task := <-node.disconnector.(*DisconnectQueue).disconnect
   352  		assert.Equal(t, session, task, "Expected to disconnect session")
   353  	})
   354  
   355  	t.Run("Non-disconnectable session", func(t *testing.T) {
   356  		session := NewMockSessionWithEnv("1", node, "/cable", &map[string]string{"id": "test_id"})
   357  		// Authenticate via controller marks session as disconnectable automatically
   358  		_, err := node.Authenticate(session)
   359  		require.NoError(t, err)
   360  
   361  		session.disconnectInterest = false
   362  
   363  		assert.Nil(t, node.Disconnect(session))
   364  
   365  		assert.Equal(t, 0, node.disconnector.Size(), "Expected disconnect to have 0 tasks in a queue")
   366  	})
   367  }
   368  
   369  func TestHistory(t *testing.T) {
   370  	node := NewMockNode()
   371  
   372  	broker := &mocks.Broker{}
   373  	node.SetBroker(broker)
   374  
   375  	broker.
   376  		On("CommitSession", mock.Anything, mock.Anything).
   377  		Return(nil)
   378  
   379  	go node.hub.Run()
   380  	defer node.hub.Shutdown()
   381  
   382  	session := NewMockSession("14", node)
   383  
   384  	session.subscriptions.AddChannel("test_channel")
   385  	session.subscriptions.AddChannelStream("test_channel", "streamo")
   386  	session.subscriptions.AddChannelStream("test_channel", "emptissimo")
   387  
   388  	stream := []common.StreamMessage{
   389  		{
   390  			Stream: "streamo",
   391  			Data:   "ciao",
   392  			Offset: 22,
   393  			Epoch:  "test",
   394  		},
   395  		{
   396  			Stream: "streamo",
   397  			Data:   "buona sera",
   398  			Offset: 23,
   399  			Epoch:  "test",
   400  		},
   401  	}
   402  
   403  	var ts int64
   404  
   405  	t.Run("Successful history with only Since", func(t *testing.T) {
   406  		ts = 100200
   407  
   408  		broker.
   409  			On("HistorySince", "streamo", ts).
   410  			Return(stream, nil)
   411  		broker.
   412  			On("HistorySince", "emptissimo", ts).
   413  			Return(nil, nil)
   414  
   415  		err := node.History(
   416  			session,
   417  			&common.Message{
   418  				Identifier: "test_channel",
   419  				History: common.HistoryRequest{
   420  					Since: ts,
   421  				},
   422  			},
   423  		)
   424  		require.NoError(t, err)
   425  
   426  		history := []string{
   427  			"{\"identifier\":\"test_channel\",\"message\":\"ciao\",\"stream_id\":\"streamo\",\"epoch\":\"test\",\"offset\":22}",
   428  			"{\"identifier\":\"test_channel\",\"message\":\"buona sera\",\"stream_id\":\"streamo\",\"epoch\":\"test\",\"offset\":23}",
   429  		}
   430  
   431  		for _, msg := range history {
   432  			received, herr := session.conn.Read()
   433  			require.NoError(t, herr)
   434  
   435  			require.Equalf(
   436  				t,
   437  				msg,
   438  				string(received),
   439  				"Sent message is invalid: %s", received,
   440  			)
   441  		}
   442  
   443  		ack, err := session.conn.Read()
   444  		require.NoError(t, err)
   445  
   446  		assert.Equal(t, `{"type":"confirm_history","identifier":"test_channel"}`, string(ack))
   447  
   448  		_, err = session.conn.Read()
   449  		require.Error(t, err)
   450  	})
   451  
   452  	t.Run("Successful history with Since and Offset", func(t *testing.T) {
   453  		ts = 100300
   454  
   455  		broker.
   456  			On("HistoryFrom", "streamo", "test", uint64(20)).
   457  			Return(stream, nil)
   458  		broker.
   459  			On("HistorySince", "emptissimo", ts).
   460  			Return([]common.StreamMessage{{
   461  				Stream: "emptissimo",
   462  				Data:   "zer0",
   463  				Offset: 2,
   464  				Epoch:  "test_zero",
   465  			}}, nil)
   466  
   467  		err := node.History(
   468  			session,
   469  			&common.Message{
   470  				Identifier: "test_channel",
   471  				History: common.HistoryRequest{
   472  					Since: ts,
   473  					Streams: map[string]common.HistoryPosition{
   474  						"streamo": {Epoch: "test", Offset: 20},
   475  					},
   476  				},
   477  			},
   478  		)
   479  		require.NoError(t, err)
   480  
   481  		history := []string{
   482  			"{\"identifier\":\"test_channel\",\"message\":\"ciao\",\"stream_id\":\"streamo\",\"epoch\":\"test\",\"offset\":22}",
   483  			"{\"identifier\":\"test_channel\",\"message\":\"buona sera\",\"stream_id\":\"streamo\",\"epoch\":\"test\",\"offset\":23}",
   484  			"{\"identifier\":\"test_channel\",\"message\":\"zer0\",\"stream_id\":\"emptissimo\",\"epoch\":\"test_zero\",\"offset\":2}",
   485  		}
   486  
   487  		// The order of streams is non-deterministic, so
   488  		// we're collecting messages first and checking for inclusion later
   489  		received := []string{}
   490  
   491  		for range history {
   492  			data, herr := session.conn.Read()
   493  			require.NoError(t, herr)
   494  
   495  			received = append(received, string(data))
   496  		}
   497  
   498  		for _, msg := range history {
   499  			require.Contains(
   500  				t,
   501  				received,
   502  				msg,
   503  			)
   504  		}
   505  
   506  		ack, err := session.conn.Read()
   507  		require.NoError(t, err)
   508  
   509  		assert.Equal(t, `{"type":"confirm_history","identifier":"test_channel"}`, string(ack))
   510  
   511  		_, err = session.conn.Read()
   512  		require.Error(t, err)
   513  	})
   514  
   515  	t.Run("Fetching history with Subscribe", func(t *testing.T) {
   516  		ts = 100400
   517  
   518  		broker.
   519  			On("HistoryFrom", "streamo", "test", uint64(21)).
   520  			Return(stream, nil)
   521  		broker.
   522  			On("HistorySince", "s1", ts).
   523  			Return([]common.StreamMessage{{
   524  				Stream: "s1",
   525  				Data:   "{\"foo\":\"bar\"}",
   526  				Offset: 10,
   527  				Epoch:  "test",
   528  			}}, nil)
   529  		broker.
   530  			On("Subscribe", "stream").
   531  			Return("s1")
   532  
   533  		_, err := node.Subscribe(
   534  			session,
   535  			&common.Message{
   536  				Identifier: "with_stream",
   537  				History: common.HistoryRequest{
   538  					Since: ts,
   539  					Streams: map[string]common.HistoryPosition{
   540  						"streamo": {Epoch: "test", Offset: 21},
   541  					},
   542  				},
   543  			},
   544  		)
   545  		require.NoError(t, err)
   546  
   547  		msg, err := session.conn.Read()
   548  		require.NoError(t, err)
   549  
   550  		require.Equalf(t, "14", string(msg), "Sent message is invalid: %s", msg)
   551  
   552  		history := []string{
   553  			"{\"identifier\":\"with_stream\",\"message\":{\"foo\":\"bar\"},\"stream_id\":\"s1\",\"epoch\":\"test\",\"offset\":10}",
   554  		}
   555  
   556  		for _, msg := range history {
   557  			received, herr := session.conn.Read()
   558  			require.NoError(t, herr)
   559  
   560  			require.Equalf(
   561  				t,
   562  				msg,
   563  				string(received),
   564  				"Sent message is invalid: %s", received,
   565  			)
   566  		}
   567  
   568  		ack, err := session.conn.Read()
   569  		require.NoError(t, err)
   570  
   571  		assert.Equal(t, `{"type":"confirm_history","identifier":"with_stream"}`, string(ack))
   572  
   573  		_, err = session.conn.Read()
   574  		require.Error(t, err)
   575  	})
   576  
   577  	t.Run("Error retrieving history", func(t *testing.T) {
   578  		ts = 200100
   579  
   580  		broker.
   581  			On("HistorySince", "streamo", ts).
   582  			Return(nil, errors.New("Couldn't restore history"))
   583  		broker.
   584  			On("HistorySince", "emptissimo", ts).
   585  			Return(stream, nil)
   586  
   587  		err := node.History(
   588  			session,
   589  			&common.Message{
   590  				Identifier: "test_channel",
   591  				History: common.HistoryRequest{
   592  					Since: ts,
   593  				},
   594  			},
   595  		)
   596  
   597  		assert.Error(t, err, "Couldn't restore history")
   598  
   599  		ack, err := session.conn.Read()
   600  		require.NoError(t, err)
   601  
   602  		assert.Equal(t, `{"type":"reject_history","identifier":"test_channel"}`, string(ack))
   603  	})
   604  }
   605  
   606  func TestRestoreSession(t *testing.T) {
   607  	node := NewMockNode()
   608  
   609  	broker := &mocks.Broker{}
   610  	node.SetBroker(broker)
   611  
   612  	go node.hub.Run()
   613  	defer node.hub.Shutdown()
   614  
   615  	prev_session := NewMockSession("114", node, WithResumable(true))
   616  	prev_session.subscriptions.AddChannel("fruits_channel")
   617  	prev_session.subscriptions.AddChannelStream("fruits_channel", "arancia")
   618  	prev_session.subscriptions.AddChannelStream("fruits_channel", "limoni")
   619  	prev_session.env.MergeConnectionState(&(map[string]string{"tenant_id": "71"}))
   620  	prev_session.env.MergeChannelState("fruits_channel", &(map[string]string{"gardini": "ischia"}))
   621  
   622  	cached, err := prev_session.ToCacheEntry()
   623  	require.NoError(t, err)
   624  
   625  	broker.
   626  		On("RestoreSession", "114").
   627  		Return(cached, nil)
   628  	broker.
   629  		On("CommitSession", mock.Anything, mock.Anything).
   630  		Return(nil)
   631  	broker.
   632  		On("Subscribe", mock.Anything).
   633  		Return(func(name string) string { return name })
   634  
   635  	session := NewMockSession("214", node, WithResumable(true), WithPrevSID("114"))
   636  
   637  	t.Run("Successful restore via header", func(t *testing.T) {
   638  		res, err := node.Authenticate(session)
   639  		require.NoError(t, err)
   640  		assert.Equal(t, common.SUCCESS, res.Status)
   641  
   642  		assert.Contains(t, session.subscriptions.StreamsFor("fruits_channel"), "arancia")
   643  		assert.Contains(t, session.subscriptions.StreamsFor("fruits_channel"), "limoni")
   644  
   645  		assert.Equal(t, "71", session.env.GetConnectionStateField("tenant_id"))
   646  		assert.Equal(t, "ischia", session.env.GetChannelStateField("fruits_channel", "gardini"))
   647  
   648  		welcome, err := session.conn.Read()
   649  		require.NoError(t, err)
   650  
   651  		require.Equalf(
   652  			t,
   653  			`{"type":"welcome","sid":"214","restored":true,"restored_ids":["fruits_channel"]}`,
   654  			string(welcome),
   655  			"Sent message is invalid: %s", welcome,
   656  		)
   657  
   658  		node.hub.Broadcast("arancia", "delissimo")
   659  
   660  		msg, err := session.conn.Read()
   661  		require.NoError(t, err)
   662  
   663  		require.Equalf(
   664  			t,
   665  			`{"identifier":"fruits_channel","message":"delissimo"}`,
   666  			string(msg),
   667  			"Sent message is invalid: %s", msg,
   668  		)
   669  
   670  		node.hub.Broadcast("limoni", "acido")
   671  
   672  		msg, err = session.conn.Read()
   673  		require.NoError(t, err)
   674  
   675  		require.Equalf(
   676  			t,
   677  			`{"identifier":"fruits_channel","message":"acido"}`,
   678  			string(msg),
   679  			"Sent message is invalid: %s", msg,
   680  		)
   681  	})
   682  
   683  	t.Run("Failed to restore", func(t *testing.T) {
   684  		broker.
   685  			On("RestoreSession", "114").
   686  			Return(nil, nil)
   687  
   688  		session = NewMockSession("154", node)
   689  
   690  		res, err := node.Authenticate(session)
   691  		require.NoError(t, err)
   692  		assert.Equal(t, common.SUCCESS, res.Status)
   693  
   694  		welcome, err := session.conn.Read()
   695  		require.NoError(t, err)
   696  
   697  		require.Equalf(
   698  			t,
   699  			"welcome",
   700  			string(welcome),
   701  			"Sent message is invalid: %s", welcome,
   702  		)
   703  	})
   704  }
   705  
   706  func TestBroadcasting(t *testing.T) {
   707  	node := NewMockNode()
   708  
   709  	go node.hub.Run()
   710  	defer node.hub.Shutdown()
   711  
   712  	session := NewMockSession("14", node)
   713  	session2 := NewMockSession("15", node)
   714  
   715  	node.hub.AddSession(session)
   716  	node.hub.SubscribeSession(session, "test", "test_channel")
   717  	node.hub.SubscribeSession(session, "staind_2023", "music_channel")
   718  
   719  	node.hub.AddSession(session2)
   720  	node.hub.SubscribeSession(session2, "test", "test_channel")
   721  	node.hub.SubscribeSession(session2, "staind_2023", "music_channel")
   722  
   723  	node.broker.Subscribe("test")
   724  	node.broker.Subscribe("staind_2023")
   725  
   726  	t.Run("HandlePubSub", func(t *testing.T) {
   727  		node.HandlePubSub([]byte(`{"stream":"test","data":"\"abc123\""}`))
   728  
   729  		expected := `{"identifier":"test_channel","message":"abc123"}`
   730  
   731  		msg, err := session.conn.Read()
   732  		assert.Nil(t, err)
   733  		assert.Equalf(t, expected, string(msg), "Expected to receive %s but got %s", expected, string(msg))
   734  
   735  		msg2, err := session2.conn.Read()
   736  		assert.Nil(t, err)
   737  		assert.Equalf(t, expected, string(msg2), "Expected to receive %s but got %s", expected, string(msg2))
   738  	})
   739  
   740  	t.Run("HandlePubSub_Batch", func(t *testing.T) {
   741  		node.HandlePubSub([]byte(`[{"stream":"test","data":"\"follow me\""},{"stream":"untest","data":"\"missing\""},{"stream":"staind_2023","data":"{\"num\":7,\"title\":\"The Fray\"}"}]`))
   742  
   743  		first := `{"identifier":"test_channel","message":"follow me"}`
   744  		second := `{"identifier":"music_channel","message":{"num":7,"title":"The Fray"}}`
   745  
   746  		msgs, err := readMessages(session.conn, 2)
   747  		assert.Nil(t, err)
   748  		assert.Contains(t, msgs, first)
   749  		assert.Contains(t, msgs, second)
   750  
   751  		msgs2, err := readMessages(session2.conn, 2)
   752  		assert.Nil(t, err)
   753  		assert.Contains(t, msgs2, first)
   754  		assert.Contains(t, msgs2, second)
   755  	})
   756  
   757  	t.Run("HandleBroadcast", func(t *testing.T) {
   758  		node.HandleBroadcast([]byte(`{"stream":"staind_2023","data":"{\"num\":5,\"title\":\"Out of time\"}"}`))
   759  
   760  		expected := `{"identifier":"music_channel","message":{"num":5,"title":"Out of time"}}`
   761  
   762  		msg, err := session.conn.Read()
   763  		assert.Nil(t, err)
   764  		assert.Equalf(t, expected, string(msg), "Expected to receive %s but got %s", expected, string(msg))
   765  
   766  		msg2, err := session2.conn.Read()
   767  		assert.Nil(t, err)
   768  		assert.Equalf(t, expected, string(msg2), "Expected to receive %s but got %s", expected, string(msg2))
   769  	})
   770  
   771  	t.Run("HandleBroadcast_Batch", func(t *testing.T) {
   772  		node.HandleBroadcast([]byte(`[{"stream":"staind_2023","data":"{\"num\":9,\"title\":\"Hate me too\"}"},{"stream":"untest","data":"\"missing\""},{"stream":"staind_2023","data":"{\"num\":10,\"title\":\"Confessions of Fallen\"}"}]`))
   773  
   774  		first := `{"identifier":"music_channel","message":{"num":9,"title":"Hate me too"}}`
   775  		second := `{"identifier":"music_channel","message":{"num":10,"title":"Confessions of Fallen"}}`
   776  
   777  		msgs, err := readMessages(session.conn, 2)
   778  		assert.Nil(t, err)
   779  		assert.Contains(t, msgs, first)
   780  		assert.Contains(t, msgs, second)
   781  
   782  		msgs2, err := readMessages(session2.conn, 2)
   783  		assert.Nil(t, err)
   784  		assert.Contains(t, msgs2, first)
   785  		assert.Contains(t, msgs2, second)
   786  	})
   787  }
   788  
   789  func TestHandlePubSubWithCommand(t *testing.T) {
   790  	node := NewMockNode()
   791  
   792  	go node.hub.Run()
   793  	defer node.hub.Shutdown()
   794  
   795  	session := NewMockSession("14", node)
   796  	node.hub.AddSession(session)
   797  
   798  	node.HandlePubSub([]byte("{\"command\":\"disconnect\",\"payload\":{\"identifier\":\"14\",\"reconnect\":false}}"))
   799  
   800  	expected := string(toJSON(common.NewDisconnectMessage("remote", false)))
   801  
   802  	msg, err := session.conn.Read()
   803  	assert.Nil(t, err)
   804  	assert.Equalf(t, expected, string(msg), "Expected to receive %s but got %s", expected, string(msg))
   805  	assert.True(t, session.closed)
   806  }
   807  
   808  func TestLookupSession(t *testing.T) {
   809  	node := NewMockNode()
   810  
   811  	go node.hub.Run()
   812  	defer node.hub.Shutdown()
   813  
   814  	assert.Nil(t, node.LookupSession("{\"foo\":\"bar\"}"))
   815  
   816  	session := NewMockSession("14", node)
   817  	session.SetIdentifiers("{\"foo\":\"bar\"}")
   818  	node.hub.AddSession(session)
   819  
   820  	assert.Equal(t, session, node.LookupSession("{\"foo\":\"bar\"}"))
   821  }
   822  
   823  func toJSON(msg encoders.EncodedMessage) []byte {
   824  	b, err := json.Marshal(&msg)
   825  	if err != nil {
   826  		panic("Failed to build JSON 😲")
   827  	}
   828  
   829  	return b
   830  }
   831  
   832  func subscribeSessionToStream(s *Session, n *Node, identifier string, stream string) func() {
   833  	n.hub.AddSession(s)
   834  
   835  	s.subscriptions.AddChannel(identifier)
   836  	s.subscriptions.AddChannelStream(identifier, stream)
   837  	n.hub.SubscribeSession(s, stream, identifier)
   838  	n.broker.Subscribe(stream)
   839  
   840  	return func() {
   841  		n.hub.RemoveSession(s)
   842  	}
   843  }
   844  
   845  func readMessages(conn Connection, count int) ([]string, error) {
   846  	var messages []string
   847  
   848  	for i := 0; i < count; i++ {
   849  		msg, err := conn.Read()
   850  		if err != nil {
   851  			return nil, err
   852  		}
   853  
   854  		messages = append(messages, string(msg))
   855  	}
   856  
   857  	return messages, nil
   858  }