github.com/philippseith/signalr@v0.6.3/hubcontext_test.go (about)

     1  package signalr
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  
    13  	. "github.com/onsi/ginkgo"
    14  	. "github.com/onsi/gomega"
    15  )
    16  
    17  type contextHub struct {
    18  	Hub
    19  }
    20  
    21  func (c *contextHub) OnConnected(string) {
    22  }
    23  
    24  func (c *contextHub) CallAll() {
    25  	c.Clients().All().Send("clientFunc")
    26  }
    27  
    28  func (c *contextHub) CallCaller() {
    29  	c.Clients().Caller().Send("clientFunc")
    30  }
    31  
    32  func (c *contextHub) CallClient(connectionID string) {
    33  	c.Clients().Client(connectionID).Send("clientFunc")
    34  }
    35  
    36  func (c *contextHub) BuildGroup(connectionID1 string, connectionID2 string) {
    37  	c.Groups().AddToGroup("local", connectionID1)
    38  	c.Groups().AddToGroup("local", connectionID2)
    39  }
    40  
    41  func (c *contextHub) RemoveFromGroup(connectionID string) {
    42  	c.Groups().RemoveFromGroup("local", connectionID)
    43  }
    44  
    45  func (c *contextHub) CallGroup() {
    46  	c.Clients().Group("local").Send("clientFunc")
    47  }
    48  
    49  func (c *contextHub) AddItem(key string, value interface{}) {
    50  	c.Items().Store(key, value)
    51  }
    52  
    53  func (c *contextHub) GetItem(key string) interface{} {
    54  	if item, ok := c.Items().Load(key); ok {
    55  		return item
    56  	}
    57  	return nil
    58  }
    59  
    60  func (c *contextHub) TestConnectionID() {
    61  }
    62  
    63  func (c *contextHub) Abort() {
    64  	c.Hub.Abort()
    65  }
    66  
    67  type SimpleReceiver struct {
    68  	ch chan struct{}
    69  }
    70  
    71  func (sr *SimpleReceiver) ClientFunc() {
    72  	close(sr.ch)
    73  }
    74  
    75  func makeTCPServerAndClients(ctx context.Context, clientCount int) (Server, []Client, []*SimpleReceiver, []Connection, []Connection, error) {
    76  	server, err := NewServer(ctx, SimpleHubFactory(&contextHub{}), testLoggerOption())
    77  	if err != nil {
    78  		return nil, nil, nil, nil, nil, err
    79  	}
    80  	cliConn := make([]Connection, clientCount)
    81  	srvConn := make([]Connection, clientCount)
    82  	receiver := make([]*SimpleReceiver, clientCount)
    83  	client := make([]Client, clientCount)
    84  	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
    85  	if err != nil {
    86  		return nil, nil, nil, nil, nil, err
    87  	}
    88  	for i := 0; i < clientCount; i++ {
    89  		listener, err := net.ListenTCP("tcp", addr)
    90  		go func(i int) {
    91  			for {
    92  				tcpConn, _ := listener.Accept()
    93  				conn := NewNetConnection(ctx, tcpConn)
    94  				conn.SetConnectionID(fmt.Sprint(i))
    95  				srvConn[i] = conn
    96  				go func() { _ = server.Serve(conn) }()
    97  				break
    98  			}
    99  		}(i)
   100  		tcpConn, err := net.Dial("tcp",
   101  			fmt.Sprintf("localhost:%v", listener.Addr().(*net.TCPAddr).Port))
   102  		if err != nil {
   103  			return nil, nil, nil, nil, nil, err
   104  		}
   105  		cliConn[i] = NewNetConnection(ctx, tcpConn)
   106  		receiver[i] = &SimpleReceiver{ch: make(chan struct{})}
   107  		client[i], err = NewClient(ctx, WithConnection(cliConn[i]), WithReceiver(receiver[i]), TransferFormat("Text"), testLoggerOption())
   108  		if err != nil {
   109  			return nil, nil, nil, nil, nil, err
   110  		}
   111  		client[i].Start()
   112  		select {
   113  		case err := <-client[i].WaitForState(ctx, ClientConnected):
   114  			if err != nil {
   115  				return nil, nil, nil, nil, nil, err
   116  			}
   117  		case <-ctx.Done():
   118  			return nil, nil, nil, nil, nil, ctx.Err()
   119  		}
   120  	}
   121  	return server, client, receiver, srvConn, cliConn, nil
   122  }
   123  
   124  func makePipeClientsAndReceivers() ([]Client, []*SimpleReceiver, context.CancelFunc) {
   125  	cliConn := make([]*pipeConnection, 3)
   126  	srvConn := make([]*pipeConnection, 3)
   127  	for i := 0; i < 3; i++ {
   128  		cliConn[i], srvConn[i] = newClientServerConnections()
   129  		cliConn[i].SetConnectionID(fmt.Sprint(i))
   130  		srvConn[i].SetConnectionID(fmt.Sprint(i))
   131  	}
   132  	ctx, cancel := context.WithCancel(context.Background())
   133  	server, _ := NewServer(ctx, SimpleHubFactory(&contextHub{}), testLoggerOption())
   134  	var wg sync.WaitGroup
   135  	wg.Add(3)
   136  	for i := 0; i < 3; i++ {
   137  		go func(i int) {
   138  			wg.Done()
   139  			_ = server.Serve(srvConn[i])
   140  		}(i)
   141  	}
   142  	wg.Wait()
   143  	client := make([]Client, 3)
   144  	receiver := make([]*SimpleReceiver, 3)
   145  	for i := 0; i < 3; i++ {
   146  		receiver[i] = &SimpleReceiver{ch: make(chan struct{})}
   147  		client[i], _ = NewClient(ctx, WithConnection(cliConn[i]), WithReceiver(receiver[i]), testLoggerOption())
   148  		client[i].Start()
   149  		<-client[i].WaitForState(ctx, ClientConnected)
   150  	}
   151  	return client, receiver, cancel
   152  }
   153  
   154  var _ = Describe("HubContext", func() {
   155  	for i := 0; i < 10; i++ {
   156  		Context("Clients().All()", func() {
   157  			It("should invoke all clients", func() {
   158  				client, receiver, cancel := makePipeClientsAndReceivers()
   159  				r := <-client[0].Invoke("CallAll")
   160  				Expect(r.Error).NotTo(HaveOccurred())
   161  				result := 0
   162  				for result < 3 {
   163  					select {
   164  					case <-receiver[0].ch:
   165  						result++
   166  					case <-receiver[1].ch:
   167  						result++
   168  					case <-receiver[2].ch:
   169  						result++
   170  					case <-time.After(2 * time.Second):
   171  						Fail("timeout waiting for clients getting results")
   172  					}
   173  				}
   174  				cancel()
   175  			})
   176  		})
   177  		Context("Clients().Caller()", func() {
   178  			It("should invoke only the caller", func() {
   179  				client, receiver, cancel := makePipeClientsAndReceivers()
   180  				r := <-client[0].Invoke("CallCaller")
   181  				Expect(r.Error).NotTo(HaveOccurred())
   182  				select {
   183  				case <-receiver[0].ch:
   184  				case <-receiver[1].ch:
   185  					Fail("Wrong client received message")
   186  				case <-receiver[2].ch:
   187  					Fail("Wrong client received message")
   188  				case <-time.After(2 * time.Second):
   189  					Fail("timeout waiting for clients getting results")
   190  				}
   191  				cancel()
   192  			})
   193  		})
   194  		Context("Clients().Client()", func() {
   195  			It("should invoke only the client which was addressed", func() {
   196  				client, receiver, cancel := makePipeClientsAndReceivers()
   197  				r := <-client[0].Invoke("CallClient", "1")
   198  				Expect(r.Error).NotTo(HaveOccurred())
   199  				select {
   200  				case <-receiver[0].ch:
   201  					Fail("Wrong client received message")
   202  				case <-receiver[1].ch:
   203  				case <-receiver[2].ch:
   204  					Fail("Wrong client received message")
   205  				case <-time.After(2 * time.Second):
   206  					Fail("timeout waiting for clients getting results")
   207  				}
   208  				cancel()
   209  			})
   210  		})
   211  	}
   212  })
   213  
   214  func TestGroupShouldInvokeOnlyTheClientsInTheGroup(t *testing.T) {
   215  	ctx, cancel := context.WithCancel(context.Background())
   216  	defer cancel()
   217  	_, client, receiver, srvConn, _, err := makeTCPServerAndClients(ctx, 3)
   218  	assert.NoError(t, err)
   219  	select {
   220  	case ir := <-client[0].Invoke("buildgroup", srvConn[1].ConnectionID(), srvConn[2].ConnectionID()):
   221  		assert.NoError(t, ir.Error)
   222  	case <-time.After(100 * time.Millisecond):
   223  		assert.Fail(t, "timeout in invoke")
   224  	}
   225  	select {
   226  	case ir := <-client[0].Invoke("callgroup"):
   227  		assert.NoError(t, ir.Error)
   228  	case <-time.After(100 * time.Millisecond):
   229  		assert.Fail(t, "timeout in invoke")
   230  	}
   231  	gotCalled := 0
   232  	select {
   233  	case <-receiver[0].ch:
   234  		assert.Fail(t, "client 1 received message for client 2, 3")
   235  	case <-receiver[1].ch:
   236  		gotCalled++
   237  	case <-receiver[2].ch:
   238  		gotCalled++
   239  	case <-time.After(100 * time.Millisecond):
   240  		if gotCalled < 2 {
   241  			assert.Fail(t, "timeout without client 2 and 3 got called")
   242  		}
   243  	}
   244  }
   245  
   246  func TestRemoveClientsShouldRemoveClientsFromTheGroup(t *testing.T) {
   247  	ctx, cancel := context.WithCancel(context.Background())
   248  	defer cancel()
   249  	_, client, receiver, srvConn, _, err := makeTCPServerAndClients(ctx, 3)
   250  	assert.NoError(t, err)
   251  	select {
   252  	case ir := <-client[0].Invoke("buildgroup", srvConn[1].ConnectionID(), srvConn[2].ConnectionID()):
   253  		assert.NoError(t, ir.Error)
   254  	case <-time.After(100 * time.Millisecond):
   255  		assert.Fail(t, "timeout in invoke")
   256  	}
   257  	select {
   258  	case ir := <-client[0].Invoke("removefromgroup", srvConn[2].ConnectionID()):
   259  		assert.NoError(t, ir.Error)
   260  	case <-time.After(100 * time.Millisecond):
   261  		assert.Fail(t, "timeout in invoke")
   262  	}
   263  	select {
   264  	case ir := <-client[0].Invoke("callgroup"):
   265  		assert.NoError(t, ir.Error)
   266  	case <-time.After(100 * time.Millisecond):
   267  		assert.Fail(t, "timeout in invoke")
   268  	}
   269  	gotCalled := false
   270  	select {
   271  	case <-receiver[0].ch:
   272  		assert.Fail(t, "client 1 received message for client 2")
   273  	case <-receiver[1].ch:
   274  		gotCalled = true
   275  	case <-receiver[2].ch:
   276  		assert.Fail(t, "client 3 received message for client 2")
   277  	case <-time.After(100 * time.Millisecond):
   278  		if !gotCalled {
   279  			assert.Fail(t, "timeout without client 3 got called")
   280  		}
   281  	}
   282  }
   283  
   284  func TestItemsShouldHoldItemsConnectionWise(t *testing.T) {
   285  	ctx, cancel := context.WithCancel(context.Background())
   286  	defer cancel()
   287  	_, client, _, _, _, err := makeTCPServerAndClients(ctx, 2)
   288  	assert.NoError(t, err)
   289  	select {
   290  	case ir := <-client[0].Invoke("additem", "first", 1):
   291  		assert.NoError(t, ir.Error)
   292  	case <-time.After(100 * time.Millisecond):
   293  		assert.Fail(t, "timeout in invoke")
   294  	}
   295  	select {
   296  	case ir := <-client[0].Invoke("getitem", "first"):
   297  		assert.NoError(t, ir.Error)
   298  		assert.Equal(t, ir.Value, 1.0)
   299  	case <-time.After(100 * time.Millisecond):
   300  		assert.Fail(t, "timeout in invoke")
   301  	}
   302  	select {
   303  	case ir := <-client[1].Invoke("getitem", "first"):
   304  		assert.NoError(t, ir.Error)
   305  		assert.Equal(t, ir.Value, nil)
   306  	case <-time.After(100 * time.Millisecond):
   307  		assert.Fail(t, "timeout in invoke")
   308  	}
   309  }
   310  
   311  func TestAbortShouldAbortTheConnectionOfTheCurrentCaller(t *testing.T) {
   312  	ctx, cancel := context.WithCancel(context.Background())
   313  	defer cancel()
   314  	_, client, _, _, _, err := makeTCPServerAndClients(ctx, 2)
   315  	assert.NoError(t, err)
   316  	select {
   317  	case ir := <-client[0].Invoke("abort"):
   318  		assert.Error(t, ir.Error)
   319  		select {
   320  		case err := <-client[0].WaitForState(ctx, ClientClosed):
   321  			assert.NoError(t, err)
   322  		case <-time.After(500 * time.Millisecond):
   323  			assert.Fail(t, "timeout waiting for client close")
   324  		}
   325  	case <-time.After(100 * time.Millisecond):
   326  		assert.Fail(t, "timeout in invoke")
   327  	}
   328  	select {
   329  	case ir := <-client[1].Invoke("additem", "first", 2):
   330  		assert.NoError(t, ir.Error)
   331  	case <-time.After(100 * time.Millisecond):
   332  		assert.Fail(t, "timeout in invoke")
   333  	}
   334  	select {
   335  	case ir := <-client[1].Invoke("getitem", "first"):
   336  		assert.NoError(t, ir.Error)
   337  		assert.Equal(t, ir.Value, 2.0)
   338  	case <-time.After(100 * time.Millisecond):
   339  		assert.Fail(t, "timeout in invoke")
   340  	}
   341  }