Skip to content

Commit

Permalink
Merge pull request #245 from btwear/lmove-command
Browse files Browse the repository at this point in the history
Implement LMOVE
  • Loading branch information
alicebob committed Jan 19, 2022
2 parents febc6fc + ba14f6d commit b821ff3
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -123,6 +123,7 @@ Implemented commands:
- RPOPLPUSH
- RPUSH
- RPUSHX
- LMOVE
- Pub/Sub (complete)
- PSUBSCRIBE
- PUBLISH
Expand Down
50 changes: 50 additions & 0 deletions cmd_list.go
Expand Up @@ -36,6 +36,7 @@ func commandsList(m *Miniredis) {
m.srv.Register("RPOPLPUSH", m.cmdRpoplpush)
m.srv.Register("RPUSH", m.cmdRpush)
m.srv.Register("RPUSHX", m.cmdRpushx)
m.srv.Register("LMOVE", m.cmdLmove)
}

// BLPOP
Expand Down Expand Up @@ -771,3 +772,52 @@ func (m *Miniredis) cmdBrpoplpush(c *server.Peer, cmd string, args []string) {
},
)
}

// LMOVE
func (m *Miniredis) cmdLmove(c *server.Peer, cmd string, args []string) {
if len(args) != 4 {
setDirty(c)
c.WriteError(errWrongNumber(cmd))
return
}
if !m.handleAuth(c) {
return
}
if m.checkPubsub(c, cmd) {
return
}

src, dst, srcDir, dstDir := args[0], args[1], strings.ToLower(args[2]), strings.ToLower(args[3])

withTx(m, c, func(c *server.Peer, ctx *connCtx) {
db := m.db(ctx.selectedDB)

if !db.exists(src) {
c.WriteNull()
return
}
if db.t(src) != "list" || (db.exists(dst) && db.t(dst) != "list") {
c.WriteError(msgWrongType)
return
}
var elem string
if srcDir == "left" {
elem = db.listLpop(src)
} else if srcDir == "right" {
elem = db.listPop(src)
} else {
c.WriteError(msgSyntaxError)
return
}

if dstDir == "left" {
db.listLpush(dst, elem)
} else if dstDir == "right" {
db.listPush(dst, elem)
} else {
c.WriteError(msgSyntaxError)
return
}
c.WriteBulk(elem)
})
}
131 changes: 131 additions & 0 deletions cmd_list_test.go
Expand Up @@ -1274,3 +1274,134 @@ func TestBrpoplpushTimeout(t *testing.T) {
t.Error("BRPOPLPUSH took too long")
}
}

func TestLmove(t *testing.T) {
s, err := Run()
ok(t, err)
defer s.Close()
c, err := proto.Dial(s.Addr())
ok(t, err)
defer c.Close()

s.Push("src", "LR", "LL", "RR", "RL")
s.Push("dst", "m1", "m2", "m3")
// RIGHT LEFT
{
mustDo(t, c,
"LMOVE", "src", "dst", "RIGHT", "LEFT",
proto.String("RL"),
)
s.CheckList(t, "src", "LR", "LL", "RR")
s.CheckList(t, "dst", "RL", "m1", "m2", "m3")
}
// LEFT RIGHT
{
mustDo(t, c,
"LMOVE", "src", "dst", "LEFT", "RIGHT",
proto.String("LR"),
)
s.CheckList(t, "src", "LL", "RR")
s.CheckList(t, "dst", "RL", "m1", "m2", "m3", "LR")
}
// RIGHT RIGHT
{
mustDo(t, c,
"LMOVE", "src", "dst", "RIGHT", "RIGHT",
proto.String("RR"),
)
s.CheckList(t, "src", "LL")
s.CheckList(t, "dst", "RL", "m1", "m2", "m3", "LR", "RR")
}
// LEFT LEFT
{
mustDo(t, c,
"LMOVE", "src", "dst", "LEFT", "LEFT",
proto.String("LL"),
)
assert(t, !s.Exists("src"), "src exists")
s.CheckList(t, "dst", "LL", "RL", "m1", "m2", "m3", "LR", "RR")
}

// Non exising lists
{
s.Push("ll", "aap", "noot", "mies")

mustDo(t, c,
"LMOVE", "ll", "nosuch", "RIGHT", "LEFT",
proto.String("mies"),
)
assert(t, s.Exists("nosuch"), "nosuch exists")
s.CheckList(t, "ll", "aap", "noot")
s.CheckList(t, "nosuch", "mies")

mustNil(t, c,
"LMOVE", "nosuch2", "ll", "RIGHT", "LEFT",
)
}

// Cycle
{
s.Push("cycle", "aap", "noot", "mies")

mustDo(t, c,
"LMOVE", "cycle", "cycle", "RIGHT", "LEFT",
proto.String("mies"),
)
s.CheckList(t, "cycle", "mies", "aap", "noot")

mustDo(t, c,
"LMOVE", "cycle", "cycle", "LEFT", "RIGHT",
proto.String("mies"),
)
s.CheckList(t, "cycle", "aap", "noot", "mies")
}

// Error cases
t.Run("errors", func(t *testing.T) {
s.Push("src", "aap", "noot", "mies")
s.Push("dst", "aap", "noot", "mies")
mustDo(t, c,
"LMOVE",
proto.Error(errWrongNumber("lmove")),
)
mustDo(t, c,
"LMOVE", "l",
proto.Error(errWrongNumber("lmove")),
)
mustDo(t, c,
"LMOVE", "l", "l",
proto.Error(errWrongNumber("lmove")),
)
mustDo(t, c,
"LMOVE", "l", "l", "l",
proto.Error(errWrongNumber("lmove")),
)
mustDo(t, c,
"LMOVE", "too", "many", "many", "many", "arguments",
proto.Error(errWrongNumber("lmove")),
)

s.Set("str", "string!")
mustDo(t, c,
"LMOVE", "str", "src", "left", "right",
proto.Error(msgWrongType),
)
mustDo(t, c,
"LMOVE", "src", "str", "left", "right",
proto.Error(msgWrongType),
)

mustDo(t, c,
"LMOVE", "src", "dst", "no", "good",
proto.Error("ERR syntax error"),
)
mustDo(t, c,
"LMOVE", "src", "dst", "invalid", "right",
proto.Error("ERR syntax error"),
)
mustDo(t, c,
"LMOVE", "src", "dst", "left", "invalid",
proto.Error("ERR syntax error"),
)
})
}
49 changes: 49 additions & 0 deletions integration/list_test.go
Expand Up @@ -455,3 +455,52 @@ func TestBrpoplpush(t *testing.T) {
},
)
}

func TestLmove(t *testing.T) {
testRaw(t, func(c *client) {
c.Do("RPUSH", "src", "LR", "LL", "RR", "RL")
c.Do("LMOVE", "src", "dst", "LEFT", "RIGHT")
c.Do("LRANGE", "src", "0", "-1")
c.Do("LRANGE", "dst", "0", "-1")
c.Do("LMOVE", "src", "dst", "RIGHT", "LEFT")
c.Do("LMOVE", "src", "dst", "LEFT", "LEFT")
c.Do("LMOVE", "src", "dst", "RIGHT", "RIGHT") // now empty
c.Do("EXISTS", "src")
c.Do("LRANGE", "dst", "0", "-1")

// Cycle left to right
c.Do("RPUSH", "round", "aap", "noot", "mies")
c.Do("LMOVE", "round", "round", "LEFT", "RIGHT")
c.Do("LRANGE", "round", "0", "-1")
c.Do("LMOVE", "round", "round", "LEFT", "RIGHT")
c.Do("LMOVE", "round", "round", "LEFT", "RIGHT")
c.Do("LMOVE", "round", "round", "LEFT", "RIGHT")
c.Do("LMOVE", "round", "round", "LEFT", "RIGHT")
c.Do("LRANGE", "round", "0", "-1")
// Cycle right to left
c.Do("LMOVE", "round", "round", "RIGHT", "LEFT")
c.Do("LRANGE", "round", "0", "-1")
c.Do("LMOVE", "round", "round", "RIGHT", "LEFT")
c.Do("LMOVE", "round", "round", "RIGHT", "LEFT")
c.Do("LMOVE", "round", "round", "RIGHT", "LEFT")
c.Do("LMOVE", "round", "round", "RIGHT", "LEFT")
c.Do("LRANGE", "round", "0", "-1")
// Cycle same side
c.Do("LMOVE", "round", "round", "LEFT", "LEFT")
c.Do("LRANGE", "round", "0", "-1")
c.Do("LMOVE", "round", "round", "RIGHT", "RIGHT")
c.Do("LRANGE", "round", "0", "-1")

// failure cases
c.Do("RPUSH", "chk", "aap", "noot", "mies")
c.Error("wrong number", "LMOVE")
c.Error("wrong number", "LMOVE", "chk")
c.Error("wrong number", "LMOVE", "chk", "dst")
c.Error("wrong number", "LMOVE", "chk", "dst", "chk")
c.Error("wrong number", "LMOVE", "chk", "dst", "chk", "too", "many")
c.Do("SET", "str", "I am a string")
c.Error("wrong kind", "LMOVE", "chk", "str", "LEFT", "LEFT")
c.Error("wrong kind", "LMOVE", "str", "chk", "LEFT", "LEFT")
c.Do("LRANGE", "chk", "0", "-1")
})
}

0 comments on commit b821ff3

Please sign in to comment.