github.com/philippseith/signalr@v0.6.3/serveroptions_test.go (about) 1 package signalr 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "strings" 8 "time" 9 10 "github.com/go-kit/log" 11 "github.com/google/uuid" 12 . "github.com/onsi/ginkgo" 13 . "github.com/onsi/gomega" 14 ) 15 16 type singleHub struct { 17 Hub 18 id string 19 } 20 21 func (s *singleHub) OnConnected(string) { 22 s.initUUID() 23 singleHubMsg <- s.id 24 } 25 26 func (s *singleHub) GetUUID() string { 27 s.initUUID() 28 singleHubMsg <- s.id 29 return s.id 30 } 31 32 func (s *singleHub) initUUID() { 33 if s.id == "" { 34 s.id = uuid.New().String() 35 } 36 } 37 38 var singleHubMsg = make(chan string, 100) 39 40 var _ = Describe("Server options", func() { 41 42 Describe("UseHub option", func() { 43 Context("When the UseHub option is used", func() { 44 It("should use the same hub instance on all invocations", func(done Done) { 45 server, err := NewServer(context.TODO(), UseHub(&singleHub{})) 46 Expect(server).NotTo(BeNil()) 47 Expect(err).To(BeNil()) 48 conn1 := newTestingConnectionForServer() 49 Expect(conn1).NotTo(BeNil()) 50 go func() { _ = server.Serve(conn1) }() 51 <-singleHubMsg 52 conn2 := newTestingConnectionForServer() 53 Expect(conn2).NotTo(BeNil()) 54 go func() { _ = server.Serve(conn2) }() 55 <-singleHubMsg 56 // Call GetId twice. If different instances used the results are different 57 var uuid string 58 conn1.ClientSend(`{"type":1,"invocationId": "123","target":"getuuid"}`) 59 <-singleHubMsg 60 select { 61 case message := <-conn1.received: 62 Expect(message).To(BeAssignableToTypeOf(completionMessage{})) 63 uuid = fmt.Sprint(message.(completionMessage).Result) 64 case <-time.After(1000 * time.Millisecond): 65 Fail("timed out") 66 } 67 conn1.ClientSend(`{"type":1,"invocationId": "456","target":"getuuid"}`) 68 <-singleHubMsg 69 select { 70 case message := <-conn1.received: 71 Expect(message).To(BeAssignableToTypeOf(completionMessage{})) 72 Expect(fmt.Sprint(message.(completionMessage).Result)).To(Equal(uuid)) 73 case <-time.After(1000 * time.Millisecond): 74 Fail("timed out") 75 } 76 // Even on another connection, the uuid should be the same 77 conn2.ClientSend(`{"type":1,"invocationId": "456","target":"getuuid"}`) 78 <-singleHubMsg 79 select { 80 case message := <-conn2.received: 81 Expect(message).To(BeAssignableToTypeOf(completionMessage{})) 82 Expect(fmt.Sprint(message.(completionMessage).Result)).To(Equal(uuid)) 83 case <-time.After(1000 * time.Millisecond): 84 Fail("timed out") 85 } 86 close(done) 87 }, 3.0) 88 }) 89 Context("When UseHub is used on a client", func() { 90 It("should return an error", func(done Done) { 91 _, err := NewClient(context.TODO(), WithConnection(newTestingConnection()), testLoggerOption(), UseHub(&singleHub{})) 92 Expect(err).To(HaveOccurred()) 93 close(done) 94 }) 95 }) 96 }) 97 98 Describe("SimpleHubFactory option", func() { 99 Context("When the SimpleHubFactory option is used", func() { 100 It("should call the hub factory on each hub method invocation", func(done Done) { 101 server, err := NewServer(context.TODO(), SimpleHubFactory(&singleHub{}), testLoggerOption()) 102 Expect(server).NotTo(BeNil()) 103 Expect(err).To(BeNil()) 104 conn := newTestingConnectionForServer() 105 Expect(conn).NotTo(BeNil()) 106 go func() { _ = server.Serve(conn) }() 107 uuids := make(map[string]interface{}) 108 uuid := <-singleHubMsg 109 if _, ok := uuids[uuid]; ok { 110 Fail("same hub called twice") 111 } else { 112 uuids[uuid] = nil 113 } 114 conn.ClientSend(`{"type":1,"invocationId": "123","target":"getuuid"}`) 115 select { 116 case uuid = <-singleHubMsg: 117 if _, ok := uuids[uuid]; ok { 118 Fail("same hub called twice") 119 } else { 120 uuids[uuid] = nil 121 } 122 case <-time.After(1000 * time.Millisecond): 123 Fail("timed out") 124 } 125 conn.ClientSend(`{"type":1,"invocationId": "456","target":"getuuid"}`) 126 select { 127 case uuid = <-singleHubMsg: 128 if _, ok := uuids[uuid]; ok { 129 Fail("same hub called twice") 130 } else { 131 uuids[uuid] = nil 132 } 133 case <-time.After(1000 * time.Millisecond): 134 Fail("timed out") 135 } 136 close(done) 137 }, 2.0) 138 }) 139 Context("When SimpleHubFactory is used on a client", func() { 140 It("should return an error", func(done Done) { 141 _, err := NewClient(context.TODO(), WithConnection(newTestingConnection()), testLoggerOption(), SimpleHubFactory(&singleHub{})) 142 Expect(err).To(HaveOccurred()) 143 close(done) 144 }) 145 }) 146 }) 147 148 Describe("Logger option", func() { 149 Context("When the Logger option with debug false is used", func() { 150 It("calling a method correctly should log no events", func(done Done) { 151 cw := newChannelWriter() 152 server, err := NewServer(context.TODO(), UseHub(&invocationHub{}), Logger(log.NewLogfmtLogger(cw), false)) 153 Expect(server).NotTo(BeNil()) 154 Expect(err).To(BeNil()) 155 conn := newTestingConnectionForServer() 156 Expect(conn).NotTo(BeNil()) 157 go func() { _ = server.Serve(conn) }() 158 conn.ClientSend(`{"type":1,"invocationId": "123","target":"simple"}`) 159 <-invocationQueue 160 select { 161 case logEntry := <-cw.Chan(): 162 Fail(fmt.Sprintf("log entry written: %v", string(logEntry))) 163 case <-time.After(1000 * time.Millisecond): 164 break 165 } 166 close(done) 167 }, 2.0) 168 }) 169 Context("When the Logger option with debug true is used", func() { 170 It("calling a method correctly should log events", func(done Done) { 171 cw := newChannelWriter() 172 server, err := NewServer(context.TODO(), UseHub(&invocationHub{}), Logger(log.NewLogfmtLogger(cw), true)) 173 Expect(server).NotTo(BeNil()) 174 Expect(err).To(BeNil()) 175 conn := newTestingConnectionForServer() 176 Expect(conn).NotTo(BeNil()) 177 go func() { _ = server.Serve(conn) }() 178 conn.ClientSend(`{"type":1,"invocationId": "123","target":"simple"}`) 179 <-invocationQueue 180 select { 181 case <-cw.Chan(): 182 break 183 case <-time.After(1000 * time.Millisecond): 184 Fail("timed out") 185 } 186 close(done) 187 }, 2.0) 188 }) 189 Context("When the Logger option with debug false is used", func() { 190 It("calling a method incorrectly should log events", func(done Done) { 191 cw := newChannelWriter() 192 server, err := NewServer(context.TODO(), UseHub(&invocationHub{}), Logger(log.NewLogfmtLogger(cw), false)) 193 Expect(server).NotTo(BeNil()) 194 Expect(err).To(BeNil()) 195 conn := newTestingConnectionForServer() 196 Expect(conn).NotTo(BeNil()) 197 go func() { _ = server.Serve(conn) }() 198 conn.ClientSend(`{"type":1,"invocationId": "123","target":"sumple is simple with typo"}`) 199 select { 200 case <-cw.Chan(): 201 break 202 case <-time.After(1000 * time.Millisecond): 203 Fail("timed out") 204 } 205 close(done) 206 }, 2.0) 207 }) 208 Context("When no option which sets the hub type is used, NewServer", func() { 209 It("should return an error", func(done Done) { 210 _, err := NewServer(context.TODO(), testLoggerOption()) 211 Expect(err).NotTo(BeNil()) 212 close(done) 213 }) 214 }) 215 Context("When an option returns an error, NewServer", func() { 216 It("should return an error", func(done Done) { 217 _, err := NewServer(context.TODO(), func(Party) error { return errors.New("bad option") }) 218 Expect(err).NotTo(BeNil()) 219 close(done) 220 }) 221 }) 222 }) 223 224 Describe("EnableDetailedErrors option", func() { 225 Context("When the EnableDetailedErrors option false is used, calling a method which panics", func() { 226 It("should return a completion, which contains only the panic", func(done Done) { 227 server, err := NewServer(context.TODO(), UseHub(&invocationHub{}), testLoggerOption()) 228 Expect(server).NotTo(BeNil()) 229 Expect(err).To(BeNil()) 230 conn := newTestingConnectionForServer() 231 Expect(conn).NotTo(BeNil()) 232 go func() { _ = server.Serve(conn) }() 233 conn.ClientSend(`{"type":1,"invocationId": "ppp","target":"Panic"}`) 234 <-invocationQueue 235 select { 236 case m := <-conn.ReceiveChan(): 237 Expect(m).To(BeAssignableToTypeOf(completionMessage{})) 238 cm := m.(completionMessage) 239 Expect(cm.Error).To(Equal("Don't panic!\n")) 240 case <-time.After(100 * time.Millisecond): 241 Fail("timed out") 242 } 243 close(done) 244 }, 1.0) 245 }) 246 Context("When the EnableDetailedErrors option true is used, calling a method which panics", func() { 247 It("should return a completion, which contains only the panic", func(done Done) { 248 server, err := NewServer(context.TODO(), UseHub(&invocationHub{}), EnableDetailedErrors(true), testLoggerOption()) 249 Expect(server).NotTo(BeNil()) 250 Expect(err).To(BeNil()) 251 conn := newTestingConnectionForServer() 252 Expect(conn).NotTo(BeNil()) 253 go func() { _ = server.Serve(conn) }() 254 conn.ClientSend(`{"type":1,"invocationId": "ppp","target":"Panic"}`) 255 <-invocationQueue 256 select { 257 case m := <-conn.ReceiveChan(): 258 Expect(m).To(BeAssignableToTypeOf(completionMessage{})) 259 cm := m.(completionMessage) 260 Expect(cm.Error).NotTo(Equal("Don't panic!\n")) 261 case <-time.After(100 * time.Millisecond): 262 Fail("timed out") 263 } 264 close(done) 265 }, 1.0) 266 }) 267 }) 268 269 Describe("TimeoutInterval option", func() { 270 Context("When the TimeoutInterval has expired without any client message", func() { 271 It("the connection should be closed", func(done Done) { 272 server, err := NewServer(context.TODO(), UseHub(&invocationHub{}), TimeoutInterval(100*time.Millisecond), testLoggerOption()) 273 Expect(server).NotTo(BeNil()) 274 Expect(err).To(BeNil()) 275 conn := newTestingConnectionForServer() 276 Expect(conn).NotTo(BeNil()) 277 go func() { _ = server.Serve(conn) }() 278 time.Sleep(200 * time.Millisecond) 279 conn.ClientSend(`{"type":1,"invocationId": "timeout100","target":"Simple"}`) 280 select { 281 case m := <-conn.ReceiveChan(): 282 Expect(m).To(BeAssignableToTypeOf(closeMessage{})) 283 cm := m.(closeMessage) 284 Expect(cm.Error).NotTo(BeNil()) 285 case <-time.After(200 * time.Millisecond): 286 Fail("timed out") 287 case <-invocationQueue: 288 Fail("hub method invoked") 289 } 290 close(done) 291 }, 2.0) 292 }) 293 }) 294 295 Describe("KeepAliveInterval option", func() { 296 Context("When the KeepAliveInterval has expired without any server message", func() { 297 It("a ping should have been sent", func(done Done) { 298 server, err := NewServer(context.TODO(), UseHub(&invocationHub{}), KeepAliveInterval(200*time.Millisecond), testLoggerOption()) 299 Expect(server).NotTo(BeNil()) 300 Expect(err).To(BeNil()) 301 conn := newTestingConnection() 302 go func() { _ = server.Serve(conn) }() 303 // Send initial Handshake 304 conn.ClientSend(`{"protocol": "json","version": 1}`) 305 // Handshake response 306 hr, _ := conn.ClientReceive() 307 Expect(hr).To(Equal("{}")) 308 for i := 0; i < 5; i++ { 309 // Wait for ping 310 hmc := make(chan interface{}, 1) 311 go func() { 312 m, _ := conn.ClientReceive() 313 hmc <- m 314 }() 315 select { 316 case m := <-hmc: 317 Expect(strings.TrimRight(m.(string), "\n")).To(Equal("{\"type\":6}")) 318 case <-time.After(600 * time.Millisecond): 319 Fail("timed out") 320 } 321 } 322 server.cancel() 323 close(done) 324 }, 2.0) 325 }) 326 }) 327 328 Describe("StreamBufferCapacity option", func() { 329 Context("When the StreamBufferCapacity is 0", func() { 330 It("should return an error", func(done Done) { 331 _, err := NewServer(context.TODO(), UseHub(&singleHub{}), StreamBufferCapacity(0), testLoggerOption()) 332 Expect(err).NotTo(BeNil()) 333 close(done) 334 }) 335 }) 336 }) 337 338 Describe("MaximumReceiveMessageSize option", func() { 339 Context("When the MaximumReceiveMessageSize is 0", func() { 340 It("should return an error", func(done Done) { 341 _, err := NewServer(context.TODO(), UseHub(&singleHub{}), MaximumReceiveMessageSize(0), testLoggerOption()) 342 Expect(err).NotTo(BeNil()) 343 close(done) 344 }) 345 }) 346 }) 347 Describe("HTTPTransports option", func() { 348 Context("When HTTPTransports is one of WebSockets, ServerSentEvents or both", func() { 349 It("should set these transports", func(done Done) { 350 s, err := NewServer(context.TODO(), UseHub(&singleHub{}), HTTPTransports(TransportWebSockets), testLoggerOption()) 351 Expect(err).NotTo(HaveOccurred()) 352 Expect(s.availableTransports()).To(ContainElement(TransportWebSockets)) 353 close(done) 354 }) 355 It("should set these transports", func(done Done) { 356 s, err := NewServer(context.TODO(), UseHub(&singleHub{}), HTTPTransports(TransportServerSentEvents), testLoggerOption()) 357 Expect(err).NotTo(HaveOccurred()) 358 Expect(s.availableTransports()).To(ContainElement(TransportServerSentEvents)) 359 close(done) 360 }) 361 It("should set these transports", func(done Done) { 362 s, err := NewServer(context.TODO(), UseHub(&singleHub{}), HTTPTransports(TransportServerSentEvents, TransportWebSockets), testLoggerOption()) 363 Expect(err).NotTo(HaveOccurred()) 364 Expect(s.availableTransports()).To(ContainElement(TransportWebSockets)) 365 Expect(s.availableTransports()).To(ContainElement(TransportServerSentEvents)) 366 close(done) 367 }) 368 }) 369 Context("When HTTPTransports is none of WebSockets, ServerSentEvents", func() { 370 It("should return an error", func(done Done) { 371 _, err := NewServer(context.TODO(), UseHub(&singleHub{}), HTTPTransports("WebTransport"), testLoggerOption()) 372 Expect(err).To(HaveOccurred()) 373 close(done) 374 }) 375 }) 376 Context("When HTTPTransports is used on a client", func() { 377 It("should return an error", func(done Done) { 378 _, err := NewClient(context.TODO(), WithConnection(newTestingConnection()), HTTPTransports(TransportServerSentEvents), testLoggerOption()) 379 Expect(err).To(HaveOccurred()) 380 close(done) 381 }) 382 }) 383 384 }) 385 }) 386 387 type channelWriter struct { 388 channel chan []byte 389 } 390 391 func (c *channelWriter) Write(p []byte) (n int, err error) { 392 c.channel <- p 393 return len(p), nil 394 } 395 396 func (c *channelWriter) Chan() chan []byte { 397 return c.channel 398 } 399 400 func newChannelWriter() *channelWriter { 401 return &channelWriter{make(chan []byte, 100)} 402 } 403 404 // type mapLogger struct { 405 // c chan bool 406 // m map[string]string 407 // } 408 // 409 // func (m *mapLogger) Log(keyvals ...interface{}) error { 410 // m.m = make(map[string]string) 411 // for i := 0; i < len(keyvals); i += 2 { 412 // m.m[keyvals[i].(string)] = keyvals[i+1].(string) 413 // } 414 // m.c <- true 415 // return nil 416 // }