From e01aa11486d54a0e869f5bdfeb37160b21c20fd2 Mon Sep 17 00:00:00 2001 From: yinzhidong Date: Tue, 30 Apr 2024 17:54:36 +0800 Subject: [PATCH] feat: command - ping (#19) --- internal/command/command.go | 2 + internal/command/conn/ping.go | 41 ++++++++++++++ internal/command/conn/ping_test.go | 89 ++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 internal/command/conn/ping.go create mode 100644 internal/command/conn/ping_test.go diff --git a/internal/command/command.go b/internal/command/command.go index bade24e..65d905c 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -49,6 +49,8 @@ func Parse(args [][]byte) (redis.Cmd, error) { // connection case "echo": return conn.ParseEcho(b) + case "ping": + return conn.ParsePing(b) // key case "del": diff --git a/internal/command/conn/ping.go b/internal/command/conn/ping.go new file mode 100644 index 0000000..ac16556 --- /dev/null +++ b/internal/command/conn/ping.go @@ -0,0 +1,41 @@ +package conn + +import ( + "strings" + + "github.com/nalgeon/redka/internal/parser" + "github.com/nalgeon/redka/internal/redis" +) + +const ( + PONG = "PONG" +) + +// Returns PONG if no argument is provided, otherwise return a copy of the argument as a bulk +// https://redis.io/commands/ping +type Ping struct { + redis.BaseCmd + Parts []string +} + + +func ParsePing(b redis.BaseCmd) (*Ping, error) { + cmd := &Ping{BaseCmd: b} + err := parser.New( + parser.Strings(&cmd.Parts), + ).Required(0).Run(cmd.Args()) + if err != nil { + return cmd, err + } + return cmd, nil +} + +func (c *Ping) Run(w redis.Writer, _ redis.Redka) (any, error) { + if len(c.Parts) == 0 { + w.WriteAny(PONG) + return PONG, nil + } + out := strings.Join(c.Parts, " ") + w.WriteAny(out) + return out, nil +} diff --git a/internal/command/conn/ping_test.go b/internal/command/conn/ping_test.go new file mode 100644 index 0000000..6074561 --- /dev/null +++ b/internal/command/conn/ping_test.go @@ -0,0 +1,89 @@ +package conn_test + +import ( + "testing" + + "github.com/nalgeon/redka/internal/command" + "github.com/nalgeon/redka/internal/command/conn" + "github.com/nalgeon/redka/internal/redis" + "github.com/nalgeon/redka/internal/testx" +) + +func TestPingParse(t *testing.T) { + tests := []struct { + name string + args [][]byte + want []string + err error + }{ + { + name: "ping", + args: command.BuildArgs("ping"), + want: []string(nil), + err: nil, + }, + { + name: "ping hello", + args: command.BuildArgs("ping", "hello"), + want: []string{"hello"}, + err: nil, + }, + { + name: "ping one two", + args: command.BuildArgs("ping", "one", "two"), + want: []string{"one", "two"}, + err: nil, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + cmd, err := command.Parse(test.args) + testx.AssertEqual(t, err, test.err) + if err == nil { + testx.AssertEqual(t, cmd.(*conn.Ping).Parts, test.want) + } + }) + } +} + +func TestPingExec(t *testing.T) { + db, red := getDB(t) + defer db.Close() + + tests := []struct { + name string + cmd *conn.Ping + res any + out string + }{ + { + name: "ping", + cmd: command.MustParse[*conn.Ping]("ping"), + res: "PONG", + out: "PONG", + }, + { + name: "ping hello", + cmd: command.MustParse[*conn.Ping]("ping hello"), + res: "hello", + out: "hello", + }, + { + name: "ping one two", + cmd: command.MustParse[*conn.Ping]("ping one two"), + res: "one two", + out: "one two", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + conn := redis.NewFakeConn() + res, err := test.cmd.Run(conn, red) + testx.AssertNoErr(t, err) + testx.AssertEqual(t, res, test.res) + testx.AssertEqual(t, conn.Out(), test.out) + }) + } +}