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 }