github.com/clubpay/ronykit/kit@v0.14.4-0.20240515065620-d0dace45cbc7/edge_test.go (about) 1 package kit_test 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io" 8 "strings" 9 "sync" 10 "testing" 11 "time" 12 13 "github.com/clubpay/ronykit/kit" 14 "github.com/clubpay/ronykit/kit/desc" 15 "github.com/clubpay/ronykit/kit/utils" 16 . "github.com/onsi/ginkgo/v2" 17 . "github.com/onsi/gomega" 18 ) 19 20 func TestRonykit(t *testing.T) { 21 RegisterFailHandler(Fail) 22 RunSpecs(t, "Ronykit Suite") 23 } 24 25 type testSelector struct{} 26 27 func (t testSelector) Query(_ string) any { 28 return nil 29 } 30 31 func (t testSelector) GetEncoding() kit.Encoding { 32 return kit.JSON 33 } 34 35 type testGateway struct { 36 d kit.GatewayDelegate 37 } 38 39 var _ kit.Gateway = (*testGateway)(nil) 40 41 func (t *testGateway) Send(c *testConn, msg []byte) { 42 t.d.OnMessage(c, msg) 43 } 44 45 func (t *testGateway) Start(_ context.Context, _ kit.GatewayStartConfig) error { 46 return nil 47 } 48 49 func (t *testGateway) Shutdown(_ context.Context) error { 50 return nil 51 } 52 53 func (t *testGateway) Subscribe(d kit.GatewayDelegate) { 54 t.d = d 55 } 56 57 func (t *testGateway) Dispatch(ctx *kit.Context, in []byte) (kit.ExecuteArg, error) { 58 ctx.In().SetMsg(kit.RawMessage(in)) 59 60 return kit.ExecuteArg{ 61 ServiceName: "testService", 62 ContractID: "testService.1", 63 Route: "someRoute", 64 }, nil 65 } 66 67 func (t *testGateway) Register( 68 _, _ string, _ kit.Encoding, _ kit.RouteSelector, _ kit.Message, 69 ) { 70 } 71 72 type testCluster struct { 73 mtx sync.Mutex 74 delegates map[string]kit.ClusterDelegate 75 kv map[string]string 76 m chan struct { 77 id string 78 data []byte 79 } 80 } 81 82 var ( 83 _ kit.Cluster = (*testCluster)(nil) 84 _ kit.ClusterStore = (*testCluster)(nil) 85 ) 86 87 func newTestCluster() *testCluster { 88 t := &testCluster{ 89 delegates: map[string]kit.ClusterDelegate{}, 90 m: make(chan struct { 91 id string 92 data []byte 93 }, 10), 94 } 95 96 go func() { 97 for x := range t.m { 98 t.mtx.Lock() 99 d, ok := t.delegates[x.id] 100 t.mtx.Unlock() 101 if ok { 102 d.OnMessage(x.data) 103 } 104 } 105 }() 106 107 return t 108 } 109 110 func (t *testCluster) Start(ctx context.Context) error { 111 return nil 112 } 113 114 func (t *testCluster) Shutdown(ctx context.Context) error { 115 return nil 116 } 117 118 func (t *testCluster) Subscribe(id string, d kit.ClusterDelegate) { 119 t.mtx.Lock() 120 if t.delegates == nil { 121 t.delegates = map[string]kit.ClusterDelegate{} 122 } 123 t.delegates[id] = d 124 t.mtx.Unlock() 125 } 126 127 func (t *testCluster) Subscribers() ([]string, error) { 128 var members []string 129 t.mtx.Lock() 130 for m := range t.delegates { 131 members = append(members, m) 132 } 133 t.mtx.Unlock() 134 135 return members, nil 136 } 137 138 func (t *testCluster) Publish(id string, data []byte) error { 139 t.m <- struct { 140 id string 141 data []byte 142 }{id: id, data: data} 143 144 return nil 145 } 146 147 func (t *testCluster) Store() kit.ClusterStore { 148 return t 149 } 150 151 func (t *testCluster) Set(key, value string, _ time.Duration) error { 152 if t.kv == nil { 153 t.kv = map[string]string{} 154 } 155 156 t.kv[key] = value 157 158 return nil 159 } 160 161 func (t *testCluster) Delete(key string) error { 162 if t.kv == nil { 163 t.kv = map[string]string{} 164 } 165 166 delete(t.kv, key) 167 168 return nil 169 } 170 171 func (t *testCluster) Get(key string) (string, error) { 172 return t.kv[key], nil 173 } 174 175 func (t *testCluster) Scan(prefix string, cb func(key string) bool) error { 176 for k := range t.kv { 177 if strings.HasPrefix(k, prefix) { 178 if !cb(k) { 179 return nil 180 } 181 } 182 } 183 184 return nil 185 } 186 187 func (t *testCluster) ScanWithValue(prefix string, cb func(string, string) bool) error { 188 for k, v := range t.kv { 189 if strings.HasPrefix(k, prefix) { 190 if !cb(k, v) { 191 return nil 192 } 193 } 194 } 195 196 return nil 197 } 198 199 type testConn struct { 200 id uint64 201 clientIP string 202 stream bool 203 kv map[string]string 204 buf *bytes.Buffer 205 } 206 207 var _ kit.Conn = (*testConn)(nil) 208 209 func newTestConn(id uint64, clientIP string, stream bool) *testConn { 210 return &testConn{ 211 id: id, 212 clientIP: clientIP, 213 stream: stream, 214 kv: map[string]string{}, 215 buf: &bytes.Buffer{}, 216 } 217 } 218 219 func (t testConn) ConnID() uint64 { 220 return t.id 221 } 222 223 func (t testConn) ClientIP() string { 224 return t.clientIP 225 } 226 227 func (t testConn) Write(data []byte) (int, error) { 228 return t.buf.Write(data) 229 } 230 231 func (t *testConn) WriteEnvelope(e *kit.Envelope) error { 232 b, err := kit.MarshalMessage(e.GetMsg()) 233 if err != nil { 234 return err 235 } 236 _, err = t.buf.Write(b) 237 238 return err 239 } 240 241 func (t testConn) Read() ([]byte, error) { 242 return io.ReadAll(t.buf) 243 } 244 245 func (t testConn) ReadString() (string, error) { 246 d, err := t.Read() 247 if err != nil { 248 return "", err 249 } 250 return string(d), nil 251 } 252 253 func (t testConn) Stream() bool { 254 return t.stream 255 } 256 257 func (t testConn) Walk(f func(key string, val string) bool) { 258 for k, v := range t.kv { 259 if !f(k, v) { 260 return 261 } 262 } 263 } 264 265 func (t testConn) Get(key string) string { 266 return t.kv[key] 267 } 268 269 func (t *testConn) Set(key string, val string) { 270 t.kv[key] = val 271 } 272 273 func (t testConn) Keys() []string { 274 keys := make([]string, 0, len(t.kv)) 275 for k := range t.kv { 276 keys = append(keys, k) 277 } 278 279 return keys 280 } 281 282 // testRESTSelector implements kit.RESTRouteSelector for testing purposes. 283 type testRESTSelector struct { 284 enc kit.Encoding 285 method string 286 path string 287 } 288 289 func (t testRESTSelector) Query(q string) any { 290 return nil 291 } 292 293 func (t testRESTSelector) GetEncoding() kit.Encoding { 294 return t.enc 295 } 296 297 func (t testRESTSelector) GetMethod() string { 298 return t.method 299 } 300 301 func (t testRESTSelector) GetPath() string { 302 return t.path 303 } 304 305 // testRPCSelector implements kit.RPCSelector for testing purposes. 306 type testRPCSelector struct { 307 enc kit.Encoding 308 predicate string 309 } 310 311 func (t testRPCSelector) Query(_ string) any { 312 return nil 313 } 314 315 func (t testRPCSelector) GetEncoding() kit.Encoding { 316 return t.enc 317 } 318 319 func (t testRPCSelector) GetPredicate() string { 320 return t.predicate 321 } 322 323 var _ = Describe("EdgeServer/Simple", func() { 324 var ( 325 b *testGateway 326 edge *kit.EdgeServer 327 ) 328 BeforeEach(func() { 329 b = &testGateway{} 330 var serviceDesc desc.ServiceDescFunc = func() *desc.Service { 331 return desc.NewService("testService"). 332 AddContract( 333 desc.NewContract(). 334 SetInput(&kit.RawMessage{}). 335 SetOutput(&kit.RawMessage{}). 336 AddSelector(testSelector{}). 337 AddHandler( 338 func(ctx *kit.Context) { 339 ctx.Out(). 340 SetMsg(ctx.In().GetMsg()). 341 Send() 342 343 return 344 }, 345 ), 346 ) 347 } 348 edge = kit.NewServer( 349 kit.WithGateway(b), 350 kit.WithServiceDesc(serviceDesc.Desc()), 351 ) 352 edge.Start(nil) 353 }) 354 AfterEach(func() { 355 edge.Shutdown(nil) 356 }) 357 358 DescribeTable("should echo back the message", 359 func(msg []byte) { 360 c := newTestConn(utils.RandomUint64(0), "", false) 361 b.Send(c, msg) 362 Expect(c.Read()).To(BeEquivalentTo(msg)) 363 }, 364 Entry("a raw string", kit.RawMessage("Hello this is a simple message")), 365 Entry("a ToJSON string", kit.RawMessage(`{"cmd": "something", "key1": 123, "key2": "val2"}`)), 366 ) 367 }) 368 369 var _ = Describe("EdgeServer/GlobalHandlers", func() { 370 var ( 371 b *testGateway 372 edge *kit.EdgeServer 373 ) 374 BeforeEach(func() { 375 b = &testGateway{} 376 var serviceDesc desc.ServiceDescFunc = func() *desc.Service { 377 return desc.NewService("testService"). 378 AddContract( 379 desc.NewContract(). 380 SetInput(&kit.RawMessage{}). 381 SetOutput(&kit.RawMessage{}). 382 AddSelector(testSelector{}). 383 AddHandler( 384 func(ctx *kit.Context) { 385 in := utils.B2S(ctx.In().GetMsg().(kit.RawMessage)) //nolint:forcetypeassert 386 out := fmt.Sprintf("%s-%s-%s", 387 ctx.GetString("PRE_KEY", ""), 388 in, 389 ctx.GetString("POST_KEY", ""), 390 ) 391 ctx.Out(). 392 SetMsg(kit.RawMessage(out)). 393 Send() 394 395 return 396 }, 397 ), 398 ) 399 } 400 edge = kit.NewServer( 401 kit.WithGateway(b), 402 kit.WithGlobalHandlers( 403 func(ctx *kit.Context) { 404 ctx.Set("PRE_KEY", "PRE_VALUE") 405 }, 406 ), 407 kit.WithServiceDesc(serviceDesc.Desc()), 408 ) 409 edge.Start(nil) 410 }) 411 AfterEach(func() { 412 edge.Shutdown(nil) 413 }) 414 415 DescribeTable("should echo back the message", 416 func(msg []byte) { 417 c := newTestConn(utils.RandomUint64(0), "", false) 418 b.Send(c, msg) 419 Expect(c.Read()).To(BeEquivalentTo(fmt.Sprintf("PRE_VALUE-%s-", string(msg)))) 420 }, 421 Entry("a raw string", kit.RawMessage("Hello this is a simple message")), 422 Entry("a ToJSON string", kit.RawMessage(`{"cmd": "something", "key1": 123, "key2": "val2"}`)), 423 ) 424 }) 425 426 var _ = Describe("EdgeServer/Cluster", func() { 427 var ( 428 b1 *testGateway 429 b2 *testGateway 430 c *testCluster 431 edge1 *kit.EdgeServer 432 edge2 *kit.EdgeServer 433 ) 434 BeforeEach(func() { 435 b1 = &testGateway{} 436 b2 = &testGateway{} 437 c = newTestCluster() 438 439 var serviceDesc = func(id string) desc.ServiceDescFunc { 440 return func() *desc.Service { 441 return desc.NewService("testService"). 442 AddContract( 443 desc.NewContract(). 444 SetInput(kit.RawMessage{}). 445 SetOutput(kit.RawMessage{}). 446 AddSelector(testSelector{}). 447 SetCoordinator( 448 func(ctx *kit.LimitedContext) (string, error) { 449 members, err := ctx.ClusterMembers() 450 if err != nil { 451 return "", err 452 } 453 454 for _, m := range members { 455 if m != ctx.ClusterID() { 456 return m, nil 457 } 458 } 459 460 return ctx.ClusterID(), nil 461 }, 462 ). 463 AddHandler( 464 func(ctx *kit.Context) { 465 ctx.Out(). 466 SetMsg(kit.RawMessage(id)). 467 Send() 468 469 return 470 }, 471 ), 472 ) 473 } 474 } 475 edge1 = kit.NewServer( 476 kit.WithGateway(b1), 477 kit.WithCluster(c), 478 kit.WithServiceDesc(serviceDesc("edge1").Desc()), 479 ) 480 edge1.Start(nil) 481 edge2 = kit.NewServer( 482 kit.WithGateway(b2), 483 kit.WithCluster(c), 484 kit.WithServiceDesc(serviceDesc("edge2").Desc()), 485 ) 486 edge2.Start(nil) 487 }) 488 AfterEach(func() { 489 edge1.Shutdown(nil) 490 edge2.Shutdown(nil) 491 }) 492 493 DescribeTable("should echo back the message", 494 func(msg []byte) { 495 c := newTestConn(utils.RandomUint64(0), "", false) 496 c.Set("X-Hdr1", "edge1") 497 b1.Send(c, msg) 498 Expect(c.ReadString()).To(BeEquivalentTo("edge2")) 499 Expect(c.Get("X-Hdr1")).To(BeEquivalentTo("edge1")) 500 }, 501 Entry("a raw string", kit.RawMessage("Hello this is a simple message")), 502 Entry("a ToJSON string", kit.RawMessage(`{"cmd": "something", "key1": 123, "key2": "val2"}`)), 503 ) 504 }) 505 506 func BenchmarkServer(b *testing.B) { 507 bundle := &testGateway{} 508 s := kit.NewServer( 509 kit.WithGateway(bundle), 510 kit.WithService( 511 desc.NewService("testService"). 512 AddContract( 513 desc.NewContract(). 514 AddSelector(testSelector{}). 515 AddHandler( 516 func(ctx *kit.Context) { 517 ctx.Out(). 518 SetMsg(ctx.In().GetMsg()). 519 Send() 520 521 return 522 }, 523 ), 524 ). 525 Generate(), 526 ), 527 ).Start(nil) 528 defer s.Shutdown(nil) 529 530 req := []byte(utils.RandomID(24)) 531 b.ResetTimer() 532 b.ReportAllocs() 533 b.RunParallel( 534 func(pb *testing.PB) { 535 for pb.Next() { 536 c := newTestConn(utils.RandomUint64(0), "", false) 537 bundle.Send(c, req) 538 } 539 }, 540 ) 541 }