Files
redis-go/database/list_test.go
2023-05-28 20:49:06 +08:00

530 lines
18 KiB
Go

package database
import (
"strconv"
"testing"
"github.com/hdt3213/godis/lib/utils"
"github.com/hdt3213/godis/redis/protocol"
"github.com/hdt3213/godis/redis/protocol/asserts"
)
func TestPush(t *testing.T) {
testDB.Flush()
size := 100
// rpush single
key := utils.RandString(10)
values := make([][]byte, size)
for i := 0; i < size; i++ {
value := utils.RandString(10)
values[i] = []byte(value)
result := testDB.Exec(nil, utils.ToCmdLine("rpush", key, value))
if intResult, _ := result.(*protocol.IntReply); intResult.Code != int64(i+1) {
t.Errorf("expected %d, actually %d", i+1, intResult.Code)
}
}
actual := testDB.Exec(nil, utils.ToCmdLine("lrange", key, "0", "-1"))
expected := protocol.MakeMultiBulkReply(values)
if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) {
t.Error("push error")
}
testDB.Remove(key)
// rpush multi
key = utils.RandString(10)
args := make([]string, size+1)
args[0] = key
values = make([][]byte, size)
for i := 0; i < size; i++ {
value := utils.RandString(10)
values[i] = []byte(value)
args[i+1] = value
}
result := testDB.Exec(nil, utils.ToCmdLine2("rpush", args...))
if intResult, _ := result.(*protocol.IntReply); intResult.Code != int64(size) {
t.Errorf("expected %d, actually %d", size, intResult.Code)
}
actual = testDB.Exec(nil, utils.ToCmdLine("lrange", key, "0", "-1"))
expected = protocol.MakeMultiBulkReply(values)
if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) {
t.Error("push error")
}
testDB.Remove(key)
// left push single
key = utils.RandString(10)
values = make([][]byte, size)
for i := 0; i < size; i++ {
value := utils.RandString(10)
values[size-i-1] = []byte(value)
result = testDB.Exec(nil, utils.ToCmdLine("lpush", key, value))
if intResult, _ := result.(*protocol.IntReply); intResult.Code != int64(i+1) {
t.Errorf("expected %d, actually %d", i+1, intResult.Code)
}
}
actual = testDB.Exec(nil, utils.ToCmdLine("lrange", key, "0", "-1"))
expected = protocol.MakeMultiBulkReply(values)
if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) {
t.Error("push error")
}
testDB.Remove(key)
// left push multi
key = utils.RandString(10)
args = make([]string, size+1)
args[0] = key
expectedValues := make([][]byte, size)
for i := 0; i < size; i++ {
value := utils.RandString(10)
args[i+1] = value
expectedValues[size-i-1] = []byte(value)
}
// result = execLPush(testDB, values)
result = testDB.Exec(nil, utils.ToCmdLine2("lpush", args...))
if intResult, _ := result.(*protocol.IntReply); intResult.Code != int64(size) {
t.Errorf("expected %d, actually %d", size, intResult.Code)
}
actual = testDB.Exec(nil, utils.ToCmdLine("lrange", key, "0", "-1"))
expected = protocol.MakeMultiBulkReply(expectedValues)
if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) {
t.Error("push error")
}
testDB.Remove(key)
}
func TestLRange(t *testing.T) {
// prepare list
testDB.Flush()
size := 100
key := utils.RandString(10)
values := make([][]byte, size)
for i := 0; i < size; i++ {
value := utils.RandString(10)
testDB.Exec(nil, utils.ToCmdLine("rpush", key, value))
values[i] = []byte(value)
}
start := "0"
end := "9"
actual := testDB.Exec(nil, utils.ToCmdLine("lrange", key, start, end))
expected := protocol.MakeMultiBulkReply(values[0:10])
if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) {
t.Errorf("range error [%s, %s]", start, end)
}
start = "0"
end = "200"
actual = testDB.Exec(nil, utils.ToCmdLine("lrange", key, start, end))
expected = protocol.MakeMultiBulkReply(values)
if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) {
t.Errorf("range error [%s, %s]", start, end)
}
start = "0"
end = "-10"
actual = testDB.Exec(nil, utils.ToCmdLine("lrange", key, start, end))
expected = protocol.MakeMultiBulkReply(values[0 : size-10+1])
if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) {
t.Errorf("range error [%s, %s]", start, end)
}
start = "0"
end = "-200"
actual = testDB.Exec(nil, utils.ToCmdLine("lrange", key, start, end))
expected = protocol.MakeMultiBulkReply(values[0:0])
if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) {
t.Errorf("range error [%s, %s]", start, end)
}
start = "-10"
end = "-1"
actual = testDB.Exec(nil, utils.ToCmdLine("lrange", key, start, end))
expected = protocol.MakeMultiBulkReply(values[90:])
if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) {
t.Errorf("range error [%s, %s]", start, end)
}
}
func TestLIndex(t *testing.T) {
// prepare list
testDB.Flush()
size := 100
key := utils.RandString(10)
values := make([][]byte, size)
for i := 0; i < size; i++ {
value := utils.RandString(10)
testDB.Exec(nil, utils.ToCmdLine("rpush", key, value))
values[i] = []byte(value)
}
result := testDB.Exec(nil, utils.ToCmdLine("llen", key))
if intResult, _ := result.(*protocol.IntReply); intResult.Code != int64(size) {
t.Errorf("expected %d, actually %d", size, intResult.Code)
}
for i := 0; i < size; i++ {
result = testDB.Exec(nil, utils.ToCmdLine("lindex", key, strconv.Itoa(i)))
expected := protocol.MakeBulkReply(values[i])
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
}
for i := 1; i <= size; i++ {
result = testDB.Exec(nil, utils.ToCmdLine("lindex", key, strconv.Itoa(-i)))
expected := protocol.MakeBulkReply(values[size-i])
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
}
}
func TestLRem(t *testing.T) {
// prepare list
testDB.Flush()
key := utils.RandString(10)
values := []string{key, "a", "b", "a", "a", "c", "a", "a"}
testDB.Exec(nil, utils.ToCmdLine2("rpush", values...))
result := testDB.Exec(nil, utils.ToCmdLine("lrem", key, "1", "a"))
if intResult, _ := result.(*protocol.IntReply); intResult.Code != 1 {
t.Errorf("expected %d, actually %d", 1, intResult.Code)
}
result = testDB.Exec(nil, utils.ToCmdLine("llen", key))
if intResult, _ := result.(*protocol.IntReply); intResult.Code != 6 {
t.Errorf("expected %d, actually %d", 6, intResult.Code)
}
result = testDB.Exec(nil, utils.ToCmdLine("lrem", key, "-2", "a"))
if intResult, _ := result.(*protocol.IntReply); intResult.Code != 2 {
t.Errorf("expected %d, actually %d", 2, intResult.Code)
}
result = testDB.Exec(nil, utils.ToCmdLine("llen", key))
if intResult, _ := result.(*protocol.IntReply); intResult.Code != 4 {
t.Errorf("expected %d, actually %d", 4, intResult.Code)
}
result = testDB.Exec(nil, utils.ToCmdLine("lrem", key, "0", "a"))
if intResult, _ := result.(*protocol.IntReply); intResult.Code != 2 {
t.Errorf("expected %d, actually %d", 2, intResult.Code)
}
result = testDB.Exec(nil, utils.ToCmdLine("llen", key))
if intResult, _ := result.(*protocol.IntReply); intResult.Code != 2 {
t.Errorf("expected %d, actually %d", 2, intResult.Code)
}
}
func TestLSet(t *testing.T) {
testDB.Flush()
key := utils.RandString(10)
values := []string{key, "a", "b", "c", "d", "e", "f"}
testDB.Exec(nil, utils.ToCmdLine2("rpush", values...))
// test positive index
size := len(values) - 1
for i := 0; i < size; i++ {
indexStr := strconv.Itoa(i)
value := utils.RandString(10)
result := testDB.Exec(nil, utils.ToCmdLine("lset", key, indexStr, value))
if _, ok := result.(*protocol.OkReply); !ok {
t.Errorf("expected OK, actually %s", string(result.ToBytes()))
}
result = testDB.Exec(nil, utils.ToCmdLine("lindex", key, indexStr))
expected := protocol.MakeBulkReply([]byte(value))
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
}
// test negative index
for i := 1; i <= size; i++ {
value := utils.RandString(10)
result := testDB.Exec(nil, utils.ToCmdLine("lset", key, strconv.Itoa(-i), value))
if _, ok := result.(*protocol.OkReply); !ok {
t.Errorf("expected OK, actually %s", string(result.ToBytes()))
}
result = testDB.Exec(nil, utils.ToCmdLine("lindex", key, strconv.Itoa(len(values)-i-1)))
expected := protocol.MakeBulkReply([]byte(value))
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
}
// test illegal index
value := utils.RandString(10)
result := testDB.Exec(nil, utils.ToCmdLine("lset", key, strconv.Itoa(-len(values)-1), value))
expected := protocol.MakeErrReply("ERR index out of range")
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
result = testDB.Exec(nil, utils.ToCmdLine("lset", key, strconv.Itoa(len(values)), value))
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
result = testDB.Exec(nil, utils.ToCmdLine("lset", key, "a", value))
expected = protocol.MakeErrReply("ERR value is not an integer or out of range")
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
}
func TestLPop(t *testing.T) {
testDB.Flush()
key := utils.RandString(10)
values := []string{key, "a", "b", "c", "d", "e", "f"}
testDB.Exec(nil, utils.ToCmdLine2("rpush", values...))
size := len(values) - 1
for i := 0; i < size; i++ {
result := testDB.Exec(nil, utils.ToCmdLine("lpop", key))
expected := protocol.MakeBulkReply([]byte(values[i+1]))
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
}
result := testDB.Exec(nil, utils.ToCmdLine("rpop", key))
expected := &protocol.NullBulkReply{}
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
}
func TestRPop(t *testing.T) {
testDB.Flush()
key := utils.RandString(10)
values := []string{key, "a", "b", "c", "d", "e", "f"}
testDB.Exec(nil, utils.ToCmdLine2("rpush", values...))
size := len(values) - 1
for i := 0; i < size; i++ {
result := testDB.Exec(nil, utils.ToCmdLine("rpop", key))
expected := protocol.MakeBulkReply([]byte(values[len(values)-i-1]))
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
}
result := testDB.Exec(nil, utils.ToCmdLine("rpop", key))
expected := &protocol.NullBulkReply{}
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
}
func TestRPopLPush(t *testing.T) {
testDB.Flush()
key1 := utils.RandString(10)
key2 := utils.RandString(10)
values := []string{key1, "a", "b", "c", "d", "e", "f"}
testDB.Exec(nil, utils.ToCmdLine2("rpush", values...))
size := len(values) - 1
for i := 0; i < size; i++ {
result := testDB.Exec(nil, utils.ToCmdLine("rpoplpush", key1, key2))
expected := protocol.MakeBulkReply([]byte(values[len(values)-i-1]))
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
result = testDB.Exec(nil, utils.ToCmdLine("lindex", key2, "0"))
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
}
result := testDB.Exec(nil, utils.ToCmdLine("rpop", key1))
expected := &protocol.NullBulkReply{}
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
}
func TestRPushX(t *testing.T) {
testDB.Flush()
key := utils.RandString(10)
result := testDB.Exec(nil, utils.ToCmdLine("rpushx", key, "1"))
expected := protocol.MakeIntReply(int64(0))
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
testDB.Exec(nil, utils.ToCmdLine("rpush", key, "1"))
for i := 0; i < 10; i++ {
value := utils.RandString(10)
result = testDB.Exec(nil, utils.ToCmdLine("rpushx", key, value))
expected := protocol.MakeIntReply(int64(i + 2))
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
result = testDB.Exec(nil, utils.ToCmdLine("lindex", key, "-1"))
expected2 := protocol.MakeBulkReply([]byte(value))
if !utils.BytesEquals(result.ToBytes(), expected2.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected2.ToBytes()), string(result.ToBytes()))
}
}
}
func TestLPushX(t *testing.T) {
testDB.Flush()
key := utils.RandString(10)
result := testDB.Exec(nil, utils.ToCmdLine("rpushx", key, "1"))
expected := protocol.MakeIntReply(int64(0))
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
testDB.Exec(nil, utils.ToCmdLine("lpush", key, "1"))
for i := 0; i < 10; i++ {
value := utils.RandString(10)
result = testDB.Exec(nil, utils.ToCmdLine("lpushx", key, value))
expected := protocol.MakeIntReply(int64(i + 2))
if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))
}
result = testDB.Exec(nil, utils.ToCmdLine("lindex", key, "0"))
expected2 := protocol.MakeBulkReply([]byte(value))
if !utils.BytesEquals(result.ToBytes(), expected2.ToBytes()) {
t.Errorf("expected %s, actually %s", string(expected2.ToBytes()), string(result.ToBytes()))
}
}
}
func TestLTrim(t *testing.T) {
testDB.Flush()
key := utils.RandString(10)
values := []string{"a", "b", "c", "d", "e", "f"}
result := testDB.Exec(nil, utils.ToCmdLine("rpush", key, "a", "b", "c", "d", "e", "f"))
asserts.AssertIntReply(t, result, 6)
// case1
result1 := testDB.Exec(nil, utils.ToCmdLine("ltrim", key, "1", "-2"))
asserts.AssertStatusReply(t, result1, "OK")
actualValue1 := testDB.Exec(nil, utils.ToCmdLine("lrange", key, "0", "-1"))
asserts.AssertMultiBulkReply(t, actualValue1, values[1:5])
// case2
result2 := testDB.Exec(nil, utils.ToCmdLine("ltrim", key, "-3", "-2"))
asserts.AssertStatusReply(t, result2, "OK")
actualValue2 := testDB.Exec(nil, utils.ToCmdLine("lrange", key, "0", "-1"))
asserts.AssertMultiBulkReply(t, actualValue2, values[2:4])
// case3
result3 := testDB.Exec(nil, utils.ToCmdLine("ltrim", key, "1", "0"))
asserts.AssertStatusReply(t, result3, "OK")
actualValue3 := testDB.Exec(nil, utils.ToCmdLine("lrange", key, "0", "-1"))
asserts.AssertMultiBulkReplySize(t, actualValue3, 0)
}
func TestLInsert(t *testing.T) {
testDB.Flush()
key := utils.RandString(10)
result := testDB.Exec(nil, utils.ToCmdLine("rpush", key, "a", "b", "c", "d", "e", "f"))
asserts.AssertIntReply(t, result, 6)
// case1
result = testDB.Exec(nil, utils.ToCmdLine("linsert", key, "before", "d", "0"))
asserts.AssertIntReply(t, result, 7)
values1 := testDB.Exec(nil, utils.ToCmdLine("lrange", key, "0", "-1"))
asserts.AssertMultiBulkReply(t, values1, []string{"a", "b", "c", "0", "d", "e", "f"})
// case2
result = testDB.Exec(nil, utils.ToCmdLine("linsert", key, "after", "d", "1"))
asserts.AssertIntReply(t, result, 8)
values2 := testDB.Exec(nil, utils.ToCmdLine("lrange", key, "0", "-1"))
asserts.AssertMultiBulkReply(t, values2, []string{"a", "b", "c", "0", "d", "1", "e", "f"})
// case3
result3 := testDB.Exec(nil, utils.ToCmdLine("linsert", key, "test", "d", "1"))
asserts.AssertErrReply(t, result3, "ERR syntax error")
// case4
result4 := testDB.Exec(nil, utils.ToCmdLine("linsert", key, "test", "d"))
asserts.AssertErrReply(t, result4, "ERR wrong number of arguments for 'linsert' command")
// case5
result5 := testDB.Exec(nil, utils.ToCmdLine("linsert", key, "before", "z", "2"))
asserts.AssertIntReply(t, result5, -1)
}
func TestUndoLPush(t *testing.T) {
testDB.Flush()
key := utils.RandString(10)
value := utils.RandString(10)
cmdLine := utils.ToCmdLine("lpush", key, value)
testDB.Exec(nil, cmdLine)
undoCmdLines := undoLPush(testDB, cmdLine[1:])
testDB.Exec(nil, cmdLine)
for _, cmdLine := range undoCmdLines {
testDB.Exec(nil, cmdLine)
}
result := testDB.Exec(nil, utils.ToCmdLine("llen", key))
asserts.AssertIntReply(t, result, 1)
}
func TestUndoLPop(t *testing.T) {
testDB.Flush()
key := utils.RandString(10)
value := utils.RandString(10)
testDB.Exec(nil, utils.ToCmdLine("lpush", key, value, value))
cmdLine := utils.ToCmdLine("lpop", key)
undoCmdLines := undoLPop(testDB, cmdLine[1:])
testDB.Exec(nil, cmdLine)
for _, cmdLine := range undoCmdLines {
testDB.Exec(nil, cmdLine)
}
result := testDB.Exec(nil, utils.ToCmdLine("llen", key))
asserts.AssertIntReply(t, result, 2)
}
func TestUndoLSet(t *testing.T) {
testDB.Flush()
key := utils.RandString(10)
value := utils.RandString(10)
value2 := utils.RandString(10)
testDB.Exec(nil, utils.ToCmdLine("lpush", key, value, value))
cmdLine := utils.ToCmdLine("lset", key, "1", value2)
undoCmdLines := undoLSet(testDB, cmdLine[1:])
testDB.Exec(nil, cmdLine)
for _, cmdLine := range undoCmdLines {
testDB.Exec(nil, cmdLine)
}
result := testDB.Exec(nil, utils.ToCmdLine("lindex", key, "1"))
asserts.AssertBulkReply(t, result, value)
}
func TestUndoRPop(t *testing.T) {
testDB.Flush()
key := utils.RandString(10)
value := utils.RandString(10)
testDB.Exec(nil, utils.ToCmdLine("rpush", key, value, value))
cmdLine := utils.ToCmdLine("rpop", key)
undoCmdLines := undoRPop(testDB, cmdLine[1:])
testDB.Exec(nil, cmdLine)
for _, cmdLine := range undoCmdLines {
testDB.Exec(nil, cmdLine)
}
result := testDB.Exec(nil, utils.ToCmdLine("llen", key))
asserts.AssertIntReply(t, result, 2)
}
func TestUndoRPopLPush(t *testing.T) {
testDB.Flush()
key1 := utils.RandString(10)
key2 := utils.RandString(10)
value := utils.RandString(10)
testDB.Exec(nil, utils.ToCmdLine("lpush", key1, value))
cmdLine := utils.ToCmdLine("rpoplpush", key1, key2)
undoCmdLines := undoRPopLPush(testDB, cmdLine[1:])
testDB.Exec(nil, cmdLine)
for _, cmdLine := range undoCmdLines {
testDB.Exec(nil, cmdLine)
}
result := testDB.Exec(nil, utils.ToCmdLine("llen", key1))
asserts.AssertIntReply(t, result, 1)
result = testDB.Exec(nil, utils.ToCmdLine("llen", key2))
asserts.AssertIntReply(t, result, 0)
}