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  }