Files
redka/internal/command/set/sscan_test.go
2025-07-13 13:05:18 +05:00

241 lines
5.3 KiB
Go

package set
import (
"fmt"
"testing"
"github.com/nalgeon/be"
"github.com/nalgeon/redka/internal/core"
"github.com/nalgeon/redka/internal/redis"
"github.com/nalgeon/redka/internal/rset"
)
func TestSScanParse(t *testing.T) {
tests := []struct {
cmd string
key string
cursor int
match string
count int
err error
}{
{
cmd: "sscan",
key: "",
cursor: 0,
match: "*",
count: 0,
err: redis.ErrInvalidArgNum,
},
{
cmd: "sscan key",
key: "",
cursor: 0,
match: "*",
count: 0,
err: redis.ErrInvalidArgNum,
},
{
cmd: "sscan key 15",
key: "key",
cursor: 15,
match: "*",
count: 0,
err: nil,
},
{
cmd: "sscan key 15 match *",
key: "key",
cursor: 15,
match: "*",
count: 0,
err: nil,
},
{
cmd: "sscan key 15 match * count 5",
key: "key",
cursor: 15,
match: "*",
count: 5,
err: nil,
},
{
cmd: "sscan key 15 count 5 match *",
key: "key",
cursor: 15,
match: "*",
count: 5,
err: nil,
},
{
cmd: "sscan key 15 match m2* count 5",
key: "key",
cursor: 15,
match: "m2*",
count: 5,
err: nil,
},
{
cmd: "sscan key ten",
key: "",
cursor: 0,
match: "",
count: 0,
err: redis.ErrInvalidInt,
},
{
cmd: "sscan key 15 *",
key: "",
cursor: 0,
match: "",
count: 0,
err: redis.ErrSyntaxError,
},
{
cmd: "sscan key 15 * 5",
key: "",
cursor: 0,
match: "",
count: 0,
err: redis.ErrSyntaxError,
},
}
for _, test := range tests {
t.Run(test.cmd, func(t *testing.T) {
cmd, err := redis.Parse(ParseSScan, test.cmd)
be.Equal(t, err, test.err)
if err == nil {
be.Equal(t, cmd.key, test.key)
be.Equal(t, cmd.cursor, test.cursor)
be.Equal(t, cmd.match, test.match)
be.Equal(t, cmd.count, test.count)
} else {
be.Equal(t, cmd, SScan{})
}
})
}
}
func TestSScanExec(t *testing.T) {
red := getRedka(t)
_, _ = red.Set().Add("key", "m11", "m12", "m21", "m22", "m31")
t.Run("sscan all", func(t *testing.T) {
var cursor int
{
cmd := redis.MustParse(ParseSScan, "sscan key 0")
conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red)
be.Err(t, err, nil)
sres := res.(rset.ScanResult)
be.True(t, sres.Cursor > 0)
be.Equal(t, len(sres.Items), 5)
be.Equal(t, sres.Items[0], core.Value("m11"))
be.Equal(t, sres.Items[4], core.Value("m31"))
wantOut := fmt.Sprintf("2,%d,5,m11,m12,m21,m22,m31", sres.Cursor)
be.Equal(t, conn.Out(), wantOut)
cursor = sres.Cursor
}
{
next := fmt.Sprintf("sscan key %d", cursor)
cmd := redis.MustParse(ParseSScan, next)
conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red)
be.Err(t, err, nil)
sres := res.(rset.ScanResult)
be.Equal(t, sres.Cursor, 0)
be.Equal(t, len(sres.Items), 0)
be.Equal(t, conn.Out(), "2,0,0")
}
})
t.Run("sscan pattern", func(t *testing.T) {
cmd := redis.MustParse(ParseSScan, "sscan key 0 match m2*")
conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red)
be.Err(t, err, nil)
sres := res.(rset.ScanResult)
be.True(t, sres.Cursor > 0)
be.Equal(t, len(sres.Items), 2)
be.Equal(t, sres.Items[0].String(), "m21")
be.Equal(t, sres.Items[1].String(), "m22")
wantOut := fmt.Sprintf("2,%d,2,m21,m22", sres.Cursor)
be.Equal(t, conn.Out(), wantOut)
})
t.Run("sscan count", func(t *testing.T) {
var cursor int
{
// page 1
cmd := redis.MustParse(ParseSScan, "sscan key 0 match * count 2")
conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red)
be.Err(t, err, nil)
sres := res.(rset.ScanResult)
be.True(t, sres.Cursor > 0)
be.Equal(t, len(sres.Items), 2)
be.Equal(t, sres.Items[0].String(), "m11")
be.Equal(t, sres.Items[1].String(), "m12")
wantOut := fmt.Sprintf("2,%d,2,m11,m12", sres.Cursor)
be.Equal(t, conn.Out(), wantOut)
cursor = sres.Cursor
}
{
// page 2
next := fmt.Sprintf("sscan key %d match * count 2", cursor)
cmd := redis.MustParse(ParseSScan, next)
conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red)
be.Err(t, err, nil)
sres := res.(rset.ScanResult)
be.True(t, sres.Cursor > cursor)
be.Equal(t, len(sres.Items), 2)
be.Equal(t, sres.Items[0].String(), "m21")
be.Equal(t, sres.Items[1].String(), "m22")
wantOut := fmt.Sprintf("2,%d,2,m21,m22", sres.Cursor)
be.Equal(t, conn.Out(), wantOut)
cursor = sres.Cursor
}
{
// page 3
next := fmt.Sprintf("sscan key %d match * count 2", cursor)
cmd := redis.MustParse(ParseSScan, next)
conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red)
be.Err(t, err, nil)
sres := res.(rset.ScanResult)
be.True(t, sres.Cursor > cursor)
be.Equal(t, len(sres.Items), 1)
be.Equal(t, sres.Items[0].String(), "m31")
wantOut := fmt.Sprintf("2,%d,1,m31", sres.Cursor)
be.Equal(t, conn.Out(), wantOut)
cursor = sres.Cursor
}
{
// no more pages
next := fmt.Sprintf("sscan key %d match * count 2", cursor)
cmd := redis.MustParse(ParseSScan, next)
conn := redis.NewFakeConn()
res, err := cmd.Run(conn, red)
be.Err(t, err, nil)
sres := res.(rset.ScanResult)
be.Equal(t, sres.Cursor, 0)
be.Equal(t, len(sres.Items), 0)
be.Equal(t, conn.Out(), "2,0,0")
}
})
}