refactor: command - better encapsulation and simpler tests

This commit is contained in:
Anton
2024-04-30 15:54:27 +05:00
parent 21951bc7e2
commit bd8208bae4
121 changed files with 2038 additions and 2498 deletions

View File

@@ -14,25 +14,6 @@ import (
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
) )
func MustParse[T redis.Cmd](s string) T {
parts := strings.Split(s, " ")
args := BuildArgs(parts[0], parts[1:]...)
cmd, err := Parse(args)
if err != nil {
panic(err)
}
return cmd.(T)
}
func BuildArgs(name string, args ...string) [][]byte {
rargs := make([][]byte, len(args)+1)
rargs[0] = []byte(name)
for i, arg := range args {
rargs[i+1] = []byte(arg)
}
return rargs
}
// Parse parses a text representation of a command into a Cmd. // Parse parses a text representation of a command into a Cmd.
func Parse(args [][]byte) (redis.Cmd, error) { func Parse(args [][]byte) (redis.Cmd, error) {
name := strings.ToLower(string(args[0])) name := strings.ToLower(string(args[0]))

View File

@@ -1,4 +1,4 @@
package conn_test package conn
import ( import (
"testing" "testing"

View File

@@ -12,13 +12,13 @@ import (
// https://redis.io/commands/echo // https://redis.io/commands/echo
type Echo struct { type Echo struct {
redis.BaseCmd redis.BaseCmd
Parts []string parts []string
} }
func ParseEcho(b redis.BaseCmd) (*Echo, error) { func ParseEcho(b redis.BaseCmd) (*Echo, error) {
cmd := &Echo{BaseCmd: b} cmd := &Echo{BaseCmd: b}
err := parser.New( err := parser.New(
parser.Strings(&cmd.Parts), parser.Strings(&cmd.parts),
).Required(1).Run(cmd.Args()) ).Required(1).Run(cmd.Args())
if err != nil { if err != nil {
return cmd, err return cmd, err
@@ -27,7 +27,7 @@ func ParseEcho(b redis.BaseCmd) (*Echo, error) {
} }
func (c *Echo) Run(w redis.Writer, _ redis.Redka) (any, error) { func (c *Echo) Run(w redis.Writer, _ redis.Redka) (any, error) {
out := strings.Join(c.Parts, " ") out := strings.Join(c.parts, " ")
w.WriteAny(out) w.WriteAny(out)
return out, nil return out, nil
} }

View File

@@ -1,47 +1,42 @@
package conn_test package conn
import ( import (
"testing" "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/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestEchoParse(t *testing.T) { func TestEchoParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte args [][]byte
want []string want []string
err error err error
}{ }{
{ {
name: "echo", cmd: "echo",
args: command.BuildArgs("echo"),
want: []string{}, want: []string{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "echo hello", cmd: "echo hello",
args: command.BuildArgs("echo", "hello"),
want: []string{"hello"}, want: []string{"hello"},
err: nil, err: nil,
}, },
{ {
name: "echo one two", cmd: "echo one two",
args: command.BuildArgs("echo", "one", "two"),
want: []string{"one", "two"}, want: []string{"one", "two"},
err: nil, err: nil,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseEcho, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
testx.AssertEqual(t, cmd.(*conn.Echo).Parts, test.want) testx.AssertEqual(t, cmd.parts, test.want)
} }
}) })
} }
@@ -52,29 +47,27 @@ func TestEchoExec(t *testing.T) {
defer db.Close() defer db.Close()
tests := []struct { tests := []struct {
name string cmd string
cmd *conn.Echo res any
res any out string
out string
}{ }{
{ {
name: "echo hello", cmd: "echo hello",
cmd: command.MustParse[*conn.Echo]("echo hello"), res: "hello",
res: "hello", out: "hello",
out: "hello",
}, },
{ {
name: "echo one two", cmd: "echo one two",
cmd: command.MustParse[*conn.Echo]("echo one two"), res: "one two",
res: "one two", out: "one two",
out: "one two",
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := test.cmd.Run(conn, red) cmd := redis.MustParse(ParseEcho, test.cmd)
res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
testx.AssertEqual(t, res, test.res) testx.AssertEqual(t, res, test.res)
testx.AssertEqual(t, conn.Out(), test.out) testx.AssertEqual(t, conn.Out(), test.out)

View File

@@ -1,41 +1,37 @@
package conn package conn
import ( import (
"strings" "github.com/nalgeon/redka/internal/parser"
"github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/parser" )
"github.com/nalgeon/redka/internal/redis"
) const (
PONG = "PONG"
const ( )
PONG = "PONG"
) // Returns the server's liveliness response.
// https://redis.io/commands/ping
// Returns PONG if no argument is provided, otherwise return a copy of the argument as a bulk type Ping struct {
// https://redis.io/commands/ping redis.BaseCmd
type Ping struct { message string
redis.BaseCmd }
Parts []string
} func ParsePing(b redis.BaseCmd) (*Ping, error) {
cmd := &Ping{BaseCmd: b}
err := parser.New(
func ParsePing(b redis.BaseCmd) (*Ping, error) { parser.String(&cmd.message),
cmd := &Ping{BaseCmd: b} ).Required(0).Run(cmd.Args())
err := parser.New( if err != nil {
parser.Strings(&cmd.Parts), return cmd, err
).Required(0).Run(cmd.Args()) }
if err != nil { return cmd, nil
return cmd, err }
}
return cmd, nil func (c *Ping) Run(w redis.Writer, _ redis.Redka) (any, error) {
} if c.message == "" {
w.WriteAny(PONG)
func (c *Ping) Run(w redis.Writer, _ redis.Redka) (any, error) { return PONG, nil
if len(c.Parts) == 0 { }
w.WriteAny(PONG) w.WriteBulkString(c.message)
return PONG, nil return c.message, nil
} }
out := strings.Join(c.Parts, " ")
w.WriteAny(out)
return out, nil
}

View File

@@ -1,89 +1,75 @@
package conn_test package conn
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/command/conn" "github.com/nalgeon/redka/internal/testx"
"github.com/nalgeon/redka/internal/redis" )
"github.com/nalgeon/redka/internal/testx"
) func TestPingParse(t *testing.T) {
tests := []struct {
func TestPingParse(t *testing.T) { cmd string
tests := []struct { want string
name string err error
args [][]byte }{
want []string {
err error cmd: "ping",
}{ want: "",
{ err: nil,
name: "ping", },
args: command.BuildArgs("ping"), {
want: []string(nil), cmd: "ping hello",
err: nil, want: "hello",
}, err: nil,
{ },
name: "ping hello", {
args: command.BuildArgs("ping", "hello"), cmd: "ping one two",
want: []string{"hello"}, want: "",
err: nil, err: redis.ErrSyntaxError,
}, },
{ }
name: "ping one two",
args: command.BuildArgs("ping", "one", "two"), for _, test := range tests {
want: []string{"one", "two"}, t.Run(test.cmd, func(t *testing.T) {
err: nil, cmd, err := redis.Parse(ParsePing, test.cmd)
}, testx.AssertEqual(t, err, test.err)
} if err == nil {
testx.AssertEqual(t, cmd.message, test.want)
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 {
cmd string
func TestPingExec(t *testing.T) { res any
db, red := getDB(t) out string
defer db.Close() }{
{
tests := []struct { cmd: "ping",
name string res: "PONG",
cmd *conn.Ping out: "PONG",
res any },
out string {
}{ cmd: "ping hello",
{ res: "hello",
name: "ping", out: "hello",
cmd: command.MustParse[*conn.Ping]("ping"), },
res: "PONG", }
out: "PONG",
}, for _, test := range tests {
{ t.Run(test.cmd, func(t *testing.T) {
name: "ping hello", conn := redis.NewFakeConn()
cmd: command.MustParse[*conn.Ping]("ping hello"), cmd := redis.MustParse(ParsePing, test.cmd)
res: "hello", res, err := cmd.Run(conn, red)
out: "hello", testx.AssertNoErr(t, err)
}, testx.AssertEqual(t, res, test.res)
{ testx.AssertEqual(t, conn.Out(), test.out)
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)
})
}
}

View File

@@ -1,4 +1,4 @@
package hash_test package hash
import ( import (
"testing" "testing"

View File

@@ -11,15 +11,15 @@ import (
// https://redis.io/commands/hdel // https://redis.io/commands/hdel
type HDel struct { type HDel struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Fields []string fields []string
} }
func ParseHDel(b redis.BaseCmd) (*HDel, error) { func ParseHDel(b redis.BaseCmd) (*HDel, error) {
cmd := &HDel{BaseCmd: b} cmd := &HDel{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.Strings(&cmd.Fields), parser.Strings(&cmd.fields),
).Required(2).Run(cmd.Args()) ).Required(2).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -28,7 +28,7 @@ func ParseHDel(b redis.BaseCmd) (*HDel, error) {
} }
func (cmd *HDel) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *HDel) Run(w redis.Writer, red redis.Redka) (any, error) {
count, err := red.Hash().Delete(cmd.Key, cmd.Fields...) count, err := red.Hash().Delete(cmd.key, cmd.fields...)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,46 +1,39 @@
package hash_test package hash
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/hash"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestHDelParse(t *testing.T) { func TestHDelParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte
key string key string
fields []string fields []string
err error err error
}{ }{
{ {
name: "hdel", cmd: "hdel",
args: command.BuildArgs("hdel"),
key: "", key: "",
fields: nil, fields: nil,
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hdel person", cmd: "hdel person",
args: command.BuildArgs("hdel", "person"),
key: "", key: "",
fields: nil, fields: nil,
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hdel person name", cmd: "hdel person name",
args: command.BuildArgs("hdel", "person", "name"),
key: "person", key: "person",
fields: []string{"name"}, fields: []string{"name"},
err: nil, err: nil,
}, },
{ {
name: "hdel person name age", cmd: "hdel person name age",
args: command.BuildArgs("hdel", "person", "name", "age"),
key: "person", key: "person",
fields: []string{"name", "age"}, fields: []string{"name", "age"},
err: nil, err: nil,
@@ -48,13 +41,12 @@ func TestHDelParse(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseHDel, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*hash.HDel) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, cm.Key, test.key) testx.AssertEqual(t, cmd.fields, test.fields)
testx.AssertEqual(t, cm.Fields, test.fields)
} }
}) })
} }
@@ -68,7 +60,7 @@ func TestHDelExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
cmd := command.MustParse[*hash.HDel]("hdel person name") cmd := redis.MustParse(ParseHDel, "hdel person name")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -89,7 +81,7 @@ func TestHDelExec(t *testing.T) {
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
_, _ = db.Hash().Set("person", "happy", true) _, _ = db.Hash().Set("person", "happy", true)
cmd := command.MustParse[*hash.HDel]("hdel person name happy city") cmd := redis.MustParse(ParseHDel, "hdel person name happy city")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -111,7 +103,7 @@ func TestHDelExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
cmd := command.MustParse[*hash.HDel]("hdel person name age") cmd := redis.MustParse(ParseHDel, "hdel person name age")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)

View File

@@ -7,8 +7,8 @@ import "github.com/nalgeon/redka/internal/redis"
// https://redis.io/commands/hexists // https://redis.io/commands/hexists
type HExists struct { type HExists struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Field string field string
} }
func ParseHExists(b redis.BaseCmd) (*HExists, error) { func ParseHExists(b redis.BaseCmd) (*HExists, error) {
@@ -16,13 +16,13 @@ func ParseHExists(b redis.BaseCmd) (*HExists, error) {
if len(cmd.Args()) != 2 { if len(cmd.Args()) != 2 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Key = string(cmd.Args()[0]) cmd.key = string(cmd.Args()[0])
cmd.Field = string(cmd.Args()[1]) cmd.field = string(cmd.Args()[1])
return cmd, nil return cmd, nil
} }
func (cmd *HExists) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *HExists) Run(w redis.Writer, red redis.Redka) (any, error) {
ok, err := red.Hash().Exists(cmd.Key, cmd.Field) ok, err := red.Hash().Exists(cmd.key, cmd.field)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,46 +1,39 @@
package hash_test package hash
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/hash"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestHExistsParse(t *testing.T) { func TestHExistsParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte
key string key string
field string field string
err error err error
}{ }{
{ {
name: "hexists", cmd: "hexists",
args: command.BuildArgs("hexists"),
key: "", key: "",
field: "", field: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hexists person", cmd: "hexists person",
args: command.BuildArgs("hexists", "person"),
key: "", key: "",
field: "", field: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hexists person name", cmd: "hexists person name",
args: command.BuildArgs("hexists", "person", "name"),
key: "person", key: "person",
field: "name", field: "name",
err: nil, err: nil,
}, },
{ {
name: "hexists person name age", cmd: "hexists person name age",
args: command.BuildArgs("hexists", "person", "name", "age"),
key: "", key: "",
field: "", field: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
@@ -48,13 +41,12 @@ func TestHExistsParse(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseHExists, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*hash.HExists) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, cm.Key, test.key) testx.AssertEqual(t, cmd.field, test.field)
testx.AssertEqual(t, cm.Field, test.field)
} }
}) })
} }
@@ -67,7 +59,7 @@ func TestHExistsExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
cmd := command.MustParse[*hash.HExists]("hexists person name") cmd := redis.MustParse(ParseHExists, "hexists person name")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -81,7 +73,7 @@ func TestHExistsExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
cmd := command.MustParse[*hash.HExists]("hexists person age") cmd := redis.MustParse(ParseHExists, "hexists person age")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -93,7 +85,7 @@ func TestHExistsExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*hash.HExists]("hexists person name") cmd := redis.MustParse(ParseHExists, "hexists person name")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)

View File

@@ -10,8 +10,8 @@ import (
// https://redis.io/commands/hget // https://redis.io/commands/hget
type HGet struct { type HGet struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Field string field string
} }
func ParseHGet(b redis.BaseCmd) (*HGet, error) { func ParseHGet(b redis.BaseCmd) (*HGet, error) {
@@ -19,13 +19,13 @@ func ParseHGet(b redis.BaseCmd) (*HGet, error) {
if len(cmd.Args()) != 2 { if len(cmd.Args()) != 2 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Key = string(cmd.Args()[0]) cmd.key = string(cmd.Args()[0])
cmd.Field = string(cmd.Args()[1]) cmd.field = string(cmd.Args()[1])
return cmd, nil return cmd, nil
} }
func (cmd *HGet) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *HGet) Run(w redis.Writer, red redis.Redka) (any, error) {
val, err := red.Hash().Get(cmd.Key, cmd.Field) val, err := red.Hash().Get(cmd.key, cmd.field)
if err == core.ErrNotFound { if err == core.ErrNotFound {
w.WriteNull() w.WriteNull()
return val, nil return val, nil

View File

@@ -1,10 +1,8 @@
package hash_test package hash
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/hash"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,36 +10,31 @@ import (
func TestHGetParse(t *testing.T) { func TestHGetParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte
key string key string
field string field string
err error err error
}{ }{
{ {
name: "hget", cmd: "hget",
args: command.BuildArgs("hget"),
key: "", key: "",
field: "", field: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hget person", cmd: "hget person",
args: command.BuildArgs("hget", "person"),
key: "", key: "",
field: "", field: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hget person name", cmd: "hget person name",
args: command.BuildArgs("hget", "person", "name"),
key: "person", key: "person",
field: "name", field: "name",
err: nil, err: nil,
}, },
{ {
name: "hget person name age", cmd: "hget person name age",
args: command.BuildArgs("hget", "person", "name", "age"),
key: "", key: "",
field: "", field: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
@@ -49,13 +42,12 @@ func TestHGetParse(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseHGet, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*hash.HGet) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, cm.Key, test.key) testx.AssertEqual(t, cmd.field, test.field)
testx.AssertEqual(t, cm.Field, test.field)
} }
}) })
} }
@@ -68,7 +60,7 @@ func TestHGetExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
cmd := command.MustParse[*hash.HGet]("hget person name") cmd := redis.MustParse(ParseHGet, "hget person name")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -82,7 +74,7 @@ func TestHGetExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
cmd := command.MustParse[*hash.HGet]("hget person age") cmd := redis.MustParse(ParseHGet, "hget person age")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -94,7 +86,7 @@ func TestHGetExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*hash.HGet]("hget person name") cmd := redis.MustParse(ParseHGet, "hget person name")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)

View File

@@ -7,7 +7,7 @@ import "github.com/nalgeon/redka/internal/redis"
// https://redis.io/commands/hgetall // https://redis.io/commands/hgetall
type HGetAll struct { type HGetAll struct {
redis.BaseCmd redis.BaseCmd
Key string key string
} }
func ParseHGetAll(b redis.BaseCmd) (*HGetAll, error) { func ParseHGetAll(b redis.BaseCmd) (*HGetAll, error) {
@@ -15,12 +15,12 @@ func ParseHGetAll(b redis.BaseCmd) (*HGetAll, error) {
if len(cmd.Args()) != 1 { if len(cmd.Args()) != 1 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Key = string(cmd.Args()[0]) cmd.key = string(cmd.Args()[0])
return cmd, nil return cmd, nil
} }
func (cmd *HGetAll) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *HGetAll) Run(w redis.Writer, red redis.Redka) (any, error) {
items, err := red.Hash().Items(cmd.Key) items, err := red.Hash().Items(cmd.key)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,10 +1,8 @@
package hash_test package hash
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/hash"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,38 +10,33 @@ import (
func TestHGetAllParse(t *testing.T) { func TestHGetAllParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte key string
key string err error
err error
}{ }{
{ {
name: "hgetall", cmd: "hgetall",
args: command.BuildArgs("hgetall"), key: "",
key: "", err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hgetall person", cmd: "hgetall person",
args: command.BuildArgs("hgetall", "person"), key: "person",
key: "person", err: nil,
err: nil,
}, },
{ {
name: "hgetall person name", cmd: "hgetall person name",
args: command.BuildArgs("hgetall", "person", "name"), key: "",
key: "", err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseHGetAll, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*hash.HGetAll) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, cm.Key, test.key)
} }
}) })
} }
@@ -57,7 +50,7 @@ func TestHGetAllExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
cmd := command.MustParse[*hash.HGetAll]("hgetall person") cmd := redis.MustParse(ParseHGetAll, "hgetall person")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -73,7 +66,7 @@ func TestHGetAllExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*hash.HGetAll]("hgetall person") cmd := redis.MustParse(ParseHGetAll, "hgetall person")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)

View File

@@ -11,17 +11,17 @@ import (
// https://redis.io/commands/hincrby // https://redis.io/commands/hincrby
type HIncrBy struct { type HIncrBy struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Field string field string
Delta int delta int
} }
func ParseHIncrBy(b redis.BaseCmd) (*HIncrBy, error) { func ParseHIncrBy(b redis.BaseCmd) (*HIncrBy, error) {
cmd := &HIncrBy{BaseCmd: b} cmd := &HIncrBy{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.String(&cmd.Field), parser.String(&cmd.field),
parser.Int(&cmd.Delta), parser.Int(&cmd.delta),
).Required(3).Run(cmd.Args()) ).Required(3).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -30,7 +30,7 @@ func ParseHIncrBy(b redis.BaseCmd) (*HIncrBy, error) {
} }
func (cmd *HIncrBy) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *HIncrBy) Run(w redis.Writer, red redis.Redka) (any, error) {
val, err := red.Hash().Incr(cmd.Key, cmd.Field, cmd.Delta) val, err := red.Hash().Incr(cmd.key, cmd.field, cmd.delta)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,10 +1,8 @@
package hash_test package hash
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/hash"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,37 +10,32 @@ import (
func TestHIncrByParse(t *testing.T) { func TestHIncrByParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte
key string key string
field string field string
delta int delta int
err error err error
}{ }{
{ {
name: "hincrby", cmd: "hincrby",
args: command.BuildArgs("hincrby"),
key: "", key: "",
field: "", field: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hincrby person", cmd: "hincrby person",
args: command.BuildArgs("hincrby", "person"),
key: "", key: "",
field: "", field: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hincrby person age", cmd: "hincrby person age",
args: command.BuildArgs("hincrby", "person", "age"),
key: "", key: "",
field: "", field: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hincrby person age 10", cmd: "hincrby person age 10",
args: command.BuildArgs("hincrby", "person", "age", "10"),
key: "person", key: "person",
field: "age", field: "age",
delta: 10, delta: 10,
@@ -51,14 +44,13 @@ func TestHIncrByParse(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseHIncrBy, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*hash.HIncrBy) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, cm.Key, test.key) testx.AssertEqual(t, cmd.field, test.field)
testx.AssertEqual(t, cm.Field, test.field) testx.AssertEqual(t, cmd.delta, test.delta)
testx.AssertEqual(t, cm.Delta, test.delta)
} }
}) })
} }
@@ -71,7 +63,7 @@ func TestHIncrByExec(t *testing.T) {
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
cmd := command.MustParse[*hash.HIncrBy]("hincrby person age 10") cmd := redis.MustParse(ParseHIncrBy, "hincrby person age 10")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -88,7 +80,7 @@ func TestHIncrByExec(t *testing.T) {
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
cmd := command.MustParse[*hash.HIncrBy]("hincrby person age -10") cmd := redis.MustParse(ParseHIncrBy, "hincrby person age -10")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -105,7 +97,7 @@ func TestHIncrByExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
cmd := command.MustParse[*hash.HIncrBy]("hincrby person age 10") cmd := redis.MustParse(ParseHIncrBy, "hincrby person age 10")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -120,7 +112,7 @@ func TestHIncrByExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*hash.HIncrBy]("hincrby person age 10") cmd := redis.MustParse(ParseHIncrBy, "hincrby person age 10")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)

View File

@@ -1,8 +1,6 @@
package hash package hash
import ( import (
"strconv"
"github.com/nalgeon/redka/internal/parser" "github.com/nalgeon/redka/internal/parser"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
) )
@@ -13,17 +11,17 @@ import (
// https://redis.io/commands/hincrbyfloat // https://redis.io/commands/hincrbyfloat
type HIncrByFloat struct { type HIncrByFloat struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Field string field string
Delta float64 delta float64
} }
func ParseHIncrByFloat(b redis.BaseCmd) (*HIncrByFloat, error) { func ParseHIncrByFloat(b redis.BaseCmd) (*HIncrByFloat, error) {
cmd := &HIncrByFloat{BaseCmd: b} cmd := &HIncrByFloat{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.String(&cmd.Field), parser.String(&cmd.field),
parser.Float(&cmd.Delta), parser.Float(&cmd.delta),
).Required(3).Run(cmd.Args()) ).Required(3).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -32,11 +30,11 @@ func ParseHIncrByFloat(b redis.BaseCmd) (*HIncrByFloat, error) {
} }
func (cmd *HIncrByFloat) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *HIncrByFloat) Run(w redis.Writer, red redis.Redka) (any, error) {
val, err := red.Hash().IncrFloat(cmd.Key, cmd.Field, cmd.Delta) val, err := red.Hash().IncrFloat(cmd.key, cmd.field, cmd.delta)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err
} }
w.WriteBulkString(strconv.FormatFloat(val, 'f', -1, 64)) redis.WriteFloat(w, val)
return val, nil return val, nil
} }

View File

@@ -1,10 +1,8 @@
package hash_test package hash
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/hash"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,37 +10,32 @@ import (
func TestHIncrByFloatParse(t *testing.T) { func TestHIncrByFloatParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte
key string key string
field string field string
delta float64 delta float64
err error err error
}{ }{
{ {
name: "hincrbyfloat", cmd: "hincrbyfloat",
args: command.BuildArgs("hincrbyfloat"),
key: "", key: "",
field: "", field: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hincrbyfloat person", cmd: "hincrbyfloat person",
args: command.BuildArgs("hincrbyfloat", "person"),
key: "", key: "",
field: "", field: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hincrbyfloat person age", cmd: "hincrbyfloat person age",
args: command.BuildArgs("hincrbyfloat", "person", "age"),
key: "", key: "",
field: "", field: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hincrbyfloat person age 10.5", cmd: "hincrbyfloat person age 10.5",
args: command.BuildArgs("hincrbyfloat", "person", "age", "10.5"),
key: "person", key: "person",
field: "age", field: "age",
delta: 10.5, delta: 10.5,
@@ -51,14 +44,13 @@ func TestHIncrByFloatParse(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseHIncrByFloat, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*hash.HIncrByFloat) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, cm.Key, test.key) testx.AssertEqual(t, cmd.field, test.field)
testx.AssertEqual(t, cm.Field, test.field) testx.AssertEqual(t, cmd.delta, test.delta)
testx.AssertEqual(t, cm.Delta, test.delta)
} }
}) })
} }
@@ -71,7 +63,7 @@ func TestHIncrByFloatExec(t *testing.T) {
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
cmd := command.MustParse[*hash.HIncrByFloat]("hincrbyfloat person age 10.5") cmd := redis.MustParse(ParseHIncrByFloat, "hincrbyfloat person age 10.5")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -88,7 +80,7 @@ func TestHIncrByFloatExec(t *testing.T) {
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
cmd := command.MustParse[*hash.HIncrByFloat]("hincrbyfloat person age -10.5") cmd := redis.MustParse(ParseHIncrByFloat, "hincrbyfloat person age -10.5")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -105,7 +97,7 @@ func TestHIncrByFloatExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
cmd := command.MustParse[*hash.HIncrByFloat]("hincrbyfloat person age 10.5") cmd := redis.MustParse(ParseHIncrByFloat, "hincrbyfloat person age 10.5")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -120,7 +112,7 @@ func TestHIncrByFloatExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*hash.HIncrByFloat]("hincrbyfloat person age 10.5") cmd := redis.MustParse(ParseHIncrByFloat, "hincrbyfloat person age 10.5")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)

View File

@@ -7,7 +7,7 @@ import "github.com/nalgeon/redka/internal/redis"
// https://redis.io/commands/hkeys // https://redis.io/commands/hkeys
type HKeys struct { type HKeys struct {
redis.BaseCmd redis.BaseCmd
Key string key string
} }
func ParseHKeys(b redis.BaseCmd) (*HKeys, error) { func ParseHKeys(b redis.BaseCmd) (*HKeys, error) {
@@ -15,12 +15,12 @@ func ParseHKeys(b redis.BaseCmd) (*HKeys, error) {
if len(cmd.Args()) != 1 { if len(cmd.Args()) != 1 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Key = string(cmd.Args()[0]) cmd.key = string(cmd.Args()[0])
return cmd, nil return cmd, nil
} }
func (cmd *HKeys) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *HKeys) Run(w redis.Writer, red redis.Redka) (any, error) {
fields, err := red.Hash().Fields(cmd.Key) fields, err := red.Hash().Fields(cmd.key)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,48 +1,41 @@
package hash_test package hash
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/hash"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestHKeysParse(t *testing.T) { func TestHKeysParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte key string
key string err error
err error
}{ }{
{ {
name: "hkeys", cmd: "hkeys",
args: command.BuildArgs("hkeys"), key: "",
key: "", err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hkeys person", cmd: "hkeys person",
args: command.BuildArgs("hkeys", "person"), key: "person",
key: "person", err: nil,
err: nil,
}, },
{ {
name: "hkeys person name", cmd: "hkeys person name",
args: command.BuildArgs("hkeys", "person", "name"), key: "",
key: "", err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseHKeys, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*hash.HKeys) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, cm.Key, test.key)
} }
}) })
} }
@@ -56,7 +49,7 @@ func TestHKeysExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
cmd := command.MustParse[*hash.HKeys]("hkeys person") cmd := redis.MustParse(ParseHKeys, "hkeys person")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -70,7 +63,7 @@ func TestHKeysExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*hash.HKeys]("hkeys person") cmd := redis.MustParse(ParseHKeys, "hkeys person")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)

View File

@@ -7,7 +7,7 @@ import "github.com/nalgeon/redka/internal/redis"
// https://redis.io/commands/hlen // https://redis.io/commands/hlen
type HLen struct { type HLen struct {
redis.BaseCmd redis.BaseCmd
Key string key string
} }
func ParseHLen(b redis.BaseCmd) (*HLen, error) { func ParseHLen(b redis.BaseCmd) (*HLen, error) {
@@ -15,12 +15,12 @@ func ParseHLen(b redis.BaseCmd) (*HLen, error) {
if len(cmd.Args()) != 1 { if len(cmd.Args()) != 1 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Key = string(cmd.Args()[0]) cmd.key = string(cmd.Args()[0])
return cmd, nil return cmd, nil
} }
func (cmd *HLen) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *HLen) Run(w redis.Writer, red redis.Redka) (any, error) {
count, err := red.Hash().Len(cmd.Key) count, err := red.Hash().Len(cmd.key)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,48 +1,41 @@
package hash_test package hash
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/hash"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestHLenParse(t *testing.T) { func TestHLenParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte key string
key string err error
err error
}{ }{
{ {
name: "hlen", cmd: "hlen",
args: command.BuildArgs("hlen"), key: "",
key: "", err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hlen person", cmd: "hlen person",
args: command.BuildArgs("hlen", "person"), key: "person",
key: "person", err: nil,
err: nil,
}, },
{ {
name: "hlen person name", cmd: "hlen person name",
args: command.BuildArgs("hlen", "person", "name"), key: "",
key: "", err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseHLen, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*hash.HLen) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, cm.Key, test.key)
} }
}) })
} }
@@ -56,7 +49,7 @@ func TestHLenExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
cmd := command.MustParse[*hash.HLen]("hlen person") cmd := redis.MustParse(ParseHLen, "hlen person")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -68,7 +61,7 @@ func TestHLenExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*hash.HLen]("hlen person") cmd := redis.MustParse(ParseHLen, "hlen person")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)

View File

@@ -11,15 +11,15 @@ import (
// https://redis.io/commands/hmget // https://redis.io/commands/hmget
type HMGet struct { type HMGet struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Fields []string fields []string
} }
func ParseHMGet(b redis.BaseCmd) (*HMGet, error) { func ParseHMGet(b redis.BaseCmd) (*HMGet, error) {
cmd := &HMGet{BaseCmd: b} cmd := &HMGet{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.Strings(&cmd.Fields), parser.Strings(&cmd.fields),
).Required(2).Run(cmd.Args()) ).Required(2).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -29,7 +29,7 @@ func ParseHMGet(b redis.BaseCmd) (*HMGet, error) {
func (cmd *HMGet) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *HMGet) Run(w redis.Writer, red redis.Redka) (any, error) {
// Get the field-value map for requested fields. // Get the field-value map for requested fields.
items, err := red.Hash().GetMany(cmd.Key, cmd.Fields...) items, err := red.Hash().GetMany(cmd.key, cmd.fields...)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err
@@ -38,8 +38,8 @@ func (cmd *HMGet) Run(w redis.Writer, red redis.Redka) (any, error) {
// Build the result slice. // Build the result slice.
// It will contain all values in the order of fields. // It will contain all values in the order of fields.
// Missing fields will have nil values. // Missing fields will have nil values.
vals := make([]core.Value, len(cmd.Fields)) vals := make([]core.Value, len(cmd.fields))
for i, field := range cmd.Fields { for i, field := range cmd.fields {
vals[i] = items[field] vals[i] = items[field]
} }

View File

@@ -1,10 +1,8 @@
package hash_test package hash
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/hash"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,36 +10,31 @@ import (
func TestHMGetParse(t *testing.T) { func TestHMGetParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte
key string key string
fields []string fields []string
err error err error
}{ }{
{ {
name: "hmget", cmd: "hmget",
args: command.BuildArgs("hmget"),
key: "", key: "",
fields: nil, fields: nil,
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hmget person", cmd: "hmget person",
args: command.BuildArgs("hmget", "person"),
key: "", key: "",
fields: nil, fields: nil,
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hmget person name", cmd: "hmget person name",
args: command.BuildArgs("hmget", "person", "name"),
key: "person", key: "person",
fields: []string{"name"}, fields: []string{"name"},
err: nil, err: nil,
}, },
{ {
name: "hmget person name age", cmd: "hmget person name age",
args: command.BuildArgs("hmget", "person", "name", "age"),
key: "person", key: "person",
fields: []string{"name", "age"}, fields: []string{"name", "age"},
err: nil, err: nil,
@@ -49,13 +42,12 @@ func TestHMGetParse(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseHMGet, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*hash.HMGet) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, cm.Key, test.key) testx.AssertEqual(t, cmd.fields, test.fields)
testx.AssertEqual(t, cm.Fields, test.fields)
} }
}) })
} }
@@ -69,7 +61,7 @@ func TestHMGetExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
cmd := command.MustParse[*hash.HMGet]("hmget person name") cmd := redis.MustParse(ParseHMGet, "hmget person name")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -87,7 +79,7 @@ func TestHMGetExec(t *testing.T) {
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
_, _ = db.Hash().Set("person", "happy", true) _, _ = db.Hash().Set("person", "happy", true)
cmd := command.MustParse[*hash.HMGet]("hmget person name happy city") cmd := redis.MustParse(ParseHMGet, "hmget person name happy city")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -106,7 +98,7 @@ func TestHMGetExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
cmd := command.MustParse[*hash.HMGet]("hmget person name age") cmd := redis.MustParse(ParseHMGet, "hmget person name age")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)

View File

@@ -10,15 +10,15 @@ import (
// https://redis.io/commands/hmset // https://redis.io/commands/hmset
type HMSet struct { type HMSet struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Items map[string]any items map[string]any
} }
func ParseHMSet(b redis.BaseCmd) (*HMSet, error) { func ParseHMSet(b redis.BaseCmd) (*HMSet, error) {
cmd := &HMSet{BaseCmd: b} cmd := &HMSet{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.AnyMap(&cmd.Items), parser.AnyMap(&cmd.items),
).Required(3).Run(cmd.Args()) ).Required(3).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -27,7 +27,7 @@ func ParseHMSet(b redis.BaseCmd) (*HMSet, error) {
} }
func (cmd *HMSet) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *HMSet) Run(w redis.Writer, red redis.Redka) (any, error) {
count, err := red.Hash().SetMany(cmd.Key, cmd.Items) count, err := red.Hash().SetMany(cmd.key, cmd.items)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,55 +1,46 @@
package hash_test package hash
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/hash"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestHMSetParse(t *testing.T) { func TestHMSetParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want HMSet
want hash.HMSet
err error err error
}{ }{
{ {
name: "hmset", cmd: "hmset",
args: command.BuildArgs("hmset"), want: HMSet{},
want: hash.HMSet{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hmset person", cmd: "hmset person",
args: command.BuildArgs("hmset", "person"), want: HMSet{},
want: hash.HMSet{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hmset person name", cmd: "hmset person name",
args: command.BuildArgs("hmset", "person", "name"), want: HMSet{},
want: hash.HMSet{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hmset person name alice", cmd: "hmset person name alice",
args: command.BuildArgs("hmset", "person", "name", "alice"), want: HMSet{key: "person", items: map[string]any{"name": []byte("alice")}},
want: hash.HMSet{Key: "person", Items: map[string]any{"name": []byte("alice")}},
err: nil, err: nil,
}, },
{ {
name: "hmset person name alice age", cmd: "hmset person name alice age",
args: command.BuildArgs("hmset", "person", "name", "alice", "age"), want: HMSet{},
want: hash.HMSet{},
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
{ {
name: "hmset person name alice age 25", cmd: "hmset person name alice age 25",
args: command.BuildArgs("hmset", "person", "name", "alice", "age", "25"), want: HMSet{key: "person", items: map[string]any{
want: hash.HMSet{Key: "person", Items: map[string]any{
"name": []byte("alice"), "name": []byte("alice"),
"age": []byte("25"), "age": []byte("25"),
}}, }},
@@ -58,13 +49,12 @@ func TestHMSetParse(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseHMSet, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*hash.HMSet) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.items, test.want.items)
testx.AssertEqual(t, cm.Items, test.want.Items)
} }
}) })
} }
@@ -75,7 +65,7 @@ func TestHMSetExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*hash.HMSet]("hmset person name alice") cmd := redis.MustParse(ParseHMSet, "hmset person name alice")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -90,7 +80,7 @@ func TestHMSetExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*hash.HMSet]("hmset person name alice age 25") cmd := redis.MustParse(ParseHMSet, "hmset person name alice age 25")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -109,7 +99,7 @@ func TestHMSetExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
cmd := command.MustParse[*hash.HMSet]("hmset person name bob age 50") cmd := redis.MustParse(ParseHMSet, "hmset person name bob age 50")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -129,7 +119,7 @@ func TestHMSetExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
cmd := command.MustParse[*hash.HMSet]("hmset person name bob age 50") cmd := redis.MustParse(ParseHMSet, "hmset person name bob age 50")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -10,35 +10,35 @@ import (
// https://redis.io/commands/hscan // https://redis.io/commands/hscan
type HScan struct { type HScan struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Cursor int cursor int
Match string match string
Count int count int
} }
func ParseHScan(b redis.BaseCmd) (*HScan, error) { func ParseHScan(b redis.BaseCmd) (*HScan, error) {
cmd := &HScan{BaseCmd: b} cmd := &HScan{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.Int(&cmd.Cursor), parser.Int(&cmd.cursor),
parser.Named("match", parser.String(&cmd.Match)), parser.Named("match", parser.String(&cmd.match)),
parser.Named("count", parser.Int(&cmd.Count)), parser.Named("count", parser.Int(&cmd.count)),
).Required(2).Run(cmd.Args()) ).Required(2).Run(cmd.Args())
if err != nil { if err != nil {
return cmd, err return cmd, err
} }
// all keys by default // all keys by default
if cmd.Match == "" { if cmd.match == "" {
cmd.Match = "*" cmd.match = "*"
} }
return cmd, nil return cmd, nil
} }
func (cmd *HScan) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *HScan) Run(w redis.Writer, red redis.Redka) (any, error) {
res, err := red.Hash().Scan(cmd.Key, cmd.Cursor, cmd.Match, cmd.Count) res, err := red.Hash().Scan(cmd.key, cmd.cursor, cmd.match, cmd.count)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,10 +1,8 @@
package hash_test package hash
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/hash"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/rhash" "github.com/nalgeon/redka/internal/rhash"
@@ -13,8 +11,7 @@ import (
func TestHScanParse(t *testing.T) { func TestHScanParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte
key string key string
cursor int cursor int
match string match string
@@ -22,8 +19,7 @@ func TestHScanParse(t *testing.T) {
err error err error
}{ }{
{ {
name: "hscan", cmd: "hscan",
args: command.BuildArgs("hscan"),
key: "", key: "",
cursor: 0, cursor: 0,
match: "*", match: "*",
@@ -31,8 +27,7 @@ func TestHScanParse(t *testing.T) {
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hscan person", cmd: "hscan person",
args: command.BuildArgs("hscan", "person"),
key: "", key: "",
cursor: 0, cursor: 0,
match: "*", match: "*",
@@ -40,8 +35,7 @@ func TestHScanParse(t *testing.T) {
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hscan person 15", cmd: "hscan person 15",
args: command.BuildArgs("hscan", "person", "15"),
key: "person", key: "person",
cursor: 15, cursor: 15,
match: "*", match: "*",
@@ -49,8 +43,7 @@ func TestHScanParse(t *testing.T) {
err: nil, err: nil,
}, },
{ {
name: "hscan person 15 match *", cmd: "hscan person 15 match *",
args: command.BuildArgs("hscan", "person", "15", "match", "*"),
key: "person", key: "person",
cursor: 15, cursor: 15,
match: "*", match: "*",
@@ -58,8 +51,7 @@ func TestHScanParse(t *testing.T) {
err: nil, err: nil,
}, },
{ {
name: "hscan person 15 match * count 5", cmd: "hscan person 15 match * count 5",
args: command.BuildArgs("hscan", "person", "15", "match", "*", "count", "5"),
key: "person", key: "person",
cursor: 15, cursor: 15,
match: "*", match: "*",
@@ -67,8 +59,7 @@ func TestHScanParse(t *testing.T) {
err: nil, err: nil,
}, },
{ {
name: "hscan person 15 count 5 match *", cmd: "hscan person 15 count 5 match *",
args: command.BuildArgs("hscan", "person", "15", "count", "5", "match", "*"),
key: "person", key: "person",
cursor: 15, cursor: 15,
match: "*", match: "*",
@@ -76,8 +67,7 @@ func TestHScanParse(t *testing.T) {
err: nil, err: nil,
}, },
{ {
name: "hscan person 15 match k2* count 5", cmd: "hscan person 15 match k2* count 5",
args: command.BuildArgs("hscan", "person", "15", "match", "k2*", "count", "5"),
key: "person", key: "person",
cursor: 15, cursor: 15,
match: "k2*", match: "k2*",
@@ -85,8 +75,7 @@ func TestHScanParse(t *testing.T) {
err: nil, err: nil,
}, },
{ {
name: "hscan person ten", cmd: "hscan person ten",
args: command.BuildArgs("hscan", "person", "ten"),
key: "", key: "",
cursor: 0, cursor: 0,
match: "", match: "",
@@ -94,8 +83,7 @@ func TestHScanParse(t *testing.T) {
err: redis.ErrInvalidInt, err: redis.ErrInvalidInt,
}, },
{ {
name: "hscan person 15 *", cmd: "hscan person 15 *",
args: command.BuildArgs("hscan", "person", "15", "*"),
key: "", key: "",
cursor: 0, cursor: 0,
match: "", match: "",
@@ -103,8 +91,7 @@ func TestHScanParse(t *testing.T) {
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
{ {
name: "hscan person 15 * 5", cmd: "hscan person 15 * 5",
args: command.BuildArgs("hscan", "person", "15", "*", "5"),
key: "", key: "",
cursor: 0, cursor: 0,
match: "", match: "",
@@ -114,15 +101,14 @@ func TestHScanParse(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseHScan, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
scmd := cmd.(*hash.HScan) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, scmd.Key, test.key) testx.AssertEqual(t, cmd.cursor, test.cursor)
testx.AssertEqual(t, scmd.Cursor, test.cursor) testx.AssertEqual(t, cmd.match, test.match)
testx.AssertEqual(t, scmd.Match, test.match) testx.AssertEqual(t, cmd.count, test.count)
testx.AssertEqual(t, scmd.Count, test.count)
} }
}) })
} }
@@ -140,7 +126,7 @@ func TestHScanExec(t *testing.T) {
t.Run("hscan all", func(t *testing.T) { t.Run("hscan all", func(t *testing.T) {
{ {
cmd := command.MustParse[*hash.HScan]("hscan key 0") cmd := redis.MustParse(ParseHScan, "hscan key 0")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -156,7 +142,7 @@ func TestHScanExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "2,5,10,f11,11,f12,12,f21,21,f22,22,f31,31") testx.AssertEqual(t, conn.Out(), "2,5,10,f11,11,f12,12,f21,21,f22,22,f31,31")
} }
{ {
cmd := command.MustParse[*hash.HScan]("hscan key 5") cmd := redis.MustParse(ParseHScan, "hscan key 5")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -170,7 +156,7 @@ func TestHScanExec(t *testing.T) {
}) })
t.Run("hscan pattern", func(t *testing.T) { t.Run("hscan pattern", func(t *testing.T) {
cmd := command.MustParse[*hash.HScan]("hscan key 0 match f2*") cmd := redis.MustParse(ParseHScan, "hscan key 0 match f2*")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -189,7 +175,7 @@ func TestHScanExec(t *testing.T) {
t.Run("hscan count", func(t *testing.T) { t.Run("hscan count", func(t *testing.T) {
{ {
// page 1 // page 1
cmd := command.MustParse[*hash.HScan]("hscan key 0 match * count 2") cmd := redis.MustParse(ParseHScan, "hscan key 0 match * count 2")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -206,7 +192,7 @@ func TestHScanExec(t *testing.T) {
} }
{ {
// page 2 // page 2
cmd := command.MustParse[*hash.HScan]("hscan key 2 match * count 2") cmd := redis.MustParse(ParseHScan, "hscan key 2 match * count 2")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -223,7 +209,7 @@ func TestHScanExec(t *testing.T) {
} }
{ {
// page 3 // page 3
cmd := command.MustParse[*hash.HScan]("hscan key 4 match * count 2") cmd := redis.MustParse(ParseHScan, "hscan key 4 match * count 2")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -238,7 +224,7 @@ func TestHScanExec(t *testing.T) {
} }
{ {
// no more pages // no more pages
cmd := command.MustParse[*hash.HScan]("hscan key 5 match * count 2") cmd := redis.MustParse(ParseHScan, "hscan key 5 match * count 2")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)

View File

@@ -10,15 +10,15 @@ import (
// https://redis.io/commands/hset // https://redis.io/commands/hset
type HSet struct { type HSet struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Items map[string]any items map[string]any
} }
func ParseHSet(b redis.BaseCmd) (*HSet, error) { func ParseHSet(b redis.BaseCmd) (*HSet, error) {
cmd := &HSet{BaseCmd: b} cmd := &HSet{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.AnyMap(&cmd.Items), parser.AnyMap(&cmd.items),
).Required(3).Run(cmd.Args()) ).Required(3).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -27,7 +27,7 @@ func ParseHSet(b redis.BaseCmd) (*HSet, error) {
} }
func (cmd *HSet) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *HSet) Run(w redis.Writer, red redis.Redka) (any, error) {
count, err := red.Hash().SetMany(cmd.Key, cmd.Items) count, err := red.Hash().SetMany(cmd.key, cmd.items)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,55 +1,46 @@
package hash_test package hash
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/hash"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestHSetParse(t *testing.T) { func TestHSetParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want HSet
want hash.HSet
err error err error
}{ }{
{ {
name: "hset", cmd: "hset",
args: command.BuildArgs("hset"), want: HSet{},
want: hash.HSet{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hset person", cmd: "hset person",
args: command.BuildArgs("hset", "person"), want: HSet{},
want: hash.HSet{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hset person name", cmd: "hset person name",
args: command.BuildArgs("hset", "person", "name"), want: HSet{},
want: hash.HSet{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hset person name alice", cmd: "hset person name alice",
args: command.BuildArgs("hset", "person", "name", "alice"), want: HSet{key: "person", items: map[string]any{"name": []byte("alice")}},
want: hash.HSet{Key: "person", Items: map[string]any{"name": []byte("alice")}},
err: nil, err: nil,
}, },
{ {
name: "hset person name alice age", cmd: "hset person name alice age",
args: command.BuildArgs("hset", "person", "name", "alice", "age"), want: HSet{},
want: hash.HSet{},
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
{ {
name: "hset person name alice age 25", cmd: "hset person name alice age 25",
args: command.BuildArgs("hset", "person", "name", "alice", "age", "25"), want: HSet{key: "person", items: map[string]any{
want: hash.HSet{Key: "person", Items: map[string]any{
"name": []byte("alice"), "name": []byte("alice"),
"age": []byte("25"), "age": []byte("25"),
}}, }},
@@ -58,13 +49,12 @@ func TestHSetParse(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseHSet, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*hash.HSet) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.items, test.want.items)
testx.AssertEqual(t, cm.Items, test.want.Items)
} }
}) })
} }
@@ -75,7 +65,7 @@ func TestHSetExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*hash.HSet]("hset person name alice") cmd := redis.MustParse(ParseHSet, "hset person name alice")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -90,7 +80,7 @@ func TestHSetExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*hash.HSet]("hset person name alice age 25") cmd := redis.MustParse(ParseHSet, "hset person name alice age 25")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -109,7 +99,7 @@ func TestHSetExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
cmd := command.MustParse[*hash.HSet]("hset person name bob age 50") cmd := redis.MustParse(ParseHSet, "hset person name bob age 50")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -129,7 +119,7 @@ func TestHSetExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
cmd := command.MustParse[*hash.HSet]("hset person name bob age 50") cmd := redis.MustParse(ParseHSet, "hset person name bob age 50")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -7,9 +7,9 @@ import "github.com/nalgeon/redka/internal/redis"
// https://redis.io/commands/hsetnx // https://redis.io/commands/hsetnx
type HSetNX struct { type HSetNX struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Field string field string
Value []byte value []byte
} }
func ParseHSetNX(b redis.BaseCmd) (*HSetNX, error) { func ParseHSetNX(b redis.BaseCmd) (*HSetNX, error) {
@@ -17,14 +17,14 @@ func ParseHSetNX(b redis.BaseCmd) (*HSetNX, error) {
if len(cmd.Args()) != 3 { if len(cmd.Args()) != 3 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Key = string(cmd.Args()[0]) cmd.key = string(cmd.Args()[0])
cmd.Field = string(cmd.Args()[1]) cmd.field = string(cmd.Args()[1])
cmd.Value = cmd.Args()[2] cmd.value = cmd.Args()[2]
return cmd, nil return cmd, nil
} }
func (cmd *HSetNX) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *HSetNX) Run(w redis.Writer, red redis.Redka) (any, error) {
ok, err := red.Hash().SetNotExists(cmd.Key, cmd.Field, cmd.Value) ok, err := red.Hash().SetNotExists(cmd.key, cmd.field, cmd.value)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,61 +1,52 @@
package hash_test package hash
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/hash"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestHSetNXParse(t *testing.T) { func TestHSetNXParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want HSetNX
want hash.HSetNX
err error err error
}{ }{
{ {
name: "hsetnx", cmd: "hsetnx",
args: command.BuildArgs("hsetnx"), want: HSetNX{},
want: hash.HSetNX{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hsetnx person", cmd: "hsetnx person",
args: command.BuildArgs("hsetnx", "person"), want: HSetNX{},
want: hash.HSetNX{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hsetnx person name", cmd: "hsetnx person name",
args: command.BuildArgs("hsetnx", "person", "name"), want: HSetNX{},
want: hash.HSetNX{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hsetnx person name alice", cmd: "hsetnx person name alice",
args: command.BuildArgs("hsetnx", "person", "name", "alice"), want: HSetNX{key: "person", field: "name", value: []byte("alice")},
want: hash.HSetNX{Key: "person", Field: "name", Value: []byte("alice")},
err: nil, err: nil,
}, },
{ {
name: "hsetnx person name alice age 25", cmd: "hsetnx person name alice age 25",
args: command.BuildArgs("hsetnx", "person", "name", "alice", "age", "25"), want: HSetNX{},
want: hash.HSetNX{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseHSetNX, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*hash.HSetNX) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.value, test.want.value)
testx.AssertEqual(t, cm.Value, test.want.Value)
} }
}) })
} }
@@ -66,7 +57,7 @@ func TestHSetNXExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*hash.HSetNX]("hsetnx person name alice") cmd := redis.MustParse(ParseHSetNX, "hsetnx person name alice")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -83,7 +74,7 @@ func TestHSetNXExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
cmd := command.MustParse[*hash.HSetNX]("hsetnx person name bob") cmd := redis.MustParse(ParseHSetNX, "hsetnx person name bob")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -7,7 +7,7 @@ import "github.com/nalgeon/redka/internal/redis"
// https://redis.io/commands/hvals // https://redis.io/commands/hvals
type HVals struct { type HVals struct {
redis.BaseCmd redis.BaseCmd
Key string key string
} }
func ParseHVals(b redis.BaseCmd) (*HVals, error) { func ParseHVals(b redis.BaseCmd) (*HVals, error) {
@@ -15,12 +15,12 @@ func ParseHVals(b redis.BaseCmd) (*HVals, error) {
if len(cmd.Args()) != 1 { if len(cmd.Args()) != 1 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Key = string(cmd.Args()[0]) cmd.key = string(cmd.Args()[0])
return cmd, nil return cmd, nil
} }
func (cmd *HVals) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *HVals) Run(w redis.Writer, red redis.Redka) (any, error) {
vals, err := red.Hash().Values(cmd.Key) vals, err := red.Hash().Values(cmd.key)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,10 +1,8 @@
package hash_test package hash
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/hash"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,38 +10,33 @@ import (
func TestHValsParse(t *testing.T) { func TestHValsParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte key string
key string err error
err error
}{ }{
{ {
name: "hvals", cmd: "hvals",
args: command.BuildArgs("hvals"), key: "",
key: "", err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
{ {
name: "hvals person", cmd: "hvals person",
args: command.BuildArgs("hvals", "person"), key: "person",
key: "person", err: nil,
err: nil,
}, },
{ {
name: "hvals person name", cmd: "hvals person name",
args: command.BuildArgs("hvals", "person", "name"), key: "",
key: "", err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseHVals, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*hash.HVals) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, cm.Key, test.key)
} }
}) })
} }
@@ -57,7 +50,7 @@ func TestHValsExec(t *testing.T) {
_, _ = db.Hash().Set("person", "name", "alice") _, _ = db.Hash().Set("person", "name", "alice")
_, _ = db.Hash().Set("person", "age", 25) _, _ = db.Hash().Set("person", "age", 25)
cmd := command.MustParse[*hash.HVals]("hvals person") cmd := redis.MustParse(ParseHVals, "hvals person")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -69,7 +62,7 @@ func TestHValsExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*hash.HVals]("hvals person") cmd := redis.MustParse(ParseHVals, "hvals person")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)

View File

@@ -10,13 +10,13 @@ import (
// https://redis.io/commands/del // https://redis.io/commands/del
type Del struct { type Del struct {
redis.BaseCmd redis.BaseCmd
Keys []string keys []string
} }
func ParseDel(b redis.BaseCmd) (*Del, error) { func ParseDel(b redis.BaseCmd) (*Del, error) {
cmd := &Del{BaseCmd: b} cmd := &Del{BaseCmd: b}
err := parser.New( err := parser.New(
parser.Strings(&cmd.Keys), parser.Strings(&cmd.keys),
).Required(1).Run(cmd.Args()) ).Required(1).Run(cmd.Args())
if err != nil { if err != nil {
return cmd, err return cmd, err
@@ -25,7 +25,7 @@ func ParseDel(b redis.BaseCmd) (*Del, error) {
} }
func (cmd *Del) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *Del) Run(w redis.Writer, red redis.Redka) (any, error) {
count, err := red.Key().Delete(cmd.Keys...) count, err := red.Key().Delete(cmd.keys...)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,47 +1,41 @@
package key_test package key
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/key"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestDelParse(t *testing.T) { func TestDelParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte
want []string want []string
err error err error
}{ }{
{ {
name: "del", cmd: "del",
args: command.BuildArgs("del"),
want: nil, want: nil,
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "del name", cmd: "del name",
args: command.BuildArgs("del", "name"),
want: []string{"name"}, want: []string{"name"},
err: nil, err: nil,
}, },
{ {
name: "del name age", cmd: "del name age",
args: command.BuildArgs("del", "name", "age"),
want: []string{"name", "age"}, want: []string{"name", "age"},
err: nil, err: nil,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseDel, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
testx.AssertEqual(t, cmd.(*key.Del).Keys, test.want) testx.AssertEqual(t, cmd.keys, test.want)
} }
}) })
} }
@@ -49,33 +43,29 @@ func TestDelParse(t *testing.T) {
func TestDelExec(t *testing.T) { func TestDelExec(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
cmd *key.Del res any
res any out string
out string
}{ }{
{ {
name: "del one", cmd: "del name",
cmd: command.MustParse[*key.Del]("del name"), res: 1,
res: 1, out: "1",
out: "1",
}, },
{ {
name: "del all", cmd: "del name age",
cmd: command.MustParse[*key.Del]("del name age"), res: 2,
res: 2, out: "2",
out: "2",
}, },
{ {
name: "del some", cmd: "del name age street",
cmd: command.MustParse[*key.Del]("del name age street"), res: 2,
res: 2, out: "2",
out: "2",
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
@@ -84,7 +74,8 @@ func TestDelExec(t *testing.T) {
_ = db.Str().Set("city", "paris") _ = db.Str().Set("city", "paris")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := test.cmd.Run(conn, red) cmd := redis.MustParse(ParseDel, test.cmd)
res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
testx.AssertEqual(t, res, test.res) testx.AssertEqual(t, res, test.res)
testx.AssertEqual(t, conn.Out(), test.out) testx.AssertEqual(t, conn.Out(), test.out)

View File

@@ -10,13 +10,13 @@ import (
// https://redis.io/commands/exists // https://redis.io/commands/exists
type Exists struct { type Exists struct {
redis.BaseCmd redis.BaseCmd
Keys []string keys []string
} }
func ParseExists(b redis.BaseCmd) (*Exists, error) { func ParseExists(b redis.BaseCmd) (*Exists, error) {
cmd := &Exists{BaseCmd: b} cmd := &Exists{BaseCmd: b}
err := parser.New( err := parser.New(
parser.Strings(&cmd.Keys), parser.Strings(&cmd.keys),
).Required(1).Run(cmd.Args()) ).Required(1).Run(cmd.Args())
if err != nil { if err != nil {
return cmd, err return cmd, err
@@ -25,7 +25,7 @@ func ParseExists(b redis.BaseCmd) (*Exists, error) {
} }
func (cmd *Exists) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *Exists) Run(w redis.Writer, red redis.Redka) (any, error) {
count, err := red.Key().Count(cmd.Keys...) count, err := red.Key().Count(cmd.keys...)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,47 +1,41 @@
package key_test package key
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/key"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestExistsParse(t *testing.T) { func TestExistsParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte
want []string want []string
err error err error
}{ }{
{ {
name: "exists", cmd: "exists",
args: command.BuildArgs("exists"),
want: nil, want: nil,
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "exists name", cmd: "exists name",
args: command.BuildArgs("exists", "name"),
want: []string{"name"}, want: []string{"name"},
err: nil, err: nil,
}, },
{ {
name: "exists name age", cmd: "exists name age",
args: command.BuildArgs("exists", "name", "age"),
want: []string{"name", "age"}, want: []string{"name", "age"},
err: nil, err: nil,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseExists, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
testx.AssertEqual(t, cmd.(*key.Exists).Keys, test.want) testx.AssertEqual(t, cmd.keys, test.want)
} }
}) })
} }
@@ -56,35 +50,32 @@ func TestExistsExec(t *testing.T) {
_ = db.Str().Set("city", "paris") _ = db.Str().Set("city", "paris")
tests := []struct { tests := []struct {
name string cmd string
cmd *key.Exists res any
res any out string
out string
}{ }{
{ {
name: "exists one", cmd: "exists name",
cmd: command.MustParse[*key.Exists]("exists name"), res: 1,
res: 1, out: "1",
out: "1",
}, },
{ {
name: "exists all", cmd: "exists name age",
cmd: command.MustParse[*key.Exists]("exists name age"), res: 2,
res: 2, out: "2",
out: "2",
}, },
{ {
name: "exists some", cmd: "exists name age street",
cmd: command.MustParse[*key.Exists]("exists name age street"), res: 2,
res: 2, out: "2",
out: "2",
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := test.cmd.Run(conn, red) cmd := redis.MustParse(ParseExists, test.cmd)
res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
testx.AssertEqual(t, res, test.res) testx.AssertEqual(t, res, test.res)
testx.AssertEqual(t, conn.Out(), test.out) testx.AssertEqual(t, conn.Out(), test.out)

View File

@@ -13,8 +13,8 @@ import (
// https://redis.io/commands/expire // https://redis.io/commands/expire
type Expire struct { type Expire struct {
redis.BaseCmd redis.BaseCmd
Key string key string
TTL time.Duration ttl time.Duration
} }
func ParseExpire(b redis.BaseCmd, multi int) (*Expire, error) { func ParseExpire(b redis.BaseCmd, multi int) (*Expire, error) {
@@ -22,19 +22,19 @@ func ParseExpire(b redis.BaseCmd, multi int) (*Expire, error) {
var ttl int var ttl int
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.Int(&ttl), parser.Int(&ttl),
).Required(2).Run(cmd.Args()) ).Required(2).Run(cmd.Args())
if err != nil { if err != nil {
return cmd, err return cmd, err
} }
cmd.TTL = time.Duration(multi*ttl) * time.Millisecond cmd.ttl = time.Duration(multi*ttl) * time.Millisecond
return cmd, nil return cmd, nil
} }
func (cmd *Expire) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *Expire) Run(w redis.Writer, red redis.Redka) (any, error) {
err := red.Key().Expire(cmd.Key, cmd.TTL) err := red.Key().Expire(cmd.key, cmd.ttl)
if err != nil && err != core.ErrNotFound { if err != nil && err != core.ErrNotFound {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,80 +1,79 @@
package key_test package key
import ( import (
"testing" "testing"
"time" "time"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/key"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestExpireParse(t *testing.T) { func TestExpireParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte key string
key string ttl time.Duration
ttl time.Duration err error
err error
}{ }{
{ {
name: "expire", cmd: "expire",
args: command.BuildArgs("expire"), key: "",
key: "", ttl: 0,
ttl: 0, err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
{ {
name: "expire name", cmd: "expire name",
args: command.BuildArgs("expire", "name"), key: "",
key: "", ttl: 0,
ttl: 0, err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
{ {
name: "expire name 60", cmd: "expire name 60",
args: command.BuildArgs("expire", "name", "60"), key: "name",
key: "name", ttl: 60 * 1000 * time.Millisecond,
ttl: 60 * 1000 * time.Millisecond, err: nil,
err: nil,
}, },
{ {
name: "expire name age", cmd: "expire name age",
args: command.BuildArgs("expire", "name", "age"), key: "",
key: "", ttl: 0,
ttl: 0, err: redis.ErrInvalidInt,
err: redis.ErrInvalidInt,
}, },
{ {
name: "expire name 60 age 60", cmd: "expire name 60 age 60",
args: command.BuildArgs("expire", "name", "60", "age", "60"), key: "",
key: "", ttl: 0,
ttl: 0, err: redis.ErrSyntaxError,
err: redis.ErrSyntaxError,
}, },
} }
parse := func(b redis.BaseCmd) (*Expire, error) {
return ParseExpire(b, 1000)
}
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(parse, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
testx.AssertEqual(t, cmd.(*key.Expire).Key, test.key) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, cmd.(*key.Expire).TTL, test.ttl) testx.AssertEqual(t, cmd.ttl, test.ttl)
} }
}) })
} }
} }
func TestExpireExec(t *testing.T) { func TestExpireExec(t *testing.T) {
parse := func(b redis.BaseCmd) (*Expire, error) {
return ParseExpire(b, 1000)
}
t.Run("create expire", func(t *testing.T) { t.Run("create expire", func(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.Expire]("expire name 60") cmd := redis.MustParse(parse, "expire name 60")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -92,7 +91,7 @@ func TestExpireExec(t *testing.T) {
_ = db.Str().SetExpires("name", "alice", 60*time.Second) _ = db.Str().SetExpires("name", "alice", 60*time.Second)
cmd := command.MustParse[*key.Expire]("expire name 30") cmd := redis.MustParse(parse, "expire name 30")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -110,7 +109,7 @@ func TestExpireExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.Expire]("expire name 0") cmd := redis.MustParse(parse, "expire name 0")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -127,7 +126,7 @@ func TestExpireExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.Expire]("expire name -10") cmd := redis.MustParse(parse, "expire name -10")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -144,7 +143,7 @@ func TestExpireExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.Expire]("expire age 60") cmd := redis.MustParse(parse, "expire age 60")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -13,8 +13,8 @@ import (
// https://redis.io/commands/expireat // https://redis.io/commands/expireat
type ExpireAt struct { type ExpireAt struct {
redis.BaseCmd redis.BaseCmd
Key string key string
At time.Time at time.Time
} }
func ParseExpireAt(b redis.BaseCmd, multi int) (*ExpireAt, error) { func ParseExpireAt(b redis.BaseCmd, multi int) (*ExpireAt, error) {
@@ -22,19 +22,19 @@ func ParseExpireAt(b redis.BaseCmd, multi int) (*ExpireAt, error) {
var at int var at int
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.Int(&at), parser.Int(&at),
).Required(2).Run(cmd.Args()) ).Required(2).Run(cmd.Args())
if err != nil { if err != nil {
return cmd, err return cmd, err
} }
cmd.At = time.UnixMilli(int64(multi * at)) cmd.at = time.UnixMilli(int64(multi * at))
return cmd, nil return cmd, nil
} }
func (cmd *ExpireAt) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *ExpireAt) Run(w redis.Writer, red redis.Redka) (any, error) {
err := red.Key().ExpireAt(cmd.Key, cmd.At) err := red.Key().ExpireAt(cmd.key, cmd.at)
if err != nil && err != core.ErrNotFound { if err != nil && err != core.ErrNotFound {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,74 +1,73 @@
package key_test package key
import ( import (
"fmt" "fmt"
"testing" "testing"
"time" "time"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/key"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestExpireAtParse(t *testing.T) { func TestExpireAtParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte key string
key string at time.Time
at time.Time err error
err error
}{ }{
{ {
name: "expireat", cmd: "expireat",
args: command.BuildArgs("expireat"), key: "",
key: "", at: time.Time{},
at: time.Time{}, err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
{ {
name: "expireat name", cmd: "expireat name",
args: command.BuildArgs("expire", "name"), key: "",
key: "", at: time.Time{},
at: time.Time{}, err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
{ {
name: "expireat name 60", cmd: "expireat name 60",
args: command.BuildArgs("expireat", "name", fmt.Sprintf("%d", time.Now().Add(60*time.Second).Unix())), key: "name",
key: "name", at: time.UnixMilli(60 * 1000),
at: time.Now().Add(60 * time.Second), err: nil,
err: nil,
}, },
{ {
name: "expireat name age", cmd: "expireat name age",
args: command.BuildArgs("expireat", "name", "age"), key: "",
key: "", at: time.Time{},
at: time.Time{}, err: redis.ErrInvalidInt,
err: redis.ErrInvalidInt,
}, },
{ {
name: "expireat name 60 age 60", cmd: "expireat name 60 age 60",
args: command.BuildArgs("expireat", "name", "60", "age", "60"), key: "",
key: "", at: time.Time{},
at: time.Time{}, err: redis.ErrSyntaxError,
err: redis.ErrSyntaxError,
}, },
} }
parse := func(b redis.BaseCmd) (*ExpireAt, error) {
return ParseExpireAt(b, 1000)
}
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(parse, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
testx.AssertEqual(t, cmd.(*key.ExpireAt).Key, test.key) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, cmd.(*key.ExpireAt).At.Unix(), test.at.Unix()) testx.AssertEqual(t, cmd.at.Unix(), test.at.Unix())
} }
}) })
} }
} }
func TestExpireAtExec(t *testing.T) { func TestExpireAtExec(t *testing.T) {
parse := func(b redis.BaseCmd) (*ExpireAt, error) {
return ParseExpireAt(b, 1000)
}
t.Run("create expireat", func(t *testing.T) { t.Run("create expireat", func(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
@@ -76,7 +75,7 @@ func TestExpireAtExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
expireAt := time.Now().Add(60 * time.Second) expireAt := time.Now().Add(60 * time.Second)
cmd := command.MustParse[*key.ExpireAt](fmt.Sprintf("expireat name %d", expireAt.Unix())) cmd := redis.MustParse(parse, fmt.Sprintf("expireat name %d", expireAt.Unix()))
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -94,14 +93,14 @@ func TestExpireAtExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
expireAt := time.Now() expireAt := time.Now()
cmd := command.MustParse[*key.ExpireAt](fmt.Sprintf("expireat name %d", expireAt.Add(60*time.Second).Unix())) cmd := redis.MustParse(parse, fmt.Sprintf("expireat name %d", expireAt.Add(60*time.Second).Unix()))
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
testx.AssertEqual(t, res, true) testx.AssertEqual(t, res, true)
testx.AssertEqual(t, conn.Out(), "1") testx.AssertEqual(t, conn.Out(), "1")
cmd = command.MustParse[*key.ExpireAt](fmt.Sprintf("expireat name %d", expireAt.Add(20*time.Second).Unix())) cmd = redis.MustParse(parse, fmt.Sprintf("expireat name %d", expireAt.Add(20*time.Second).Unix()))
conn = redis.NewFakeConn() conn = redis.NewFakeConn()
res, err = cmd.Run(conn, red) res, err = cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -118,7 +117,7 @@ func TestExpireAtExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.ExpireAt]("expireat name 0") cmd := redis.MustParse(parse, "expireat name 0")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -135,7 +134,7 @@ func TestExpireAtExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.ExpireAt]("expireat name -10") cmd := redis.MustParse(parse, "expireat name -10")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -152,7 +151,7 @@ func TestExpireAtExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.ExpireAt]("expireat age 1700000000") cmd := redis.MustParse(parse, "expireat age 1700000000")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -1,40 +1,34 @@
package key_test package key
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/key"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestFlushDBParse(t *testing.T) { func TestFlushDBParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte err error
err error
}{ }{
{ {
name: "flushdb", cmd: "flushdb",
args: command.BuildArgs("flushdb"), err: nil,
err: nil,
}, },
{ {
name: "flushdb name", cmd: "flushdb name",
args: command.BuildArgs("flushdb", "name"), err: redis.ErrSyntaxError,
err: redis.ErrSyntaxError,
}, },
{ {
name: "flushdb 1", cmd: "flushdb 1",
args: command.BuildArgs("flushdb", "1"), err: redis.ErrSyntaxError,
err: redis.ErrSyntaxError,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
_, err := command.Parse(test.args) _, err := redis.Parse(ParseFlushDB, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
}) })
} }
@@ -48,7 +42,7 @@ func TestFlushDBExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
_ = db.Str().Set("age", 25) _ = db.Str().Set("age", 25)
cmd := command.MustParse[*key.FlushDB]("flushdb") cmd := redis.MustParse(ParseFlushDB, "flushdb")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -63,7 +57,7 @@ func TestFlushDBExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*key.FlushDB]("flushdb") cmd := redis.MustParse(ParseFlushDB, "flushdb")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -1,4 +1,4 @@
package key_test package key
import ( import (
"testing" "testing"

View File

@@ -7,7 +7,7 @@ import "github.com/nalgeon/redka/internal/redis"
// https://redis.io/commands/keys // https://redis.io/commands/keys
type Keys struct { type Keys struct {
redis.BaseCmd redis.BaseCmd
Pattern string pattern string
} }
func ParseKeys(b redis.BaseCmd) (*Keys, error) { func ParseKeys(b redis.BaseCmd) (*Keys, error) {
@@ -15,12 +15,12 @@ func ParseKeys(b redis.BaseCmd) (*Keys, error) {
if len(cmd.Args()) != 1 { if len(cmd.Args()) != 1 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Pattern = string(cmd.Args()[0]) cmd.pattern = string(cmd.Args()[0])
return cmd, nil return cmd, nil
} }
func (cmd *Keys) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *Keys) Run(w redis.Writer, red redis.Redka) (any, error) {
keys, err := red.Key().Keys(cmd.Pattern) keys, err := red.Key().Keys(cmd.pattern)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,10 +1,8 @@
package key_test package key
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/key"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,43 +10,38 @@ import (
func TestKeysParse(t *testing.T) { func TestKeysParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte
want string want string
err error err error
}{ }{
{ {
name: "keys", cmd: "keys",
args: command.BuildArgs("keys"),
want: "", want: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "keys *", cmd: "keys *",
args: command.BuildArgs("keys", "*"),
want: "*", want: "*",
err: nil, err: nil,
}, },
{ {
name: "keys 2*", cmd: "keys k2*",
args: command.BuildArgs("keys", "k2*"),
want: "k2*", want: "k2*",
err: nil, err: nil,
}, },
{ {
name: "keys * k2*", cmd: "keys * k2*",
args: command.BuildArgs("keys", "*", "k2*"),
want: "", want: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseKeys, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
testx.AssertEqual(t, cmd.(*key.Keys).Pattern, test.want) testx.AssertEqual(t, cmd.pattern, test.want)
} }
}) })
} }
@@ -65,41 +58,37 @@ func TestKeysExec(t *testing.T) {
_ = db.Str().Set("k31", "31") _ = db.Str().Set("k31", "31")
tests := []struct { tests := []struct {
name string cmd string
cmd *key.Keys res []string
res []string out string
out string
}{ }{
{ {
name: "all keys", cmd: "keys *",
cmd: command.MustParse[*key.Keys]("keys *"), res: []string{"k11", "k12", "k21", "k22", "k31"},
res: []string{"k11", "k12", "k21", "k22", "k31"}, out: "5,k11,k12,k21,k22,k31",
out: "5,k11,k12,k21,k22,k31",
}, },
{ {
name: "some keys", cmd: "keys k2*",
cmd: command.MustParse[*key.Keys]("keys k2*"), res: []string{"k21", "k22"},
res: []string{"k21", "k22"}, out: "2,k21,k22",
out: "2,k21,k22",
}, },
{ {
name: "one key", cmd: "keys k12",
cmd: command.MustParse[*key.Keys]("keys k12"), res: []string{"k12"},
res: []string{"k12"}, out: "1,k12",
out: "1,k12",
}, },
{ {
name: "not found", cmd: "keys name",
cmd: command.MustParse[*key.Keys]("keys name"), res: []string{},
res: []string{}, out: "0",
out: "0",
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
keys, err := test.cmd.Run(conn, red) cmd := redis.MustParse(ParseKeys, test.cmd)
keys, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
for i, key := range keys.([]core.Key) { for i, key := range keys.([]core.Key) {
testx.AssertEqual(t, key.Key, test.res[i]) testx.AssertEqual(t, key.Key, test.res[i])

View File

@@ -10,7 +10,7 @@ import (
// https://redis.io/commands/persist // https://redis.io/commands/persist
type Persist struct { type Persist struct {
redis.BaseCmd redis.BaseCmd
Key string key string
} }
func ParsePersist(b redis.BaseCmd) (*Persist, error) { func ParsePersist(b redis.BaseCmd) (*Persist, error) {
@@ -18,12 +18,12 @@ func ParsePersist(b redis.BaseCmd) (*Persist, error) {
if len(cmd.Args()) != 1 { if len(cmd.Args()) != 1 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Key = string(cmd.Args()[0]) cmd.key = string(cmd.Args()[0])
return cmd, nil return cmd, nil
} }
func (cmd *Persist) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *Persist) Run(w redis.Writer, red redis.Redka) (any, error) {
err := red.Key().Persist(cmd.Key) err := red.Key().Persist(cmd.key)
if err != nil && err != core.ErrNotFound { if err != nil && err != core.ErrNotFound {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,48 +1,42 @@
package key_test package key
import ( import (
"testing" "testing"
"time" "time"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/key"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestPersistParse(t *testing.T) { func TestPersistParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte key string
key string err error
err error
}{ }{
{ {
name: "persist", cmd: "persist",
args: command.BuildArgs("persist"), key: "",
key: "", err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
{ {
name: "persist name", cmd: "persist name",
args: command.BuildArgs("persist", "name"), key: "name",
key: "name", err: nil,
err: nil,
}, },
{ {
name: "persist name age", cmd: "persist name age",
args: command.BuildArgs("persist", "name", "age"), key: "",
key: "", err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParsePersist, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
testx.AssertEqual(t, cmd.(*key.Persist).Key, test.key) testx.AssertEqual(t, cmd.key, test.key)
} }
}) })
} }
@@ -55,7 +49,7 @@ func TestPersistExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.Persist]("persist name") cmd := redis.MustParse(ParsePersist, "persist name")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -72,7 +66,7 @@ func TestPersistExec(t *testing.T) {
_ = db.Str().SetExpires("name", "alice", 60*time.Second) _ = db.Str().SetExpires("name", "alice", 60*time.Second)
cmd := command.MustParse[*key.Persist]("persist name") cmd := redis.MustParse(ParsePersist, "persist name")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -89,7 +83,7 @@ func TestPersistExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.Persist]("persist age") cmd := redis.MustParse(ParsePersist, "persist age")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -1,80 +1,79 @@
package key_test package key
import ( import (
"testing" "testing"
"time" "time"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/key"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestPExpireParse(t *testing.T) { func TestPExpireParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte key string
key string ttl time.Duration
ttl time.Duration err error
err error
}{ }{
{ {
name: "pexpire", cmd: "pexpire",
args: command.BuildArgs("pexpire"), key: "",
key: "", ttl: 0,
ttl: 0, err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
{ {
name: "pexpire name", cmd: "pexpire name",
args: command.BuildArgs("pexpire", "name"), key: "",
key: "", ttl: 0,
ttl: 0, err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
{ {
name: "pexpire name 5000", cmd: "pexpire name 5000",
args: command.BuildArgs("pexpire", "name", "5000"), key: "name",
key: "name", ttl: 5000 * time.Millisecond,
ttl: 5000 * time.Millisecond, err: nil,
err: nil,
}, },
{ {
name: "pexpire name age", cmd: "pexpire name age",
args: command.BuildArgs("pexpire", "name", "age"), key: "",
key: "", ttl: 0,
ttl: 0, err: redis.ErrInvalidInt,
err: redis.ErrInvalidInt,
}, },
{ {
name: "pexpire name 100 age 100", cmd: "pexpire name 100 age 100",
args: command.BuildArgs("pexpire", "name", "100", "age", "100"), key: "",
key: "", ttl: 0,
ttl: 0, err: redis.ErrSyntaxError,
err: redis.ErrSyntaxError,
}, },
} }
parse := func(b redis.BaseCmd) (*Expire, error) {
return ParseExpire(b, 1)
}
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(parse, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
testx.AssertEqual(t, cmd.(*key.Expire).Key, test.key) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, cmd.(*key.Expire).TTL, test.ttl) testx.AssertEqual(t, cmd.ttl, test.ttl)
} }
}) })
} }
} }
func TestPExpireExec(t *testing.T) { func TestPExpireExec(t *testing.T) {
db, red := getDB(t) parse := func(b redis.BaseCmd) (*Expire, error) {
defer db.Close() return ParseExpire(b, 1)
}
t.Run("create pexpire", func(t *testing.T) { t.Run("create pexpire", func(t *testing.T) {
db, red := getDB(t)
defer db.Close()
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.Expire]("pexpire name 60000") cmd := redis.MustParse(parse, "pexpire name 60000")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -87,9 +86,11 @@ func TestPExpireExec(t *testing.T) {
}) })
t.Run("update pexpire", func(t *testing.T) { t.Run("update pexpire", func(t *testing.T) {
db, red := getDB(t)
defer db.Close()
_ = db.Str().SetExpires("name", "alice", 60*time.Second) _ = db.Str().SetExpires("name", "alice", 60*time.Second)
cmd := command.MustParse[*key.Expire]("pexpire name 30000") cmd := redis.MustParse(parse, "pexpire name 30000")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -102,9 +103,11 @@ func TestPExpireExec(t *testing.T) {
}) })
t.Run("set to zero", func(t *testing.T) { t.Run("set to zero", func(t *testing.T) {
db, red := getDB(t)
defer db.Close()
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.Expire]("pexpire name 0") cmd := redis.MustParse(parse, "pexpire name 0")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -116,9 +119,11 @@ func TestPExpireExec(t *testing.T) {
}) })
t.Run("negative", func(t *testing.T) { t.Run("negative", func(t *testing.T) {
db, red := getDB(t)
defer db.Close()
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.Expire]("pexpire name -1000") cmd := redis.MustParse(parse, "pexpire name -1000")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -130,9 +135,11 @@ func TestPExpireExec(t *testing.T) {
}) })
t.Run("not found", func(t *testing.T) { t.Run("not found", func(t *testing.T) {
db, red := getDB(t)
defer db.Close()
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.Expire]("pexpire age 1000") cmd := redis.MustParse(parse, "pexpire age 1000")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -1,11 +1,9 @@
package key_test package key
import ( import (
"slices" "slices"
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/key"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -13,30 +11,26 @@ import (
func TestRandomKeyParse(t *testing.T) { func TestRandomKeyParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte err error
err error
}{ }{
{ {
name: "randomkey", cmd: "randomkey",
args: command.BuildArgs("randomkey"), err: nil,
err: nil,
}, },
{ {
name: "randomkey name", cmd: "randomkey name",
args: command.BuildArgs("randomkey", "name"), err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
{ {
name: "randomkey name age", cmd: "randomkey name age",
args: command.BuildArgs("randomkey", "name", "age"), err: redis.ErrInvalidArgNum,
err: redis.ErrInvalidArgNum,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
_, err := command.Parse(test.args) _, err := redis.Parse(ParseRandomKey, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
}) })
} }
@@ -52,7 +46,7 @@ func TestRandomKeyExec(t *testing.T) {
keys := []string{"name", "age", "city"} keys := []string{"name", "age", "city"}
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
cmd := command.MustParse[*key.RandomKey]("randomkey") cmd := redis.MustParse(ParseRandomKey, "randomkey")
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
testx.AssertEqual(t, slices.Contains(keys, res.(core.Key).Key), true) testx.AssertEqual(t, slices.Contains(keys, res.(core.Key).Key), true)
@@ -62,7 +56,7 @@ func TestRandomKeyExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
cmd := command.MustParse[*key.RandomKey]("randomkey") cmd := redis.MustParse(ParseRandomKey, "randomkey")
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
testx.AssertEqual(t, res, nil) testx.AssertEqual(t, res, nil)

View File

@@ -7,8 +7,8 @@ import "github.com/nalgeon/redka/internal/redis"
// https://redis.io/commands/rename // https://redis.io/commands/rename
type Rename struct { type Rename struct {
redis.BaseCmd redis.BaseCmd
Key string key string
NewKey string newKey string
} }
func ParseRename(b redis.BaseCmd) (*Rename, error) { func ParseRename(b redis.BaseCmd) (*Rename, error) {
@@ -16,13 +16,13 @@ func ParseRename(b redis.BaseCmd) (*Rename, error) {
if len(cmd.Args()) != 2 { if len(cmd.Args()) != 2 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Key = string(cmd.Args()[0]) cmd.key = string(cmd.Args()[0])
cmd.NewKey = string(cmd.Args()[1]) cmd.newKey = string(cmd.Args()[1])
return cmd, nil return cmd, nil
} }
func (cmd *Rename) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *Rename) Run(w redis.Writer, red redis.Redka) (any, error) {
err := red.Key().Rename(cmd.Key, cmd.NewKey) err := red.Key().Rename(cmd.key, cmd.newKey)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,10 +1,8 @@
package key_test package key
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/key"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,29 +10,25 @@ import (
func TestRenameParse(t *testing.T) { func TestRenameParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte
key string key string
newKey string newKey string
err error err error
}{ }{
{ {
name: "rename", cmd: "rename",
args: command.BuildArgs("rename"),
key: "", key: "",
newKey: "", newKey: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "rename name", cmd: "rename name",
args: command.BuildArgs("rename", "name"),
key: "", key: "",
newKey: "", newKey: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "rename name title", cmd: "rename name title",
args: command.BuildArgs("rename", "name", "title"),
key: "name", key: "name",
newKey: "title", newKey: "title",
err: nil, err: nil,
@@ -42,12 +36,12 @@ func TestRenameParse(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseRename, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
testx.AssertEqual(t, cmd.(*key.Rename).Key, test.key) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, cmd.(*key.Rename).NewKey, test.newKey) testx.AssertEqual(t, cmd.newKey, test.newKey)
} }
}) })
} }
@@ -60,7 +54,7 @@ func TestRenameExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.Rename]("rename name title") cmd := redis.MustParse(ParseRename, "rename name title")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -80,7 +74,7 @@ func TestRenameExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
_ = db.Str().Set("title", "bob") _ = db.Str().Set("title", "bob")
cmd := command.MustParse[*key.Rename]("rename name title") cmd := redis.MustParse(ParseRename, "rename name title")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -101,7 +95,7 @@ func TestRenameExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.Rename]("rename name name") cmd := redis.MustParse(ParseRename, "rename name name")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -120,7 +114,7 @@ func TestRenameExec(t *testing.T) {
_ = db.Str().Set("title", "bob") _ = db.Str().Set("title", "bob")
cmd := command.MustParse[*key.Rename]("rename name title") cmd := redis.MustParse(ParseRename, "rename name title")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertEqual(t, err, core.ErrNotFound) testx.AssertEqual(t, err, core.ErrNotFound)

View File

@@ -7,8 +7,8 @@ import "github.com/nalgeon/redka/internal/redis"
// https://redis.io/commands/renamenx // https://redis.io/commands/renamenx
type RenameNX struct { type RenameNX struct {
redis.BaseCmd redis.BaseCmd
Key string key string
NewKey string newKey string
} }
func ParseRenameNX(b redis.BaseCmd) (*RenameNX, error) { func ParseRenameNX(b redis.BaseCmd) (*RenameNX, error) {
@@ -16,13 +16,13 @@ func ParseRenameNX(b redis.BaseCmd) (*RenameNX, error) {
if len(cmd.Args()) != 2 { if len(cmd.Args()) != 2 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Key = string(cmd.Args()[0]) cmd.key = string(cmd.Args()[0])
cmd.NewKey = string(cmd.Args()[1]) cmd.newKey = string(cmd.Args()[1])
return cmd, nil return cmd, nil
} }
func (cmd *RenameNX) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *RenameNX) Run(w redis.Writer, red redis.Redka) (any, error) {
ok, err := red.Key().RenameNotExists(cmd.Key, cmd.NewKey) ok, err := red.Key().RenameNotExists(cmd.key, cmd.newKey)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,10 +1,8 @@
package key_test package key
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/key"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,29 +10,25 @@ import (
func TestRenameNXParse(t *testing.T) { func TestRenameNXParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte
key string key string
newKey string newKey string
err error err error
}{ }{
{ {
name: "renamenx", cmd: "renamenx",
args: command.BuildArgs("renamenx"),
key: "", key: "",
newKey: "", newKey: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "renamenx name", cmd: "renamenx name",
args: command.BuildArgs("renamenx", "name"),
key: "", key: "",
newKey: "", newKey: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "renamenx name title", cmd: "renamenx name title",
args: command.BuildArgs("renamenx", "name", "title"),
key: "name", key: "name",
newKey: "title", newKey: "title",
err: nil, err: nil,
@@ -42,12 +36,12 @@ func TestRenameNXParse(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseRenameNX, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
testx.AssertEqual(t, cmd.(*key.RenameNX).Key, test.key) testx.AssertEqual(t, cmd.key, test.key)
testx.AssertEqual(t, cmd.(*key.RenameNX).NewKey, test.newKey) testx.AssertEqual(t, cmd.newKey, test.newKey)
} }
}) })
} }
@@ -60,7 +54,7 @@ func TestRenameNXExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.RenameNX]("renamenx name title") cmd := redis.MustParse(ParseRenameNX, "renamenx name title")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -80,7 +74,7 @@ func TestRenameNXExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
_ = db.Str().Set("title", "bob") _ = db.Str().Set("title", "bob")
cmd := command.MustParse[*key.RenameNX]("renamenx name title") cmd := redis.MustParse(ParseRenameNX, "renamenx name title")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -103,7 +97,7 @@ func TestRenameNXExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*key.RenameNX]("renamenx name name") cmd := redis.MustParse(ParseRenameNX, "renamenx name name")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -122,7 +116,7 @@ func TestRenameNXExec(t *testing.T) {
_ = db.Str().Set("title", "bob") _ = db.Str().Set("title", "bob")
cmd := command.MustParse[*key.RenameNX]("renamenx name title") cmd := redis.MustParse(ParseRenameNX, "renamenx name title")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertEqual(t, err, core.ErrNotFound) testx.AssertEqual(t, err, core.ErrNotFound)

View File

@@ -10,33 +10,33 @@ import (
// https://redis.io/commands/scan // https://redis.io/commands/scan
type Scan struct { type Scan struct {
redis.BaseCmd redis.BaseCmd
Cursor int cursor int
Match string match string
Count int count int
} }
func ParseScan(b redis.BaseCmd) (*Scan, error) { func ParseScan(b redis.BaseCmd) (*Scan, error) {
cmd := &Scan{BaseCmd: b} cmd := &Scan{BaseCmd: b}
err := parser.New( err := parser.New(
parser.Int(&cmd.Cursor), parser.Int(&cmd.cursor),
parser.Named("match", parser.String(&cmd.Match)), parser.Named("match", parser.String(&cmd.match)),
parser.Named("count", parser.Int(&cmd.Count)), parser.Named("count", parser.Int(&cmd.count)),
).Required(1).Run(cmd.Args()) ).Required(1).Run(cmd.Args())
if err != nil { if err != nil {
return cmd, err return cmd, err
} }
// all keys by default // all keys by default
if cmd.Match == "" { if cmd.match == "" {
cmd.Match = "*" cmd.match = "*"
} }
return cmd, nil return cmd, nil
} }
func (cmd *Scan) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *Scan) Run(w redis.Writer, red redis.Redka) (any, error) {
res, err := red.Key().Scan(cmd.Cursor, cmd.Match, cmd.Count) res, err := red.Key().Scan(cmd.cursor, cmd.match, cmd.count)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,10 +1,8 @@
package key_test package key
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/key"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/rkey" "github.com/nalgeon/redka/internal/rkey"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,88 +10,77 @@ import (
func TestScanParse(t *testing.T) { func TestScanParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte
cursor int cursor int
match string match string
count int count int
err error err error
}{ }{
{ {
name: "scan", cmd: "scan",
args: command.BuildArgs("scan"),
cursor: 0, cursor: 0,
match: "*", match: "*",
count: 0, count: 0,
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "scan 15", cmd: "scan 15",
args: command.BuildArgs("scan", "15"),
cursor: 15, cursor: 15,
match: "*", match: "*",
count: 0, count: 0,
err: nil, err: nil,
}, },
{ {
name: "scan 15 match *", cmd: "scan 15 match *",
args: command.BuildArgs("scan", "15", "match", "*"),
cursor: 15, cursor: 15,
match: "*", match: "*",
count: 0, count: 0,
err: nil, err: nil,
}, },
{ {
name: "scan 15 match * count 5", cmd: "scan 15 match * count 5",
args: command.BuildArgs("scan", "15", "match", "*", "count", "5"),
cursor: 15, cursor: 15,
match: "*", match: "*",
count: 5, count: 5,
err: nil, err: nil,
}, },
{ {
name: "scan 15 match * count ok", cmd: "scan 15 match * count ok",
args: command.BuildArgs("scan", "15", "match", "*", "count", "ok"),
cursor: 15, cursor: 15,
match: "*", match: "*",
count: 0, count: 0,
err: redis.ErrInvalidInt, err: redis.ErrInvalidInt,
}, },
{ {
name: "scan 15 count 5 match *", cmd: "scan 15 count 5 match *",
args: command.BuildArgs("scan", "15", "count", "5", "match", "*"),
cursor: 15, cursor: 15,
match: "*", match: "*",
count: 5, count: 5,
err: nil, err: nil,
}, },
{ {
name: "scan 15 match k2* count 5", cmd: "scan 15 match k2* count 5",
args: command.BuildArgs("scan", "15", "match", "k2*", "count", "5"),
cursor: 15, cursor: 15,
match: "k2*", match: "k2*",
count: 5, count: 5,
err: nil, err: nil,
}, },
{ {
name: "scan ten", cmd: "scan ten",
args: command.BuildArgs("scan", "ten"),
cursor: 0, cursor: 0,
match: "", match: "",
count: 0, count: 0,
err: redis.ErrInvalidInt, err: redis.ErrInvalidInt,
}, },
{ {
name: "scan 15 *", cmd: "scan 15 *",
args: command.BuildArgs("scan", "15", "*"),
cursor: 0, cursor: 0,
match: "", match: "",
count: 0, count: 0,
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
{ {
name: "scan 15 * 5", cmd: "scan 15 * 5",
args: command.BuildArgs("scan", "15", "*", "5"),
cursor: 0, cursor: 0,
match: "", match: "",
count: 0, count: 0,
@@ -102,14 +89,13 @@ func TestScanParse(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseScan, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
scmd := cmd.(*key.Scan) testx.AssertEqual(t, cmd.cursor, test.cursor)
testx.AssertEqual(t, scmd.Cursor, test.cursor) testx.AssertEqual(t, cmd.match, test.match)
testx.AssertEqual(t, scmd.Match, test.match) testx.AssertEqual(t, cmd.count, test.count)
testx.AssertEqual(t, scmd.Count, test.count)
} }
}) })
} }
@@ -127,7 +113,7 @@ func TestScanExec(t *testing.T) {
t.Run("scan all", func(t *testing.T) { t.Run("scan all", func(t *testing.T) {
{ {
cmd := command.MustParse[*key.Scan]("scan 0") cmd := redis.MustParse(ParseScan, "scan 0")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -141,7 +127,7 @@ func TestScanExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "2,5,5,k11,k12,k21,k22,k31") testx.AssertEqual(t, conn.Out(), "2,5,5,k11,k12,k21,k22,k31")
} }
{ {
cmd := command.MustParse[*key.Scan]("scan 5") cmd := redis.MustParse(ParseScan, "scan 5")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -155,7 +141,7 @@ func TestScanExec(t *testing.T) {
}) })
t.Run("scan pattern", func(t *testing.T) { t.Run("scan pattern", func(t *testing.T) {
cmd := command.MustParse[*key.Scan]("scan 0 match k2*") cmd := redis.MustParse(ParseScan, "scan 0 match k2*")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -172,7 +158,7 @@ func TestScanExec(t *testing.T) {
t.Run("scan count", func(t *testing.T) { t.Run("scan count", func(t *testing.T) {
{ {
// page 1 // page 1
cmd := command.MustParse[*key.Scan]("scan 0 match * count 2") cmd := redis.MustParse(ParseScan, "scan 0 match * count 2")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -187,7 +173,7 @@ func TestScanExec(t *testing.T) {
} }
{ {
// page 2 // page 2
cmd := command.MustParse[*key.Scan]("scan 2 match * count 2") cmd := redis.MustParse(ParseScan, "scan 2 match * count 2")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -202,7 +188,7 @@ func TestScanExec(t *testing.T) {
} }
{ {
// page 3 // page 3
cmd := command.MustParse[*key.Scan]("scan 4 match * count 2") cmd := redis.MustParse(ParseScan, "scan 4 match * count 2")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
@@ -216,7 +202,7 @@ func TestScanExec(t *testing.T) {
} }
{ {
// no more pages // no more pages
cmd := command.MustParse[*key.Scan]("scan 5 match * count 2") cmd := redis.MustParse(ParseScan, "scan 5 match * count 2")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)

View File

@@ -1,60 +1,61 @@
package string_test package string
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
str "github.com/nalgeon/redka/internal/command/string"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestDecrParse(t *testing.T) { func TestDecrParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want Incr
want str.Incr
err error err error
}{ }{
{ {
name: "decr", cmd: "decr",
args: command.BuildArgs("decr"), want: Incr{},
want: str.Incr{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "decr age", cmd: "decr age",
args: command.BuildArgs("decr", "age"), want: Incr{key: "age", delta: -1},
want: str.Incr{Key: "age", Delta: -1},
err: nil, err: nil,
}, },
{ {
name: "decr age 42", cmd: "decr age 42",
args: command.BuildArgs("decr", "age", "42"), want: Incr{},
want: str.Incr{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
} }
parse := func(b redis.BaseCmd) (*Incr, error) {
return ParseIncr(b, -1)
}
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(parse, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*str.Incr) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.delta, test.want.delta)
testx.AssertEqual(t, cm.Delta, test.want.Delta)
} }
}) })
} }
} }
func TestDecrExec(t *testing.T) { func TestDecrExec(t *testing.T) {
db, red := getDB(t) parse := func(b redis.BaseCmd) (*Incr, error) {
defer db.Close() return ParseIncr(b, -1)
}
t.Run("create", func(t *testing.T) { t.Run("create", func(t *testing.T) {
cmd := command.MustParse[*str.Incr]("decr age") db, red := getDB(t)
defer db.Close()
cmd := redis.MustParse(parse, "decr age")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -66,9 +67,11 @@ func TestDecrExec(t *testing.T) {
}) })
t.Run("decr", func(t *testing.T) { t.Run("decr", func(t *testing.T) {
db, red := getDB(t)
defer db.Close()
_ = db.Str().Set("age", "25") _ = db.Str().Set("age", "25")
cmd := command.MustParse[*str.Incr]("decr age") cmd := redis.MustParse(parse, "decr age")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -1,60 +1,61 @@
package string_test package string
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
str "github.com/nalgeon/redka/internal/command/string"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestDecrByParse(t *testing.T) { func TestDecrByParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want IncrBy
want str.IncrBy
err error err error
}{ }{
{ {
name: "decrby", cmd: "decrby",
args: command.BuildArgs("decrby"), want: IncrBy{},
want: str.IncrBy{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "decrby age", cmd: "decrby age",
args: command.BuildArgs("decrby", "age"), want: IncrBy{},
want: str.IncrBy{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "decrby age 42", cmd: "decrby age 42",
args: command.BuildArgs("decrby", "age", "42"), want: IncrBy{key: "age", delta: -42},
want: str.IncrBy{Key: "age", Delta: -42},
err: nil, err: nil,
}, },
} }
parse := func(b redis.BaseCmd) (*IncrBy, error) {
return ParseIncrBy(b, -1)
}
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(parse, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*str.IncrBy) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.delta, test.want.delta)
testx.AssertEqual(t, cm.Delta, test.want.Delta)
} }
}) })
} }
} }
func TestDecrByExec(t *testing.T) { func TestDecrByExec(t *testing.T) {
db, red := getDB(t) parse := func(b redis.BaseCmd) (*IncrBy, error) {
defer db.Close() return ParseIncrBy(b, -1)
}
t.Run("create", func(t *testing.T) { t.Run("create", func(t *testing.T) {
cmd := command.MustParse[*str.IncrBy]("decrby age 12") db, red := getDB(t)
defer db.Close()
cmd := redis.MustParse(parse, "decrby age 12")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -66,9 +67,11 @@ func TestDecrByExec(t *testing.T) {
}) })
t.Run("decrby", func(t *testing.T) { t.Run("decrby", func(t *testing.T) {
db, red := getDB(t)
defer db.Close()
_ = db.Str().Set("age", "25") _ = db.Str().Set("age", "25")
cmd := command.MustParse[*str.IncrBy]("decrby age 12") cmd := redis.MustParse(parse, "decrby age 12")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -10,7 +10,7 @@ import (
// https://redis.io/commands/get // https://redis.io/commands/get
type Get struct { type Get struct {
redis.BaseCmd redis.BaseCmd
Key string key string
} }
func ParseGet(b redis.BaseCmd) (*Get, error) { func ParseGet(b redis.BaseCmd) (*Get, error) {
@@ -18,12 +18,12 @@ func ParseGet(b redis.BaseCmd) (*Get, error) {
if len(cmd.Args()) != 1 { if len(cmd.Args()) != 1 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Key = string(cmd.Args()[0]) cmd.key = string(cmd.Args()[0])
return cmd, nil return cmd, nil
} }
func (cmd *Get) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *Get) Run(w redis.Writer, red redis.Redka) (any, error) {
val, err := red.Str().Get(cmd.Key) val, err := red.Str().Get(cmd.key)
if err == core.ErrNotFound { if err == core.ErrNotFound {
w.WriteNull() w.WriteNull()
return val, nil return val, nil

View File

@@ -1,10 +1,8 @@
package string_test package string
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
str "github.com/nalgeon/redka/internal/command/string"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,37 +10,33 @@ import (
func TestGetParse(t *testing.T) { func TestGetParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte
want string want string
err error err error
}{ }{
{ {
name: "get", cmd: "get",
args: command.BuildArgs("get"),
want: "", want: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "get name", cmd: "get name",
args: command.BuildArgs("get", "name"),
want: "name", want: "name",
err: nil, err: nil,
}, },
{ {
name: "get name age", cmd: "get name age",
args: command.BuildArgs("get", "name", "age"),
want: "", want: "",
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseGet, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
testx.AssertEqual(t, cmd.(*str.Get).Key, test.want) testx.AssertEqual(t, cmd.key, test.want)
} }
}) })
} }
@@ -55,29 +49,27 @@ func TestGetExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
tests := []struct { tests := []struct {
name string cmd string
cmd *str.Get res any
res any out string
out string
}{ }{
{ {
name: "get found", cmd: "get name",
cmd: command.MustParse[*str.Get]("get name"), res: core.Value("alice"),
res: core.Value("alice"), out: "alice",
out: "alice",
}, },
{ {
name: "get not found", cmd: "get age",
cmd: command.MustParse[*str.Get]("get age"), res: core.Value(nil),
res: core.Value(nil), out: "(nil)",
out: "(nil)",
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := test.cmd.Run(conn, red) cmd := redis.MustParse(ParseGet, test.cmd)
res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
testx.AssertEqual(t, res, test.res) testx.AssertEqual(t, res, test.res)
testx.AssertEqual(t, conn.Out(), test.out) testx.AssertEqual(t, conn.Out(), test.out)

View File

@@ -7,8 +7,8 @@ import "github.com/nalgeon/redka/internal/redis"
// https://redis.io/commands/getset // https://redis.io/commands/getset
type GetSet struct { type GetSet struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Value []byte value []byte
} }
func ParseGetSet(b redis.BaseCmd) (*GetSet, error) { func ParseGetSet(b redis.BaseCmd) (*GetSet, error) {
@@ -16,13 +16,13 @@ func ParseGetSet(b redis.BaseCmd) (*GetSet, error) {
if len(cmd.Args()) != 2 { if len(cmd.Args()) != 2 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Key = string(cmd.Args()[0]) cmd.key = string(cmd.Args()[0])
cmd.Value = cmd.Args()[1] cmd.value = cmd.Args()[1]
return cmd, nil return cmd, nil
} }
func (cmd *GetSet) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *GetSet) Run(w redis.Writer, red redis.Redka) (any, error) {
out, err := red.Str().SetWith(cmd.Key, cmd.Value).Run() out, err := red.Str().SetWith(cmd.key, cmd.value).Run()
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,10 +1,8 @@
package string_test package string
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
str "github.com/nalgeon/redka/internal/command/string"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,45 +10,39 @@ import (
func TestGetSetParse(t *testing.T) { func TestGetSetParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want GetSet
want str.GetSet
err error err error
}{ }{
{ {
name: "getset", cmd: "getset",
args: command.BuildArgs("getset"), want: GetSet{},
want: str.GetSet{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "getset name", cmd: "getset name",
args: command.BuildArgs("getset", "name"), want: GetSet{},
want: str.GetSet{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "getset name alice", cmd: "getset name alice",
args: command.BuildArgs("getset", "name", "alice"), want: GetSet{key: "name", value: []byte("alice")},
want: str.GetSet{Key: "name", Value: []byte("alice")},
err: nil, err: nil,
}, },
{ {
name: "getset name alice 60", cmd: "getset name alice 60",
args: command.BuildArgs("getset", "name", "alice", "60"), want: GetSet{},
want: str.GetSet{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseGetSet, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*str.GetSet) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.value, test.want.value)
testx.AssertEqual(t, cm.Value, test.want.Value)
} }
}) })
} }
@@ -61,7 +53,7 @@ func TestGetSetExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*str.GetSet]("getset name alice") cmd := redis.MustParse(ParseGetSet, "getset name alice")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -78,7 +70,7 @@ func TestGetSetExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*str.GetSet]("getset name bob") cmd := redis.MustParse(ParseGetSet, "getset name bob")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -13,8 +13,8 @@ import "github.com/nalgeon/redka/internal/redis"
// https://redis.io/commands/decr // https://redis.io/commands/decr
type Incr struct { type Incr struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Delta int delta int
} }
func ParseIncr(b redis.BaseCmd, sign int) (*Incr, error) { func ParseIncr(b redis.BaseCmd, sign int) (*Incr, error) {
@@ -22,13 +22,13 @@ func ParseIncr(b redis.BaseCmd, sign int) (*Incr, error) {
if len(cmd.Args()) != 1 { if len(cmd.Args()) != 1 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Key = string(cmd.Args()[0]) cmd.key = string(cmd.Args()[0])
cmd.Delta = sign cmd.delta = sign
return cmd, nil return cmd, nil
} }
func (cmd *Incr) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *Incr) Run(w redis.Writer, red redis.Redka) (any, error) {
val, err := red.Str().Incr(cmd.Key, cmd.Delta) val, err := red.Str().Incr(cmd.key, cmd.delta)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,60 +1,61 @@
package string_test package string
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
str "github.com/nalgeon/redka/internal/command/string"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestIncrParse(t *testing.T) { func TestIncrParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want Incr
want str.Incr
err error err error
}{ }{
{ {
name: "incr", cmd: "incr",
args: command.BuildArgs("incr"), want: Incr{},
want: str.Incr{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "incr age", cmd: "incr age",
args: command.BuildArgs("incr", "age"), want: Incr{key: "age", delta: 1},
want: str.Incr{Key: "age", Delta: 1},
err: nil, err: nil,
}, },
{ {
name: "incr age 42", cmd: "incr age 42",
args: command.BuildArgs("incr", "age", "42"), want: Incr{},
want: str.Incr{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
} }
parse := func(b redis.BaseCmd) (*Incr, error) {
return ParseIncr(b, 1)
}
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(parse, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*str.Incr) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.delta, test.want.delta)
testx.AssertEqual(t, cm.Delta, test.want.Delta)
} }
}) })
} }
} }
func TestIncrExec(t *testing.T) { func TestIncrExec(t *testing.T) {
db, red := getDB(t) parse := func(b redis.BaseCmd) (*Incr, error) {
defer db.Close() return ParseIncr(b, 1)
}
t.Run("create", func(t *testing.T) { t.Run("create", func(t *testing.T) {
cmd := command.MustParse[*str.Incr]("incr age") db, red := getDB(t)
defer db.Close()
cmd := redis.MustParse(parse, "incr age")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -66,9 +67,11 @@ func TestIncrExec(t *testing.T) {
}) })
t.Run("incr", func(t *testing.T) { t.Run("incr", func(t *testing.T) {
db, red := getDB(t)
defer db.Close()
_ = db.Str().Set("age", "25") _ = db.Str().Set("age", "25")
cmd := command.MustParse[*str.Incr]("incr age") cmd := redis.MustParse(parse, "incr age")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -16,25 +16,25 @@ import (
// https://redis.io/commands/decrby // https://redis.io/commands/decrby
type IncrBy struct { type IncrBy struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Delta int delta int
} }
func ParseIncrBy(b redis.BaseCmd, sign int) (*IncrBy, error) { func ParseIncrBy(b redis.BaseCmd, sign int) (*IncrBy, error) {
cmd := &IncrBy{BaseCmd: b} cmd := &IncrBy{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.Int(&cmd.Delta), parser.Int(&cmd.delta),
).Required(2).Run(cmd.Args()) ).Required(2).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
} }
cmd.Delta *= sign cmd.delta *= sign
return cmd, nil return cmd, nil
} }
func (cmd *IncrBy) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *IncrBy) Run(w redis.Writer, red redis.Redka) (any, error) {
val, err := red.Str().Incr(cmd.Key, cmd.Delta) val, err := red.Str().Incr(cmd.key, cmd.delta)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,60 +1,61 @@
package string_test package string
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
str "github.com/nalgeon/redka/internal/command/string"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestIncrByParse(t *testing.T) { func TestIncrByParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want IncrBy
want str.IncrBy
err error err error
}{ }{
{ {
name: "incrby", cmd: "incrby",
args: command.BuildArgs("incrby"), want: IncrBy{},
want: str.IncrBy{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "incrby age", cmd: "incrby age",
args: command.BuildArgs("incrby", "age"), want: IncrBy{},
want: str.IncrBy{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "incrby age 42", cmd: "incrby age 42",
args: command.BuildArgs("incrby", "age", "42"), want: IncrBy{key: "age", delta: 42},
want: str.IncrBy{Key: "age", Delta: 42},
err: nil, err: nil,
}, },
} }
parse := func(b redis.BaseCmd) (*IncrBy, error) {
return ParseIncrBy(b, 1)
}
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(parse, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*str.IncrBy) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.delta, test.want.delta)
testx.AssertEqual(t, cm.Delta, test.want.Delta)
} }
}) })
} }
} }
func TestIncrByExec(t *testing.T) { func TestIncrByExec(t *testing.T) {
db, red := getDB(t) parse := func(b redis.BaseCmd) (*IncrBy, error) {
defer db.Close() return ParseIncrBy(b, 1)
}
t.Run("create", func(t *testing.T) { t.Run("create", func(t *testing.T) {
cmd := command.MustParse[*str.IncrBy]("incrby age 42") db, red := getDB(t)
defer db.Close()
cmd := redis.MustParse(parse, "incrby age 42")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -66,9 +67,11 @@ func TestIncrByExec(t *testing.T) {
}) })
t.Run("incrby", func(t *testing.T) { t.Run("incrby", func(t *testing.T) {
db, red := getDB(t)
defer db.Close()
_ = db.Str().Set("age", "25") _ = db.Str().Set("age", "25")
cmd := command.MustParse[*str.IncrBy]("incrby age 42") cmd := redis.MustParse(parse, "incrby age 42")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -1,8 +1,6 @@
package string package string
import ( import (
"strconv"
"github.com/nalgeon/redka/internal/parser" "github.com/nalgeon/redka/internal/parser"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
) )
@@ -13,15 +11,15 @@ import (
// https://redis.io/commands/incrbyfloat // https://redis.io/commands/incrbyfloat
type IncrByFloat struct { type IncrByFloat struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Delta float64 delta float64
} }
func ParseIncrByFloat(b redis.BaseCmd) (*IncrByFloat, error) { func ParseIncrByFloat(b redis.BaseCmd) (*IncrByFloat, error) {
cmd := &IncrByFloat{BaseCmd: b} cmd := &IncrByFloat{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.Float(&cmd.Delta), parser.Float(&cmd.delta),
).Required(2).Run(cmd.Args()) ).Required(2).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -30,11 +28,11 @@ func ParseIncrByFloat(b redis.BaseCmd) (*IncrByFloat, error) {
} }
func (cmd *IncrByFloat) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *IncrByFloat) Run(w redis.Writer, red redis.Redka) (any, error) {
val, err := red.Str().IncrFloat(cmd.Key, cmd.Delta) val, err := red.Str().IncrFloat(cmd.key, cmd.delta)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err
} }
w.WriteBulkString(strconv.FormatFloat(val, 'f', -1, 64)) redis.WriteFloat(w, val)
return val, nil return val, nil
} }

View File

@@ -1,61 +1,52 @@
package string_test package string
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
str "github.com/nalgeon/redka/internal/command/string"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestIncrByFloatParse(t *testing.T) { func TestIncrByFloatParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want IncrByFloat
want str.IncrByFloat
err error err error
}{ }{
{ {
name: "incrbyfloat", cmd: "incrbyfloat",
args: command.BuildArgs("incrbyfloat"), want: IncrByFloat{},
want: str.IncrByFloat{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "incrbyfloat age", cmd: "incrbyfloat age",
args: command.BuildArgs("incrbyfloat", "age"), want: IncrByFloat{},
want: str.IncrByFloat{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "incrbyfloat age 4.2", cmd: "incrbyfloat age 4.2",
args: command.BuildArgs("incrbyfloat", "age", "4.2"), want: IncrByFloat{key: "age", delta: 4.2},
want: str.IncrByFloat{Key: "age", Delta: 4.2},
err: nil, err: nil,
}, },
{ {
name: "incrbyfloat age -4.2", cmd: "incrbyfloat age -4.2",
args: command.BuildArgs("incrbyfloat", "age", "4.2"), want: IncrByFloat{key: "age", delta: -4.2},
want: str.IncrByFloat{Key: "age", Delta: 4.2},
err: nil, err: nil,
}, },
{ {
name: "incrbyfloat age 2.0e2", cmd: "incrbyfloat age 2.0e2",
args: command.BuildArgs("incrbyfloat", "age", "2.0e2"), want: IncrByFloat{key: "age", delta: 2.0e2},
want: str.IncrByFloat{Key: "age", Delta: 2.0e2},
err: nil, err: nil,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseIncrByFloat, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*str.IncrByFloat) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.delta, test.want.delta)
testx.AssertEqual(t, cm.Delta, test.want.Delta)
} }
}) })
} }
@@ -66,43 +57,39 @@ func TestIncrByFloatExec(t *testing.T) {
defer db.Close() defer db.Close()
tests := []struct { tests := []struct {
name string cmd string
cmd *str.IncrByFloat res any
res any out string
out string
}{ }{
{ {
name: "positive", cmd: "incrbyfloat age 4.2",
cmd: command.MustParse[*str.IncrByFloat]("incrbyfloat age 4.2"), res: 29.2,
res: 29.2, out: "29.2",
out: "29.2",
}, },
{ {
name: "negative", cmd: "incrbyfloat age -4.2",
cmd: command.MustParse[*str.IncrByFloat]("incrbyfloat age -4.2"), res: 20.8,
res: 20.8, out: "20.8",
out: "20.8",
}, },
{ {
name: "zero", cmd: "incrbyfloat age 0",
cmd: command.MustParse[*str.IncrByFloat]("incrbyfloat age 0"), res: 25.0,
res: 25.0, out: "25",
out: "25",
}, },
{ {
name: "exponential", cmd: "incrbyfloat age 2.0e2",
cmd: command.MustParse[*str.IncrByFloat]("incrbyfloat age 2.0e2"), res: 225.0,
res: 225.0, out: "225",
out: "225",
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
_ = db.Str().Set("age", 25) _ = db.Str().Set("age", 25)
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := test.cmd.Run(conn, red) cmd := redis.MustParse(ParseIncrByFloat, test.cmd)
res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
testx.AssertEqual(t, res, test.res) testx.AssertEqual(t, res, test.res)
testx.AssertEqual(t, conn.Out(), test.out) testx.AssertEqual(t, conn.Out(), test.out)

View File

@@ -10,7 +10,7 @@ import (
// https://redis.io/commands/mget // https://redis.io/commands/mget
type MGet struct { type MGet struct {
redis.BaseCmd redis.BaseCmd
Keys []string keys []string
} }
func ParseMGet(b redis.BaseCmd) (*MGet, error) { func ParseMGet(b redis.BaseCmd) (*MGet, error) {
@@ -18,16 +18,16 @@ func ParseMGet(b redis.BaseCmd) (*MGet, error) {
if len(cmd.Args()) < 1 { if len(cmd.Args()) < 1 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Keys = make([]string, len(cmd.Args())) cmd.keys = make([]string, len(cmd.Args()))
for i, arg := range cmd.Args() { for i, arg := range cmd.Args() {
cmd.Keys[i] = string(arg) cmd.keys[i] = string(arg)
} }
return cmd, nil return cmd, nil
} }
func (cmd *MGet) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *MGet) Run(w redis.Writer, red redis.Redka) (any, error) {
// Get the key-value map for requested keys. // Get the key-value map for requested keys.
items, err := red.Str().GetMany(cmd.Keys...) items, err := red.Str().GetMany(cmd.keys...)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err
@@ -36,8 +36,8 @@ func (cmd *MGet) Run(w redis.Writer, red redis.Redka) (any, error) {
// Build the result slice. // Build the result slice.
// It will contain all values in the order of keys. // It will contain all values in the order of keys.
// Missing keys will have nil values. // Missing keys will have nil values.
vals := make([]core.Value, len(cmd.Keys)) vals := make([]core.Value, len(cmd.keys))
for i, key := range cmd.Keys { for i, key := range cmd.keys {
vals[i] = items[key] vals[i] = items[key]
} }

View File

@@ -1,10 +1,8 @@
package string_test package string
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
str "github.com/nalgeon/redka/internal/command/string"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,38 +10,33 @@ import (
func TestMGetParse(t *testing.T) { func TestMGetParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte
want []string want []string
err error err error
}{ }{
{ {
name: "mget", cmd: "mget",
args: command.BuildArgs("mget"),
want: nil, want: nil,
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "mget name", cmd: "mget name",
args: command.BuildArgs("mget", "name"),
want: []string{"name"}, want: []string{"name"},
err: nil, err: nil,
}, },
{ {
name: "mget name age", cmd: "mget name age",
args: command.BuildArgs("mget", "name", "age"),
want: []string{"name", "age"}, want: []string{"name", "age"},
err: nil, err: nil,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseMGet, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*str.MGet) testx.AssertEqual(t, cmd.keys, test.want)
testx.AssertEqual(t, cm.Keys, test.want)
} }
}) })
} }
@@ -57,41 +50,37 @@ func TestMGetExec(t *testing.T) {
_ = db.Str().Set("age", 25) _ = db.Str().Set("age", 25)
tests := []struct { tests := []struct {
name string cmd string
cmd *str.MGet res any
res any out string
out string
}{ }{
{ {
name: "single key", cmd: "mget name",
cmd: command.MustParse[*str.MGet]("mget name"), res: []core.Value{core.Value("alice")},
res: []core.Value{core.Value("alice")}, out: "1,alice",
out: "1,alice",
}, },
{ {
name: "multiple keys", cmd: "mget name age",
cmd: command.MustParse[*str.MGet]("mget name age"), res: []core.Value{core.Value("alice"), core.Value("25")},
res: []core.Value{core.Value("alice"), core.Value("25")}, out: "2,alice,25",
out: "2,alice,25",
}, },
{ {
name: "some not found", cmd: "mget name city age",
cmd: command.MustParse[*str.MGet]("mget name city age"), res: []core.Value{core.Value("alice"), core.Value(nil), core.Value("25")},
res: []core.Value{core.Value("alice"), core.Value(nil), core.Value("25")}, out: "3,alice,(nil),25",
out: "3,alice,(nil),25",
}, },
{ {
name: "all not found", cmd: "mget one two",
cmd: command.MustParse[*str.MGet]("mget one two"), res: []core.Value{core.Value(nil), core.Value(nil)},
res: []core.Value{core.Value(nil), core.Value(nil)}, out: "2,(nil),(nil)",
out: "2,(nil),(nil)",
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := test.cmd.Run(conn, red) cmd := redis.MustParse(ParseMGet, test.cmd)
res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
testx.AssertEqual(t, res, test.res) testx.AssertEqual(t, res, test.res)
testx.AssertEqual(t, conn.Out(), test.out) testx.AssertEqual(t, conn.Out(), test.out)

View File

@@ -10,13 +10,13 @@ import (
// https://redis.io/commands/mset // https://redis.io/commands/mset
type MSet struct { type MSet struct {
redis.BaseCmd redis.BaseCmd
Items map[string]any items map[string]any
} }
func ParseMSet(b redis.BaseCmd) (*MSet, error) { func ParseMSet(b redis.BaseCmd) (*MSet, error) {
cmd := &MSet{BaseCmd: b} cmd := &MSet{BaseCmd: b}
err := parser.New( err := parser.New(
parser.AnyMap(&cmd.Items), parser.AnyMap(&cmd.items),
).Required(2).Run(cmd.Args()) ).Required(2).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -25,7 +25,7 @@ func ParseMSet(b redis.BaseCmd) (*MSet, error) {
} }
func (cmd *MSet) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *MSet) Run(w redis.Writer, red redis.Redka) (any, error) {
err := red.Str().SetMany(cmd.Items) err := red.Str().SetMany(cmd.items)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,49 +1,41 @@
package string_test package string
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
str "github.com/nalgeon/redka/internal/command/string"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestMSetParse(t *testing.T) { func TestMSetParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want MSet
want str.MSet
err error err error
}{ }{
{ {
name: "mset", cmd: "mset",
args: command.BuildArgs("mset"), want: MSet{},
want: str.MSet{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "mset name", cmd: "mset name",
args: command.BuildArgs("mset", "name"), want: MSet{},
want: str.MSet{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "mset name alice", cmd: "mset name alice",
args: command.BuildArgs("mset", "name", "alice"), want: MSet{items: map[string]any{"name": []byte("alice")}},
want: str.MSet{Items: map[string]any{"name": []byte("alice")}},
err: nil, err: nil,
}, },
{ {
name: "mset name alice age", cmd: "mset name alice age",
args: command.BuildArgs("mset", "name", "alice", "age"), want: MSet{},
want: str.MSet{},
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
{ {
name: "mset name alice age 25", cmd: "mset name alice age 25",
args: command.BuildArgs("mset", "name", "alice", "age", "25"), want: MSet{items: map[string]any{
want: str.MSet{Items: map[string]any{
"name": []byte("alice"), "name": []byte("alice"),
"age": []byte("25"), "age": []byte("25"),
}}, }},
@@ -52,12 +44,11 @@ func TestMSetParse(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseMSet, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*str.MSet) testx.AssertEqual(t, cmd.items, test.want.items)
testx.AssertEqual(t, cm.Items, test.want.Items)
} }
}) })
} }
@@ -68,7 +59,7 @@ func TestMSetExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*str.MSet]("mset name alice") cmd := redis.MustParse(ParseMSet, "mset name alice")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -83,7 +74,7 @@ func TestMSetExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*str.MSet]("mset name alice age 25") cmd := redis.MustParse(ParseMSet, "mset name alice age 25")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -102,7 +93,7 @@ func TestMSetExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*str.MSet]("mset name bob age 50") cmd := redis.MustParse(ParseMSet, "mset name bob age 50")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -122,7 +113,7 @@ func TestMSetExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
_ = db.Str().Set("age", 25) _ = db.Str().Set("age", 25)
cmd := command.MustParse[*str.MSet]("mset name bob age 50") cmd := redis.MustParse(ParseMSet, "mset name bob age 50")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -1,74 +1,73 @@
package string_test package string
import ( import (
"testing" "testing"
"time" "time"
"github.com/nalgeon/redka/internal/command"
str "github.com/nalgeon/redka/internal/command/string"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestPSetEXParse(t *testing.T) { func TestPSetEXParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want SetEX
want str.SetEX
err error err error
}{ }{
{ {
name: "psetex", cmd: "psetex",
args: command.BuildArgs("psetex"), want: SetEX{},
want: str.SetEX{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "psetex name", cmd: "psetex name",
args: command.BuildArgs("psetex", "name"), want: SetEX{},
want: str.SetEX{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "psetex name alice", cmd: "psetex name alice",
args: command.BuildArgs("psetex", "name", "alice"), want: SetEX{},
want: str.SetEX{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "psetex name alice 60", cmd: "psetex name alice 60",
args: command.BuildArgs("psetex", "name", "alice", "60"), want: SetEX{},
want: str.SetEX{},
err: redis.ErrInvalidInt, err: redis.ErrInvalidInt,
}, },
{ {
name: "psetex name 60 alice", cmd: "psetex name 60 alice",
args: command.BuildArgs("psetex", "name", "60", "alice"), want: SetEX{key: "name", value: []byte("alice"), ttl: 60 * time.Millisecond},
want: str.SetEX{Key: "name", Value: []byte("alice"), TTL: 60 * time.Millisecond},
err: nil, err: nil,
}, },
} }
parse := func(b redis.BaseCmd) (*SetEX, error) {
return ParseSetEX(b, 1)
}
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(parse, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*str.SetEX) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.value, test.want.value)
testx.AssertEqual(t, cm.Value, test.want.Value) testx.AssertEqual(t, cmd.ttl, test.want.ttl)
testx.AssertEqual(t, cm.TTL, test.want.TTL)
} }
}) })
} }
} }
func TestPSetEXExec(t *testing.T) { func TestPSetEXExec(t *testing.T) {
parse := func(b redis.BaseCmd) (*SetEX, error) {
return ParseSetEX(b, 1)
}
t.Run("create", func(t *testing.T) { t.Run("create", func(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*str.SetEX]("psetex name 60000 alice") cmd := redis.MustParse(parse, "psetex name 60000 alice")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -89,7 +88,7 @@ func TestPSetEXExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*str.SetEX]("psetex name 60000 bob") cmd := redis.MustParse(parse, "psetex name 60000 bob")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -110,7 +109,7 @@ func TestPSetEXExec(t *testing.T) {
_ = db.Str().SetExpires("name", "alice", 60*time.Second) _ = db.Str().SetExpires("name", "alice", 60*time.Second)
cmd := command.MustParse[*str.SetEX]("psetex name 10000 bob") cmd := redis.MustParse(parse, "psetex name 10000 bob")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -14,14 +14,14 @@ import (
// https://redis.io/commands/set // https://redis.io/commands/set
type Set struct { type Set struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Value []byte value []byte
IfNX bool ifNX bool
IfXX bool ifXX bool
Get bool get bool
TTL time.Duration ttl time.Duration
At time.Time at time.Time
KeepTTL bool keepTTL bool
} }
func ParseSet(b redis.BaseCmd) (*Set, error) { func ParseSet(b redis.BaseCmd) (*Set, error) {
@@ -30,19 +30,19 @@ func ParseSet(b redis.BaseCmd) (*Set, error) {
// Parse the command arguments. // Parse the command arguments.
var ttlSec, ttlMs, atSec, atMs int var ttlSec, ttlMs, atSec, atMs int
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.Bytes(&cmd.Value), parser.Bytes(&cmd.value),
parser.OneOf( parser.OneOf(
parser.Flag("nx", &cmd.IfNX), parser.Flag("nx", &cmd.ifNX),
parser.Flag("xx", &cmd.IfXX), parser.Flag("xx", &cmd.ifXX),
), ),
parser.Flag("get", &cmd.Get), parser.Flag("get", &cmd.get),
parser.OneOf( parser.OneOf(
parser.Named("ex", parser.Int(&ttlSec)), parser.Named("ex", parser.Int(&ttlSec)),
parser.Named("px", parser.Int(&ttlMs)), parser.Named("px", parser.Int(&ttlMs)),
parser.Named("exat", parser.Int(&atSec)), parser.Named("exat", parser.Int(&atSec)),
parser.Named("pxat", parser.Int(&atMs)), parser.Named("pxat", parser.Int(&atMs)),
parser.Flag("keepttl", &cmd.KeepTTL), parser.Flag("keepttl", &cmd.keepTTL),
), ),
).Required(2).Run(cmd.Args()) ).Required(2).Run(cmd.Args())
if err != nil { if err != nil {
@@ -51,15 +51,15 @@ func ParseSet(b redis.BaseCmd) (*Set, error) {
// Set the expiration time. // Set the expiration time.
if ttlSec > 0 { if ttlSec > 0 {
cmd.TTL = time.Duration(ttlSec) * time.Second cmd.ttl = time.Duration(ttlSec) * time.Second
} else if ttlMs > 0 { } else if ttlMs > 0 {
cmd.TTL = time.Duration(ttlMs) * time.Millisecond cmd.ttl = time.Duration(ttlMs) * time.Millisecond
} else if atSec > 0 { } else if atSec > 0 {
cmd.At = time.Unix(int64(atSec), 0) cmd.at = time.Unix(int64(atSec), 0)
} else if atMs > 0 { } else if atMs > 0 {
cmd.At = time.Unix(0, int64(atMs)*int64(time.Millisecond)) cmd.at = time.Unix(0, int64(atMs)*int64(time.Millisecond))
} }
if cmd.TTL < 0 { if cmd.ttl < 0 {
return cmd, redis.ErrInvalidExpireTime return cmd, redis.ErrInvalidExpireTime
} }
@@ -67,9 +67,9 @@ func ParseSet(b redis.BaseCmd) (*Set, error) {
} }
func (cmd *Set) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *Set) Run(w redis.Writer, red redis.Redka) (any, error) {
if !cmd.IfNX && !cmd.IfXX && !cmd.Get && !cmd.KeepTTL && cmd.At.IsZero() { if !cmd.ifNX && !cmd.ifXX && !cmd.get && !cmd.keepTTL && cmd.at.IsZero() {
// Simple SET without additional options (except ttl). // Simple SET without additional options (except ttl).
err := red.Str().SetExpires(cmd.Key, cmd.Value, cmd.TTL) err := red.Str().SetExpires(cmd.key, cmd.value, cmd.ttl)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err
@@ -79,26 +79,26 @@ func (cmd *Set) Run(w redis.Writer, red redis.Redka) (any, error) {
} }
// SET with additional options. // SET with additional options.
op := red.Str().SetWith(cmd.Key, cmd.Value) op := red.Str().SetWith(cmd.key, cmd.value)
if cmd.IfXX { if cmd.ifXX {
op = op.IfExists() op = op.IfExists()
} else if cmd.IfNX { } else if cmd.ifNX {
op = op.IfNotExists() op = op.IfNotExists()
} }
if cmd.TTL > 0 { if cmd.ttl > 0 {
op = op.TTL(cmd.TTL) op = op.TTL(cmd.ttl)
} else if !cmd.At.IsZero() { } else if !cmd.at.IsZero() {
op = op.At(cmd.At) op = op.At(cmd.at)
} else if cmd.KeepTTL { } else if cmd.keepTTL {
op = op.KeepTTL() op = op.KeepTTL()
} }
out, err := op.Run() out, err := op.Run()
// Determine the output status. // Determine the output status.
var ok bool var ok bool
if cmd.IfXX { if cmd.ifXX {
ok = out.Updated ok = out.Updated
} else if cmd.IfNX { } else if cmd.ifNX {
ok = out.Created ok = out.Created
} else { } else {
ok = err == nil ok = err == nil
@@ -110,7 +110,7 @@ func (cmd *Set) Run(w redis.Writer, red redis.Redka) (any, error) {
return nil, err return nil, err
} }
if cmd.Get { if cmd.get {
// GET given: The key didn't exist before the SET. // GET given: The key didn't exist before the SET.
if !out.Prev.Exists() { if !out.Prev.Exists() {
w.WriteNull() w.WriteNull()

View File

@@ -1,11 +1,9 @@
package string_test package string
import ( import (
"testing" "testing"
"time" "time"
"github.com/nalgeon/redka/internal/command"
str "github.com/nalgeon/redka/internal/command/string"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -13,128 +11,125 @@ import (
func TestSetParse(t *testing.T) { func TestSetParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want Set
want str.Set
err error err error
}{ }{
{ {
name: "set", cmd: "set",
args: command.BuildArgs("set"), want: Set{},
want: str.Set{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "set name", cmd: "set name",
args: command.BuildArgs("set", "name"), want: Set{},
want: str.Set{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "set name alice", cmd: "set name alice",
args: command.BuildArgs("set", "name", "alice"), want: Set{key: "name", value: []byte("alice")},
want: str.Set{Key: "name", Value: []byte("alice")},
err: nil, err: nil,
}, },
{ {
name: "set name alice nx", cmd: "set name alice nx",
args: command.BuildArgs("set", "name", "alice", "nx"), want: Set{key: "name", value: []byte("alice"), ifNX: true},
want: str.Set{Key: "name", Value: []byte("alice"), IfNX: true},
err: nil, err: nil,
}, },
{ {
name: "set name alice xx", cmd: "set name alice xx",
args: command.BuildArgs("set", "name", "alice", "xx"), want: Set{key: "name", value: []byte("alice"), ifXX: true},
want: str.Set{Key: "name", Value: []byte("alice"), IfXX: true},
err: nil, err: nil,
}, },
{ {
name: "set name alice nx xx", cmd: "set name alice nx xx",
args: command.BuildArgs("set", "name", "alice", "nx", "xx"), want: Set{},
want: str.Set{},
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
{ {
name: "set name alice ex 10", cmd: "set name alice ex 10",
args: command.BuildArgs("set", "name", "alice", "ex", "10"), want: Set{key: "name", value: []byte("alice"), ttl: 10 * time.Second},
want: str.Set{Key: "name", Value: []byte("alice"), TTL: 10 * time.Second},
err: nil, err: nil,
}, },
{ {
name: "set name alice ex 0", cmd: "set name alice ex 0",
args: command.BuildArgs("set", "name", "alice", "ex", "0"), want: Set{key: "name", value: []byte("alice"), ttl: 0},
want: str.Set{Key: "name", Value: []byte("alice"), TTL: 0},
err: nil, err: nil,
}, },
{ {
name: "set name alice px 10", cmd: "set name alice px 10",
args: command.BuildArgs("set", "name", "alice", "px", "10"), want: Set{key: "name", value: []byte("alice"), ttl: 10 * time.Millisecond},
want: str.Set{Key: "name", Value: []byte("alice"), TTL: 10 * time.Millisecond},
err: nil, err: nil,
}, },
{ {
name: "set name alice exat 1577882096", cmd: "set name alice exat 1577882096",
args: command.BuildArgs("set", "name", "alice", "exat", "1577882096"), want: Set{
want: str.Set{Key: "name", Value: []byte("alice"), key: "name", value: []byte("alice"),
At: time.Date(2020, 1, 1, 12, 34, 56, 0, time.UTC)}, at: time.Date(2020, 1, 1, 12, 34, 56, 0, time.UTC),
},
err: nil, err: nil,
}, },
{ {
name: "set name alice pxat 1577882096000", cmd: "set name alice pxat 1577882096000",
args: command.BuildArgs("set", "name", "alice", "exat", "1577882096000"), want: Set{
want: str.Set{Key: "name", Value: []byte("alice"), key: "name", value: []byte("alice"),
At: time.Date(2020, 1, 1, 12, 34, 56, 0, time.UTC)}, at: time.Date(2020, 1, 1, 12, 34, 56, 0, time.UTC),
},
err: nil, err: nil,
}, },
{ {
name: "set name alice keepttl", cmd: "set name alice keepttl",
args: command.BuildArgs("set", "name", "alice", "keepttl"), want: Set{key: "name", value: []byte("alice"), keepTTL: true},
want: str.Set{Key: "name", Value: []byte("alice"), KeepTTL: true},
err: nil, err: nil,
}, },
{ {
name: "set name alice ex 10 keepttl", cmd: "set name alice ex 10 keepttl",
args: command.BuildArgs("set", "name", "alice", "ex", "10", "keepttl"), want: Set{},
want: str.Set{},
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
{ {
name: "set name alice nx ex 10", cmd: "set name alice nx ex 10",
args: command.BuildArgs("set", "name", "alice", "nx", "ex", "10"), want: Set{
want: str.Set{Key: "name", Value: []byte("alice"), IfNX: true, TTL: 10 * time.Second}, key: "name", value: []byte("alice"),
err: nil, ifNX: true, ttl: 10 * time.Second,
},
err: nil,
}, },
{ {
name: "set name alice xx px 10", cmd: "set name alice xx px 10",
args: command.BuildArgs("set", "name", "alice", "xx", "px", "10"), want: Set{
want: str.Set{Key: "name", Value: []byte("alice"), IfXX: true, TTL: 10 * time.Millisecond}, key: "name", value: []byte("alice"),
err: nil, ifXX: true, ttl: 10 * time.Millisecond,
},
err: nil,
}, },
{ {
name: "set name alice ex 10 nx", cmd: "set name alice ex 10 nx",
args: command.BuildArgs("set", "name", "alice", "ex", "10", "nx"), want: Set{
want: str.Set{Key: "name", Value: []byte("alice"), IfNX: true, TTL: 10 * time.Second}, key: "name", value: []byte("alice"),
err: nil, ifNX: true, ttl: 10 * time.Second,
},
err: nil,
}, },
{ {
name: "set name alice nx get ex 10", cmd: "set name alice nx get ex 10",
args: command.BuildArgs("set", "name", "alice", "nx", "ex", "10"), want: Set{
want: str.Set{Key: "name", Value: []byte("alice"), IfNX: true, Get: true, TTL: 10 * time.Second}, key: "name", value: []byte("alice"),
err: nil, ifNX: true, get: true, ttl: 10 * time.Second,
},
err: nil,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseSet, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
setCmd := cmd.(*str.Set) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, setCmd.Key, test.want.Key) testx.AssertEqual(t, cmd.value, test.want.value)
testx.AssertEqual(t, setCmd.Value, test.want.Value) testx.AssertEqual(t, cmd.ifNX, test.want.ifNX)
testx.AssertEqual(t, setCmd.IfNX, test.want.IfNX) testx.AssertEqual(t, cmd.ifXX, test.want.ifXX)
testx.AssertEqual(t, setCmd.IfXX, test.want.IfXX) testx.AssertEqual(t, cmd.ttl, test.want.ttl)
testx.AssertEqual(t, setCmd.TTL, test.want.TTL)
} }
}) })
} }
@@ -145,77 +140,67 @@ func TestSetExec(t *testing.T) {
defer db.Close() defer db.Close()
tests := []struct { tests := []struct {
name string cmd string
cmd *str.Set res any
res any out string
out string
}{ }{
{ {
name: "set", cmd: "set name alice",
cmd: command.MustParse[*str.Set]("set name alice"), res: true,
res: true, out: "OK",
out: "OK",
}, },
{ {
name: "set nx conflict", cmd: "set name alice nx",
cmd: command.MustParse[*str.Set]("set name alice nx"), res: false,
res: false, out: "(nil)",
out: "(nil)",
}, },
{ {
name: "set nx", cmd: "set age alice nx",
cmd: command.MustParse[*str.Set]("set age alice nx"), res: true,
res: true, out: "OK",
out: "OK",
}, },
{ {
name: "set xx", cmd: "set name bob xx",
cmd: command.MustParse[*str.Set]("set name bob xx"), res: true,
res: true, out: "OK",
out: "OK",
}, },
{ {
name: "set xx conflict", cmd: "set city paris xx",
cmd: command.MustParse[*str.Set]("set city paris xx"), res: false,
res: false, out: "(nil)",
out: "(nil)",
}, },
{ {
name: "set ex", cmd: "set name alice ex 10",
cmd: command.MustParse[*str.Set]("set name alice ex 10"), res: true,
res: true, out: "OK",
out: "OK",
}, },
{ {
name: "set keepttl", cmd: "set name alice keepttl",
cmd: command.MustParse[*str.Set]("set name alice keepttl"), res: true,
res: true, out: "OK",
out: "OK",
}, },
{ {
name: "set nx ex", cmd: "set color blue nx ex 10",
cmd: command.MustParse[*str.Set]("set color blue nx ex 10"), res: true,
res: true, out: "OK",
out: "OK",
}, },
{ {
name: "set get", cmd: "set name bob get",
cmd: command.MustParse[*str.Set]("set name bob get"), res: core.Value("alice"),
res: core.Value("alice"), out: "alice",
out: "alice",
}, },
{ {
name: "set get nil", cmd: "set country france get",
cmd: command.MustParse[*str.Set]("set country france get"), res: core.Value(nil),
res: core.Value(nil), out: "(nil)",
out: "(nil)",
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := test.cmd.Run(conn, red) cmd := redis.MustParse(ParseSet, test.cmd)
res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
testx.AssertEqual(t, res, test.res) testx.AssertEqual(t, res, test.res)
testx.AssertEqual(t, conn.Out(), test.out) testx.AssertEqual(t, conn.Out(), test.out)

View File

@@ -13,28 +13,28 @@ import (
// https://redis.io/commands/setex // https://redis.io/commands/setex
type SetEX struct { type SetEX struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Value []byte value []byte
TTL time.Duration ttl time.Duration
} }
func ParseSetEX(b redis.BaseCmd, multi int) (*SetEX, error) { func ParseSetEX(b redis.BaseCmd, multi int) (*SetEX, error) {
cmd := &SetEX{BaseCmd: b} cmd := &SetEX{BaseCmd: b}
var ttl int var ttl int
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.Int(&ttl), parser.Int(&ttl),
parser.Bytes(&cmd.Value), parser.Bytes(&cmd.value),
).Required(3).Run(cmd.Args()) ).Required(3).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
} }
cmd.TTL = time.Duration(multi*ttl) * time.Millisecond cmd.ttl = time.Duration(multi*ttl) * time.Millisecond
return cmd, nil return cmd, nil
} }
func (cmd *SetEX) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *SetEX) Run(w redis.Writer, red redis.Redka) (any, error) {
err := red.Str().SetExpires(cmd.Key, cmd.Value, cmd.TTL) err := red.Str().SetExpires(cmd.key, cmd.value, cmd.ttl)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,74 +1,73 @@
package string_test package string
import ( import (
"testing" "testing"
"time" "time"
"github.com/nalgeon/redka/internal/command"
str "github.com/nalgeon/redka/internal/command/string"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestSetEXParse(t *testing.T) { func TestSetEXParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want SetEX
want str.SetEX
err error err error
}{ }{
{ {
name: "setex", cmd: "setex",
args: command.BuildArgs("setex"), want: SetEX{},
want: str.SetEX{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "setex name", cmd: "setex name",
args: command.BuildArgs("setex", "name"), want: SetEX{},
want: str.SetEX{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "setex name alice", cmd: "setex name alice",
args: command.BuildArgs("setex", "name", "alice"), want: SetEX{},
want: str.SetEX{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "setex name alice 60", cmd: "setex name alice 60",
args: command.BuildArgs("setex", "name", "alice", "60"), want: SetEX{},
want: str.SetEX{},
err: redis.ErrInvalidInt, err: redis.ErrInvalidInt,
}, },
{ {
name: "setex name 60 alice", cmd: "setex name 60 alice",
args: command.BuildArgs("setex", "name", "60", "alice"), want: SetEX{key: "name", value: []byte("alice"), ttl: 60 * 1000 * time.Millisecond},
want: str.SetEX{Key: "name", Value: []byte("alice"), TTL: 60 * 1000 * time.Millisecond},
err: nil, err: nil,
}, },
} }
parse := func(b redis.BaseCmd) (*SetEX, error) {
return ParseSetEX(b, 1000)
}
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(parse, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*str.SetEX) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.value, test.want.value)
testx.AssertEqual(t, cm.Value, test.want.Value) testx.AssertEqual(t, cmd.ttl, test.want.ttl)
testx.AssertEqual(t, cm.TTL, test.want.TTL)
} }
}) })
} }
} }
func TestSetEXExec(t *testing.T) { func TestSetEXExec(t *testing.T) {
parse := func(b redis.BaseCmd) (*SetEX, error) {
return ParseSetEX(b, 1000)
}
t.Run("create", func(t *testing.T) { t.Run("create", func(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*str.SetEX]("setex name 60 alice") cmd := redis.MustParse(parse, "setex name 60 alice")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -89,7 +88,7 @@ func TestSetEXExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*str.SetEX]("setex name 60 bob") cmd := redis.MustParse(parse, "setex name 60 bob")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -110,7 +109,7 @@ func TestSetEXExec(t *testing.T) {
_ = db.Str().SetExpires("name", "alice", 60*time.Second) _ = db.Str().SetExpires("name", "alice", 60*time.Second)
cmd := command.MustParse[*str.SetEX]("setex name 10 bob") cmd := redis.MustParse(parse, "setex name 10 bob")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -7,8 +7,8 @@ import "github.com/nalgeon/redka/internal/redis"
// https://redis.io/commands/setnx // https://redis.io/commands/setnx
type SetNX struct { type SetNX struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Value []byte value []byte
} }
func ParseSetNX(b redis.BaseCmd) (*SetNX, error) { func ParseSetNX(b redis.BaseCmd) (*SetNX, error) {
@@ -16,13 +16,13 @@ func ParseSetNX(b redis.BaseCmd) (*SetNX, error) {
if len(cmd.Args()) != 2 { if len(cmd.Args()) != 2 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Key = string(cmd.Args()[0]) cmd.key = string(cmd.Args()[0])
cmd.Value = cmd.Args()[1] cmd.value = cmd.Args()[1]
return cmd, nil return cmd, nil
} }
func (cmd *SetNX) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *SetNX) Run(w redis.Writer, red redis.Redka) (any, error) {
out, err := red.Str().SetWith(cmd.Key, cmd.Value).IfNotExists().Run() out, err := red.Str().SetWith(cmd.key, cmd.value).IfNotExists().Run()
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,55 +1,47 @@
package string_test package string
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
str "github.com/nalgeon/redka/internal/command/string"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestSetNXParse(t *testing.T) { func TestSetNXParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want SetNX
want str.SetNX
err error err error
}{ }{
{ {
name: "setnx", cmd: "setnx",
args: command.BuildArgs("setnx"), want: SetNX{},
want: str.SetNX{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "setnx name", cmd: "setnx name",
args: command.BuildArgs("setnx", "name"), want: SetNX{},
want: str.SetNX{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "setnx name alice", cmd: "setnx name alice",
args: command.BuildArgs("setnx", "name", "alice"), want: SetNX{key: "name", value: []byte("alice")},
want: str.SetNX{Key: "name", Value: []byte("alice")},
err: nil, err: nil,
}, },
{ {
name: "setnx name alice 60", cmd: "setnx name alice 60",
args: command.BuildArgs("setnx", "name", "alice", "60"), want: SetNX{},
want: str.SetNX{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseSetNX, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*str.SetNX) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.value, test.want.value)
testx.AssertEqual(t, cm.Value, test.want.Value)
} }
}) })
} }
@@ -60,7 +52,7 @@ func TestSetNXExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*str.SetNX]("setnx name alice") cmd := redis.MustParse(ParseSetNX, "setnx name alice")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -77,7 +69,7 @@ func TestSetNXExec(t *testing.T) {
_ = db.Str().Set("name", "alice") _ = db.Str().Set("name", "alice")
cmd := command.MustParse[*str.SetNX]("setnx name bob") cmd := redis.MustParse(ParseSetNX, "setnx name bob")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -1,4 +1,4 @@
package string_test package string
import ( import (
"testing" "testing"

View File

@@ -11,15 +11,15 @@ import (
// https://redis.io/commands/zadd // https://redis.io/commands/zadd
type ZAdd struct { type ZAdd struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Items map[any]float64 items map[any]float64
} }
func ParseZAdd(b redis.BaseCmd) (*ZAdd, error) { func ParseZAdd(b redis.BaseCmd) (*ZAdd, error) {
cmd := &ZAdd{BaseCmd: b} cmd := &ZAdd{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.FloatMap(&cmd.Items), parser.FloatMap(&cmd.items),
).Required(3).Run(cmd.Args()) ).Required(3).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -28,7 +28,7 @@ func ParseZAdd(b redis.BaseCmd) (*ZAdd, error) {
} }
func (cmd *ZAdd) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *ZAdd) Run(w redis.Writer, red redis.Redka) (any, error) {
count, err := red.ZSet().AddMany(cmd.Key, cmd.Items) count, err := red.ZSet().AddMany(cmd.key, cmd.items)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,55 +1,46 @@
package zset_test package zset
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/zset"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestZAddParse(t *testing.T) { func TestZAddParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want ZAdd
want zset.ZAdd
err error err error
}{ }{
{ {
name: "zadd", cmd: "zadd",
args: command.BuildArgs("zadd"), want: ZAdd{},
want: zset.ZAdd{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zadd key", cmd: "zadd key",
args: command.BuildArgs("zadd", "key"), want: ZAdd{},
want: zset.ZAdd{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zadd key one", cmd: "zadd key one",
args: command.BuildArgs("zadd", "key", "one"), want: ZAdd{},
want: zset.ZAdd{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zadd key 1 one", cmd: "zadd key 1.1 one",
args: command.BuildArgs("zadd", "key", "1.1", "one"), want: ZAdd{key: "key", items: map[any]float64{"one": 1.1}},
want: zset.ZAdd{Key: "key", Items: map[any]float64{"one": 1.1}},
err: nil, err: nil,
}, },
{ {
name: "zadd key 1 one 2", cmd: "zadd key 1.1 one 2.2",
args: command.BuildArgs("zadd", "key", "1.1", "one", "2.2"), want: ZAdd{},
want: zset.ZAdd{},
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
{ {
name: "zadd key one 1.1 two 2.2", cmd: "zadd key 1.1 one 2.2 two",
args: command.BuildArgs("zadd", "key", "1.1", "one", "2.2", "two"), want: ZAdd{key: "key", items: map[any]float64{
want: zset.ZAdd{Key: "key", Items: map[any]float64{
"one": 1.1, "one": 1.1,
"two": 2.2, "two": 2.2,
}}, }},
@@ -58,13 +49,12 @@ func TestZAddParse(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseZAdd, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*zset.ZAdd) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.items, test.want.items)
testx.AssertEqual(t, cm.Items, test.want.Items)
} }
}) })
} }
@@ -75,7 +65,7 @@ func TestZAddExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*zset.ZAdd]("zadd key 11 one") cmd := redis.MustParse(ParseZAdd, "zadd key 11 one")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -89,7 +79,7 @@ func TestZAddExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*zset.ZAdd]("zadd key 11 one 22 two") cmd := redis.MustParse(ParseZAdd, "zadd key 11 one 22 two")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -108,7 +98,7 @@ func TestZAddExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "one", 11) _, _ = db.ZSet().Add("key", "one", 11)
cmd := command.MustParse[*zset.ZAdd]("zadd key 12 one 22 two") cmd := redis.MustParse(ParseZAdd, "zadd key 12 one 22 two")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -127,7 +117,7 @@ func TestZAddExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "one", 11) _, _ = db.ZSet().Add("key", "one", 11)
_, _ = db.ZSet().Add("key", "two", 22) _, _ = db.ZSet().Add("key", "two", 22)
cmd := command.MustParse[*zset.ZAdd]("zadd key 12 one 23 two") cmd := redis.MustParse(ParseZAdd, "zadd key 12 one 23 two")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -144,7 +134,7 @@ func TestZAddExec(t *testing.T) {
defer db.Close() defer db.Close()
_ = db.Str().Set("key", "value") _ = db.Str().Set("key", "value")
cmd := command.MustParse[*zset.ZAdd]("zadd key 11 one") cmd := redis.MustParse(ParseZAdd, "zadd key 11 one")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -7,7 +7,7 @@ import "github.com/nalgeon/redka/internal/redis"
// https://redis.io/commands/zcard // https://redis.io/commands/zcard
type ZCard struct { type ZCard struct {
redis.BaseCmd redis.BaseCmd
Key string key string
} }
func ParseZCard(b redis.BaseCmd) (*ZCard, error) { func ParseZCard(b redis.BaseCmd) (*ZCard, error) {
@@ -15,12 +15,12 @@ func ParseZCard(b redis.BaseCmd) (*ZCard, error) {
if len(cmd.Args()) != 1 { if len(cmd.Args()) != 1 {
return cmd, redis.ErrInvalidArgNum return cmd, redis.ErrInvalidArgNum
} }
cmd.Key = string(cmd.Args()[0]) cmd.key = string(cmd.Args()[0])
return cmd, nil return cmd, nil
} }
func (cmd *ZCard) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *ZCard) Run(w redis.Writer, red redis.Redka) (any, error) {
n, err := red.ZSet().Len(cmd.Key) n, err := red.ZSet().Len(cmd.key)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,48 +1,41 @@
package zset_test package zset
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/zset"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestZCardParse(t *testing.T) { func TestZCardParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want ZCard
want zset.ZCard
err error err error
}{ }{
{ {
name: "zcard", cmd: "zcard",
args: command.BuildArgs("zcard"), want: ZCard{},
want: zset.ZCard{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zcard key", cmd: "zcard key",
args: command.BuildArgs("zcard", "key"), want: ZCard{key: "key"},
want: zset.ZCard{Key: "key"},
err: nil, err: nil,
}, },
{ {
name: "zcard key one", cmd: "zcard key one",
args: command.BuildArgs("zcard", "key", "one"), want: ZCard{},
want: zset.ZCard{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseZCard, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*zset.ZCard) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key)
} }
}) })
} }
@@ -55,7 +48,7 @@ func TestZCardExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "one", 11) _, _ = db.ZSet().Add("key", "one", 11)
_, _ = db.ZSet().Add("key", "two", 22) _, _ = db.ZSet().Add("key", "two", 22)
cmd := command.MustParse[*zset.ZCard]("zcard key") cmd := redis.MustParse(ParseZCard, "zcard key")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -68,7 +61,7 @@ func TestZCardExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "one", 11) _, _ = db.ZSet().Add("key", "one", 11)
_, _ = db.ZSet().Delete("key", "one") _, _ = db.ZSet().Delete("key", "one")
cmd := command.MustParse[*zset.ZCard]("zcard key") cmd := redis.MustParse(ParseZCard, "zcard key")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -79,7 +72,7 @@ func TestZCardExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*zset.ZCard]("zcard key") cmd := redis.MustParse(ParseZCard, "zcard key")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -91,7 +84,7 @@ func TestZCardExec(t *testing.T) {
defer db.Close() defer db.Close()
_ = db.Str().Set("key", "value") _ = db.Str().Set("key", "value")
cmd := command.MustParse[*zset.ZCard]("zcard key") cmd := redis.MustParse(ParseZCard, "zcard key")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -10,17 +10,17 @@ import (
// https://redis.io/commands/zcount // https://redis.io/commands/zcount
type ZCount struct { type ZCount struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Min float64 min float64
Max float64 max float64
} }
func ParseZCount(b redis.BaseCmd) (*ZCount, error) { func ParseZCount(b redis.BaseCmd) (*ZCount, error) {
cmd := &ZCount{BaseCmd: b} cmd := &ZCount{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.Float(&cmd.Min), parser.Float(&cmd.min),
parser.Float(&cmd.Max), parser.Float(&cmd.max),
).Required(3).Run(cmd.Args()) ).Required(3).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -29,7 +29,7 @@ func ParseZCount(b redis.BaseCmd) (*ZCount, error) {
} }
func (cmd *ZCount) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *ZCount) Run(w redis.Writer, red redis.Redka) (any, error) {
n, err := red.ZSet().Count(cmd.Key, cmd.Min, cmd.Max) n, err := red.ZSet().Count(cmd.key, cmd.min, cmd.max)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,62 +1,53 @@
package zset_test package zset
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/zset"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestZCountParse(t *testing.T) { func TestZCountParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want ZCount
want zset.ZCount
err error err error
}{ }{
{ {
name: "zcount", cmd: "zcount",
args: command.BuildArgs("zcount"), want: ZCount{},
want: zset.ZCount{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zcount key", cmd: "zcount key",
args: command.BuildArgs("zcount", "key"), want: ZCount{},
want: zset.ZCount{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zcount key 11", cmd: "zcount key 1.1",
args: command.BuildArgs("zcount", "key", "11"), want: ZCount{},
want: zset.ZCount{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zcount key 11 22", cmd: "zcount key 1.1 2.2",
args: command.BuildArgs("zcount", "key", "1.1", "2.2"), want: ZCount{key: "key", min: 1.1, max: 2.2},
want: zset.ZCount{Key: "key", Min: 1.1, Max: 2.2},
err: nil, err: nil,
}, },
{ {
name: "zcount key 11 22 33", cmd: "zcount key 1.1 2.2 3.3",
args: command.BuildArgs("zcount", "key", "1.1", "2.2", "3.3"), want: ZCount{},
want: zset.ZCount{},
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseZCount, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*zset.ZCount) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.min, test.want.min)
testx.AssertEqual(t, cm.Min, test.want.Min) testx.AssertEqual(t, cmd.max, test.want.max)
testx.AssertEqual(t, cm.Max, test.want.Max)
} }
}) })
} }
@@ -70,7 +61,7 @@ func TestZCountExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "two", 22) _, _ = db.ZSet().Add("key", "two", 22)
_, _ = db.ZSet().Add("key", "thr", 33) _, _ = db.ZSet().Add("key", "thr", 33)
cmd := command.MustParse[*zset.ZCount]("zcount key 15 25") cmd := redis.MustParse(ParseZCount, "zcount key 15 25")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -84,7 +75,7 @@ func TestZCountExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "two", 22) _, _ = db.ZSet().Add("key", "two", 22)
_, _ = db.ZSet().Add("key", "thr", 33) _, _ = db.ZSet().Add("key", "thr", 33)
cmd := command.MustParse[*zset.ZCount]("zcount key 11 33") cmd := redis.MustParse(ParseZCount, "zcount key 11 33")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -98,7 +89,7 @@ func TestZCountExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "two", 22) _, _ = db.ZSet().Add("key", "two", 22)
_, _ = db.ZSet().Add("key", "thr", 33) _, _ = db.ZSet().Add("key", "thr", 33)
cmd := command.MustParse[*zset.ZCount]("zcount key 44 55") cmd := redis.MustParse(ParseZCount, "zcount key 44 55")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -109,7 +100,7 @@ func TestZCountExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*zset.ZCount]("zcount key 11 33") cmd := redis.MustParse(ParseZCount, "zcount key 11 33")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -121,7 +112,7 @@ func TestZCountExec(t *testing.T) {
defer db.Close() defer db.Close()
_ = db.Str().Set("key", "value") _ = db.Str().Set("key", "value")
cmd := command.MustParse[*zset.ZCount]("zcount key 11 33") cmd := redis.MustParse(ParseZCount, "zcount key 11 33")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -10,17 +10,17 @@ import (
// https://redis.io/commands/zincrby // https://redis.io/commands/zincrby
type ZIncrBy struct { type ZIncrBy struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Delta float64 delta float64
Member string member string
} }
func ParseZIncrBy(b redis.BaseCmd) (*ZIncrBy, error) { func ParseZIncrBy(b redis.BaseCmd) (*ZIncrBy, error) {
cmd := &ZIncrBy{BaseCmd: b} cmd := &ZIncrBy{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.Float(&cmd.Delta), parser.Float(&cmd.delta),
parser.String(&cmd.Member), parser.String(&cmd.member),
).Required(3).Run(cmd.Args()) ).Required(3).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -29,7 +29,7 @@ func ParseZIncrBy(b redis.BaseCmd) (*ZIncrBy, error) {
} }
func (cmd *ZIncrBy) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *ZIncrBy) Run(w redis.Writer, red redis.Redka) (any, error) {
score, err := red.ZSet().Incr(cmd.Key, cmd.Member, cmd.Delta) score, err := red.ZSet().Incr(cmd.key, cmd.member, cmd.delta)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

View File

@@ -1,56 +1,48 @@
package zset_test package zset
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/zset"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestZIncrByParse(t *testing.T) { func TestZIncrByParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want ZIncrBy
want zset.ZIncrBy
err error err error
}{ }{
{ {
name: "zincrby", cmd: "zincrby",
args: command.BuildArgs("zincrby"), want: ZIncrBy{},
want: zset.ZIncrBy{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zincrby key", cmd: "zincrby key",
args: command.BuildArgs("zincrby", "key"), want: ZIncrBy{},
want: zset.ZIncrBy{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zincrby key one", cmd: "zincrby key one",
args: command.BuildArgs("zincrby", "key", "one"), want: ZIncrBy{},
want: zset.ZIncrBy{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zincrby key 11 one", cmd: "zincrby key 11 one",
args: command.BuildArgs("zincrby", "key", "11", "one"), want: ZIncrBy{key: "key", member: "one", delta: 11.0},
want: zset.ZIncrBy{Key: "key", Member: "one", Delta: 11.0},
err: nil, err: nil,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseZIncrBy, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*zset.ZIncrBy) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.member, test.want.member)
testx.AssertEqual(t, cm.Member, test.want.Member) testx.AssertEqual(t, cmd.delta, test.want.delta)
testx.AssertEqual(t, cm.Delta, test.want.Delta)
} }
}) })
} }
@@ -61,7 +53,7 @@ func TestZIncrByExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*zset.ZIncrBy]("zincrby key 25.5 one") cmd := redis.MustParse(ParseZIncrBy, "zincrby key 25.5 one")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -76,7 +68,7 @@ func TestZIncrByExec(t *testing.T) {
defer db.Close() defer db.Close()
_, _ = db.ZSet().Add("key", "one", 10) _, _ = db.ZSet().Add("key", "one", 10)
cmd := command.MustParse[*zset.ZIncrBy]("zincrby key 25.5 two") cmd := redis.MustParse(ParseZIncrBy, "zincrby key 25.5 two")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -91,7 +83,7 @@ func TestZIncrByExec(t *testing.T) {
defer db.Close() defer db.Close()
_, _ = db.ZSet().Add("key", "one", 25.5) _, _ = db.ZSet().Add("key", "one", 25.5)
cmd := command.MustParse[*zset.ZIncrBy]("zincrby key 10.5 one") cmd := redis.MustParse(ParseZIncrBy, "zincrby key 10.5 one")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -106,7 +98,7 @@ func TestZIncrByExec(t *testing.T) {
defer db.Close() defer db.Close()
_, _ = db.ZSet().Add("key", "one", 25.5) _, _ = db.ZSet().Add("key", "one", 25.5)
cmd := command.MustParse[*zset.ZIncrBy]("zincrby key -10.5 one") cmd := redis.MustParse(ParseZIncrBy, "zincrby key -10.5 one")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -121,7 +113,7 @@ func TestZIncrByExec(t *testing.T) {
defer db.Close() defer db.Close()
_ = red.Str().Set("key", "one") _ = red.Str().Set("key", "one")
cmd := command.MustParse[*zset.ZIncrBy]("zincrby key 25.5 one") cmd := redis.MustParse(ParseZIncrBy, "zincrby key 25.5 one")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -11,9 +11,9 @@ import (
// https://redis.io/commands/zinter // https://redis.io/commands/zinter
type ZInter struct { type ZInter struct {
redis.BaseCmd redis.BaseCmd
Keys []string keys []string
Aggregate string aggregate string
WithScores bool withScores bool
} }
func ParseZInter(b redis.BaseCmd) (*ZInter, error) { func ParseZInter(b redis.BaseCmd) (*ZInter, error) {
@@ -21,9 +21,9 @@ func ParseZInter(b redis.BaseCmd) (*ZInter, error) {
var nKeys int var nKeys int
err := parser.New( err := parser.New(
parser.Int(&nKeys), parser.Int(&nKeys),
parser.StringsN(&cmd.Keys, &nKeys), parser.StringsN(&cmd.keys, &nKeys),
parser.Named("aggregate", parser.Enum(&cmd.Aggregate, sqlx.Sum, sqlx.Min, sqlx.Max)), parser.Named("aggregate", parser.Enum(&cmd.aggregate, sqlx.Sum, sqlx.Min, sqlx.Max)),
parser.Flag("withscores", &cmd.WithScores), parser.Flag("withscores", &cmd.withScores),
).Required(2).Run(cmd.Args()) ).Required(2).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -32,8 +32,8 @@ func ParseZInter(b redis.BaseCmd) (*ZInter, error) {
} }
func (cmd *ZInter) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *ZInter) Run(w redis.Writer, red redis.Redka) (any, error) {
inter := red.ZSet().InterWith(cmd.Keys...) inter := red.ZSet().InterWith(cmd.keys...)
switch cmd.Aggregate { switch cmd.aggregate {
case sqlx.Min: case sqlx.Min:
inter = inter.Min() inter = inter.Min()
case sqlx.Max: case sqlx.Max:
@@ -48,7 +48,7 @@ func (cmd *ZInter) Run(w redis.Writer, red redis.Redka) (any, error) {
return nil, err return nil, err
} }
if cmd.WithScores { if cmd.withScores {
w.WriteArray(len(items) * 2) w.WriteArray(len(items) * 2)
for _, item := range items { for _, item := range items {
w.WriteBulk(item.Elem) w.WriteBulk(item.Elem)

View File

@@ -1,10 +1,8 @@
package zset_test package zset
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/zset"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/rzset" "github.com/nalgeon/redka/internal/rzset"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,82 +10,70 @@ import (
func TestZInterParse(t *testing.T) { func TestZInterParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want ZInter
want zset.ZInter
err error err error
}{ }{
{ {
name: "zinter", cmd: "zinter",
args: command.BuildArgs("zinter"), want: ZInter{},
want: zset.ZInter{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zinter 1", cmd: "zinter 1",
args: command.BuildArgs("zinter", "1"), want: ZInter{},
want: zset.ZInter{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zinter 1 key", cmd: "zinter 1 key",
args: command.BuildArgs("zinter", "1", "key"), want: ZInter{keys: []string{"key"}},
want: zset.ZInter{Keys: []string{"key"}},
err: nil, err: nil,
}, },
{ {
name: "zinter 2 k1 k2", cmd: "zinter 2 k1 k2",
args: command.BuildArgs("zinter", "2", "k1", "k2"), want: ZInter{keys: []string{"k1", "k2"}},
want: zset.ZInter{Keys: []string{"k1", "k2"}},
err: nil, err: nil,
}, },
{ {
name: "zinter 1 k1 k2", cmd: "zinter 1 k1 k2",
args: command.BuildArgs("zinter", "1", "k1", "k2"), want: ZInter{},
want: zset.ZInter{},
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
{ {
name: "zinter 2 k1 k2 min", cmd: "zinter 2 k1 k2 min",
args: command.BuildArgs("zinter", "2", "k1", "k2", "min"), want: ZInter{},
want: zset.ZInter{},
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
{ {
name: "zinter 2 k1 k2 aggregate min", cmd: "zinter 2 k1 k2 aggregate min",
args: command.BuildArgs("zinter", "2", "k1", "k2", "aggregate", "min"), want: ZInter{keys: []string{"k1", "k2"}, aggregate: "min"},
want: zset.ZInter{Keys: []string{"k1", "k2"}, Aggregate: "min"},
err: nil, err: nil,
}, },
{ {
name: "zinter 2 k1 k2 aggregate avg", cmd: "zinter 2 k1 k2 aggregate avg",
args: command.BuildArgs("zinter", "2", "k1", "k2", "aggregate", "avg"), want: ZInter{},
want: zset.ZInter{},
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
{ {
name: "zinter 2 k1 k2 withscores", cmd: "zinter 2 k1 k2 withscores",
args: command.BuildArgs("zinter", "2", "k1", "k2", "withscores"), want: ZInter{keys: []string{"k1", "k2"}, withScores: true},
want: zset.ZInter{Keys: []string{"k1", "k2"}, WithScores: true},
err: nil, err: nil,
}, },
{ {
name: "zinter 3 k1 k2 k3 withscores aggregate sum", cmd: "zinter 3 k1 k2 k3 withscores aggregate sum",
args: command.BuildArgs("zinter", "3", "k1", "k2", "k3", "withscores", "aggregate", "sum"), want: ZInter{keys: []string{"k1", "k2", "k3"}, aggregate: "sum", withScores: true},
want: zset.ZInter{Keys: []string{"k1", "k2", "k3"}, Aggregate: "sum", WithScores: true},
err: nil, err: nil,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseZInter, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*zset.ZInter) testx.AssertEqual(t, cmd.keys, test.want.keys)
testx.AssertEqual(t, cm.Keys, test.want.Keys) testx.AssertEqual(t, cmd.aggregate, test.want.aggregate)
testx.AssertEqual(t, cm.Aggregate, test.want.Aggregate) testx.AssertEqual(t, cmd.withScores, test.want.withScores)
testx.AssertEqual(t, cm.WithScores, test.want.WithScores)
} }
}) })
} }
@@ -114,7 +100,7 @@ func TestZInterExec(t *testing.T) {
"fou": 400, "fou": 400,
}) })
cmd := command.MustParse[*zset.ZInter]("zinter 3 key1 key2 key3") cmd := redis.MustParse(ParseZInter, "zinter 3 key1 key2 key3")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -141,7 +127,7 @@ func TestZInterExec(t *testing.T) {
"fou": 400, "fou": 400,
}) })
cmd := command.MustParse[*zset.ZInter]("zinter 3 key1 key2 key3 withscores") cmd := redis.MustParse(ParseZInter, "zinter 3 key1 key2 key3 withscores")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -168,7 +154,7 @@ func TestZInterExec(t *testing.T) {
"fou": 400, "fou": 400,
}) })
cmd := command.MustParse[*zset.ZInter]("zinter 3 key1 key2 key3 aggregate min withscores") cmd := redis.MustParse(ParseZInter, "zinter 3 key1 key2 key3 aggregate min withscores")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -184,7 +170,7 @@ func TestZInterExec(t *testing.T) {
"thr": 3, "thr": 3,
}) })
cmd := command.MustParse[*zset.ZInter]("zinter 1 key1") cmd := redis.MustParse(ParseZInter, "zinter 1 key1")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -198,7 +184,7 @@ func TestZInterExec(t *testing.T) {
_, _ = db.ZSet().Add("key2", "two", 1) _, _ = db.ZSet().Add("key2", "two", 1)
_, _ = db.ZSet().Add("key3", "thr", 1) _, _ = db.ZSet().Add("key3", "thr", 1)
cmd := command.MustParse[*zset.ZInter]("zinter 3 key1 key2 key3") cmd := redis.MustParse(ParseZInter, "zinter 3 key1 key2 key3")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -209,7 +195,7 @@ func TestZInterExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*zset.ZInter]("zinter 1 key") cmd := redis.MustParse(ParseZInter, "zinter 1 key")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -221,7 +207,7 @@ func TestZInterExec(t *testing.T) {
defer db.Close() defer db.Close()
_ = db.Str().Set("key", "value") _ = db.Str().Set("key", "value")
cmd := command.MustParse[*zset.ZInter]("zinter 1 key") cmd := redis.MustParse(ParseZInter, "zinter 1 key")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -11,19 +11,19 @@ import (
// https://redis.io/commands/zinterstore // https://redis.io/commands/zinterstore
type ZInterStore struct { type ZInterStore struct {
redis.BaseCmd redis.BaseCmd
Dest string dest string
Keys []string keys []string
Aggregate string aggregate string
} }
func ParseZInterStore(b redis.BaseCmd) (*ZInterStore, error) { func ParseZInterStore(b redis.BaseCmd) (*ZInterStore, error) {
cmd := &ZInterStore{BaseCmd: b} cmd := &ZInterStore{BaseCmd: b}
var nKeys int var nKeys int
err := parser.New( err := parser.New(
parser.String(&cmd.Dest), parser.String(&cmd.dest),
parser.Int(&nKeys), parser.Int(&nKeys),
parser.StringsN(&cmd.Keys, &nKeys), parser.StringsN(&cmd.keys, &nKeys),
parser.Named("aggregate", parser.Enum(&cmd.Aggregate, sqlx.Sum, sqlx.Min, sqlx.Max)), parser.Named("aggregate", parser.Enum(&cmd.aggregate, sqlx.Sum, sqlx.Min, sqlx.Max)),
).Required(3).Run(cmd.Args()) ).Required(3).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -32,8 +32,8 @@ func ParseZInterStore(b redis.BaseCmd) (*ZInterStore, error) {
} }
func (cmd *ZInterStore) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *ZInterStore) Run(w redis.Writer, red redis.Redka) (any, error) {
inter := red.ZSet().InterWith(cmd.Keys...).Dest(cmd.Dest) inter := red.ZSet().InterWith(cmd.keys...).Dest(cmd.dest)
switch cmd.Aggregate { switch cmd.aggregate {
case sqlx.Min: case sqlx.Min:
inter = inter.Min() inter = inter.Min()
case sqlx.Max: case sqlx.Max:

View File

@@ -1,10 +1,8 @@
package zset_test package zset
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/zset"
"github.com/nalgeon/redka/internal/core" "github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,76 +10,65 @@ import (
func TestZInterStoreParse(t *testing.T) { func TestZInterStoreParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want ZInterStore
want zset.ZInterStore
err error err error
}{ }{
{ {
name: "zinterstore", cmd: "zinterstore",
args: command.BuildArgs("zinterstore"), want: ZInterStore{},
want: zset.ZInterStore{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zinterstore dest", cmd: "zinterstore dest",
args: command.BuildArgs("zinterstore", "dest"), want: ZInterStore{},
want: zset.ZInterStore{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zinterstore dest 1", cmd: "zinterstore dest 1",
args: command.BuildArgs("zinterstore", "dest", "1"), want: ZInterStore{},
want: zset.ZInterStore{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zinterstore dest 1 key", cmd: "zinterstore dest 1 key",
args: command.BuildArgs("zinterstore", "dest", "1", "key"), want: ZInterStore{dest: "dest", keys: []string{"key"}},
want: zset.ZInterStore{Dest: "dest", Keys: []string{"key"}},
err: nil, err: nil,
}, },
{ {
name: "zinterstore dest 2 k1 k2", cmd: "zinterstore dest 2 k1 k2",
args: command.BuildArgs("zinterstore", "dest", "2", "k1", "k2"), want: ZInterStore{dest: "dest", keys: []string{"k1", "k2"}},
want: zset.ZInterStore{Dest: "dest", Keys: []string{"k1", "k2"}},
err: nil, err: nil,
}, },
{ {
name: "zinterstore dest 1 k1 k2", cmd: "zinterstore dest 1 k1 k2",
args: command.BuildArgs("zinterstore", "dest", "1", "k1", "k2"), want: ZInterStore{},
want: zset.ZInterStore{},
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
{ {
name: "zinterstore dest 2 k1 k2 min", cmd: "zinterstore dest 2 k1 k2 min",
args: command.BuildArgs("zinterstore", "dest", "2", "k1", "k2", "min"), want: ZInterStore{},
want: zset.ZInterStore{},
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
{ {
name: "zinterstore dest 2 k1 k2 aggregate min", cmd: "zinterstore dest 2 k1 k2 aggregate min",
args: command.BuildArgs("zinterstore", "dest", "2", "k1", "k2", "aggregate", "min"), want: ZInterStore{dest: "dest", keys: []string{"k1", "k2"}, aggregate: "min"},
want: zset.ZInterStore{Dest: "dest", Keys: []string{"k1", "k2"}, Aggregate: "min"},
err: nil, err: nil,
}, },
{ {
name: "zinterstore dest 2 k1 k2 aggregate avg", cmd: "zinterstore dest 2 k1 k2 aggregate avg",
args: command.BuildArgs("zinterstore", "dest", "2", "k1", "k2", "aggregate", "avg"), want: ZInterStore{},
want: zset.ZInterStore{},
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseZInterStore, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*zset.ZInterStore) testx.AssertEqual(t, cmd.dest, test.want.dest)
testx.AssertEqual(t, cm.Dest, test.want.Dest) testx.AssertEqual(t, cmd.keys, test.want.keys)
testx.AssertEqual(t, cm.Keys, test.want.Keys) testx.AssertEqual(t, cmd.aggregate, test.want.aggregate)
testx.AssertEqual(t, cm.Aggregate, test.want.Aggregate)
} }
}) })
} }
@@ -108,7 +95,7 @@ func TestZInterStoreExec(t *testing.T) {
"fou": 400, "fou": 400,
}) })
cmd := command.MustParse[*zset.ZInterStore]("zinterstore dest 3 key1 key2 key3") cmd := redis.MustParse(ParseZInterStore, "zinterstore dest 3 key1 key2 key3")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -139,7 +126,7 @@ func TestZInterStoreExec(t *testing.T) {
}) })
_, _ = db.ZSet().Add("dest", "one", 1) _, _ = db.ZSet().Add("dest", "one", 1)
cmd := command.MustParse[*zset.ZInterStore]("zinterstore dest 3 key1 key2 key3") cmd := redis.MustParse(ParseZInterStore, "zinterstore dest 3 key1 key2 key3")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -171,7 +158,7 @@ func TestZInterStoreExec(t *testing.T) {
"fou": 400, "fou": 400,
}) })
cmd := command.MustParse[*zset.ZInterStore]("zinterstore dest 3 key1 key2 key3 aggregate min") cmd := redis.MustParse(ParseZInterStore, "zinterstore dest 3 key1 key2 key3 aggregate min")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -192,7 +179,7 @@ func TestZInterStoreExec(t *testing.T) {
"thr": 3, "thr": 3,
}) })
cmd := command.MustParse[*zset.ZInterStore]("zinterstore dest 1 key1") cmd := redis.MustParse(ParseZInterStore, "zinterstore dest 1 key1")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -209,7 +196,7 @@ func TestZInterStoreExec(t *testing.T) {
_, _ = db.ZSet().Add("key2", "two", 1) _, _ = db.ZSet().Add("key2", "two", 1)
_, _ = db.ZSet().Add("key3", "thr", 1) _, _ = db.ZSet().Add("key3", "thr", 1)
cmd := command.MustParse[*zset.ZInterStore]("zinterstore dest 3 key1 key2 key3") cmd := redis.MustParse(ParseZInterStore, "zinterstore dest 3 key1 key2 key3")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -225,7 +212,7 @@ func TestZInterStoreExec(t *testing.T) {
_, _ = db.ZSet().Add("key1", "one", 1) _, _ = db.ZSet().Add("key1", "one", 1)
_, _ = db.ZSet().Add("dest", "one", 1) _, _ = db.ZSet().Add("dest", "one", 1)
cmd := command.MustParse[*zset.ZInterStore]("zinterstore dest 2 key1 key2") cmd := redis.MustParse(ParseZInterStore, "zinterstore dest 2 key1 key2")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -241,7 +228,7 @@ func TestZInterStoreExec(t *testing.T) {
_ = db.Str().Set("key", "value") _ = db.Str().Set("key", "value")
_, _ = db.ZSet().Add("dest", "one", 1) _, _ = db.ZSet().Add("dest", "one", 1)
cmd := command.MustParse[*zset.ZInterStore]("zinterstore dest 1 key") cmd := redis.MustParse(ParseZInterStore, "zinterstore dest 1 key")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -257,7 +244,7 @@ func TestZInterStoreExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "one", 1) _, _ = db.ZSet().Add("key", "one", 1)
_ = db.Str().Set("dest", "value") _ = db.Str().Set("dest", "value")
cmd := command.MustParse[*zset.ZInterStore]("zinterstore dest 1 key") cmd := redis.MustParse(ParseZInterStore, "zinterstore dest 1 key")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -10,26 +10,26 @@ import (
// https://redis.io/commands/zrange // https://redis.io/commands/zrange
type ZRange struct { type ZRange struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Start float64 start float64
Stop float64 stop float64
ByScore bool byScore bool
Rev bool rev bool
Offset int offset int
Count int count int
WithScores bool withScores bool
} }
func ParseZRange(b redis.BaseCmd) (*ZRange, error) { func ParseZRange(b redis.BaseCmd) (*ZRange, error) {
cmd := &ZRange{BaseCmd: b} cmd := &ZRange{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.Float(&cmd.Start), parser.Float(&cmd.start),
parser.Float(&cmd.Stop), parser.Float(&cmd.stop),
parser.Flag("byscore", &cmd.ByScore), parser.Flag("byscore", &cmd.byScore),
parser.Flag("rev", &cmd.Rev), parser.Flag("rev", &cmd.rev),
parser.Named("limit", parser.Int(&cmd.Offset), parser.Int(&cmd.Count)), parser.Named("limit", parser.Int(&cmd.offset), parser.Int(&cmd.count)),
parser.Flag("withscores", &cmd.WithScores), parser.Flag("withscores", &cmd.withScores),
).Required(3).Run(cmd.Args()) ).Required(3).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -38,26 +38,26 @@ func ParseZRange(b redis.BaseCmd) (*ZRange, error) {
} }
func (cmd *ZRange) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *ZRange) Run(w redis.Writer, red redis.Redka) (any, error) {
rang := red.ZSet().RangeWith(cmd.Key) rang := red.ZSet().RangeWith(cmd.key)
// filter by score or rank // filter by score or rank
if cmd.ByScore { if cmd.byScore {
rang = rang.ByScore(cmd.Start, cmd.Stop) rang = rang.ByScore(cmd.start, cmd.stop)
} else { } else {
rang = rang.ByRank(int(cmd.Start), int(cmd.Stop)) rang = rang.ByRank(int(cmd.start), int(cmd.stop))
} }
// sort direction // sort direction
if cmd.Rev { if cmd.rev {
rang = rang.Desc() rang = rang.Desc()
} }
// limit and offset // limit and offset
if cmd.Offset > 0 { if cmd.offset > 0 {
rang = rang.Offset(cmd.Offset) rang = rang.Offset(cmd.offset)
} }
if cmd.Count > 0 { if cmd.count > 0 {
rang = rang.Count(cmd.Count) rang = rang.Count(cmd.count)
} }
// run the command // run the command
@@ -68,7 +68,7 @@ func (cmd *ZRange) Run(w redis.Writer, red redis.Redka) (any, error) {
} }
// write the response with/without scores // write the response with/without scores
if cmd.WithScores { if cmd.withScores {
w.WriteArray(len(items) * 2) w.WriteArray(len(items) * 2)
for _, item := range items { for _, item := range items {
w.WriteBulk(item.Elem) w.WriteBulk(item.Elem)

View File

@@ -1,10 +1,8 @@
package zset_test package zset
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/zset"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/rzset" "github.com/nalgeon/redka/internal/rzset"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,95 +10,85 @@ import (
func TestZRangeParse(t *testing.T) { func TestZRangeParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want ZRange
want zset.ZRange
err error err error
}{ }{
{ {
name: "zrange", cmd: "zrange",
args: command.BuildArgs("zrange"), want: ZRange{},
want: zset.ZRange{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zrange key", cmd: "zrange key",
args: command.BuildArgs("zrange", "key"), want: ZRange{},
want: zset.ZRange{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zrange key 11", cmd: "zrange key 11",
args: command.BuildArgs("zrange", "key", "11"), want: ZRange{},
want: zset.ZRange{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zrange key 11 22", cmd: "zrange key 11 22",
args: command.BuildArgs("zrange", "key", "11", "22"), want: ZRange{key: "key", start: 11.0, stop: 22.0},
want: zset.ZRange{Key: "key", Start: 11.0, Stop: 22.0},
err: nil, err: nil,
}, },
{ {
name: "zrange key 1.1 2.2 byscore", cmd: "zrange key 1.1 2.2 byscore",
args: command.BuildArgs("zrange", "key", "1.1", "2.2", "byscore"), want: ZRange{key: "key", start: 1.1, stop: 2.2, byScore: true},
want: zset.ZRange{Key: "key", Start: 1.1, Stop: 2.2, ByScore: true},
err: nil, err: nil,
}, },
{ {
name: "zrange key byscore exclusive", cmd: "zrange key (1 (2 byscore",
args: command.BuildArgs("zrange", "key", "(1", "(2", "byscore"), want: ZRange{},
want: zset.ZRange{},
err: redis.ErrInvalidFloat, err: redis.ErrInvalidFloat,
}, },
{ {
name: "zrange key 11 22 rev", cmd: "zrange key 11 22 rev",
args: command.BuildArgs("zrange", "key", "11", "22", "rev"), want: ZRange{key: "key", start: 11.0, stop: 22.0, rev: true},
want: zset.ZRange{Key: "key", Start: 11.0, Stop: 22.0, Rev: true},
err: nil, err: nil,
}, },
{ {
name: "zrange key 11 22 byscore limit 10", cmd: "zrange key 11 22 byscore limit 10",
args: command.BuildArgs("zrange", "key", "11", "22", "byscore", "limit", "10"), want: ZRange{},
want: zset.ZRange{},
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
{ {
name: "zrange key 11 22 byscore limit 10 5", cmd: "zrange key 11 22 byscore limit 10 5",
args: command.BuildArgs("zrange", "key", "11", "22", "byscore", "limit", "10", "5"), want: ZRange{key: "key", start: 11.0, stop: 22.0, byScore: true, offset: 10, count: 5},
want: zset.ZRange{Key: "key", Start: 11.0, Stop: 22.0, ByScore: true, Offset: 10, Count: 5},
err: nil, err: nil,
}, },
{ {
name: "zrange key 11 22 withscores", cmd: "zrange key 11 22 withscores",
args: command.BuildArgs("zrange", "key", "11", "22", "withscores"), want: ZRange{key: "key", start: 11.0, stop: 22.0, withScores: true},
want: zset.ZRange{Key: "key", Start: 11.0, Stop: 22.0, WithScores: true},
err: nil, err: nil,
}, },
{ {
name: "zrange key 11 22 limit 10 5 rev byscore withscores", cmd: "zrange key 11 22 limit 10 5 rev byscore withscores",
args: command.BuildArgs("zrange", "key", "11", "22", "limit", "10", "5", want: ZRange{
"rev", "byscore", "withscores"), key: "key", start: 11.0, stop: 22.0,
want: zset.ZRange{Key: "key", Start: 11.0, Stop: 22.0, ByScore: true, byScore: true, rev: true,
Rev: true, Offset: 10, Count: 5, WithScores: true}, offset: 10, count: 5,
withScores: true,
},
err: nil, err: nil,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseZRange, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*zset.ZRange) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.start, test.want.start)
testx.AssertEqual(t, cm.Start, test.want.Start) testx.AssertEqual(t, cmd.stop, test.want.stop)
testx.AssertEqual(t, cm.Stop, test.want.Stop) testx.AssertEqual(t, cmd.byScore, test.want.byScore)
testx.AssertEqual(t, cm.ByScore, test.want.ByScore) testx.AssertEqual(t, cmd.rev, test.want.rev)
testx.AssertEqual(t, cm.Rev, test.want.Rev) testx.AssertEqual(t, cmd.offset, test.want.offset)
testx.AssertEqual(t, cm.Offset, test.want.Offset) testx.AssertEqual(t, cmd.count, test.want.count)
testx.AssertEqual(t, cm.Count, test.want.Count) testx.AssertEqual(t, cmd.withScores, test.want.withScores)
testx.AssertEqual(t, cm.WithScores, test.want.WithScores)
} }
}) })
} }
@@ -116,7 +104,7 @@ func TestZRangeExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "2nd", 2) _, _ = db.ZSet().Add("key", "2nd", 2)
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 0 1") cmd := redis.MustParse(ParseZRange, "zrange key 0 1")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -124,7 +112,7 @@ func TestZRangeExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "2,one,2nd") testx.AssertEqual(t, conn.Out(), "2,one,2nd")
} }
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 0 5") cmd := redis.MustParse(ParseZRange, "zrange key 0 5")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -132,7 +120,7 @@ func TestZRangeExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "4,one,2nd,two,thr") testx.AssertEqual(t, conn.Out(), "4,one,2nd,two,thr")
} }
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 3 5") cmd := redis.MustParse(ParseZRange, "zrange key 3 5")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -140,7 +128,7 @@ func TestZRangeExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "1,thr") testx.AssertEqual(t, conn.Out(), "1,thr")
} }
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 4 5") cmd := redis.MustParse(ParseZRange, "zrange key 4 5")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -157,7 +145,7 @@ func TestZRangeExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "2nd", 2) _, _ = db.ZSet().Add("key", "2nd", 2)
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 0 1 rev") cmd := redis.MustParse(ParseZRange, "zrange key 0 1 rev")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -165,7 +153,7 @@ func TestZRangeExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "2,thr,two") testx.AssertEqual(t, conn.Out(), "2,thr,two")
} }
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 0 5 rev") cmd := redis.MustParse(ParseZRange, "zrange key 0 5 rev")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -173,7 +161,7 @@ func TestZRangeExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "4,thr,two,2nd,one") testx.AssertEqual(t, conn.Out(), "4,thr,two,2nd,one")
} }
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 3 5 rev") cmd := redis.MustParse(ParseZRange, "zrange key 3 5 rev")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -181,7 +169,7 @@ func TestZRangeExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "1,one") testx.AssertEqual(t, conn.Out(), "1,one")
} }
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 4 5 rev") cmd := redis.MustParse(ParseZRange, "zrange key 4 5 rev")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -198,7 +186,7 @@ func TestZRangeExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "2nd", 20) _, _ = db.ZSet().Add("key", "2nd", 20)
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 0 10 byscore") cmd := redis.MustParse(ParseZRange, "zrange key 0 10 byscore")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -206,7 +194,7 @@ func TestZRangeExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "1,one") testx.AssertEqual(t, conn.Out(), "1,one")
} }
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 0 50 byscore") cmd := redis.MustParse(ParseZRange, "zrange key 0 50 byscore")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -214,7 +202,7 @@ func TestZRangeExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "4,one,2nd,two,thr") testx.AssertEqual(t, conn.Out(), "4,one,2nd,two,thr")
} }
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 30 50 byscore") cmd := redis.MustParse(ParseZRange, "zrange key 30 50 byscore")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -222,7 +210,7 @@ func TestZRangeExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "1,thr") testx.AssertEqual(t, conn.Out(), "1,thr")
} }
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 40 50 byscore") cmd := redis.MustParse(ParseZRange, "zrange key 40 50 byscore")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -239,7 +227,7 @@ func TestZRangeExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "2nd", 20) _, _ = db.ZSet().Add("key", "2nd", 20)
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 0 10 byscore rev") cmd := redis.MustParse(ParseZRange, "zrange key 0 10 byscore rev")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -247,7 +235,7 @@ func TestZRangeExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "1,one") testx.AssertEqual(t, conn.Out(), "1,one")
} }
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 0 50 byscore rev") cmd := redis.MustParse(ParseZRange, "zrange key 0 50 byscore rev")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -255,7 +243,7 @@ func TestZRangeExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "4,thr,two,2nd,one") testx.AssertEqual(t, conn.Out(), "4,thr,two,2nd,one")
} }
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 30 50 byscore rev") cmd := redis.MustParse(ParseZRange, "zrange key 30 50 byscore rev")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -263,7 +251,7 @@ func TestZRangeExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "1,thr") testx.AssertEqual(t, conn.Out(), "1,thr")
} }
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 40 50 byscore rev") cmd := redis.MustParse(ParseZRange, "zrange key 40 50 byscore rev")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -280,7 +268,7 @@ func TestZRangeExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "2nd", 20) _, _ = db.ZSet().Add("key", "2nd", 20)
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 0 50 byscore limit 0 2") cmd := redis.MustParse(ParseZRange, "zrange key 0 50 byscore limit 0 2")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -288,7 +276,7 @@ func TestZRangeExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "2,one,2nd") testx.AssertEqual(t, conn.Out(), "2,one,2nd")
} }
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 0 50 byscore limit 1 2") cmd := redis.MustParse(ParseZRange, "zrange key 0 50 byscore limit 1 2")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -296,7 +284,7 @@ func TestZRangeExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "2,2nd,two") testx.AssertEqual(t, conn.Out(), "2,2nd,two")
} }
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 0 50 byscore limit 2 5") cmd := redis.MustParse(ParseZRange, "zrange key 0 50 byscore limit 2 5")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -304,7 +292,7 @@ func TestZRangeExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "2,two,thr") testx.AssertEqual(t, conn.Out(), "2,two,thr")
} }
{ {
cmd := command.MustParse[*zset.ZRange]("zrange key 0 50 byscore limit 1 -1") cmd := redis.MustParse(ParseZRange, "zrange key 0 50 byscore limit 1 -1")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -320,7 +308,7 @@ func TestZRangeExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "thr", 3) _, _ = db.ZSet().Add("key", "thr", 3)
_, _ = db.ZSet().Add("key", "2nd", 2) _, _ = db.ZSet().Add("key", "2nd", 2)
cmd := command.MustParse[*zset.ZRange]("zrange key 0 5 withscores") cmd := redis.MustParse(ParseZRange, "zrange key 0 5 withscores")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -335,7 +323,7 @@ func TestZRangeExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "thr", 3) _, _ = db.ZSet().Add("key", "thr", 3)
_, _ = db.ZSet().Add("key", "2nd", 2) _, _ = db.ZSet().Add("key", "2nd", 2)
cmd := command.MustParse[*zset.ZRange]("zrange key -2 -1") cmd := redis.MustParse(ParseZRange, "zrange key -2 -1")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -346,7 +334,7 @@ func TestZRangeExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*zset.ZRange]("zrange key 0 1") cmd := redis.MustParse(ParseZRange, "zrange key 0 1")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -358,7 +346,7 @@ func TestZRangeExec(t *testing.T) {
defer db.Close() defer db.Close()
_ = db.Str().Set("key", "value") _ = db.Str().Set("key", "value")
cmd := command.MustParse[*zset.ZRange]("zrange key 0 1") cmd := redis.MustParse(ParseZRange, "zrange key 0 1")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -10,22 +10,22 @@ import (
// https://redis.io/commands/zrangebyscore // https://redis.io/commands/zrangebyscore
type ZRangeByScore struct { type ZRangeByScore struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Min float64 min float64
Max float64 max float64
WithScores bool withScores bool
Offset int offset int
Count int count int
} }
func ParseZRangeByScore(b redis.BaseCmd) (*ZRangeByScore, error) { func ParseZRangeByScore(b redis.BaseCmd) (*ZRangeByScore, error) {
cmd := &ZRangeByScore{BaseCmd: b} cmd := &ZRangeByScore{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.Float(&cmd.Min), parser.Float(&cmd.min),
parser.Float(&cmd.Max), parser.Float(&cmd.max),
parser.Flag("withscores", &cmd.WithScores), parser.Flag("withscores", &cmd.withScores),
parser.Named("limit", parser.Int(&cmd.Offset), parser.Int(&cmd.Count)), parser.Named("limit", parser.Int(&cmd.offset), parser.Int(&cmd.count)),
).Required(3).Run(cmd.Args()) ).Required(3).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -34,14 +34,14 @@ func ParseZRangeByScore(b redis.BaseCmd) (*ZRangeByScore, error) {
} }
func (cmd *ZRangeByScore) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *ZRangeByScore) Run(w redis.Writer, red redis.Redka) (any, error) {
rang := red.ZSet().RangeWith(cmd.Key).ByScore(cmd.Min, cmd.Max) rang := red.ZSet().RangeWith(cmd.key).ByScore(cmd.min, cmd.max)
// limit and offset // limit and offset
if cmd.Offset > 0 { if cmd.offset > 0 {
rang = rang.Offset(cmd.Offset) rang = rang.Offset(cmd.offset)
} }
if cmd.Count > 0 { if cmd.count > 0 {
rang = rang.Count(cmd.Count) rang = rang.Count(cmd.count)
} }
// run the command // run the command
@@ -52,7 +52,7 @@ func (cmd *ZRangeByScore) Run(w redis.Writer, red redis.Redka) (any, error) {
} }
// write the response with/without scores // write the response with/without scores
if cmd.WithScores { if cmd.withScores {
w.WriteArray(len(items) * 2) w.WriteArray(len(items) * 2)
for _, item := range items { for _, item := range items {
w.WriteBulk(item.Elem) w.WriteBulk(item.Elem)

View File

@@ -1,10 +1,8 @@
package zset_test package zset
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/zset"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/rzset" "github.com/nalgeon/redka/internal/rzset"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
@@ -12,81 +10,72 @@ import (
func TestZRangeByScoreParse(t *testing.T) { func TestZRangeByScoreParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want ZRangeByScore
want zset.ZRangeByScore
err error err error
}{ }{
{ {
name: "zrangebyscore", cmd: "zrangebyscore",
args: command.BuildArgs("zrangebyscore"), want: ZRangeByScore{},
want: zset.ZRangeByScore{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zrangebyscore key", cmd: "zrangebyscore key",
args: command.BuildArgs("zrangebyscore", "key"), want: ZRangeByScore{},
want: zset.ZRangeByScore{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zrangebyscore key 11", cmd: "zrangebyscore key 11",
args: command.BuildArgs("zrangebyscore", "key", "11"), want: ZRangeByScore{},
want: zset.ZRangeByScore{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zrangebyscore key 11 22", cmd: "zrangebyscore key 11 22",
args: command.BuildArgs("zrangebyscore", "key", "11", "22"), want: ZRangeByScore{key: "key", min: 11.0, max: 22.0},
want: zset.ZRangeByScore{Key: "key", Min: 11.0, Max: 22.0},
err: nil, err: nil,
}, },
{ {
name: "zrangebyscore key exclusive", cmd: "zrangebyscore key (1 (2",
args: command.BuildArgs("zrangebyscore", "key", "(1", "(2"), want: ZRangeByScore{},
want: zset.ZRangeByScore{},
err: redis.ErrInvalidFloat, err: redis.ErrInvalidFloat,
}, },
{ {
name: "zrangebyscore key 11 22 limit 10", cmd: "zrangebyscore key 11 22 limit 10",
args: command.BuildArgs("zrangebyscore", "key", "11", "22", "limit", "10"), want: ZRangeByScore{},
want: zset.ZRangeByScore{},
err: redis.ErrSyntaxError, err: redis.ErrSyntaxError,
}, },
{ {
name: "zrangebyscore key 11 22 limit 10 5", cmd: "zrangebyscore key 11 22 limit 10 5",
args: command.BuildArgs("zrangebyscore", "key", "11", "22", "limit", "10", "5"), want: ZRangeByScore{key: "key", min: 11.0, max: 22.0, offset: 10, count: 5},
want: zset.ZRangeByScore{Key: "key", Min: 11.0, Max: 22.0, Offset: 10, Count: 5},
err: nil, err: nil,
}, },
{ {
name: "zrangebyscore key 11 22 withscores", cmd: "zrangebyscore key 11 22 withscores",
args: command.BuildArgs("zrangebyscore", "key", "11", "22", "withscores"), want: ZRangeByScore{key: "key", min: 11.0, max: 22.0, withScores: true},
want: zset.ZRangeByScore{Key: "key", Min: 11.0, Max: 22.0, WithScores: true},
err: nil, err: nil,
}, },
{ {
name: "zrangebyscore key 11 22 limit 10 5 withscores", cmd: "zrangebyscore key 11 22 limit 10 5 withscores",
args: command.BuildArgs("zrangebyscore", "key", "11", "22", want: ZRangeByScore{
"limit", "10", "5", "withscores"), key: "key", min: 11.0, max: 22.0,
want: zset.ZRangeByScore{Key: "key", Min: 11.0, Max: 22.0, offset: 10, count: 5,
Offset: 10, Count: 5, WithScores: true}, withScores: true,
},
err: nil, err: nil,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseZRangeByScore, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*zset.ZRangeByScore) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.min, test.want.min)
testx.AssertEqual(t, cm.Min, test.want.Min) testx.AssertEqual(t, cmd.max, test.want.max)
testx.AssertEqual(t, cm.Max, test.want.Max) testx.AssertEqual(t, cmd.offset, test.want.offset)
testx.AssertEqual(t, cm.Offset, test.want.Offset) testx.AssertEqual(t, cmd.count, test.want.count)
testx.AssertEqual(t, cm.Count, test.want.Count) testx.AssertEqual(t, cmd.withScores, test.want.withScores)
testx.AssertEqual(t, cm.WithScores, test.want.WithScores)
} }
}) })
} }
@@ -102,7 +91,7 @@ func TestZRangeByScoreExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "2nd", 20) _, _ = db.ZSet().Add("key", "2nd", 20)
{ {
cmd := command.MustParse[*zset.ZRangeByScore]("zrangebyscore key 0 10") cmd := redis.MustParse(ParseZRangeByScore, "zrangebyscore key 0 10")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -110,7 +99,7 @@ func TestZRangeByScoreExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "1,one") testx.AssertEqual(t, conn.Out(), "1,one")
} }
{ {
cmd := command.MustParse[*zset.ZRangeByScore]("zrangebyscore key 0 50") cmd := redis.MustParse(ParseZRangeByScore, "zrangebyscore key 0 50")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -118,7 +107,7 @@ func TestZRangeByScoreExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "4,one,2nd,two,thr") testx.AssertEqual(t, conn.Out(), "4,one,2nd,two,thr")
} }
{ {
cmd := command.MustParse[*zset.ZRangeByScore]("zrangebyscore key 30 50") cmd := redis.MustParse(ParseZRangeByScore, "zrangebyscore key 30 50")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -126,7 +115,7 @@ func TestZRangeByScoreExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "1,thr") testx.AssertEqual(t, conn.Out(), "1,thr")
} }
{ {
cmd := command.MustParse[*zset.ZRangeByScore]("zrangebyscore key 40 50") cmd := redis.MustParse(ParseZRangeByScore, "zrangebyscore key 40 50")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -143,7 +132,7 @@ func TestZRangeByScoreExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "2nd", 20) _, _ = db.ZSet().Add("key", "2nd", 20)
{ {
cmd := command.MustParse[*zset.ZRangeByScore]("zrangebyscore key 0 50 limit 0 2") cmd := redis.MustParse(ParseZRangeByScore, "zrangebyscore key 0 50 limit 0 2")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -151,7 +140,7 @@ func TestZRangeByScoreExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "2,one,2nd") testx.AssertEqual(t, conn.Out(), "2,one,2nd")
} }
{ {
cmd := command.MustParse[*zset.ZRangeByScore]("zrangebyscore key 0 50 limit 1 2") cmd := redis.MustParse(ParseZRangeByScore, "zrangebyscore key 0 50 limit 1 2")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -159,7 +148,7 @@ func TestZRangeByScoreExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "2,2nd,two") testx.AssertEqual(t, conn.Out(), "2,2nd,two")
} }
{ {
cmd := command.MustParse[*zset.ZRangeByScore]("zrangebyscore key 0 50 limit 2 5") cmd := redis.MustParse(ParseZRangeByScore, "zrangebyscore key 0 50 limit 2 5")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -167,7 +156,7 @@ func TestZRangeByScoreExec(t *testing.T) {
testx.AssertEqual(t, conn.Out(), "2,two,thr") testx.AssertEqual(t, conn.Out(), "2,two,thr")
} }
{ {
cmd := command.MustParse[*zset.ZRangeByScore]("zrangebyscore key 0 50 limit 1 -1") cmd := redis.MustParse(ParseZRangeByScore, "zrangebyscore key 0 50 limit 1 -1")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -183,7 +172,7 @@ func TestZRangeByScoreExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "thr", 30) _, _ = db.ZSet().Add("key", "thr", 30)
_, _ = db.ZSet().Add("key", "2nd", 20) _, _ = db.ZSet().Add("key", "2nd", 20)
cmd := command.MustParse[*zset.ZRangeByScore]("zrangebyscore key 10 50 withscores") cmd := redis.MustParse(ParseZRangeByScore, "zrangebyscore key 10 50 withscores")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -198,7 +187,7 @@ func TestZRangeByScoreExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "thr", -30) _, _ = db.ZSet().Add("key", "thr", -30)
_, _ = db.ZSet().Add("key", "2nd", -20) _, _ = db.ZSet().Add("key", "2nd", -20)
cmd := command.MustParse[*zset.ZRangeByScore]("zrangebyscore key -20 -10") cmd := redis.MustParse(ParseZRangeByScore, "zrangebyscore key -20 -10")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -209,7 +198,7 @@ func TestZRangeByScoreExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*zset.ZRangeByScore]("zrangebyscore key 0 1") cmd := redis.MustParse(ParseZRangeByScore, "zrangebyscore key 0 1")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -221,7 +210,7 @@ func TestZRangeByScoreExec(t *testing.T) {
defer db.Close() defer db.Close()
_ = db.Str().Set("key", "value") _ = db.Str().Set("key", "value")
cmd := command.MustParse[*zset.ZRangeByScore]("zrangebyscore key 0 1") cmd := redis.MustParse(ParseZRangeByScore, "zrangebyscore key 0 1")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -11,17 +11,17 @@ import (
// https://redis.io/commands/zrank // https://redis.io/commands/zrank
type ZRank struct { type ZRank struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Member string member string
WithScore bool withScore bool
} }
func ParseZRank(b redis.BaseCmd) (*ZRank, error) { func ParseZRank(b redis.BaseCmd) (*ZRank, error) {
cmd := &ZRank{BaseCmd: b} cmd := &ZRank{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.String(&cmd.Member), parser.String(&cmd.member),
parser.Flag("withscore", &cmd.WithScore), parser.Flag("withscore", &cmd.withScore),
).Required(2).Run(cmd.Args()) ).Required(2).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -30,7 +30,7 @@ func ParseZRank(b redis.BaseCmd) (*ZRank, error) {
} }
func (cmd *ZRank) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *ZRank) Run(w redis.Writer, red redis.Redka) (any, error) {
rank, score, err := red.ZSet().GetRank(cmd.Key, cmd.Member) rank, score, err := red.ZSet().GetRank(cmd.key, cmd.member)
if err == core.ErrNotFound { if err == core.ErrNotFound {
w.WriteNull() w.WriteNull()
return nil, nil return nil, nil
@@ -39,7 +39,7 @@ func (cmd *ZRank) Run(w redis.Writer, red redis.Redka) (any, error) {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err
} }
if cmd.WithScore { if cmd.withScore {
w.WriteArray(2) w.WriteArray(2)
w.WriteInt(rank) w.WriteInt(rank)
redis.WriteFloat(w, score) redis.WriteFloat(w, score)

View File

@@ -1,56 +1,48 @@
package zset_test package zset
import ( import (
"testing" "testing"
"github.com/nalgeon/redka/internal/command"
"github.com/nalgeon/redka/internal/command/zset"
"github.com/nalgeon/redka/internal/redis" "github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/testx" "github.com/nalgeon/redka/internal/testx"
) )
func TestZRankParse(t *testing.T) { func TestZRankParse(t *testing.T) {
tests := []struct { tests := []struct {
name string cmd string
args [][]byte want ZRank
want zset.ZRank
err error err error
}{ }{
{ {
name: "zrank", cmd: "zrank",
args: command.BuildArgs("zrank"), want: ZRank{},
want: zset.ZRank{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zrank key", cmd: "zrank key",
args: command.BuildArgs("zrank", "key"), want: ZRank{},
want: zset.ZRank{},
err: redis.ErrInvalidArgNum, err: redis.ErrInvalidArgNum,
}, },
{ {
name: "zrank key member", cmd: "zrank key member",
args: command.BuildArgs("zrank", "key", "member"), want: ZRank{key: "key", member: "member"},
want: zset.ZRank{Key: "key", Member: "member"},
err: nil, err: nil,
}, },
{ {
name: "zrank key member withscore", cmd: "zrank key member withscore",
args: command.BuildArgs("zrank", "key", "member", "withscore"), want: ZRank{key: "key", member: "member", withScore: true},
want: zset.ZRank{Key: "key", Member: "member", WithScore: true},
err: nil, err: nil,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.cmd, func(t *testing.T) {
cmd, err := command.Parse(test.args) cmd, err := redis.Parse(ParseZRank, test.cmd)
testx.AssertEqual(t, err, test.err) testx.AssertEqual(t, err, test.err)
if err == nil { if err == nil {
cm := cmd.(*zset.ZRank) testx.AssertEqual(t, cmd.key, test.want.key)
testx.AssertEqual(t, cm.Key, test.want.Key) testx.AssertEqual(t, cmd.member, test.want.member)
testx.AssertEqual(t, cm.Member, test.want.Member) testx.AssertEqual(t, cmd.withScore, test.want.withScore)
testx.AssertEqual(t, cm.WithScore, test.want.WithScore)
} }
}) })
} }
@@ -63,7 +55,7 @@ func TestZRankExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "one", 11) _, _ = db.ZSet().Add("key", "one", 11)
_, _ = db.ZSet().Add("key", "two", 22) _, _ = db.ZSet().Add("key", "two", 22)
cmd := command.MustParse[*zset.ZRank]("zrank key two") cmd := redis.MustParse(ParseZRank, "zrank key two")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -76,7 +68,7 @@ func TestZRankExec(t *testing.T) {
_, _ = db.ZSet().Add("key", "one", 11) _, _ = db.ZSet().Add("key", "one", 11)
_, _ = db.ZSet().Add("key", "two", 22) _, _ = db.ZSet().Add("key", "two", 22)
cmd := command.MustParse[*zset.ZRank]("zrank key two withscore") cmd := redis.MustParse(ParseZRank, "zrank key two withscore")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -88,7 +80,7 @@ func TestZRankExec(t *testing.T) {
defer db.Close() defer db.Close()
_, _ = db.ZSet().Add("key", "one", 11) _, _ = db.ZSet().Add("key", "one", 11)
cmd := command.MustParse[*zset.ZRank]("zrank key two") cmd := redis.MustParse(ParseZRank, "zrank key two")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -99,7 +91,7 @@ func TestZRankExec(t *testing.T) {
db, red := getDB(t) db, red := getDB(t)
defer db.Close() defer db.Close()
cmd := command.MustParse[*zset.ZRank]("zrank key two") cmd := redis.MustParse(ParseZRank, "zrank key two")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)
@@ -111,7 +103,7 @@ func TestZRankExec(t *testing.T) {
defer db.Close() defer db.Close()
_ = db.Str().Set("key", "value") _ = db.Str().Set("key", "value")
cmd := command.MustParse[*zset.ZRank]("zrank key two") cmd := redis.MustParse(ParseZRank, "zrank key two")
conn := redis.NewFakeConn() conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red) res, err := cmd.Run(conn, red)
testx.AssertNoErr(t, err) testx.AssertNoErr(t, err)

View File

@@ -10,15 +10,15 @@ import (
// https://redis.io/commands/zrem // https://redis.io/commands/zrem
type ZRem struct { type ZRem struct {
redis.BaseCmd redis.BaseCmd
Key string key string
Members []any members []any
} }
func ParseZRem(b redis.BaseCmd) (*ZRem, error) { func ParseZRem(b redis.BaseCmd) (*ZRem, error) {
cmd := &ZRem{BaseCmd: b} cmd := &ZRem{BaseCmd: b}
err := parser.New( err := parser.New(
parser.String(&cmd.Key), parser.String(&cmd.key),
parser.Anys(&cmd.Members), parser.Anys(&cmd.members),
).Required(2).Run(cmd.Args()) ).Required(2).Run(cmd.Args())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -27,7 +27,7 @@ func ParseZRem(b redis.BaseCmd) (*ZRem, error) {
} }
func (cmd *ZRem) Run(w redis.Writer, red redis.Redka) (any, error) { func (cmd *ZRem) Run(w redis.Writer, red redis.Redka) (any, error) {
n, err := red.ZSet().Delete(cmd.Key, cmd.Members...) n, err := red.ZSet().Delete(cmd.key, cmd.members...)
if err != nil { if err != nil {
w.WriteError(cmd.Error(err)) w.WriteError(cmd.Error(err))
return nil, err return nil, err

Some files were not shown because too many files have changed in this diff Show More