From 762166884502fc2c008950e98ab5c4c568526b2f Mon Sep 17 00:00:00 2001
From: a <a@tuxpa.in>
Date: Tue, 1 Aug 2023 02:57:53 -0500
Subject: [PATCH] wip

---
 contrib/codecs/http/codec.go       |   6 +-
 contrib/codecs/redis/client.go     | 231 +++++++++++++++++++++++++++++
 contrib/codecs/redis/codec.go      |  89 +++++++++++
 contrib/codecs/redis/codec_test.go |  39 +++++
 contrib/codecs/redis/const.go      |  18 +++
 contrib/codecs/redis/redis.go      |  69 +++++++++
 contrib/codecs/redis/server.go     |  32 ++++
 go.mod                             |   7 +
 go.sum                             |  19 +++
 pkg/jrpctest/suites.go             |   2 +-
 10 files changed, 510 insertions(+), 2 deletions(-)
 create mode 100644 contrib/codecs/redis/client.go
 create mode 100644 contrib/codecs/redis/codec.go
 create mode 100644 contrib/codecs/redis/codec_test.go
 create mode 100644 contrib/codecs/redis/const.go
 create mode 100644 contrib/codecs/redis/redis.go
 create mode 100644 contrib/codecs/redis/server.go

diff --git a/contrib/codecs/http/codec.go b/contrib/codecs/http/codec.go
index 4949b64..ee21a7d 100644
--- a/contrib/codecs/http/codec.go
+++ b/contrib/codecs/http/codec.go
@@ -37,10 +37,14 @@ type httpError struct {
 }
 
 func NewCodec(w http.ResponseWriter, r *http.Request) *Codec {
+	ir := io.Writer(w)
+	if w == nil {
+		ir = io.Discard
+	}
 	c := &Codec{
 		r:     r,
 		w:     w,
-		wr:    bufio.NewWriter(w),
+		wr:    bufio.NewWriter(ir),
 		msgs:  make(chan *serverutil.Bundle, 1),
 		errCh: make(chan httpError, 1),
 	}
diff --git a/contrib/codecs/redis/client.go b/contrib/codecs/redis/client.go
new file mode 100644
index 0000000..c73ff35
--- /dev/null
+++ b/contrib/codecs/redis/client.go
@@ -0,0 +1,231 @@
+package redis
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"fmt"
+	"sync"
+
+	"gfx.cafe/open/jrpc/pkg/clientutil"
+	"gfx.cafe/open/jrpc/pkg/codec"
+	"github.com/redis/go-redis/v9"
+	"github.com/rs/xid"
+)
+
+type Client struct {
+	p *clientutil.IdReply
+
+	c        redis.UniversalClient
+	clientId string
+	domain   string
+
+	ctx context.Context
+	cn  context.CancelFunc
+
+	m       codec.Middlewares
+	handler codec.Handler
+	mu      sync.RWMutex
+
+	handlerPeer codec.PeerInfo
+}
+
+func NewClient(c redis.UniversalClient, domain string) *Client {
+	cl := &Client{
+		c: c,
+		p: clientutil.NewIdReply(),
+		handlerPeer: codec.PeerInfo{
+			Transport:  "redis",
+			RemoteAddr: "",
+		},
+		domain: domain,
+		// this doesn't need to be secure bc... you have access to the redis instance lol
+		clientId: xid.New().String(),
+		handler:  codec.HandlerFunc(func(w codec.ResponseWriter, r *codec.Request) {}),
+	}
+	cl.ctx, cl.cn = context.WithCancel(context.Background())
+	go cl.listen()
+	return cl
+}
+
+func (c *Client) SetHandlerPeer(pi codec.PeerInfo) {
+	c.handlerPeer = pi
+}
+
+func (c *Client) Mount(h codec.Middleware) {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+	c.m = append(c.m, h)
+	c.handler = c.m.HandlerFunc(func(w codec.ResponseWriter, r *codec.Request) {
+		// do nothing on no handler
+	})
+}
+
+func (c *Client) listen() error {
+	subCh := fmt.Sprintf(c.domain + "." + c.clientId)
+	sub := c.c.PSubscribe(c.ctx, subCh)
+	msgCh := sub.Channel()
+	for {
+		incomingMsg := <-msgCh
+		msgs, _ := codec.ParseMessage(stringToBytes(incomingMsg.Payload))
+		for i := range msgs {
+			v := msgs[i]
+			if v == nil {
+				continue
+			}
+			id := v.ID
+			//  messages without ids are notifications
+			if id == nil {
+				var handler codec.Handler
+				c.mu.RLock()
+				handler = c.handler
+				c.mu.RUnlock()
+				// writer should only be allowed to send notifications
+				// reader should contain the message above
+				// the context is the client context
+				req := codec.NewRawRequest(c.ctx,
+					nil,
+					v.Method,
+					v.Params,
+				)
+				req.Peer = c.handlerPeer
+				handler.ServeRPC(nil, req)
+				continue
+			}
+			var err error
+			if v.Error != nil {
+				err = v.Error
+			}
+			c.p.Resolve(*id, v.Result, err)
+		}
+	}
+
+}
+
+func (c *Client) Do(ctx context.Context, result any, method string, params any) error {
+	id := c.p.NextId()
+	req, err := codec.NewRequest(ctx, codec.NewId(id), method, params)
+	if err != nil {
+		return err
+	}
+	fwd, err := json.Marshal(req)
+	if err != nil {
+		return err
+	}
+	toFwd, _ := json.Marshal(&RedisRequest{
+		ReplyChannel: c.clientId,
+		Message:      fwd,
+	})
+	err = c.writeContext(req.Context(), toFwd)
+	if err != nil {
+		return err
+	}
+	ans, err := c.p.Ask(req.Context(), *id)
+	if err != nil {
+		return err
+	}
+	if result != nil {
+		err = json.Unmarshal(ans, result)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (c *Client) BatchCall(ctx context.Context, b ...*codec.BatchElem) error {
+	if ctx == nil {
+		ctx = context.Background()
+	}
+	buf := new(bytes.Buffer)
+	enc := json.NewEncoder(buf)
+	reqs := make([]*codec.Request, 0, len(b))
+	ids := make([]*codec.ID, 0, len(b))
+	for _, v := range b {
+		id := c.p.NextId()
+		req, err := codec.NewRequest(ctx, codec.NewId(id), v.Method, v.Params)
+		if err != nil {
+			return err
+		}
+		ids = append(ids, id)
+		reqs = append(reqs, req)
+	}
+	err := enc.Encode(reqs)
+	if err != nil {
+		return err
+	}
+	pkg, _ := json.Marshal(&RedisRequest{
+		ReplyChannel: c.clientId,
+		Message:      buf.Bytes(),
+	})
+	err = c.writeContext(ctx, pkg)
+	if err != nil {
+		return err
+	}
+	// TODO: wait for response
+	wg := sync.WaitGroup{}
+	wg.Add(len(ids))
+	for i := range ids {
+		idx := i
+		go func() {
+			defer wg.Done()
+			ans, err := c.p.Ask(reqs[idx].Context(), *ids[idx])
+			if err != nil {
+				b[idx].Error = err
+				return
+			}
+			if b[idx].Result != nil {
+				err = json.Unmarshal(ans, b[idx].Result)
+				if err != nil {
+					b[idx].Error = err
+					return
+				}
+			}
+		}()
+	}
+	wg.Wait()
+
+	return err
+}
+func (c *Client) Notify(ctx context.Context, method string, params any) error {
+	if ctx == nil {
+		ctx = context.Background()
+	}
+	req, err := codec.NewRequest(ctx, nil, method, params)
+	if err != nil {
+		return err
+	}
+	fwd, err := json.Marshal(req)
+	if err != nil {
+		return err
+	}
+	return c.writeContext(ctx, fwd)
+}
+
+func (c *Client) SetHeader(key string, value string) {
+}
+
+func (c *Client) Close() error {
+	c.cn()
+	return nil
+}
+
+func (c *Client) writeContext(ctx context.Context, xs []byte) error {
+	errch := make(chan error)
+	go func() {
+		err := c.c.LPush(ctx, c.domain+reqDomainSuffix, xs).Err()
+		select {
+		case errch <- err:
+		case <-ctx.Done():
+		case <-c.ctx.Done():
+		}
+	}()
+	select {
+	case err := <-errch:
+		return err
+	case <-c.ctx.Done():
+		return c.ctx.Err()
+	case <-ctx.Done():
+		return ctx.Err()
+	}
+}
diff --git a/contrib/codecs/redis/codec.go b/contrib/codecs/redis/codec.go
new file mode 100644
index 0000000..5c0ec05
--- /dev/null
+++ b/contrib/codecs/redis/codec.go
@@ -0,0 +1,89 @@
+package redis
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"sync/atomic"
+
+	"gfx.cafe/open/jrpc/pkg/codec"
+	"gfx.cafe/open/jrpc/pkg/serverutil"
+)
+
+var _ codec.ReaderWriter = (*Codec)(nil)
+
+type Codec struct {
+	ctx context.Context
+	cn  func()
+
+	wr      bytes.Buffer
+	replier func(json.RawMessage) error
+	ansCh   chan *serverutil.Bundle
+	closed  atomic.Bool
+	closeCh chan struct{}
+
+	i codec.PeerInfo
+}
+
+type httpError struct {
+	code int
+	err  error
+}
+
+func NewCodec(req *RedisRequest, replier func(json.RawMessage) error) *Codec {
+	c := &Codec{
+		replier: replier,
+		ansCh:   make(chan *serverutil.Bundle, 1),
+		closeCh: make(chan struct{}),
+	}
+	c.ctx, c.cn = context.WithCancel(context.Background())
+	bundle := serverutil.ParseBundle(req.Message)
+	c.ansCh <- bundle
+	return c
+}
+
+// gets the peer info
+func (c *Codec) PeerInfo() codec.PeerInfo {
+	return c.i
+}
+
+func (c *Codec) ReadBatch(ctx context.Context) ([]*codec.Message, bool, error) {
+	select {
+	case ans := <-c.ansCh:
+		return ans.Messages, ans.Batch, nil
+	case <-ctx.Done():
+		return nil, false, ctx.Err()
+	case <-c.ctx.Done():
+		return nil, false, c.ctx.Err()
+	}
+}
+
+// closes the connection
+func (c *Codec) Close() error {
+	if c.closed.CompareAndSwap(false, true) {
+		close(c.closeCh)
+	}
+	c.cn()
+	return nil
+}
+
+func (c *Codec) Write(p []byte) (n int, err error) {
+	return c.wr.Write(p)
+}
+
+func (c *Codec) Flush() (err error) {
+	c.replier(c.wr.Bytes())
+	c.wr.Reset()
+	c.Close()
+	return
+}
+
+// Closed returns a channel which is closed when the connection is closed.
+func (c *Codec) Closed() <-chan struct{} {
+	return c.closeCh
+}
+
+// RemoteAddr returns the peer address of the connection.
+func (c *Codec) RemoteAddr() string {
+	return ""
+}
diff --git a/contrib/codecs/redis/codec_test.go b/contrib/codecs/redis/codec_test.go
new file mode 100644
index 0000000..000a913
--- /dev/null
+++ b/contrib/codecs/redis/codec_test.go
@@ -0,0 +1,39 @@
+package redis
+
+import (
+	"context"
+	"testing"
+
+	"gfx.cafe/open/jrpc/pkg/codec"
+	"gfx.cafe/open/jrpc/pkg/server"
+
+	"gfx.cafe/open/jrpc/pkg/jrpctest"
+	"github.com/alicebob/miniredis/v2"
+	"github.com/redis/go-redis/v9"
+	"github.com/stretchr/testify/require"
+)
+
+func TestBasicSuite(t *testing.T) {
+	redisServer := miniredis.RunT(t)
+	_ = redisServer
+	connOpts := &redis.UniversalOptions{
+		Addrs: []string{"localhost:6379"},
+	}
+	domain := "jrpc"
+
+	jrpctest.RunBasicTestSuite(t, jrpctest.BasicTestSuiteArgs{
+		ServerMaker: func() (*server.Server, jrpctest.ClientMaker, func()) {
+			ctx, cn := context.WithCancel(context.Background())
+			ss, err := CreateServerStream(ctx, domain, connOpts)
+			require.NoError(t, err)
+			s := jrpctest.NewServer()
+			go (&Server{Server: s}).ServeRedis(ctx, ss)
+			return s, func() codec.Conn {
+					conn := NewClient(redis.NewUniversalClient(connOpts), domain)
+					return conn
+				}, func() {
+					cn()
+				}
+		},
+	})
+}
diff --git a/contrib/codecs/redis/const.go b/contrib/codecs/redis/const.go
new file mode 100644
index 0000000..62e650c
--- /dev/null
+++ b/contrib/codecs/redis/const.go
@@ -0,0 +1,18 @@
+package redis
+
+import "errors"
+
+const (
+	// NOTE: if you change this, you will have to change the thing in jrpctest... its what its for now until tests get refactored
+	maxRequestContentLength = 1024 * 1024 * 5
+	contentType             = "application/json"
+)
+
+// https://www.jsonrpc.org/historical/json-rpc-over-http.html#id13
+var acceptedContentTypes = []string{
+	// https://www.jsonrpc.org/historical/json-rpc-over-http.html#id13
+	contentType, "application/json-rpc", "application/jsonrequest",
+	// these are added because they make sense, fight me!
+	"application/jsonrpc2", "application/json-rpc2", "application/jrpc",
+}
+var ErrInvalidContentType = errors.New("invalid content type")
diff --git a/contrib/codecs/redis/redis.go b/contrib/codecs/redis/redis.go
new file mode 100644
index 0000000..656a9a4
--- /dev/null
+++ b/contrib/codecs/redis/redis.go
@@ -0,0 +1,69 @@
+package redis
+
+import (
+	"context"
+	"encoding/json"
+	"log"
+	"time"
+	"unsafe"
+
+	"github.com/redis/go-redis/v9"
+	"github.com/rs/xid"
+)
+
+type ServerStream struct {
+	client redis.UniversalClient
+	domain string
+
+	id xid.ID
+}
+
+func CreateServerStream(ctx context.Context, domain string, opts *redis.UniversalOptions) (*ServerStream, error) {
+	c := redis.NewUniversalClient(opts)
+	// the xid doesn't need to be secure, since we assume anyone with access to the redis cluster can do anything anyways.
+	s := &ServerStream{
+		client: c,
+		id:     xid.New(),
+		domain: domain,
+	}
+	return s, nil
+}
+
+type RedisRequest struct {
+	ReplyChannel string          `json:"r"`
+	Message      json.RawMessage `json:"msg"`
+}
+
+func (s *ServerStream) ReadRequest(ctx context.Context) (*RedisRequest, func(json.RawMessage) error, error) {
+	timeout := time.Hour
+	res, err := s.client.BLPop(ctx, timeout, s.domain+reqDomainSuffix).Result()
+	if err != nil {
+		return nil, nil, err
+	}
+	if len(res) != 2 {
+		return nil, nil, err
+	}
+	redisReq := &RedisRequest{}
+	err = json.Unmarshal(stringToBytes(res[1]), redisReq)
+	if err != nil {
+		return nil, nil, err
+	}
+	log.Println("got req", redisReq.ReplyChannel, string(redisReq.Message))
+	return redisReq, func(rm json.RawMessage) error {
+		target := s.domain + "." + redisReq.ReplyChannel
+		log.Println("replying", target, string(rm))
+		return s.client.Publish(context.Background(), target, string(rm)).Err()
+	}, nil
+}
+
+const reqDomainSuffix = ".req"
+const respDomainSuffix = ".resp"
+
+func stringToBytes(s string) []byte {
+	return *(*[]byte)(unsafe.Pointer(
+		&struct {
+			string
+			Cap int
+		}{s, len(s)},
+	))
+}
diff --git a/contrib/codecs/redis/server.go b/contrib/codecs/redis/server.go
new file mode 100644
index 0000000..54771e1
--- /dev/null
+++ b/contrib/codecs/redis/server.go
@@ -0,0 +1,32 @@
+package redis
+
+import (
+	"context"
+
+	"gfx.cafe/open/jrpc/pkg/server"
+	"tuxpa.in/a/zlog/log"
+)
+
+type Server struct {
+	Server *server.Server
+}
+
+func (s *Server) ServeRedis(ctx context.Context, stream *ServerStream) {
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		default:
+		}
+		req, fn, err := stream.ReadRequest(ctx)
+		if err != nil {
+			log.Err(err).Msg("while reading bpop")
+			continue
+		}
+		if req == nil {
+			continue
+		}
+		cd := NewCodec(req, fn)
+		s.Server.ServeCodec(ctx, cd)
+	}
+}
diff --git a/go.mod b/go.mod
index bc2cf24..606f5e2 100644
--- a/go.mod
+++ b/go.mod
@@ -8,11 +8,14 @@ require (
 	gfx.cafe/util/go/frand v0.0.0-20230121041905-80dafb1e973e
 	gfx.cafe/util/go/generic v0.0.0-20230502013805-237fcc25d586
 	github.com/alecthomas/kong v0.7.1
+	github.com/alicebob/miniredis/v2 v2.30.4
 	github.com/davecgh/go-spew v1.1.1
 	github.com/deckarep/golang-set v1.8.0
 	github.com/go-faster/jx v1.0.0
 	github.com/goccy/go-json v0.10.0
 	github.com/iancoleman/strcase v0.3.0
+	github.com/redis/go-redis/v9 v9.0.5
+	github.com/rs/xid v1.4.0
 	github.com/stretchr/testify v1.8.2
 	sigs.k8s.io/yaml v1.3.0
 	tuxpa.in/a/zlog v1.61.0
@@ -20,6 +23,9 @@ require (
 
 require (
 	github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
+	github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect
+	github.com/cespare/xxhash/v2 v2.2.0 // indirect
+	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 	github.com/go-faster/errors v0.6.1 // indirect
 	github.com/klauspost/compress v1.15.15 // indirect
 	github.com/kr/pretty v0.3.1 // indirect
@@ -28,6 +34,7 @@ require (
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/rs/zerolog v1.29.0 // indirect
 	github.com/segmentio/asm v1.2.0 // indirect
+	github.com/yuin/gopher-lua v1.1.0 // indirect
 	golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect
 	golang.org/x/sys v0.7.0 // indirect
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
diff --git a/go.sum b/go.sum
index accaa5d..fdfc007 100644
--- a/go.sum
+++ b/go.sum
@@ -12,6 +12,17 @@ github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2o
 github.com/alecthomas/kong v0.7.1 h1:azoTh0IOfwlAX3qN9sHWTxACE2oV8Bg2gAwBsMwDQY4=
 github.com/alecthomas/kong v0.7.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U=
 github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
+github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
+github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
+github.com/alicebob/miniredis/v2 v2.30.4 h1:8S4/o1/KoUArAGbGwPxcwf0krlzceva2XVOSchFS7Eo=
+github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg=
+github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=
+github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
+github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
+github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -20,6 +31,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
 github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
 github.com/go-faster/errors v0.6.1 h1:nNIPOBkprlKzkThvS/0YaX8Zs9KewLCOSFQS5BU06FI=
 github.com/go-faster/errors v0.6.1/go.mod h1:5MGV2/2T9yvlrbhe9pD9LO5Z/2zCSq2T8j+Jpi2LAyY=
 github.com/go-faster/jx v1.0.0 h1:HE+ms2e6ZGkZ6u13t8u+onBinrPvIPI+0hWXGELm74g=
@@ -53,8 +66,11 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o=
+github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
 github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
+github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
 github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
 github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
 github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
@@ -68,8 +84,11 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
 github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE=
+github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
 golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg=
 golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
+golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/pkg/jrpctest/suites.go b/pkg/jrpctest/suites.go
index f91364b..9bb6bf1 100644
--- a/pkg/jrpctest/suites.go
+++ b/pkg/jrpctest/suites.go
@@ -122,7 +122,7 @@ func RunBasicTestSuite(t *testing.T, args BasicTestSuiteArgs) {
 		}
 	})
 
-	makeTest("ResposeType", func(t *testing.T, server *server.Server, client codec.Conn) {
+	makeTest("ResposeType2", func(t *testing.T, server *server.Server, client codec.Conn) {
 		if err := codec.CallInto(nil, client, nil, "test_echo", "hello", 10, &EchoArgs{"world"}); err != nil {
 			t.Errorf("Passing nil as result should be fine, but got an error: %v", err)
 		}
-- 
GitLab