You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@kvrocks.apache.org by GitBox <gi...@apache.org> on 2022/10/18 09:02:20 UTC

[GitHub] [incubator-kvrocks] tanruixiang commented on a diff in pull request #1009: Move TCL test unit/type/list to Go case

tanruixiang commented on code in PR #1009:
URL: https://github.com/apache/incubator-kvrocks/pull/1009#discussion_r997907127


##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())

Review Comment:
   ```suggestion
   		require.EqualValues(t, 1, rdb.RPush(ctx, "myziplist2", "a").Val())
   		require.EqualValues(t, 2, rdb.LPush(ctx, "myziplist2", "b").Val())
   		require.EqualValues(t, 3, rdb.LPush(ctx, "myziplist2", "c").Val())
   		require.EqualValues(t, 3, rdb.LLen(ctx, "myziplist2").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())

Review Comment:
   ```suggestion
   		require.EqualValues(t, 1, rdb.LPush(ctx, "myziplist1", "aa").Val())
   		require.EqualValues(t, 2, rdb.RPush(ctx, "myziplist1", "bb").Val())
   		require.EqualValues(t, 3, rdb.RPush(ctx, "myziplist1", "cc").Val())
   		require.EqualValues(t, 3, rdb.LLen(ctx, "myziplist1").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())

Review Comment:
   ```suggestion
   		require.EqualValues(t, 1, rdb.Del(ctx, "mylist2").Val())
   		require.EqualValues(t, 0, rdb.Exists(ctx, "mylist2").Val())
   		require.EqualValues(t, 0, rdb.LLen(ctx, "mylist2").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())

Review Comment:
   ```suggestion
   		require.EqualValues(t, 0, rdb.LPushX(ctx, "xlist", "a").Val())
   		require.EqualValues(t, 0, rdb.LLen(ctx, "xlist").Val())
   		require.EqualValues(t, 0, rdb.RPushX(ctx, "xlist", "a").Val())
   		require.EqualValues(t, 0, rdb.LLen(ctx, "xlist").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LPUSHX, RPUSHX - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{large, "c"})
+
+			require.Equal(t, int64(3), rdb.RPushX(ctx, "xlist", "d").Val())
+			require.Equal(t, int64(4), rdb.LPushX(ctx, "xlist", "a").Val())
+			require.Equal(t, int64(6), rdb.RPushX(ctx, "xlist", "42", "x").Val())
+			require.Equal(t, int64(9), rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())

Review Comment:
   ```suggestion
   			require.EqualValues(t, 3, rdb.RPushX(ctx, "xlist", "d").Val())
   			require.EqualValues(t, 4, rdb.LPushX(ctx, "xlist", "a").Val())
   			require.EqualValues(t, 6, rdb.RPushX(ctx, "xlist", "42", "x").Val())
   			require.EqualValues(t, 9, rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LPUSHX, RPUSHX - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{large, "c"})
+
+			require.Equal(t, int64(3), rdb.RPushX(ctx, "xlist", "d").Val())
+			require.Equal(t, int64(4), rdb.LPushX(ctx, "xlist", "a").Val())
+			require.Equal(t, int64(6), rdb.RPushX(ctx, "xlist", "42", "x").Val())
+			require.Equal(t, int64(9), rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())
+			require.Equal(t, []string{"y1", "y2", "y3", "a", large, "c", "d", "42", "x"}, rdb.LRange(ctx, "xlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LINSERT - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{"a", large, "c", "d"})
+
+			require.Equal(t, int64(5), rdb.LInsert(ctx, "xlist", "before", "c", "zz").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(6), rdb.LInsert(ctx, "xlist", "after", "c", "yy").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "xlist", "after", "d", "dd").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "after", "bad", "ddd").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(8), rdb.LInsert(ctx, "xlist", "before", "a", "aa").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "before", "bad", "aaa").Val())
+			require.Equal(t, []string{"aa", "a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+
+			// check inserting integer encoded value
+			require.Equal(t, int64(9), rdb.LInsert(ctx, "xlist", "before", "aa", "42").Val())

Review Comment:
   ```suggestion
   			require.EqualValues(t, 9, rdb.LInsert(ctx, "xlist", "before", "aa", "42").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LPUSHX, RPUSHX - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{large, "c"})
+
+			require.Equal(t, int64(3), rdb.RPushX(ctx, "xlist", "d").Val())
+			require.Equal(t, int64(4), rdb.LPushX(ctx, "xlist", "a").Val())
+			require.Equal(t, int64(6), rdb.RPushX(ctx, "xlist", "42", "x").Val())
+			require.Equal(t, int64(9), rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())
+			require.Equal(t, []string{"y1", "y2", "y3", "a", large, "c", "d", "42", "x"}, rdb.LRange(ctx, "xlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LINSERT - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{"a", large, "c", "d"})
+
+			require.Equal(t, int64(5), rdb.LInsert(ctx, "xlist", "before", "c", "zz").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(6), rdb.LInsert(ctx, "xlist", "after", "c", "yy").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "xlist", "after", "d", "dd").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "after", "bad", "ddd").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(8), rdb.LInsert(ctx, "xlist", "before", "a", "aa").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "before", "bad", "aaa").Val())
+			require.Equal(t, []string{"aa", "a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+
+			// check inserting integer encoded value
+			require.Equal(t, int64(9), rdb.LInsert(ctx, "xlist", "before", "aa", "42").Val())
+			require.Equal(t, "42", rdb.LRange(ctx, "xlist", 0, 0).Val()[0])
+		})
+
+	}
+
+	t.Run("LINSERT raise error on bad syntax", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LInsert(ctx, "xlist", "aft3r", "aa", "42").Err(), ".*syntax.*error.*")
+	})
+
+	listType := "quicklist"
+	for _, num := range []int{250, 500} {
+
+		checkNumberedListConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				require.Equal(t, strconv.Itoa(i), rdb.LIndex(ctx, key, int64(i)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-i-1), rdb.LIndex(ctx, key, int64(int(l)-i-1)).Val())
+			}
+		}
+
+		checkRandomAccessConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				rint := rand.Intn(int(l))
+				require.Equal(t, strconv.Itoa(rint), rdb.LIndex(ctx, key, int64(rint)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-rint-1), rdb.LIndex(ctx, key, int64(int(l)-rint-1)).Val())
+			}
+		}
+
+		t.Run(fmt.Sprintf("LINDEX consistency test - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			for i := 0; i < num; i++ {
+				rdb.RPush(ctx, "mylist", strconv.Itoa(i))
+			}
+			checkNumberedListConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("LINDEX random access - %s", listType), func(t *testing.T) {
+			checkRandomAccessConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("Check if list is still ok after a DEBUG RELOAD - %s", listType), func(t *testing.T) {
+			checkNumberedListConsistency("mylist")
+			checkRandomAccessConsistency("mylist")
+		})
+	}
+	t.Run("LLEN against non-list value error", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		rdb.Set(ctx, "mylist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LLen(ctx, "mylist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LLEN against non existing key", func(t *testing.T) {
+		require.Equal(t, int64(0), rdb.LLen(ctx, "not-a-key").Val())
+	})
+
+	t.Run("LINDEX against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LIndex(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LINDEX against non existing key", func(t *testing.T) {
+		require.Equal(t, "", rdb.LIndex(ctx, "not-a-key", 10).Val())
+	})
+
+	t.Run("LPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("RPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.RPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("RPOPLPUSH base case - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist1", "mylist2")
+			createList("mylist1", []string{"a", large, "c", "d"})
+			require.Equal(t, "d", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, []string{"a", large}, rdb.LRange(ctx, "mylist1", 0, -1).Val())
+			require.Equal(t, []string{"c", "d"}, rdb.LRange(ctx, "mylist2", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("RPOPLPUSH with the same list as src and dst - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"a", large, "c"})
+			require.Equal(t, []string{"a", large, "c"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist", "mylist").Val())
+			require.Equal(t, []string{"c", "a", large}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		for otherListType, otherLarge := range largeValue {
+
+			t.Run(fmt.Sprintf("RPOPLPUSH with %s source and existing target %s", listType, otherListType), func(t *testing.T) {
+				createList("srclist", []string{"a", "b", "c", large})
+				createList("dstlist", []string{otherLarge})
+				require.Equal(t, large, rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, "c", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, []string{"a", "b"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+				require.Equal(t, []string{"c", large, otherLarge}, rdb.LRange(ctx, "dstlist", 0, -1).Val())
+			})
+
+		}
+	}
+
+	t.Run("RPOPLPUSH against non existing key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "dstlist").Val())
+	})
+
+	t.Run("RPOPLPUSH against non list src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		rdb.Set(ctx, "srclist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "newlist").Val())
+
+	})
+
+	t.Run("RPOPLPUSH against non list dst key", func(t *testing.T) {
+		createList("srclist", []string{"a", "b", "c", "d"})
+
+		rdb.Set(ctx, "dstlist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "dstlist").Val())
+		require.Equal(t, []string{"a", "b", "c", "d"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+	})
+
+	t.Run("RPOPLPUSH against non existing src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("Basic LPOP/RPOP - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2"})
+
+			require.Equal(t, large, rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "2", rdb.RPop(ctx, "mylist").Val())
+			require.Equal(t, "1", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, int64(0), rdb.LLen(ctx, "mylist").Val())

Review Comment:
   ```suggestion
   			require.EqualValues(t, 0, rdb.LLen(ctx, "mylist").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())

Review Comment:
   ```suggestion
   			require.EqualValues(t, 1, rdb.LLen(ctx, "blist1").Val())
   			require.EqualValues(t, 1, rdb.LLen(ctx, "blist2").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LPUSHX, RPUSHX - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{large, "c"})
+
+			require.Equal(t, int64(3), rdb.RPushX(ctx, "xlist", "d").Val())
+			require.Equal(t, int64(4), rdb.LPushX(ctx, "xlist", "a").Val())
+			require.Equal(t, int64(6), rdb.RPushX(ctx, "xlist", "42", "x").Val())
+			require.Equal(t, int64(9), rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())
+			require.Equal(t, []string{"y1", "y2", "y3", "a", large, "c", "d", "42", "x"}, rdb.LRange(ctx, "xlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LINSERT - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{"a", large, "c", "d"})
+
+			require.Equal(t, int64(5), rdb.LInsert(ctx, "xlist", "before", "c", "zz").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(6), rdb.LInsert(ctx, "xlist", "after", "c", "yy").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "xlist", "after", "d", "dd").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "after", "bad", "ddd").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(8), rdb.LInsert(ctx, "xlist", "before", "a", "aa").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "before", "bad", "aaa").Val())
+			require.Equal(t, []string{"aa", "a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+
+			// check inserting integer encoded value
+			require.Equal(t, int64(9), rdb.LInsert(ctx, "xlist", "before", "aa", "42").Val())
+			require.Equal(t, "42", rdb.LRange(ctx, "xlist", 0, 0).Val()[0])
+		})
+
+	}
+
+	t.Run("LINSERT raise error on bad syntax", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LInsert(ctx, "xlist", "aft3r", "aa", "42").Err(), ".*syntax.*error.*")
+	})
+
+	listType := "quicklist"
+	for _, num := range []int{250, 500} {
+
+		checkNumberedListConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				require.Equal(t, strconv.Itoa(i), rdb.LIndex(ctx, key, int64(i)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-i-1), rdb.LIndex(ctx, key, int64(int(l)-i-1)).Val())
+			}
+		}
+
+		checkRandomAccessConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				rint := rand.Intn(int(l))
+				require.Equal(t, strconv.Itoa(rint), rdb.LIndex(ctx, key, int64(rint)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-rint-1), rdb.LIndex(ctx, key, int64(int(l)-rint-1)).Val())
+			}
+		}
+
+		t.Run(fmt.Sprintf("LINDEX consistency test - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			for i := 0; i < num; i++ {
+				rdb.RPush(ctx, "mylist", strconv.Itoa(i))
+			}
+			checkNumberedListConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("LINDEX random access - %s", listType), func(t *testing.T) {
+			checkRandomAccessConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("Check if list is still ok after a DEBUG RELOAD - %s", listType), func(t *testing.T) {
+			checkNumberedListConsistency("mylist")
+			checkRandomAccessConsistency("mylist")
+		})
+	}
+	t.Run("LLEN against non-list value error", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		rdb.Set(ctx, "mylist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LLen(ctx, "mylist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LLEN against non existing key", func(t *testing.T) {
+		require.Equal(t, int64(0), rdb.LLen(ctx, "not-a-key").Val())
+	})
+
+	t.Run("LINDEX against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LIndex(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LINDEX against non existing key", func(t *testing.T) {
+		require.Equal(t, "", rdb.LIndex(ctx, "not-a-key", 10).Val())
+	})
+
+	t.Run("LPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("RPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.RPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("RPOPLPUSH base case - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist1", "mylist2")
+			createList("mylist1", []string{"a", large, "c", "d"})
+			require.Equal(t, "d", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, []string{"a", large}, rdb.LRange(ctx, "mylist1", 0, -1).Val())
+			require.Equal(t, []string{"c", "d"}, rdb.LRange(ctx, "mylist2", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("RPOPLPUSH with the same list as src and dst - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"a", large, "c"})
+			require.Equal(t, []string{"a", large, "c"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist", "mylist").Val())
+			require.Equal(t, []string{"c", "a", large}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		for otherListType, otherLarge := range largeValue {
+
+			t.Run(fmt.Sprintf("RPOPLPUSH with %s source and existing target %s", listType, otherListType), func(t *testing.T) {
+				createList("srclist", []string{"a", "b", "c", large})
+				createList("dstlist", []string{otherLarge})
+				require.Equal(t, large, rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, "c", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, []string{"a", "b"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+				require.Equal(t, []string{"c", large, otherLarge}, rdb.LRange(ctx, "dstlist", 0, -1).Val())
+			})
+
+		}
+	}
+
+	t.Run("RPOPLPUSH against non existing key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "dstlist").Val())
+	})
+
+	t.Run("RPOPLPUSH against non list src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		rdb.Set(ctx, "srclist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "newlist").Val())
+
+	})
+
+	t.Run("RPOPLPUSH against non list dst key", func(t *testing.T) {
+		createList("srclist", []string{"a", "b", "c", "d"})
+
+		rdb.Set(ctx, "dstlist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "dstlist").Val())
+		require.Equal(t, []string{"a", "b", "c", "d"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+	})
+
+	t.Run("RPOPLPUSH against non existing src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("Basic LPOP/RPOP - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2"})
+
+			require.Equal(t, large, rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "2", rdb.RPop(ctx, "mylist").Val())
+			require.Equal(t, "1", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, int64(0), rdb.LLen(ctx, "mylist").Val())
+
+			// pop on empty list
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+		})
+	}
+
+	t.Run("LPOP/RPOP against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "notalist", "foo", 0)
+		util.ErrorRegexp(t, rdb.LPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+		util.ErrorRegexp(t, rdb.RPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LPOP/RPOP with wrong number of arguments", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "1", "1").Err(), ".*wrong number of arguments.*")
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "2", "2").Err(), ".*wrong number of arguments.*")
+	})
+
+	t.Run("RPOP/LPOP with the optional count argument", func(t *testing.T) {
+		require.Equal(t, int64(7), rdb.LPush(ctx, "listcount", "aa", "bb", "cc", "dd", "ee", "ff", "gg").Val())
+		require.Equal(t, []string{"gg"}, rdb.LPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"ff", "ee"}, rdb.LPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"aa", "bb"}, rdb.RPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"cc"}, rdb.RPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"dd"}, rdb.LPopCount(ctx, "listcount", 123).Val())
+		util.ErrorRegexp(t, rdb.LPopCount(ctx, "forbatqaz", -123).Err(), ".*ERR.*range.*")
+	})
+
+	t.Run("LPOP/RPOP with the count 0 returns an empty array", func(t *testing.T) {
+		// Make sure we can distinguish between an empty array and a null response
+		rdb.Do(ctx, "readraw", "1")
+		rdb.LPush(ctx, "listcount", "zoro")
+		require.Equal(t, []string{}, rdb.LPopCount(ctx, "listcount", 0).Val())
+		require.Equal(t, []string{}, rdb.RPopCount(ctx, "listcount", 0).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, "", rdb.LPop(ctx, "non_existing_key").Val())
+		require.Equal(t, "", rdb.RPop(ctx, "non_existing_key").Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP with <count> against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 1).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 1).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	listType = "quicklist"
+	for _, num := range []int{250, 500} {
+		t.Run(fmt.Sprintf("Mass RPOP/LPOP - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			sum1 := 0
+			for i := 0; i < num; i++ {
+				rdb.LPush(ctx, "mylist", strconv.Itoa(i))
+				sum1 += i
+			}
+			sum2 := 0
+			for i := 0; i < num/2; i++ {
+				if v1, err := strconv.Atoi(rdb.LPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v1
+				}
+				if v2, err := strconv.Atoi(rdb.RPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v2
+				}
+			}
+			require.Equal(t, sum1, sum2)
+		})
+	}
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("LRANGE basics - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{"1", "2", "3", "4", "5", "6", "7", "8"}, rdb.LRange(ctx, "mylist", 1, -2).Val())
+			require.Equal(t, []string{"7", "8", "9"}, rdb.LRange(ctx, "mylist", -3, -1).Val())
+			require.Equal(t, []string{"4"}, rdb.LRange(ctx, "mylist", 4, 4).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE inverted indexes - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 6, 2).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range indexes including the full list - $type - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large, "1", "2", "3"}, rdb.LRange(ctx, "mylist", -1000, 1000).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range negative end index - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large}, rdb.LRange(ctx, "mylist", 0, -4).Val())
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 0, -5).Val())
+		})
+	}
+
+	t.Run("LRANGE against non existing key", func(t *testing.T) {
+		require.Equal(t, []string{}, rdb.LRange(ctx, "nosuchkey", 0, 1).Val())
+	})
+
+	for listType, large := range largeValue {
+
+		trimList := func(listType string, min, max int64) []string {
+			rdb.Del(ctx, "mylist")
+			createList("mylist", []string{"1", "2", "3", "4", large})
+			rdb.LTrim(ctx, "mylist", min, max)
+			return rdb.LRange(ctx, "mylist", 0, -1).Val()
+		}
+
+		t.Run(fmt.Sprintf("LTRIM basics - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, 0))
+			require.Equal(t, []string{"1", "2"}, trimList(listType, 0, 1))
+			require.Equal(t, []string{"1", "2", "3"}, trimList(listType, 0, 2))
+			require.Equal(t, []string{"2", "3"}, trimList(listType, 1, 2))
+			require.Equal(t, []string{"2", "3", "4", large}, trimList(listType, 1, -1))
+			require.Equal(t, []string{"2", "3", "4"}, trimList(listType, 1, -2))
+			require.Equal(t, []string{"4", large}, trimList(listType, -2, -1))
+			require.Equal(t, []string{large}, trimList(listType, -1, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -5, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -10, 10))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 5))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 10))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM out of range negative end index - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, -5))
+			require.Equal(t, []string{}, trimList(listType, 0, -6))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM lrem elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+			require.Equal(t, int64(2), rdb.LRem(ctx, "myotherlist", 4, "3").Val())
+			require.Equal(t, []string{"2", "4", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LTRIM linsert elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist1", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist1", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist1", 0, -1).Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "myotherlist1", "before", "9", "0").Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "myotherlist1", "before", "4", "0").Val())
+			require.Equal(t, []string{"2", "3", "0", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist1", 0, -1).Val())
+		})
+
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LSET - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"99", "98", large, "96", "95"})
+
+			rdb.LSet(ctx, "mylist", 1, "foo")
+			rdb.LSet(ctx, "mylist", -1, "bar")
+			require.Equal(t, []string{"99", "foo", large, "96", "bar"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LSET out of range index - %s", listType), func(t *testing.T) {
+			util.ErrorRegexp(t, rdb.LSet(ctx, "mylist", 10, "foo").Err(), "ERR.*range.*")
+		})
+	}
+
+	t.Run("LSET against non existing key", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LSet(ctx, "nosuchkey", 10, "foo").Err(), "ERR.*NotFound.*")
+	})
+
+	t.Run("LSET against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "nolist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LSet(ctx, "nolist", 0, "foo").Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, e := range largeValue {
+		t.Run(fmt.Sprintf("LREM remove all the occurrences - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{e, "foo", "bar", "foobar", "foobared", "zap", "bar", "test", "foo"})
+
+			require.Equal(t, int64(2), rdb.LRem(ctx, "mylist", 0, "bar").Val())
+			require.Equal(t, []string{e, "foo", "foobar", "foobared", "zap", "test", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM remove the first occurrence - %s", listType), func(t *testing.T) {
+			require.Equal(t, int64(1), rdb.LRem(ctx, "mylist", 1, "foo").Val())

Review Comment:
   ```suggestion
   			require.EqualValues(t, 1, rdb.LRem(ctx, "mylist", 1, "foo").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())

Review Comment:
   ```suggestion
   		require.EqualValues(t, 1, rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
   		require.EqualValues(t, 2, rdb.RPush(ctx, "mylist1", "b").Val())
   		require.EqualValues(t, 3, rdb.RPush(ctx, "mylist1", "c").Val())
   		require.EqualValues(t, 3, rdb.LLen(ctx, "mylist1").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())

Review Comment:
   ```suggestion
   		require.EqualValues(t, 1, rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
   		require.EqualValues(t, 2, rdb.LPush(ctx, "mylist2", "b").Val())
   		require.EqualValues(t, 3, rdb.LPush(ctx, "mylist2", "c").Val())
   		require.EqualValues(t, 3, rdb.LLen(ctx, "mylist2").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LPUSHX, RPUSHX - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{large, "c"})
+
+			require.Equal(t, int64(3), rdb.RPushX(ctx, "xlist", "d").Val())
+			require.Equal(t, int64(4), rdb.LPushX(ctx, "xlist", "a").Val())
+			require.Equal(t, int64(6), rdb.RPushX(ctx, "xlist", "42", "x").Val())
+			require.Equal(t, int64(9), rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())
+			require.Equal(t, []string{"y1", "y2", "y3", "a", large, "c", "d", "42", "x"}, rdb.LRange(ctx, "xlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LINSERT - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{"a", large, "c", "d"})
+
+			require.Equal(t, int64(5), rdb.LInsert(ctx, "xlist", "before", "c", "zz").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(6), rdb.LInsert(ctx, "xlist", "after", "c", "yy").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "xlist", "after", "d", "dd").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "after", "bad", "ddd").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(8), rdb.LInsert(ctx, "xlist", "before", "a", "aa").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "before", "bad", "aaa").Val())
+			require.Equal(t, []string{"aa", "a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+
+			// check inserting integer encoded value
+			require.Equal(t, int64(9), rdb.LInsert(ctx, "xlist", "before", "aa", "42").Val())
+			require.Equal(t, "42", rdb.LRange(ctx, "xlist", 0, 0).Val()[0])
+		})
+
+	}
+
+	t.Run("LINSERT raise error on bad syntax", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LInsert(ctx, "xlist", "aft3r", "aa", "42").Err(), ".*syntax.*error.*")
+	})
+
+	listType := "quicklist"
+	for _, num := range []int{250, 500} {
+
+		checkNumberedListConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				require.Equal(t, strconv.Itoa(i), rdb.LIndex(ctx, key, int64(i)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-i-1), rdb.LIndex(ctx, key, int64(int(l)-i-1)).Val())
+			}
+		}
+
+		checkRandomAccessConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				rint := rand.Intn(int(l))
+				require.Equal(t, strconv.Itoa(rint), rdb.LIndex(ctx, key, int64(rint)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-rint-1), rdb.LIndex(ctx, key, int64(int(l)-rint-1)).Val())
+			}
+		}
+
+		t.Run(fmt.Sprintf("LINDEX consistency test - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			for i := 0; i < num; i++ {
+				rdb.RPush(ctx, "mylist", strconv.Itoa(i))
+			}
+			checkNumberedListConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("LINDEX random access - %s", listType), func(t *testing.T) {
+			checkRandomAccessConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("Check if list is still ok after a DEBUG RELOAD - %s", listType), func(t *testing.T) {
+			checkNumberedListConsistency("mylist")
+			checkRandomAccessConsistency("mylist")
+		})
+	}
+	t.Run("LLEN against non-list value error", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		rdb.Set(ctx, "mylist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LLen(ctx, "mylist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LLEN against non existing key", func(t *testing.T) {
+		require.Equal(t, int64(0), rdb.LLen(ctx, "not-a-key").Val())
+	})
+
+	t.Run("LINDEX against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LIndex(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LINDEX against non existing key", func(t *testing.T) {
+		require.Equal(t, "", rdb.LIndex(ctx, "not-a-key", 10).Val())
+	})
+
+	t.Run("LPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("RPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.RPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("RPOPLPUSH base case - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist1", "mylist2")
+			createList("mylist1", []string{"a", large, "c", "d"})
+			require.Equal(t, "d", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, []string{"a", large}, rdb.LRange(ctx, "mylist1", 0, -1).Val())
+			require.Equal(t, []string{"c", "d"}, rdb.LRange(ctx, "mylist2", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("RPOPLPUSH with the same list as src and dst - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"a", large, "c"})
+			require.Equal(t, []string{"a", large, "c"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist", "mylist").Val())
+			require.Equal(t, []string{"c", "a", large}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		for otherListType, otherLarge := range largeValue {
+
+			t.Run(fmt.Sprintf("RPOPLPUSH with %s source and existing target %s", listType, otherListType), func(t *testing.T) {
+				createList("srclist", []string{"a", "b", "c", large})
+				createList("dstlist", []string{otherLarge})
+				require.Equal(t, large, rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, "c", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, []string{"a", "b"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+				require.Equal(t, []string{"c", large, otherLarge}, rdb.LRange(ctx, "dstlist", 0, -1).Val())
+			})
+
+		}
+	}
+
+	t.Run("RPOPLPUSH against non existing key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "dstlist").Val())
+	})
+
+	t.Run("RPOPLPUSH against non list src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		rdb.Set(ctx, "srclist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "newlist").Val())
+
+	})
+
+	t.Run("RPOPLPUSH against non list dst key", func(t *testing.T) {
+		createList("srclist", []string{"a", "b", "c", "d"})
+
+		rdb.Set(ctx, "dstlist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "dstlist").Val())
+		require.Equal(t, []string{"a", "b", "c", "d"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+	})
+
+	t.Run("RPOPLPUSH against non existing src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("Basic LPOP/RPOP - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2"})
+
+			require.Equal(t, large, rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "2", rdb.RPop(ctx, "mylist").Val())
+			require.Equal(t, "1", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, int64(0), rdb.LLen(ctx, "mylist").Val())
+
+			// pop on empty list
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+		})
+	}
+
+	t.Run("LPOP/RPOP against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "notalist", "foo", 0)
+		util.ErrorRegexp(t, rdb.LPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+		util.ErrorRegexp(t, rdb.RPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LPOP/RPOP with wrong number of arguments", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "1", "1").Err(), ".*wrong number of arguments.*")
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "2", "2").Err(), ".*wrong number of arguments.*")
+	})
+
+	t.Run("RPOP/LPOP with the optional count argument", func(t *testing.T) {
+		require.Equal(t, int64(7), rdb.LPush(ctx, "listcount", "aa", "bb", "cc", "dd", "ee", "ff", "gg").Val())
+		require.Equal(t, []string{"gg"}, rdb.LPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"ff", "ee"}, rdb.LPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"aa", "bb"}, rdb.RPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"cc"}, rdb.RPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"dd"}, rdb.LPopCount(ctx, "listcount", 123).Val())
+		util.ErrorRegexp(t, rdb.LPopCount(ctx, "forbatqaz", -123).Err(), ".*ERR.*range.*")
+	})
+
+	t.Run("LPOP/RPOP with the count 0 returns an empty array", func(t *testing.T) {
+		// Make sure we can distinguish between an empty array and a null response
+		rdb.Do(ctx, "readraw", "1")
+		rdb.LPush(ctx, "listcount", "zoro")
+		require.Equal(t, []string{}, rdb.LPopCount(ctx, "listcount", 0).Val())
+		require.Equal(t, []string{}, rdb.RPopCount(ctx, "listcount", 0).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, "", rdb.LPop(ctx, "non_existing_key").Val())
+		require.Equal(t, "", rdb.RPop(ctx, "non_existing_key").Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP with <count> against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 1).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 1).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	listType = "quicklist"
+	for _, num := range []int{250, 500} {
+		t.Run(fmt.Sprintf("Mass RPOP/LPOP - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			sum1 := 0
+			for i := 0; i < num; i++ {
+				rdb.LPush(ctx, "mylist", strconv.Itoa(i))
+				sum1 += i
+			}
+			sum2 := 0
+			for i := 0; i < num/2; i++ {
+				if v1, err := strconv.Atoi(rdb.LPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v1
+				}
+				if v2, err := strconv.Atoi(rdb.RPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v2
+				}
+			}
+			require.Equal(t, sum1, sum2)
+		})
+	}
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("LRANGE basics - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{"1", "2", "3", "4", "5", "6", "7", "8"}, rdb.LRange(ctx, "mylist", 1, -2).Val())
+			require.Equal(t, []string{"7", "8", "9"}, rdb.LRange(ctx, "mylist", -3, -1).Val())
+			require.Equal(t, []string{"4"}, rdb.LRange(ctx, "mylist", 4, 4).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE inverted indexes - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 6, 2).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range indexes including the full list - $type - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large, "1", "2", "3"}, rdb.LRange(ctx, "mylist", -1000, 1000).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range negative end index - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large}, rdb.LRange(ctx, "mylist", 0, -4).Val())
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 0, -5).Val())
+		})
+	}
+
+	t.Run("LRANGE against non existing key", func(t *testing.T) {
+		require.Equal(t, []string{}, rdb.LRange(ctx, "nosuchkey", 0, 1).Val())
+	})
+
+	for listType, large := range largeValue {
+
+		trimList := func(listType string, min, max int64) []string {
+			rdb.Del(ctx, "mylist")
+			createList("mylist", []string{"1", "2", "3", "4", large})
+			rdb.LTrim(ctx, "mylist", min, max)
+			return rdb.LRange(ctx, "mylist", 0, -1).Val()
+		}
+
+		t.Run(fmt.Sprintf("LTRIM basics - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, 0))
+			require.Equal(t, []string{"1", "2"}, trimList(listType, 0, 1))
+			require.Equal(t, []string{"1", "2", "3"}, trimList(listType, 0, 2))
+			require.Equal(t, []string{"2", "3"}, trimList(listType, 1, 2))
+			require.Equal(t, []string{"2", "3", "4", large}, trimList(listType, 1, -1))
+			require.Equal(t, []string{"2", "3", "4"}, trimList(listType, 1, -2))
+			require.Equal(t, []string{"4", large}, trimList(listType, -2, -1))
+			require.Equal(t, []string{large}, trimList(listType, -1, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -5, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -10, 10))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 5))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 10))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM out of range negative end index - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, -5))
+			require.Equal(t, []string{}, trimList(listType, 0, -6))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM lrem elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+			require.Equal(t, int64(2), rdb.LRem(ctx, "myotherlist", 4, "3").Val())
+			require.Equal(t, []string{"2", "4", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LTRIM linsert elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist1", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist1", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist1", 0, -1).Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "myotherlist1", "before", "9", "0").Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "myotherlist1", "before", "4", "0").Val())
+			require.Equal(t, []string{"2", "3", "0", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist1", 0, -1).Val())
+		})
+
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LSET - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"99", "98", large, "96", "95"})
+
+			rdb.LSet(ctx, "mylist", 1, "foo")
+			rdb.LSet(ctx, "mylist", -1, "bar")
+			require.Equal(t, []string{"99", "foo", large, "96", "bar"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LSET out of range index - %s", listType), func(t *testing.T) {
+			util.ErrorRegexp(t, rdb.LSet(ctx, "mylist", 10, "foo").Err(), "ERR.*range.*")
+		})
+	}
+
+	t.Run("LSET against non existing key", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LSet(ctx, "nosuchkey", 10, "foo").Err(), "ERR.*NotFound.*")
+	})
+
+	t.Run("LSET against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "nolist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LSet(ctx, "nolist", 0, "foo").Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, e := range largeValue {
+		t.Run(fmt.Sprintf("LREM remove all the occurrences - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{e, "foo", "bar", "foobar", "foobared", "zap", "bar", "test", "foo"})
+
+			require.Equal(t, int64(2), rdb.LRem(ctx, "mylist", 0, "bar").Val())
+			require.Equal(t, []string{e, "foo", "foobar", "foobared", "zap", "test", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM remove the first occurrence - %s", listType), func(t *testing.T) {
+			require.Equal(t, int64(1), rdb.LRem(ctx, "mylist", 1, "foo").Val())
+			require.Equal(t, []string{e, "foobar", "foobared", "zap", "test", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM remove non existing element - %s", listType), func(t *testing.T) {
+			require.Equal(t, int64(0), rdb.LRem(ctx, "mylist", 1, "nosuchelement").Val())
+			require.Equal(t, []string{e, "foobar", "foobared", "zap", "test", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM starting from tail with negative count - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{e, "foo", "bar", "foobar", "foobared", "zap", "bar", "test", "foo", "foo"})
+
+			require.Equal(t, int64(1), rdb.LRem(ctx, "mylist", -1, "bar").Val())
+			require.Equal(t, []string{e, "foo", "bar", "foobar", "foobared", "zap", "test", "foo", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM starting from tail with negative count (2) - %s", listType), func(t *testing.T) {
+			require.Equal(t, int64(2), rdb.LRem(ctx, "mylist", -2, "foo").Val())
+			require.Equal(t, []string{e, "foo", "bar", "foobar", "foobared", "zap", "test"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM deleting objects that may be int encoded - %s", listType), func(t *testing.T) {
+			createList("myotherlist", e, 1, 2, 3)
+
+			require.Equal(t, int64(1), rdb.LRem(ctx, "myotherlist", 1, 2).Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "myotherlist").Val())

Review Comment:
   ```suggestion
   			require.EqualValues(t, 1, rdb.LRem(ctx, "myotherlist", 1, 2).Val())
   			require.EqualValues(t, 3, rdb.LLen(ctx, "myotherlist").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())

Review Comment:
   ```suggestion
   			require.EqualValues(t, 0, rdb.LLen(ctx, "blist1").Val())
   			require.EqualValues(t, 1, rdb.LLen(ctx, "blist2").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())

Review Comment:
   ```suggestion
   			require.EqualValues(t, 1, rdb.LLen(ctx, "blist1").Val())
   			require.EqualValues(t, 3, rdb.LLen(ctx, "blist2").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LPUSHX, RPUSHX - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{large, "c"})
+
+			require.Equal(t, int64(3), rdb.RPushX(ctx, "xlist", "d").Val())
+			require.Equal(t, int64(4), rdb.LPushX(ctx, "xlist", "a").Val())
+			require.Equal(t, int64(6), rdb.RPushX(ctx, "xlist", "42", "x").Val())
+			require.Equal(t, int64(9), rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())
+			require.Equal(t, []string{"y1", "y2", "y3", "a", large, "c", "d", "42", "x"}, rdb.LRange(ctx, "xlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LINSERT - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{"a", large, "c", "d"})
+
+			require.Equal(t, int64(5), rdb.LInsert(ctx, "xlist", "before", "c", "zz").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(6), rdb.LInsert(ctx, "xlist", "after", "c", "yy").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "xlist", "after", "d", "dd").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "after", "bad", "ddd").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(8), rdb.LInsert(ctx, "xlist", "before", "a", "aa").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "before", "bad", "aaa").Val())

Review Comment:
   ```suggestion
   			require.EqualValues(t, 5, rdb.LInsert(ctx, "xlist", "before", "c", "zz").Val())
   			require.Equal(t, []string{"a", large, "zz", "c", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
   			require.EqualValues(t, 6, rdb.LInsert(ctx, "xlist", "after", "c", "yy").Val())
   			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
   			require.EqualValues(t, 7, rdb.LInsert(ctx, "xlist", "after", "d", "dd").Val())
   			require.EqualValues(t, -1, rdb.LInsert(ctx, "xlist", "after", "bad", "ddd").Val())
   			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
   			require.EqualValues(t, 8, rdb.LInsert(ctx, "xlist", "before", "a", "aa").Val())
   			require.EqualValues(t, -1, rdb.LInsert(ctx, "xlist", "before", "bad", "aaa").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LPUSHX, RPUSHX - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{large, "c"})
+
+			require.Equal(t, int64(3), rdb.RPushX(ctx, "xlist", "d").Val())
+			require.Equal(t, int64(4), rdb.LPushX(ctx, "xlist", "a").Val())
+			require.Equal(t, int64(6), rdb.RPushX(ctx, "xlist", "42", "x").Val())
+			require.Equal(t, int64(9), rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())
+			require.Equal(t, []string{"y1", "y2", "y3", "a", large, "c", "d", "42", "x"}, rdb.LRange(ctx, "xlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LINSERT - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{"a", large, "c", "d"})
+
+			require.Equal(t, int64(5), rdb.LInsert(ctx, "xlist", "before", "c", "zz").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(6), rdb.LInsert(ctx, "xlist", "after", "c", "yy").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "xlist", "after", "d", "dd").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "after", "bad", "ddd").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(8), rdb.LInsert(ctx, "xlist", "before", "a", "aa").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "before", "bad", "aaa").Val())
+			require.Equal(t, []string{"aa", "a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+
+			// check inserting integer encoded value
+			require.Equal(t, int64(9), rdb.LInsert(ctx, "xlist", "before", "aa", "42").Val())
+			require.Equal(t, "42", rdb.LRange(ctx, "xlist", 0, 0).Val()[0])
+		})
+
+	}
+
+	t.Run("LINSERT raise error on bad syntax", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LInsert(ctx, "xlist", "aft3r", "aa", "42").Err(), ".*syntax.*error.*")
+	})
+
+	listType := "quicklist"
+	for _, num := range []int{250, 500} {
+
+		checkNumberedListConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				require.Equal(t, strconv.Itoa(i), rdb.LIndex(ctx, key, int64(i)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-i-1), rdb.LIndex(ctx, key, int64(int(l)-i-1)).Val())
+			}
+		}
+
+		checkRandomAccessConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				rint := rand.Intn(int(l))
+				require.Equal(t, strconv.Itoa(rint), rdb.LIndex(ctx, key, int64(rint)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-rint-1), rdb.LIndex(ctx, key, int64(int(l)-rint-1)).Val())
+			}
+		}
+
+		t.Run(fmt.Sprintf("LINDEX consistency test - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			for i := 0; i < num; i++ {
+				rdb.RPush(ctx, "mylist", strconv.Itoa(i))
+			}
+			checkNumberedListConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("LINDEX random access - %s", listType), func(t *testing.T) {
+			checkRandomAccessConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("Check if list is still ok after a DEBUG RELOAD - %s", listType), func(t *testing.T) {
+			checkNumberedListConsistency("mylist")
+			checkRandomAccessConsistency("mylist")
+		})
+	}
+	t.Run("LLEN against non-list value error", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		rdb.Set(ctx, "mylist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LLen(ctx, "mylist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LLEN against non existing key", func(t *testing.T) {
+		require.Equal(t, int64(0), rdb.LLen(ctx, "not-a-key").Val())
+	})
+
+	t.Run("LINDEX against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LIndex(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LINDEX against non existing key", func(t *testing.T) {
+		require.Equal(t, "", rdb.LIndex(ctx, "not-a-key", 10).Val())
+	})
+
+	t.Run("LPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("RPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.RPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("RPOPLPUSH base case - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist1", "mylist2")
+			createList("mylist1", []string{"a", large, "c", "d"})
+			require.Equal(t, "d", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, []string{"a", large}, rdb.LRange(ctx, "mylist1", 0, -1).Val())
+			require.Equal(t, []string{"c", "d"}, rdb.LRange(ctx, "mylist2", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("RPOPLPUSH with the same list as src and dst - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"a", large, "c"})
+			require.Equal(t, []string{"a", large, "c"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist", "mylist").Val())
+			require.Equal(t, []string{"c", "a", large}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		for otherListType, otherLarge := range largeValue {
+
+			t.Run(fmt.Sprintf("RPOPLPUSH with %s source and existing target %s", listType, otherListType), func(t *testing.T) {
+				createList("srclist", []string{"a", "b", "c", large})
+				createList("dstlist", []string{otherLarge})
+				require.Equal(t, large, rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, "c", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, []string{"a", "b"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+				require.Equal(t, []string{"c", large, otherLarge}, rdb.LRange(ctx, "dstlist", 0, -1).Val())
+			})
+
+		}
+	}
+
+	t.Run("RPOPLPUSH against non existing key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "dstlist").Val())
+	})
+
+	t.Run("RPOPLPUSH against non list src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		rdb.Set(ctx, "srclist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "newlist").Val())

Review Comment:
   ```suggestion
   		require.EqualValues(t, 0, rdb.Exists(ctx, "newlist").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LPUSHX, RPUSHX - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{large, "c"})
+
+			require.Equal(t, int64(3), rdb.RPushX(ctx, "xlist", "d").Val())
+			require.Equal(t, int64(4), rdb.LPushX(ctx, "xlist", "a").Val())
+			require.Equal(t, int64(6), rdb.RPushX(ctx, "xlist", "42", "x").Val())
+			require.Equal(t, int64(9), rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())
+			require.Equal(t, []string{"y1", "y2", "y3", "a", large, "c", "d", "42", "x"}, rdb.LRange(ctx, "xlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LINSERT - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{"a", large, "c", "d"})
+
+			require.Equal(t, int64(5), rdb.LInsert(ctx, "xlist", "before", "c", "zz").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(6), rdb.LInsert(ctx, "xlist", "after", "c", "yy").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "xlist", "after", "d", "dd").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "after", "bad", "ddd").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(8), rdb.LInsert(ctx, "xlist", "before", "a", "aa").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "before", "bad", "aaa").Val())
+			require.Equal(t, []string{"aa", "a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+
+			// check inserting integer encoded value
+			require.Equal(t, int64(9), rdb.LInsert(ctx, "xlist", "before", "aa", "42").Val())
+			require.Equal(t, "42", rdb.LRange(ctx, "xlist", 0, 0).Val()[0])
+		})
+
+	}
+
+	t.Run("LINSERT raise error on bad syntax", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LInsert(ctx, "xlist", "aft3r", "aa", "42").Err(), ".*syntax.*error.*")
+	})
+
+	listType := "quicklist"
+	for _, num := range []int{250, 500} {
+
+		checkNumberedListConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				require.Equal(t, strconv.Itoa(i), rdb.LIndex(ctx, key, int64(i)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-i-1), rdb.LIndex(ctx, key, int64(int(l)-i-1)).Val())
+			}
+		}
+
+		checkRandomAccessConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				rint := rand.Intn(int(l))
+				require.Equal(t, strconv.Itoa(rint), rdb.LIndex(ctx, key, int64(rint)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-rint-1), rdb.LIndex(ctx, key, int64(int(l)-rint-1)).Val())
+			}
+		}
+
+		t.Run(fmt.Sprintf("LINDEX consistency test - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			for i := 0; i < num; i++ {
+				rdb.RPush(ctx, "mylist", strconv.Itoa(i))
+			}
+			checkNumberedListConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("LINDEX random access - %s", listType), func(t *testing.T) {
+			checkRandomAccessConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("Check if list is still ok after a DEBUG RELOAD - %s", listType), func(t *testing.T) {
+			checkNumberedListConsistency("mylist")
+			checkRandomAccessConsistency("mylist")
+		})
+	}
+	t.Run("LLEN against non-list value error", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		rdb.Set(ctx, "mylist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LLen(ctx, "mylist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LLEN against non existing key", func(t *testing.T) {
+		require.Equal(t, int64(0), rdb.LLen(ctx, "not-a-key").Val())
+	})
+
+	t.Run("LINDEX against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LIndex(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LINDEX against non existing key", func(t *testing.T) {
+		require.Equal(t, "", rdb.LIndex(ctx, "not-a-key", 10).Val())
+	})
+
+	t.Run("LPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("RPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.RPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("RPOPLPUSH base case - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist1", "mylist2")
+			createList("mylist1", []string{"a", large, "c", "d"})
+			require.Equal(t, "d", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, []string{"a", large}, rdb.LRange(ctx, "mylist1", 0, -1).Val())
+			require.Equal(t, []string{"c", "d"}, rdb.LRange(ctx, "mylist2", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("RPOPLPUSH with the same list as src and dst - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"a", large, "c"})
+			require.Equal(t, []string{"a", large, "c"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist", "mylist").Val())
+			require.Equal(t, []string{"c", "a", large}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		for otherListType, otherLarge := range largeValue {
+
+			t.Run(fmt.Sprintf("RPOPLPUSH with %s source and existing target %s", listType, otherListType), func(t *testing.T) {
+				createList("srclist", []string{"a", "b", "c", large})
+				createList("dstlist", []string{otherLarge})
+				require.Equal(t, large, rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, "c", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, []string{"a", "b"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+				require.Equal(t, []string{"c", large, otherLarge}, rdb.LRange(ctx, "dstlist", 0, -1).Val())
+			})
+
+		}
+	}
+
+	t.Run("RPOPLPUSH against non existing key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "dstlist").Val())
+	})
+
+	t.Run("RPOPLPUSH against non list src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		rdb.Set(ctx, "srclist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "newlist").Val())
+
+	})
+
+	t.Run("RPOPLPUSH against non list dst key", func(t *testing.T) {
+		createList("srclist", []string{"a", "b", "c", "d"})
+
+		rdb.Set(ctx, "dstlist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "dstlist").Val())
+		require.Equal(t, []string{"a", "b", "c", "d"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+	})
+
+	t.Run("RPOPLPUSH against non existing src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("Basic LPOP/RPOP - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2"})
+
+			require.Equal(t, large, rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "2", rdb.RPop(ctx, "mylist").Val())
+			require.Equal(t, "1", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, int64(0), rdb.LLen(ctx, "mylist").Val())
+
+			// pop on empty list
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+		})
+	}
+
+	t.Run("LPOP/RPOP against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "notalist", "foo", 0)
+		util.ErrorRegexp(t, rdb.LPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+		util.ErrorRegexp(t, rdb.RPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LPOP/RPOP with wrong number of arguments", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "1", "1").Err(), ".*wrong number of arguments.*")
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "2", "2").Err(), ".*wrong number of arguments.*")
+	})
+
+	t.Run("RPOP/LPOP with the optional count argument", func(t *testing.T) {
+		require.Equal(t, int64(7), rdb.LPush(ctx, "listcount", "aa", "bb", "cc", "dd", "ee", "ff", "gg").Val())
+		require.Equal(t, []string{"gg"}, rdb.LPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"ff", "ee"}, rdb.LPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"aa", "bb"}, rdb.RPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"cc"}, rdb.RPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"dd"}, rdb.LPopCount(ctx, "listcount", 123).Val())
+		util.ErrorRegexp(t, rdb.LPopCount(ctx, "forbatqaz", -123).Err(), ".*ERR.*range.*")
+	})
+
+	t.Run("LPOP/RPOP with the count 0 returns an empty array", func(t *testing.T) {
+		// Make sure we can distinguish between an empty array and a null response
+		rdb.Do(ctx, "readraw", "1")
+		rdb.LPush(ctx, "listcount", "zoro")
+		require.Equal(t, []string{}, rdb.LPopCount(ctx, "listcount", 0).Val())
+		require.Equal(t, []string{}, rdb.RPopCount(ctx, "listcount", 0).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, "", rdb.LPop(ctx, "non_existing_key").Val())
+		require.Equal(t, "", rdb.RPop(ctx, "non_existing_key").Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP with <count> against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 1).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 1).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	listType = "quicklist"
+	for _, num := range []int{250, 500} {
+		t.Run(fmt.Sprintf("Mass RPOP/LPOP - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			sum1 := 0
+			for i := 0; i < num; i++ {
+				rdb.LPush(ctx, "mylist", strconv.Itoa(i))
+				sum1 += i
+			}
+			sum2 := 0
+			for i := 0; i < num/2; i++ {
+				if v1, err := strconv.Atoi(rdb.LPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v1
+				}
+				if v2, err := strconv.Atoi(rdb.RPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v2
+				}
+			}
+			require.Equal(t, sum1, sum2)
+		})
+	}
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("LRANGE basics - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{"1", "2", "3", "4", "5", "6", "7", "8"}, rdb.LRange(ctx, "mylist", 1, -2).Val())
+			require.Equal(t, []string{"7", "8", "9"}, rdb.LRange(ctx, "mylist", -3, -1).Val())
+			require.Equal(t, []string{"4"}, rdb.LRange(ctx, "mylist", 4, 4).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE inverted indexes - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 6, 2).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range indexes including the full list - $type - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large, "1", "2", "3"}, rdb.LRange(ctx, "mylist", -1000, 1000).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range negative end index - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large}, rdb.LRange(ctx, "mylist", 0, -4).Val())
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 0, -5).Val())
+		})
+	}
+
+	t.Run("LRANGE against non existing key", func(t *testing.T) {
+		require.Equal(t, []string{}, rdb.LRange(ctx, "nosuchkey", 0, 1).Val())
+	})
+
+	for listType, large := range largeValue {
+
+		trimList := func(listType string, min, max int64) []string {
+			rdb.Del(ctx, "mylist")
+			createList("mylist", []string{"1", "2", "3", "4", large})
+			rdb.LTrim(ctx, "mylist", min, max)
+			return rdb.LRange(ctx, "mylist", 0, -1).Val()
+		}
+
+		t.Run(fmt.Sprintf("LTRIM basics - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, 0))
+			require.Equal(t, []string{"1", "2"}, trimList(listType, 0, 1))
+			require.Equal(t, []string{"1", "2", "3"}, trimList(listType, 0, 2))
+			require.Equal(t, []string{"2", "3"}, trimList(listType, 1, 2))
+			require.Equal(t, []string{"2", "3", "4", large}, trimList(listType, 1, -1))
+			require.Equal(t, []string{"2", "3", "4"}, trimList(listType, 1, -2))
+			require.Equal(t, []string{"4", large}, trimList(listType, -2, -1))
+			require.Equal(t, []string{large}, trimList(listType, -1, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -5, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -10, 10))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 5))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 10))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM out of range negative end index - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, -5))
+			require.Equal(t, []string{}, trimList(listType, 0, -6))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM lrem elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+			require.Equal(t, int64(2), rdb.LRem(ctx, "myotherlist", 4, "3").Val())
+			require.Equal(t, []string{"2", "4", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LTRIM linsert elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist1", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist1", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist1", 0, -1).Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "myotherlist1", "before", "9", "0").Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "myotherlist1", "before", "4", "0").Val())

Review Comment:
   ```suggestion
   			require.EqualValues(t, -1, rdb.LInsert(ctx, "myotherlist1", "before", "9", "0").Val())
   			require.EqualValues(t, 7, rdb.LInsert(ctx, "myotherlist1", "before", "4", "0").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LPUSHX, RPUSHX - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{large, "c"})
+
+			require.Equal(t, int64(3), rdb.RPushX(ctx, "xlist", "d").Val())
+			require.Equal(t, int64(4), rdb.LPushX(ctx, "xlist", "a").Val())
+			require.Equal(t, int64(6), rdb.RPushX(ctx, "xlist", "42", "x").Val())
+			require.Equal(t, int64(9), rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())
+			require.Equal(t, []string{"y1", "y2", "y3", "a", large, "c", "d", "42", "x"}, rdb.LRange(ctx, "xlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LINSERT - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{"a", large, "c", "d"})
+
+			require.Equal(t, int64(5), rdb.LInsert(ctx, "xlist", "before", "c", "zz").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(6), rdb.LInsert(ctx, "xlist", "after", "c", "yy").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "xlist", "after", "d", "dd").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "after", "bad", "ddd").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(8), rdb.LInsert(ctx, "xlist", "before", "a", "aa").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "before", "bad", "aaa").Val())
+			require.Equal(t, []string{"aa", "a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+
+			// check inserting integer encoded value
+			require.Equal(t, int64(9), rdb.LInsert(ctx, "xlist", "before", "aa", "42").Val())
+			require.Equal(t, "42", rdb.LRange(ctx, "xlist", 0, 0).Val()[0])
+		})
+
+	}
+
+	t.Run("LINSERT raise error on bad syntax", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LInsert(ctx, "xlist", "aft3r", "aa", "42").Err(), ".*syntax.*error.*")
+	})
+
+	listType := "quicklist"
+	for _, num := range []int{250, 500} {
+
+		checkNumberedListConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				require.Equal(t, strconv.Itoa(i), rdb.LIndex(ctx, key, int64(i)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-i-1), rdb.LIndex(ctx, key, int64(int(l)-i-1)).Val())
+			}
+		}
+
+		checkRandomAccessConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				rint := rand.Intn(int(l))
+				require.Equal(t, strconv.Itoa(rint), rdb.LIndex(ctx, key, int64(rint)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-rint-1), rdb.LIndex(ctx, key, int64(int(l)-rint-1)).Val())
+			}
+		}
+
+		t.Run(fmt.Sprintf("LINDEX consistency test - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			for i := 0; i < num; i++ {
+				rdb.RPush(ctx, "mylist", strconv.Itoa(i))
+			}
+			checkNumberedListConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("LINDEX random access - %s", listType), func(t *testing.T) {
+			checkRandomAccessConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("Check if list is still ok after a DEBUG RELOAD - %s", listType), func(t *testing.T) {
+			checkNumberedListConsistency("mylist")
+			checkRandomAccessConsistency("mylist")
+		})
+	}
+	t.Run("LLEN against non-list value error", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		rdb.Set(ctx, "mylist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LLen(ctx, "mylist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LLEN against non existing key", func(t *testing.T) {
+		require.Equal(t, int64(0), rdb.LLen(ctx, "not-a-key").Val())
+	})
+
+	t.Run("LINDEX against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LIndex(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LINDEX against non existing key", func(t *testing.T) {
+		require.Equal(t, "", rdb.LIndex(ctx, "not-a-key", 10).Val())
+	})
+
+	t.Run("LPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("RPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.RPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("RPOPLPUSH base case - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist1", "mylist2")
+			createList("mylist1", []string{"a", large, "c", "d"})
+			require.Equal(t, "d", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, []string{"a", large}, rdb.LRange(ctx, "mylist1", 0, -1).Val())
+			require.Equal(t, []string{"c", "d"}, rdb.LRange(ctx, "mylist2", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("RPOPLPUSH with the same list as src and dst - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"a", large, "c"})
+			require.Equal(t, []string{"a", large, "c"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist", "mylist").Val())
+			require.Equal(t, []string{"c", "a", large}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		for otherListType, otherLarge := range largeValue {
+
+			t.Run(fmt.Sprintf("RPOPLPUSH with %s source and existing target %s", listType, otherListType), func(t *testing.T) {
+				createList("srclist", []string{"a", "b", "c", large})
+				createList("dstlist", []string{otherLarge})
+				require.Equal(t, large, rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, "c", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, []string{"a", "b"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+				require.Equal(t, []string{"c", large, otherLarge}, rdb.LRange(ctx, "dstlist", 0, -1).Val())
+			})
+
+		}
+	}
+
+	t.Run("RPOPLPUSH against non existing key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "dstlist").Val())

Review Comment:
   ```suggestion
   		require.EqualValues(t, 0, rdb.Exists(ctx, "srclist").Val())
   		require.EqualValues(t, 0, rdb.Exists(ctx, "dstlist").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LPUSHX, RPUSHX - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{large, "c"})
+
+			require.Equal(t, int64(3), rdb.RPushX(ctx, "xlist", "d").Val())
+			require.Equal(t, int64(4), rdb.LPushX(ctx, "xlist", "a").Val())
+			require.Equal(t, int64(6), rdb.RPushX(ctx, "xlist", "42", "x").Val())
+			require.Equal(t, int64(9), rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())
+			require.Equal(t, []string{"y1", "y2", "y3", "a", large, "c", "d", "42", "x"}, rdb.LRange(ctx, "xlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LINSERT - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{"a", large, "c", "d"})
+
+			require.Equal(t, int64(5), rdb.LInsert(ctx, "xlist", "before", "c", "zz").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(6), rdb.LInsert(ctx, "xlist", "after", "c", "yy").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "xlist", "after", "d", "dd").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "after", "bad", "ddd").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(8), rdb.LInsert(ctx, "xlist", "before", "a", "aa").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "before", "bad", "aaa").Val())
+			require.Equal(t, []string{"aa", "a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+
+			// check inserting integer encoded value
+			require.Equal(t, int64(9), rdb.LInsert(ctx, "xlist", "before", "aa", "42").Val())
+			require.Equal(t, "42", rdb.LRange(ctx, "xlist", 0, 0).Val()[0])
+		})
+
+	}
+
+	t.Run("LINSERT raise error on bad syntax", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LInsert(ctx, "xlist", "aft3r", "aa", "42").Err(), ".*syntax.*error.*")
+	})
+
+	listType := "quicklist"
+	for _, num := range []int{250, 500} {
+
+		checkNumberedListConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				require.Equal(t, strconv.Itoa(i), rdb.LIndex(ctx, key, int64(i)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-i-1), rdb.LIndex(ctx, key, int64(int(l)-i-1)).Val())
+			}
+		}
+
+		checkRandomAccessConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				rint := rand.Intn(int(l))
+				require.Equal(t, strconv.Itoa(rint), rdb.LIndex(ctx, key, int64(rint)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-rint-1), rdb.LIndex(ctx, key, int64(int(l)-rint-1)).Val())
+			}
+		}
+
+		t.Run(fmt.Sprintf("LINDEX consistency test - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			for i := 0; i < num; i++ {
+				rdb.RPush(ctx, "mylist", strconv.Itoa(i))
+			}
+			checkNumberedListConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("LINDEX random access - %s", listType), func(t *testing.T) {
+			checkRandomAccessConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("Check if list is still ok after a DEBUG RELOAD - %s", listType), func(t *testing.T) {
+			checkNumberedListConsistency("mylist")
+			checkRandomAccessConsistency("mylist")
+		})
+	}
+	t.Run("LLEN against non-list value error", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		rdb.Set(ctx, "mylist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LLen(ctx, "mylist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LLEN against non existing key", func(t *testing.T) {
+		require.Equal(t, int64(0), rdb.LLen(ctx, "not-a-key").Val())
+	})
+
+	t.Run("LINDEX against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LIndex(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LINDEX against non existing key", func(t *testing.T) {
+		require.Equal(t, "", rdb.LIndex(ctx, "not-a-key", 10).Val())
+	})
+
+	t.Run("LPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("RPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.RPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("RPOPLPUSH base case - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist1", "mylist2")
+			createList("mylist1", []string{"a", large, "c", "d"})
+			require.Equal(t, "d", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, []string{"a", large}, rdb.LRange(ctx, "mylist1", 0, -1).Val())
+			require.Equal(t, []string{"c", "d"}, rdb.LRange(ctx, "mylist2", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("RPOPLPUSH with the same list as src and dst - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"a", large, "c"})
+			require.Equal(t, []string{"a", large, "c"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist", "mylist").Val())
+			require.Equal(t, []string{"c", "a", large}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		for otherListType, otherLarge := range largeValue {
+
+			t.Run(fmt.Sprintf("RPOPLPUSH with %s source and existing target %s", listType, otherListType), func(t *testing.T) {
+				createList("srclist", []string{"a", "b", "c", large})
+				createList("dstlist", []string{otherLarge})
+				require.Equal(t, large, rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, "c", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, []string{"a", "b"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+				require.Equal(t, []string{"c", large, otherLarge}, rdb.LRange(ctx, "dstlist", 0, -1).Val())
+			})
+
+		}
+	}
+
+	t.Run("RPOPLPUSH against non existing key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "dstlist").Val())
+	})
+
+	t.Run("RPOPLPUSH against non list src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		rdb.Set(ctx, "srclist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "newlist").Val())
+
+	})
+
+	t.Run("RPOPLPUSH against non list dst key", func(t *testing.T) {
+		createList("srclist", []string{"a", "b", "c", "d"})
+
+		rdb.Set(ctx, "dstlist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "dstlist").Val())
+		require.Equal(t, []string{"a", "b", "c", "d"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+	})
+
+	t.Run("RPOPLPUSH against non existing src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("Basic LPOP/RPOP - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2"})
+
+			require.Equal(t, large, rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "2", rdb.RPop(ctx, "mylist").Val())
+			require.Equal(t, "1", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, int64(0), rdb.LLen(ctx, "mylist").Val())
+
+			// pop on empty list
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+		})
+	}
+
+	t.Run("LPOP/RPOP against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "notalist", "foo", 0)
+		util.ErrorRegexp(t, rdb.LPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+		util.ErrorRegexp(t, rdb.RPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LPOP/RPOP with wrong number of arguments", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "1", "1").Err(), ".*wrong number of arguments.*")
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "2", "2").Err(), ".*wrong number of arguments.*")
+	})
+
+	t.Run("RPOP/LPOP with the optional count argument", func(t *testing.T) {
+		require.Equal(t, int64(7), rdb.LPush(ctx, "listcount", "aa", "bb", "cc", "dd", "ee", "ff", "gg").Val())
+		require.Equal(t, []string{"gg"}, rdb.LPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"ff", "ee"}, rdb.LPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"aa", "bb"}, rdb.RPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"cc"}, rdb.RPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"dd"}, rdb.LPopCount(ctx, "listcount", 123).Val())
+		util.ErrorRegexp(t, rdb.LPopCount(ctx, "forbatqaz", -123).Err(), ".*ERR.*range.*")
+	})
+
+	t.Run("LPOP/RPOP with the count 0 returns an empty array", func(t *testing.T) {
+		// Make sure we can distinguish between an empty array and a null response
+		rdb.Do(ctx, "readraw", "1")
+		rdb.LPush(ctx, "listcount", "zoro")
+		require.Equal(t, []string{}, rdb.LPopCount(ctx, "listcount", 0).Val())
+		require.Equal(t, []string{}, rdb.RPopCount(ctx, "listcount", 0).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, "", rdb.LPop(ctx, "non_existing_key").Val())
+		require.Equal(t, "", rdb.RPop(ctx, "non_existing_key").Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP with <count> against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 1).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 1).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	listType = "quicklist"
+	for _, num := range []int{250, 500} {
+		t.Run(fmt.Sprintf("Mass RPOP/LPOP - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			sum1 := 0
+			for i := 0; i < num; i++ {
+				rdb.LPush(ctx, "mylist", strconv.Itoa(i))
+				sum1 += i
+			}
+			sum2 := 0
+			for i := 0; i < num/2; i++ {
+				if v1, err := strconv.Atoi(rdb.LPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v1
+				}
+				if v2, err := strconv.Atoi(rdb.RPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v2
+				}
+			}
+			require.Equal(t, sum1, sum2)
+		})
+	}
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("LRANGE basics - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{"1", "2", "3", "4", "5", "6", "7", "8"}, rdb.LRange(ctx, "mylist", 1, -2).Val())
+			require.Equal(t, []string{"7", "8", "9"}, rdb.LRange(ctx, "mylist", -3, -1).Val())
+			require.Equal(t, []string{"4"}, rdb.LRange(ctx, "mylist", 4, 4).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE inverted indexes - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 6, 2).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range indexes including the full list - $type - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large, "1", "2", "3"}, rdb.LRange(ctx, "mylist", -1000, 1000).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range negative end index - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large}, rdb.LRange(ctx, "mylist", 0, -4).Val())
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 0, -5).Val())
+		})
+	}
+
+	t.Run("LRANGE against non existing key", func(t *testing.T) {
+		require.Equal(t, []string{}, rdb.LRange(ctx, "nosuchkey", 0, 1).Val())
+	})
+
+	for listType, large := range largeValue {
+
+		trimList := func(listType string, min, max int64) []string {
+			rdb.Del(ctx, "mylist")
+			createList("mylist", []string{"1", "2", "3", "4", large})
+			rdb.LTrim(ctx, "mylist", min, max)
+			return rdb.LRange(ctx, "mylist", 0, -1).Val()
+		}
+
+		t.Run(fmt.Sprintf("LTRIM basics - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, 0))
+			require.Equal(t, []string{"1", "2"}, trimList(listType, 0, 1))
+			require.Equal(t, []string{"1", "2", "3"}, trimList(listType, 0, 2))
+			require.Equal(t, []string{"2", "3"}, trimList(listType, 1, 2))
+			require.Equal(t, []string{"2", "3", "4", large}, trimList(listType, 1, -1))
+			require.Equal(t, []string{"2", "3", "4"}, trimList(listType, 1, -2))
+			require.Equal(t, []string{"4", large}, trimList(listType, -2, -1))
+			require.Equal(t, []string{large}, trimList(listType, -1, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -5, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -10, 10))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 5))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 10))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM out of range negative end index - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, -5))
+			require.Equal(t, []string{}, trimList(listType, 0, -6))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM lrem elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+			require.Equal(t, int64(2), rdb.LRem(ctx, "myotherlist", 4, "3").Val())

Review Comment:
   ```suggestion
   			require.EqualValues(t, 2, rdb.LRem(ctx, "myotherlist", 4, "3").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LPUSHX, RPUSHX - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{large, "c"})
+
+			require.Equal(t, int64(3), rdb.RPushX(ctx, "xlist", "d").Val())
+			require.Equal(t, int64(4), rdb.LPushX(ctx, "xlist", "a").Val())
+			require.Equal(t, int64(6), rdb.RPushX(ctx, "xlist", "42", "x").Val())
+			require.Equal(t, int64(9), rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())
+			require.Equal(t, []string{"y1", "y2", "y3", "a", large, "c", "d", "42", "x"}, rdb.LRange(ctx, "xlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LINSERT - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{"a", large, "c", "d"})
+
+			require.Equal(t, int64(5), rdb.LInsert(ctx, "xlist", "before", "c", "zz").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(6), rdb.LInsert(ctx, "xlist", "after", "c", "yy").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "xlist", "after", "d", "dd").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "after", "bad", "ddd").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(8), rdb.LInsert(ctx, "xlist", "before", "a", "aa").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "before", "bad", "aaa").Val())
+			require.Equal(t, []string{"aa", "a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+
+			// check inserting integer encoded value
+			require.Equal(t, int64(9), rdb.LInsert(ctx, "xlist", "before", "aa", "42").Val())
+			require.Equal(t, "42", rdb.LRange(ctx, "xlist", 0, 0).Val()[0])
+		})
+
+	}
+
+	t.Run("LINSERT raise error on bad syntax", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LInsert(ctx, "xlist", "aft3r", "aa", "42").Err(), ".*syntax.*error.*")
+	})
+
+	listType := "quicklist"
+	for _, num := range []int{250, 500} {
+
+		checkNumberedListConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				require.Equal(t, strconv.Itoa(i), rdb.LIndex(ctx, key, int64(i)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-i-1), rdb.LIndex(ctx, key, int64(int(l)-i-1)).Val())
+			}
+		}
+
+		checkRandomAccessConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				rint := rand.Intn(int(l))
+				require.Equal(t, strconv.Itoa(rint), rdb.LIndex(ctx, key, int64(rint)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-rint-1), rdb.LIndex(ctx, key, int64(int(l)-rint-1)).Val())
+			}
+		}
+
+		t.Run(fmt.Sprintf("LINDEX consistency test - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			for i := 0; i < num; i++ {
+				rdb.RPush(ctx, "mylist", strconv.Itoa(i))
+			}
+			checkNumberedListConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("LINDEX random access - %s", listType), func(t *testing.T) {
+			checkRandomAccessConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("Check if list is still ok after a DEBUG RELOAD - %s", listType), func(t *testing.T) {
+			checkNumberedListConsistency("mylist")
+			checkRandomAccessConsistency("mylist")
+		})
+	}
+	t.Run("LLEN against non-list value error", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		rdb.Set(ctx, "mylist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LLen(ctx, "mylist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LLEN against non existing key", func(t *testing.T) {
+		require.Equal(t, int64(0), rdb.LLen(ctx, "not-a-key").Val())
+	})
+
+	t.Run("LINDEX against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LIndex(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LINDEX against non existing key", func(t *testing.T) {
+		require.Equal(t, "", rdb.LIndex(ctx, "not-a-key", 10).Val())
+	})
+
+	t.Run("LPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("RPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.RPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("RPOPLPUSH base case - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist1", "mylist2")
+			createList("mylist1", []string{"a", large, "c", "d"})
+			require.Equal(t, "d", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, []string{"a", large}, rdb.LRange(ctx, "mylist1", 0, -1).Val())
+			require.Equal(t, []string{"c", "d"}, rdb.LRange(ctx, "mylist2", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("RPOPLPUSH with the same list as src and dst - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"a", large, "c"})
+			require.Equal(t, []string{"a", large, "c"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist", "mylist").Val())
+			require.Equal(t, []string{"c", "a", large}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		for otherListType, otherLarge := range largeValue {
+
+			t.Run(fmt.Sprintf("RPOPLPUSH with %s source and existing target %s", listType, otherListType), func(t *testing.T) {
+				createList("srclist", []string{"a", "b", "c", large})
+				createList("dstlist", []string{otherLarge})
+				require.Equal(t, large, rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, "c", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, []string{"a", "b"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+				require.Equal(t, []string{"c", large, otherLarge}, rdb.LRange(ctx, "dstlist", 0, -1).Val())
+			})
+
+		}
+	}
+
+	t.Run("RPOPLPUSH against non existing key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "dstlist").Val())
+	})
+
+	t.Run("RPOPLPUSH against non list src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		rdb.Set(ctx, "srclist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "newlist").Val())
+
+	})
+
+	t.Run("RPOPLPUSH against non list dst key", func(t *testing.T) {
+		createList("srclist", []string{"a", "b", "c", "d"})
+
+		rdb.Set(ctx, "dstlist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "dstlist").Val())
+		require.Equal(t, []string{"a", "b", "c", "d"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+	})
+
+	t.Run("RPOPLPUSH against non existing src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("Basic LPOP/RPOP - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2"})
+
+			require.Equal(t, large, rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "2", rdb.RPop(ctx, "mylist").Val())
+			require.Equal(t, "1", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, int64(0), rdb.LLen(ctx, "mylist").Val())
+
+			// pop on empty list
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+		})
+	}
+
+	t.Run("LPOP/RPOP against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "notalist", "foo", 0)
+		util.ErrorRegexp(t, rdb.LPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+		util.ErrorRegexp(t, rdb.RPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LPOP/RPOP with wrong number of arguments", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "1", "1").Err(), ".*wrong number of arguments.*")
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "2", "2").Err(), ".*wrong number of arguments.*")
+	})
+
+	t.Run("RPOP/LPOP with the optional count argument", func(t *testing.T) {
+		require.Equal(t, int64(7), rdb.LPush(ctx, "listcount", "aa", "bb", "cc", "dd", "ee", "ff", "gg").Val())
+		require.Equal(t, []string{"gg"}, rdb.LPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"ff", "ee"}, rdb.LPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"aa", "bb"}, rdb.RPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"cc"}, rdb.RPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"dd"}, rdb.LPopCount(ctx, "listcount", 123).Val())
+		util.ErrorRegexp(t, rdb.LPopCount(ctx, "forbatqaz", -123).Err(), ".*ERR.*range.*")
+	})
+
+	t.Run("LPOP/RPOP with the count 0 returns an empty array", func(t *testing.T) {
+		// Make sure we can distinguish between an empty array and a null response
+		rdb.Do(ctx, "readraw", "1")
+		rdb.LPush(ctx, "listcount", "zoro")
+		require.Equal(t, []string{}, rdb.LPopCount(ctx, "listcount", 0).Val())
+		require.Equal(t, []string{}, rdb.RPopCount(ctx, "listcount", 0).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, "", rdb.LPop(ctx, "non_existing_key").Val())
+		require.Equal(t, "", rdb.RPop(ctx, "non_existing_key").Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP with <count> against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 1).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 1).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	listType = "quicklist"
+	for _, num := range []int{250, 500} {
+		t.Run(fmt.Sprintf("Mass RPOP/LPOP - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			sum1 := 0
+			for i := 0; i < num; i++ {
+				rdb.LPush(ctx, "mylist", strconv.Itoa(i))
+				sum1 += i
+			}
+			sum2 := 0
+			for i := 0; i < num/2; i++ {
+				if v1, err := strconv.Atoi(rdb.LPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v1
+				}
+				if v2, err := strconv.Atoi(rdb.RPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v2
+				}
+			}
+			require.Equal(t, sum1, sum2)
+		})
+	}
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("LRANGE basics - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{"1", "2", "3", "4", "5", "6", "7", "8"}, rdb.LRange(ctx, "mylist", 1, -2).Val())
+			require.Equal(t, []string{"7", "8", "9"}, rdb.LRange(ctx, "mylist", -3, -1).Val())
+			require.Equal(t, []string{"4"}, rdb.LRange(ctx, "mylist", 4, 4).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE inverted indexes - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 6, 2).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range indexes including the full list - $type - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large, "1", "2", "3"}, rdb.LRange(ctx, "mylist", -1000, 1000).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range negative end index - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large}, rdb.LRange(ctx, "mylist", 0, -4).Val())
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 0, -5).Val())
+		})
+	}
+
+	t.Run("LRANGE against non existing key", func(t *testing.T) {
+		require.Equal(t, []string{}, rdb.LRange(ctx, "nosuchkey", 0, 1).Val())
+	})
+
+	for listType, large := range largeValue {
+
+		trimList := func(listType string, min, max int64) []string {
+			rdb.Del(ctx, "mylist")
+			createList("mylist", []string{"1", "2", "3", "4", large})
+			rdb.LTrim(ctx, "mylist", min, max)
+			return rdb.LRange(ctx, "mylist", 0, -1).Val()
+		}
+
+		t.Run(fmt.Sprintf("LTRIM basics - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, 0))
+			require.Equal(t, []string{"1", "2"}, trimList(listType, 0, 1))
+			require.Equal(t, []string{"1", "2", "3"}, trimList(listType, 0, 2))
+			require.Equal(t, []string{"2", "3"}, trimList(listType, 1, 2))
+			require.Equal(t, []string{"2", "3", "4", large}, trimList(listType, 1, -1))
+			require.Equal(t, []string{"2", "3", "4"}, trimList(listType, 1, -2))
+			require.Equal(t, []string{"4", large}, trimList(listType, -2, -1))
+			require.Equal(t, []string{large}, trimList(listType, -1, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -5, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -10, 10))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 5))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 10))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM out of range negative end index - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, -5))
+			require.Equal(t, []string{}, trimList(listType, 0, -6))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM lrem elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+			require.Equal(t, int64(2), rdb.LRem(ctx, "myotherlist", 4, "3").Val())
+			require.Equal(t, []string{"2", "4", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LTRIM linsert elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist1", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist1", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist1", 0, -1).Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "myotherlist1", "before", "9", "0").Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "myotherlist1", "before", "4", "0").Val())
+			require.Equal(t, []string{"2", "3", "0", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist1", 0, -1).Val())
+		})
+
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LSET - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"99", "98", large, "96", "95"})
+
+			rdb.LSet(ctx, "mylist", 1, "foo")
+			rdb.LSet(ctx, "mylist", -1, "bar")
+			require.Equal(t, []string{"99", "foo", large, "96", "bar"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LSET out of range index - %s", listType), func(t *testing.T) {
+			util.ErrorRegexp(t, rdb.LSet(ctx, "mylist", 10, "foo").Err(), "ERR.*range.*")
+		})
+	}
+
+	t.Run("LSET against non existing key", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LSet(ctx, "nosuchkey", 10, "foo").Err(), "ERR.*NotFound.*")
+	})
+
+	t.Run("LSET against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "nolist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LSet(ctx, "nolist", 0, "foo").Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, e := range largeValue {
+		t.Run(fmt.Sprintf("LREM remove all the occurrences - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{e, "foo", "bar", "foobar", "foobared", "zap", "bar", "test", "foo"})
+
+			require.Equal(t, int64(2), rdb.LRem(ctx, "mylist", 0, "bar").Val())
+			require.Equal(t, []string{e, "foo", "foobar", "foobared", "zap", "test", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM remove the first occurrence - %s", listType), func(t *testing.T) {
+			require.Equal(t, int64(1), rdb.LRem(ctx, "mylist", 1, "foo").Val())
+			require.Equal(t, []string{e, "foobar", "foobared", "zap", "test", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM remove non existing element - %s", listType), func(t *testing.T) {
+			require.Equal(t, int64(0), rdb.LRem(ctx, "mylist", 1, "nosuchelement").Val())

Review Comment:
   ```suggestion
   			require.EqualValues(t, 0, rdb.LRem(ctx, "mylist", 1, "nosuchelement").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LPUSHX, RPUSHX - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{large, "c"})
+
+			require.Equal(t, int64(3), rdb.RPushX(ctx, "xlist", "d").Val())
+			require.Equal(t, int64(4), rdb.LPushX(ctx, "xlist", "a").Val())
+			require.Equal(t, int64(6), rdb.RPushX(ctx, "xlist", "42", "x").Val())
+			require.Equal(t, int64(9), rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())
+			require.Equal(t, []string{"y1", "y2", "y3", "a", large, "c", "d", "42", "x"}, rdb.LRange(ctx, "xlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LINSERT - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{"a", large, "c", "d"})
+
+			require.Equal(t, int64(5), rdb.LInsert(ctx, "xlist", "before", "c", "zz").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(6), rdb.LInsert(ctx, "xlist", "after", "c", "yy").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "xlist", "after", "d", "dd").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "after", "bad", "ddd").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(8), rdb.LInsert(ctx, "xlist", "before", "a", "aa").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "before", "bad", "aaa").Val())
+			require.Equal(t, []string{"aa", "a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+
+			// check inserting integer encoded value
+			require.Equal(t, int64(9), rdb.LInsert(ctx, "xlist", "before", "aa", "42").Val())
+			require.Equal(t, "42", rdb.LRange(ctx, "xlist", 0, 0).Val()[0])
+		})
+
+	}
+
+	t.Run("LINSERT raise error on bad syntax", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LInsert(ctx, "xlist", "aft3r", "aa", "42").Err(), ".*syntax.*error.*")
+	})
+
+	listType := "quicklist"
+	for _, num := range []int{250, 500} {
+
+		checkNumberedListConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				require.Equal(t, strconv.Itoa(i), rdb.LIndex(ctx, key, int64(i)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-i-1), rdb.LIndex(ctx, key, int64(int(l)-i-1)).Val())
+			}
+		}
+
+		checkRandomAccessConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				rint := rand.Intn(int(l))
+				require.Equal(t, strconv.Itoa(rint), rdb.LIndex(ctx, key, int64(rint)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-rint-1), rdb.LIndex(ctx, key, int64(int(l)-rint-1)).Val())
+			}
+		}
+
+		t.Run(fmt.Sprintf("LINDEX consistency test - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			for i := 0; i < num; i++ {
+				rdb.RPush(ctx, "mylist", strconv.Itoa(i))
+			}
+			checkNumberedListConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("LINDEX random access - %s", listType), func(t *testing.T) {
+			checkRandomAccessConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("Check if list is still ok after a DEBUG RELOAD - %s", listType), func(t *testing.T) {
+			checkNumberedListConsistency("mylist")
+			checkRandomAccessConsistency("mylist")
+		})
+	}
+	t.Run("LLEN against non-list value error", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		rdb.Set(ctx, "mylist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LLen(ctx, "mylist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LLEN against non existing key", func(t *testing.T) {
+		require.Equal(t, int64(0), rdb.LLen(ctx, "not-a-key").Val())
+	})
+
+	t.Run("LINDEX against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LIndex(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LINDEX against non existing key", func(t *testing.T) {
+		require.Equal(t, "", rdb.LIndex(ctx, "not-a-key", 10).Val())
+	})
+
+	t.Run("LPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("RPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.RPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("RPOPLPUSH base case - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist1", "mylist2")
+			createList("mylist1", []string{"a", large, "c", "d"})
+			require.Equal(t, "d", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, []string{"a", large}, rdb.LRange(ctx, "mylist1", 0, -1).Val())
+			require.Equal(t, []string{"c", "d"}, rdb.LRange(ctx, "mylist2", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("RPOPLPUSH with the same list as src and dst - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"a", large, "c"})
+			require.Equal(t, []string{"a", large, "c"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist", "mylist").Val())
+			require.Equal(t, []string{"c", "a", large}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		for otherListType, otherLarge := range largeValue {
+
+			t.Run(fmt.Sprintf("RPOPLPUSH with %s source and existing target %s", listType, otherListType), func(t *testing.T) {
+				createList("srclist", []string{"a", "b", "c", large})
+				createList("dstlist", []string{otherLarge})
+				require.Equal(t, large, rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, "c", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, []string{"a", "b"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+				require.Equal(t, []string{"c", large, otherLarge}, rdb.LRange(ctx, "dstlist", 0, -1).Val())
+			})
+
+		}
+	}
+
+	t.Run("RPOPLPUSH against non existing key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "dstlist").Val())
+	})
+
+	t.Run("RPOPLPUSH against non list src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		rdb.Set(ctx, "srclist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "newlist").Val())
+
+	})
+
+	t.Run("RPOPLPUSH against non list dst key", func(t *testing.T) {
+		createList("srclist", []string{"a", "b", "c", "d"})
+
+		rdb.Set(ctx, "dstlist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "dstlist").Val())
+		require.Equal(t, []string{"a", "b", "c", "d"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+	})
+
+	t.Run("RPOPLPUSH against non existing src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("Basic LPOP/RPOP - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2"})
+
+			require.Equal(t, large, rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "2", rdb.RPop(ctx, "mylist").Val())
+			require.Equal(t, "1", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, int64(0), rdb.LLen(ctx, "mylist").Val())
+
+			// pop on empty list
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+		})
+	}
+
+	t.Run("LPOP/RPOP against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "notalist", "foo", 0)
+		util.ErrorRegexp(t, rdb.LPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+		util.ErrorRegexp(t, rdb.RPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LPOP/RPOP with wrong number of arguments", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "1", "1").Err(), ".*wrong number of arguments.*")
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "2", "2").Err(), ".*wrong number of arguments.*")
+	})
+
+	t.Run("RPOP/LPOP with the optional count argument", func(t *testing.T) {
+		require.Equal(t, int64(7), rdb.LPush(ctx, "listcount", "aa", "bb", "cc", "dd", "ee", "ff", "gg").Val())
+		require.Equal(t, []string{"gg"}, rdb.LPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"ff", "ee"}, rdb.LPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"aa", "bb"}, rdb.RPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"cc"}, rdb.RPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"dd"}, rdb.LPopCount(ctx, "listcount", 123).Val())
+		util.ErrorRegexp(t, rdb.LPopCount(ctx, "forbatqaz", -123).Err(), ".*ERR.*range.*")
+	})
+
+	t.Run("LPOP/RPOP with the count 0 returns an empty array", func(t *testing.T) {
+		// Make sure we can distinguish between an empty array and a null response
+		rdb.Do(ctx, "readraw", "1")
+		rdb.LPush(ctx, "listcount", "zoro")
+		require.Equal(t, []string{}, rdb.LPopCount(ctx, "listcount", 0).Val())
+		require.Equal(t, []string{}, rdb.RPopCount(ctx, "listcount", 0).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, "", rdb.LPop(ctx, "non_existing_key").Val())
+		require.Equal(t, "", rdb.RPop(ctx, "non_existing_key").Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP with <count> against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 1).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 1).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	listType = "quicklist"
+	for _, num := range []int{250, 500} {
+		t.Run(fmt.Sprintf("Mass RPOP/LPOP - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			sum1 := 0
+			for i := 0; i < num; i++ {
+				rdb.LPush(ctx, "mylist", strconv.Itoa(i))
+				sum1 += i
+			}
+			sum2 := 0
+			for i := 0; i < num/2; i++ {
+				if v1, err := strconv.Atoi(rdb.LPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v1
+				}
+				if v2, err := strconv.Atoi(rdb.RPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v2
+				}
+			}
+			require.Equal(t, sum1, sum2)
+		})
+	}
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("LRANGE basics - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{"1", "2", "3", "4", "5", "6", "7", "8"}, rdb.LRange(ctx, "mylist", 1, -2).Val())
+			require.Equal(t, []string{"7", "8", "9"}, rdb.LRange(ctx, "mylist", -3, -1).Val())
+			require.Equal(t, []string{"4"}, rdb.LRange(ctx, "mylist", 4, 4).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE inverted indexes - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 6, 2).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range indexes including the full list - $type - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large, "1", "2", "3"}, rdb.LRange(ctx, "mylist", -1000, 1000).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range negative end index - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large}, rdb.LRange(ctx, "mylist", 0, -4).Val())
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 0, -5).Val())
+		})
+	}
+
+	t.Run("LRANGE against non existing key", func(t *testing.T) {
+		require.Equal(t, []string{}, rdb.LRange(ctx, "nosuchkey", 0, 1).Val())
+	})
+
+	for listType, large := range largeValue {
+
+		trimList := func(listType string, min, max int64) []string {
+			rdb.Del(ctx, "mylist")
+			createList("mylist", []string{"1", "2", "3", "4", large})
+			rdb.LTrim(ctx, "mylist", min, max)
+			return rdb.LRange(ctx, "mylist", 0, -1).Val()
+		}
+
+		t.Run(fmt.Sprintf("LTRIM basics - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, 0))
+			require.Equal(t, []string{"1", "2"}, trimList(listType, 0, 1))
+			require.Equal(t, []string{"1", "2", "3"}, trimList(listType, 0, 2))
+			require.Equal(t, []string{"2", "3"}, trimList(listType, 1, 2))
+			require.Equal(t, []string{"2", "3", "4", large}, trimList(listType, 1, -1))
+			require.Equal(t, []string{"2", "3", "4"}, trimList(listType, 1, -2))
+			require.Equal(t, []string{"4", large}, trimList(listType, -2, -1))
+			require.Equal(t, []string{large}, trimList(listType, -1, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -5, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -10, 10))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 5))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 10))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM out of range negative end index - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, -5))
+			require.Equal(t, []string{}, trimList(listType, 0, -6))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM lrem elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+			require.Equal(t, int64(2), rdb.LRem(ctx, "myotherlist", 4, "3").Val())
+			require.Equal(t, []string{"2", "4", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LTRIM linsert elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist1", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist1", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist1", 0, -1).Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "myotherlist1", "before", "9", "0").Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "myotherlist1", "before", "4", "0").Val())
+			require.Equal(t, []string{"2", "3", "0", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist1", 0, -1).Val())
+		})
+
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LSET - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"99", "98", large, "96", "95"})
+
+			rdb.LSet(ctx, "mylist", 1, "foo")
+			rdb.LSet(ctx, "mylist", -1, "bar")
+			require.Equal(t, []string{"99", "foo", large, "96", "bar"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LSET out of range index - %s", listType), func(t *testing.T) {
+			util.ErrorRegexp(t, rdb.LSet(ctx, "mylist", 10, "foo").Err(), "ERR.*range.*")
+		})
+	}
+
+	t.Run("LSET against non existing key", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LSet(ctx, "nosuchkey", 10, "foo").Err(), "ERR.*NotFound.*")
+	})
+
+	t.Run("LSET against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "nolist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LSet(ctx, "nolist", 0, "foo").Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, e := range largeValue {
+		t.Run(fmt.Sprintf("LREM remove all the occurrences - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{e, "foo", "bar", "foobar", "foobared", "zap", "bar", "test", "foo"})
+
+			require.Equal(t, int64(2), rdb.LRem(ctx, "mylist", 0, "bar").Val())

Review Comment:
   ```suggestion
   			require.EqualValues(t, 2, rdb.LRem(ctx, "mylist", 0, "bar").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LPUSHX, RPUSHX - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{large, "c"})
+
+			require.Equal(t, int64(3), rdb.RPushX(ctx, "xlist", "d").Val())
+			require.Equal(t, int64(4), rdb.LPushX(ctx, "xlist", "a").Val())
+			require.Equal(t, int64(6), rdb.RPushX(ctx, "xlist", "42", "x").Val())
+			require.Equal(t, int64(9), rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())
+			require.Equal(t, []string{"y1", "y2", "y3", "a", large, "c", "d", "42", "x"}, rdb.LRange(ctx, "xlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LINSERT - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{"a", large, "c", "d"})
+
+			require.Equal(t, int64(5), rdb.LInsert(ctx, "xlist", "before", "c", "zz").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(6), rdb.LInsert(ctx, "xlist", "after", "c", "yy").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "xlist", "after", "d", "dd").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "after", "bad", "ddd").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(8), rdb.LInsert(ctx, "xlist", "before", "a", "aa").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "before", "bad", "aaa").Val())
+			require.Equal(t, []string{"aa", "a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+
+			// check inserting integer encoded value
+			require.Equal(t, int64(9), rdb.LInsert(ctx, "xlist", "before", "aa", "42").Val())
+			require.Equal(t, "42", rdb.LRange(ctx, "xlist", 0, 0).Val()[0])
+		})
+
+	}
+
+	t.Run("LINSERT raise error on bad syntax", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LInsert(ctx, "xlist", "aft3r", "aa", "42").Err(), ".*syntax.*error.*")
+	})
+
+	listType := "quicklist"
+	for _, num := range []int{250, 500} {
+
+		checkNumberedListConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				require.Equal(t, strconv.Itoa(i), rdb.LIndex(ctx, key, int64(i)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-i-1), rdb.LIndex(ctx, key, int64(int(l)-i-1)).Val())
+			}
+		}
+
+		checkRandomAccessConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				rint := rand.Intn(int(l))
+				require.Equal(t, strconv.Itoa(rint), rdb.LIndex(ctx, key, int64(rint)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-rint-1), rdb.LIndex(ctx, key, int64(int(l)-rint-1)).Val())
+			}
+		}
+
+		t.Run(fmt.Sprintf("LINDEX consistency test - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			for i := 0; i < num; i++ {
+				rdb.RPush(ctx, "mylist", strconv.Itoa(i))
+			}
+			checkNumberedListConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("LINDEX random access - %s", listType), func(t *testing.T) {
+			checkRandomAccessConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("Check if list is still ok after a DEBUG RELOAD - %s", listType), func(t *testing.T) {
+			checkNumberedListConsistency("mylist")
+			checkRandomAccessConsistency("mylist")
+		})
+	}
+	t.Run("LLEN against non-list value error", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		rdb.Set(ctx, "mylist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LLen(ctx, "mylist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LLEN against non existing key", func(t *testing.T) {
+		require.Equal(t, int64(0), rdb.LLen(ctx, "not-a-key").Val())
+	})
+
+	t.Run("LINDEX against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LIndex(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LINDEX against non existing key", func(t *testing.T) {
+		require.Equal(t, "", rdb.LIndex(ctx, "not-a-key", 10).Val())
+	})
+
+	t.Run("LPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("RPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.RPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("RPOPLPUSH base case - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist1", "mylist2")
+			createList("mylist1", []string{"a", large, "c", "d"})
+			require.Equal(t, "d", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, []string{"a", large}, rdb.LRange(ctx, "mylist1", 0, -1).Val())
+			require.Equal(t, []string{"c", "d"}, rdb.LRange(ctx, "mylist2", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("RPOPLPUSH with the same list as src and dst - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"a", large, "c"})
+			require.Equal(t, []string{"a", large, "c"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist", "mylist").Val())
+			require.Equal(t, []string{"c", "a", large}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		for otherListType, otherLarge := range largeValue {
+
+			t.Run(fmt.Sprintf("RPOPLPUSH with %s source and existing target %s", listType, otherListType), func(t *testing.T) {
+				createList("srclist", []string{"a", "b", "c", large})
+				createList("dstlist", []string{otherLarge})
+				require.Equal(t, large, rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, "c", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, []string{"a", "b"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+				require.Equal(t, []string{"c", large, otherLarge}, rdb.LRange(ctx, "dstlist", 0, -1).Val())
+			})
+
+		}
+	}
+
+	t.Run("RPOPLPUSH against non existing key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "dstlist").Val())
+	})
+
+	t.Run("RPOPLPUSH against non list src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		rdb.Set(ctx, "srclist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "newlist").Val())
+
+	})
+
+	t.Run("RPOPLPUSH against non list dst key", func(t *testing.T) {
+		createList("srclist", []string{"a", "b", "c", "d"})
+
+		rdb.Set(ctx, "dstlist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "dstlist").Val())
+		require.Equal(t, []string{"a", "b", "c", "d"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+	})
+
+	t.Run("RPOPLPUSH against non existing src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("Basic LPOP/RPOP - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2"})
+
+			require.Equal(t, large, rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "2", rdb.RPop(ctx, "mylist").Val())
+			require.Equal(t, "1", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, int64(0), rdb.LLen(ctx, "mylist").Val())
+
+			// pop on empty list
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+		})
+	}
+
+	t.Run("LPOP/RPOP against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "notalist", "foo", 0)
+		util.ErrorRegexp(t, rdb.LPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+		util.ErrorRegexp(t, rdb.RPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LPOP/RPOP with wrong number of arguments", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "1", "1").Err(), ".*wrong number of arguments.*")
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "2", "2").Err(), ".*wrong number of arguments.*")
+	})
+
+	t.Run("RPOP/LPOP with the optional count argument", func(t *testing.T) {
+		require.Equal(t, int64(7), rdb.LPush(ctx, "listcount", "aa", "bb", "cc", "dd", "ee", "ff", "gg").Val())
+		require.Equal(t, []string{"gg"}, rdb.LPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"ff", "ee"}, rdb.LPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"aa", "bb"}, rdb.RPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"cc"}, rdb.RPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"dd"}, rdb.LPopCount(ctx, "listcount", 123).Val())
+		util.ErrorRegexp(t, rdb.LPopCount(ctx, "forbatqaz", -123).Err(), ".*ERR.*range.*")
+	})
+
+	t.Run("LPOP/RPOP with the count 0 returns an empty array", func(t *testing.T) {
+		// Make sure we can distinguish between an empty array and a null response
+		rdb.Do(ctx, "readraw", "1")
+		rdb.LPush(ctx, "listcount", "zoro")
+		require.Equal(t, []string{}, rdb.LPopCount(ctx, "listcount", 0).Val())
+		require.Equal(t, []string{}, rdb.RPopCount(ctx, "listcount", 0).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, "", rdb.LPop(ctx, "non_existing_key").Val())
+		require.Equal(t, "", rdb.RPop(ctx, "non_existing_key").Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP with <count> against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 1).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 1).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	listType = "quicklist"
+	for _, num := range []int{250, 500} {
+		t.Run(fmt.Sprintf("Mass RPOP/LPOP - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			sum1 := 0
+			for i := 0; i < num; i++ {
+				rdb.LPush(ctx, "mylist", strconv.Itoa(i))
+				sum1 += i
+			}
+			sum2 := 0
+			for i := 0; i < num/2; i++ {
+				if v1, err := strconv.Atoi(rdb.LPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v1
+				}
+				if v2, err := strconv.Atoi(rdb.RPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v2
+				}
+			}
+			require.Equal(t, sum1, sum2)
+		})
+	}
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("LRANGE basics - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{"1", "2", "3", "4", "5", "6", "7", "8"}, rdb.LRange(ctx, "mylist", 1, -2).Val())
+			require.Equal(t, []string{"7", "8", "9"}, rdb.LRange(ctx, "mylist", -3, -1).Val())
+			require.Equal(t, []string{"4"}, rdb.LRange(ctx, "mylist", 4, 4).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE inverted indexes - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 6, 2).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range indexes including the full list - $type - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large, "1", "2", "3"}, rdb.LRange(ctx, "mylist", -1000, 1000).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range negative end index - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large}, rdb.LRange(ctx, "mylist", 0, -4).Val())
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 0, -5).Val())
+		})
+	}
+
+	t.Run("LRANGE against non existing key", func(t *testing.T) {
+		require.Equal(t, []string{}, rdb.LRange(ctx, "nosuchkey", 0, 1).Val())
+	})
+
+	for listType, large := range largeValue {
+
+		trimList := func(listType string, min, max int64) []string {
+			rdb.Del(ctx, "mylist")
+			createList("mylist", []string{"1", "2", "3", "4", large})
+			rdb.LTrim(ctx, "mylist", min, max)
+			return rdb.LRange(ctx, "mylist", 0, -1).Val()
+		}
+
+		t.Run(fmt.Sprintf("LTRIM basics - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, 0))
+			require.Equal(t, []string{"1", "2"}, trimList(listType, 0, 1))
+			require.Equal(t, []string{"1", "2", "3"}, trimList(listType, 0, 2))
+			require.Equal(t, []string{"2", "3"}, trimList(listType, 1, 2))
+			require.Equal(t, []string{"2", "3", "4", large}, trimList(listType, 1, -1))
+			require.Equal(t, []string{"2", "3", "4"}, trimList(listType, 1, -2))
+			require.Equal(t, []string{"4", large}, trimList(listType, -2, -1))
+			require.Equal(t, []string{large}, trimList(listType, -1, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -5, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -10, 10))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 5))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 10))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM out of range negative end index - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, -5))
+			require.Equal(t, []string{}, trimList(listType, 0, -6))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM lrem elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+			require.Equal(t, int64(2), rdb.LRem(ctx, "myotherlist", 4, "3").Val())
+			require.Equal(t, []string{"2", "4", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LTRIM linsert elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist1", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist1", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist1", 0, -1).Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "myotherlist1", "before", "9", "0").Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "myotherlist1", "before", "4", "0").Val())
+			require.Equal(t, []string{"2", "3", "0", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist1", 0, -1).Val())
+		})
+
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LSET - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"99", "98", large, "96", "95"})
+
+			rdb.LSet(ctx, "mylist", 1, "foo")
+			rdb.LSet(ctx, "mylist", -1, "bar")
+			require.Equal(t, []string{"99", "foo", large, "96", "bar"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LSET out of range index - %s", listType), func(t *testing.T) {
+			util.ErrorRegexp(t, rdb.LSet(ctx, "mylist", 10, "foo").Err(), "ERR.*range.*")
+		})
+	}
+
+	t.Run("LSET against non existing key", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LSet(ctx, "nosuchkey", 10, "foo").Err(), "ERR.*NotFound.*")
+	})
+
+	t.Run("LSET against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "nolist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LSet(ctx, "nolist", 0, "foo").Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, e := range largeValue {
+		t.Run(fmt.Sprintf("LREM remove all the occurrences - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{e, "foo", "bar", "foobar", "foobared", "zap", "bar", "test", "foo"})
+
+			require.Equal(t, int64(2), rdb.LRem(ctx, "mylist", 0, "bar").Val())
+			require.Equal(t, []string{e, "foo", "foobar", "foobared", "zap", "test", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM remove the first occurrence - %s", listType), func(t *testing.T) {
+			require.Equal(t, int64(1), rdb.LRem(ctx, "mylist", 1, "foo").Val())
+			require.Equal(t, []string{e, "foobar", "foobared", "zap", "test", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM remove non existing element - %s", listType), func(t *testing.T) {
+			require.Equal(t, int64(0), rdb.LRem(ctx, "mylist", 1, "nosuchelement").Val())
+			require.Equal(t, []string{e, "foobar", "foobared", "zap", "test", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM starting from tail with negative count - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{e, "foo", "bar", "foobar", "foobared", "zap", "bar", "test", "foo", "foo"})
+
+			require.Equal(t, int64(1), rdb.LRem(ctx, "mylist", -1, "bar").Val())
+			require.Equal(t, []string{e, "foo", "bar", "foobar", "foobared", "zap", "test", "foo", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM starting from tail with negative count (2) - %s", listType), func(t *testing.T) {
+			require.Equal(t, int64(2), rdb.LRem(ctx, "mylist", -2, "foo").Val())
+			require.Equal(t, []string{e, "foo", "bar", "foobar", "foobared", "zap", "test"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM deleting objects that may be int encoded - %s", listType), func(t *testing.T) {
+			createList("myotherlist", e, 1, 2, 3)
+
+			require.Equal(t, int64(1), rdb.LRem(ctx, "myotherlist", 1, 2).Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "myotherlist").Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM remove elements in repeating list - %s", listType), func(t *testing.T) {
+			createList("myotherlist", e, "a", "b", "c", "d", "e", "f", "a", "f", "a", "f")
+
+			require.Equal(t, int64(1), rdb.LRem(ctx, "myotherlist", 1, "f").Val())
+			require.Equal(t, []string{e, "a", "b", "c", "d", "e", "a", "f", "a", "f"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+			require.Equal(t, int64(2), rdb.LRem(ctx, "myotherlist", 0, "f").Val())
+			require.Equal(t, []string{e, "a", "b", "c", "d", "e", "a", "a"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+		})
+	}
+
+	t.Run("Test LMOVE on different keys", func(t *testing.T) {
+		rdb.RPush(ctx, "list1{t}", "1")
+		rdb.RPush(ctx, "list1{t}", "2")
+		rdb.RPush(ctx, "list1{t}", "3")
+		rdb.RPush(ctx, "list1{t}", "4")
+		rdb.RPush(ctx, "list1{t}", "5")
+		rdb.LMove(ctx, "list1{t}", "list2{t}", "RIGHT", "LEFT")
+		rdb.LMove(ctx, "list1{t}", "list2{t}", "LEFT", "RIGHT")
+		require.Equal(t, int64(3), rdb.LLen(ctx, "list1{t}").Val())
+		require.Equal(t, int64(2), rdb.LLen(ctx, "list2{t}").Val())

Review Comment:
   ```suggestion
   		require.EqualValues(t, 3, rdb.LLen(ctx, "list1{t}").Val())
   		require.EqualValues(t, 2, rdb.LLen(ctx, "list2{t}").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LPUSHX, RPUSHX - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{large, "c"})
+
+			require.Equal(t, int64(3), rdb.RPushX(ctx, "xlist", "d").Val())
+			require.Equal(t, int64(4), rdb.LPushX(ctx, "xlist", "a").Val())
+			require.Equal(t, int64(6), rdb.RPushX(ctx, "xlist", "42", "x").Val())
+			require.Equal(t, int64(9), rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())
+			require.Equal(t, []string{"y1", "y2", "y3", "a", large, "c", "d", "42", "x"}, rdb.LRange(ctx, "xlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LINSERT - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{"a", large, "c", "d"})
+
+			require.Equal(t, int64(5), rdb.LInsert(ctx, "xlist", "before", "c", "zz").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(6), rdb.LInsert(ctx, "xlist", "after", "c", "yy").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "xlist", "after", "d", "dd").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "after", "bad", "ddd").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(8), rdb.LInsert(ctx, "xlist", "before", "a", "aa").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "before", "bad", "aaa").Val())
+			require.Equal(t, []string{"aa", "a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+
+			// check inserting integer encoded value
+			require.Equal(t, int64(9), rdb.LInsert(ctx, "xlist", "before", "aa", "42").Val())
+			require.Equal(t, "42", rdb.LRange(ctx, "xlist", 0, 0).Val()[0])
+		})
+
+	}
+
+	t.Run("LINSERT raise error on bad syntax", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LInsert(ctx, "xlist", "aft3r", "aa", "42").Err(), ".*syntax.*error.*")
+	})
+
+	listType := "quicklist"
+	for _, num := range []int{250, 500} {
+
+		checkNumberedListConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				require.Equal(t, strconv.Itoa(i), rdb.LIndex(ctx, key, int64(i)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-i-1), rdb.LIndex(ctx, key, int64(int(l)-i-1)).Val())
+			}
+		}
+
+		checkRandomAccessConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				rint := rand.Intn(int(l))
+				require.Equal(t, strconv.Itoa(rint), rdb.LIndex(ctx, key, int64(rint)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-rint-1), rdb.LIndex(ctx, key, int64(int(l)-rint-1)).Val())
+			}
+		}
+
+		t.Run(fmt.Sprintf("LINDEX consistency test - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			for i := 0; i < num; i++ {
+				rdb.RPush(ctx, "mylist", strconv.Itoa(i))
+			}
+			checkNumberedListConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("LINDEX random access - %s", listType), func(t *testing.T) {
+			checkRandomAccessConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("Check if list is still ok after a DEBUG RELOAD - %s", listType), func(t *testing.T) {
+			checkNumberedListConsistency("mylist")
+			checkRandomAccessConsistency("mylist")
+		})
+	}
+	t.Run("LLEN against non-list value error", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		rdb.Set(ctx, "mylist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LLen(ctx, "mylist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LLEN against non existing key", func(t *testing.T) {
+		require.Equal(t, int64(0), rdb.LLen(ctx, "not-a-key").Val())
+	})
+
+	t.Run("LINDEX against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LIndex(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LINDEX against non existing key", func(t *testing.T) {
+		require.Equal(t, "", rdb.LIndex(ctx, "not-a-key", 10).Val())
+	})
+
+	t.Run("LPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("RPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.RPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("RPOPLPUSH base case - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist1", "mylist2")
+			createList("mylist1", []string{"a", large, "c", "d"})
+			require.Equal(t, "d", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, []string{"a", large}, rdb.LRange(ctx, "mylist1", 0, -1).Val())
+			require.Equal(t, []string{"c", "d"}, rdb.LRange(ctx, "mylist2", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("RPOPLPUSH with the same list as src and dst - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"a", large, "c"})
+			require.Equal(t, []string{"a", large, "c"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist", "mylist").Val())
+			require.Equal(t, []string{"c", "a", large}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		for otherListType, otherLarge := range largeValue {
+
+			t.Run(fmt.Sprintf("RPOPLPUSH with %s source and existing target %s", listType, otherListType), func(t *testing.T) {
+				createList("srclist", []string{"a", "b", "c", large})
+				createList("dstlist", []string{otherLarge})
+				require.Equal(t, large, rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, "c", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, []string{"a", "b"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+				require.Equal(t, []string{"c", large, otherLarge}, rdb.LRange(ctx, "dstlist", 0, -1).Val())
+			})
+
+		}
+	}
+
+	t.Run("RPOPLPUSH against non existing key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "dstlist").Val())
+	})
+
+	t.Run("RPOPLPUSH against non list src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		rdb.Set(ctx, "srclist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "newlist").Val())
+
+	})
+
+	t.Run("RPOPLPUSH against non list dst key", func(t *testing.T) {
+		createList("srclist", []string{"a", "b", "c", "d"})
+
+		rdb.Set(ctx, "dstlist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "dstlist").Val())
+		require.Equal(t, []string{"a", "b", "c", "d"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+	})
+
+	t.Run("RPOPLPUSH against non existing src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("Basic LPOP/RPOP - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2"})
+
+			require.Equal(t, large, rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "2", rdb.RPop(ctx, "mylist").Val())
+			require.Equal(t, "1", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, int64(0), rdb.LLen(ctx, "mylist").Val())
+
+			// pop on empty list
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+		})
+	}
+
+	t.Run("LPOP/RPOP against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "notalist", "foo", 0)
+		util.ErrorRegexp(t, rdb.LPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+		util.ErrorRegexp(t, rdb.RPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LPOP/RPOP with wrong number of arguments", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "1", "1").Err(), ".*wrong number of arguments.*")
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "2", "2").Err(), ".*wrong number of arguments.*")
+	})
+
+	t.Run("RPOP/LPOP with the optional count argument", func(t *testing.T) {
+		require.Equal(t, int64(7), rdb.LPush(ctx, "listcount", "aa", "bb", "cc", "dd", "ee", "ff", "gg").Val())
+		require.Equal(t, []string{"gg"}, rdb.LPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"ff", "ee"}, rdb.LPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"aa", "bb"}, rdb.RPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"cc"}, rdb.RPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"dd"}, rdb.LPopCount(ctx, "listcount", 123).Val())
+		util.ErrorRegexp(t, rdb.LPopCount(ctx, "forbatqaz", -123).Err(), ".*ERR.*range.*")
+	})
+
+	t.Run("LPOP/RPOP with the count 0 returns an empty array", func(t *testing.T) {
+		// Make sure we can distinguish between an empty array and a null response
+		rdb.Do(ctx, "readraw", "1")
+		rdb.LPush(ctx, "listcount", "zoro")
+		require.Equal(t, []string{}, rdb.LPopCount(ctx, "listcount", 0).Val())
+		require.Equal(t, []string{}, rdb.RPopCount(ctx, "listcount", 0).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, "", rdb.LPop(ctx, "non_existing_key").Val())
+		require.Equal(t, "", rdb.RPop(ctx, "non_existing_key").Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP with <count> against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 1).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 1).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	listType = "quicklist"
+	for _, num := range []int{250, 500} {
+		t.Run(fmt.Sprintf("Mass RPOP/LPOP - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			sum1 := 0
+			for i := 0; i < num; i++ {
+				rdb.LPush(ctx, "mylist", strconv.Itoa(i))
+				sum1 += i
+			}
+			sum2 := 0
+			for i := 0; i < num/2; i++ {
+				if v1, err := strconv.Atoi(rdb.LPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v1
+				}
+				if v2, err := strconv.Atoi(rdb.RPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v2
+				}
+			}
+			require.Equal(t, sum1, sum2)
+		})
+	}
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("LRANGE basics - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{"1", "2", "3", "4", "5", "6", "7", "8"}, rdb.LRange(ctx, "mylist", 1, -2).Val())
+			require.Equal(t, []string{"7", "8", "9"}, rdb.LRange(ctx, "mylist", -3, -1).Val())
+			require.Equal(t, []string{"4"}, rdb.LRange(ctx, "mylist", 4, 4).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE inverted indexes - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 6, 2).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range indexes including the full list - $type - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large, "1", "2", "3"}, rdb.LRange(ctx, "mylist", -1000, 1000).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range negative end index - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large}, rdb.LRange(ctx, "mylist", 0, -4).Val())
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 0, -5).Val())
+		})
+	}
+
+	t.Run("LRANGE against non existing key", func(t *testing.T) {
+		require.Equal(t, []string{}, rdb.LRange(ctx, "nosuchkey", 0, 1).Val())
+	})
+
+	for listType, large := range largeValue {
+
+		trimList := func(listType string, min, max int64) []string {
+			rdb.Del(ctx, "mylist")
+			createList("mylist", []string{"1", "2", "3", "4", large})
+			rdb.LTrim(ctx, "mylist", min, max)
+			return rdb.LRange(ctx, "mylist", 0, -1).Val()
+		}
+
+		t.Run(fmt.Sprintf("LTRIM basics - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, 0))
+			require.Equal(t, []string{"1", "2"}, trimList(listType, 0, 1))
+			require.Equal(t, []string{"1", "2", "3"}, trimList(listType, 0, 2))
+			require.Equal(t, []string{"2", "3"}, trimList(listType, 1, 2))
+			require.Equal(t, []string{"2", "3", "4", large}, trimList(listType, 1, -1))
+			require.Equal(t, []string{"2", "3", "4"}, trimList(listType, 1, -2))
+			require.Equal(t, []string{"4", large}, trimList(listType, -2, -1))
+			require.Equal(t, []string{large}, trimList(listType, -1, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -5, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -10, 10))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 5))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 10))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM out of range negative end index - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, -5))
+			require.Equal(t, []string{}, trimList(listType, 0, -6))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM lrem elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+			require.Equal(t, int64(2), rdb.LRem(ctx, "myotherlist", 4, "3").Val())
+			require.Equal(t, []string{"2", "4", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LTRIM linsert elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist1", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist1", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist1", 0, -1).Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "myotherlist1", "before", "9", "0").Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "myotherlist1", "before", "4", "0").Val())
+			require.Equal(t, []string{"2", "3", "0", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist1", 0, -1).Val())
+		})
+
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LSET - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"99", "98", large, "96", "95"})
+
+			rdb.LSet(ctx, "mylist", 1, "foo")
+			rdb.LSet(ctx, "mylist", -1, "bar")
+			require.Equal(t, []string{"99", "foo", large, "96", "bar"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LSET out of range index - %s", listType), func(t *testing.T) {
+			util.ErrorRegexp(t, rdb.LSet(ctx, "mylist", 10, "foo").Err(), "ERR.*range.*")
+		})
+	}
+
+	t.Run("LSET against non existing key", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LSet(ctx, "nosuchkey", 10, "foo").Err(), "ERR.*NotFound.*")
+	})
+
+	t.Run("LSET against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "nolist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LSet(ctx, "nolist", 0, "foo").Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, e := range largeValue {
+		t.Run(fmt.Sprintf("LREM remove all the occurrences - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{e, "foo", "bar", "foobar", "foobared", "zap", "bar", "test", "foo"})
+
+			require.Equal(t, int64(2), rdb.LRem(ctx, "mylist", 0, "bar").Val())
+			require.Equal(t, []string{e, "foo", "foobar", "foobared", "zap", "test", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM remove the first occurrence - %s", listType), func(t *testing.T) {
+			require.Equal(t, int64(1), rdb.LRem(ctx, "mylist", 1, "foo").Val())
+			require.Equal(t, []string{e, "foobar", "foobared", "zap", "test", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM remove non existing element - %s", listType), func(t *testing.T) {
+			require.Equal(t, int64(0), rdb.LRem(ctx, "mylist", 1, "nosuchelement").Val())
+			require.Equal(t, []string{e, "foobar", "foobared", "zap", "test", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM starting from tail with negative count - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{e, "foo", "bar", "foobar", "foobared", "zap", "bar", "test", "foo", "foo"})
+
+			require.Equal(t, int64(1), rdb.LRem(ctx, "mylist", -1, "bar").Val())

Review Comment:
   ```suggestion
   			require.EqualValues(t, 1, rdb.LRem(ctx, "mylist", -1, "bar").Val())
   ```



##########
tests/gocase/unit/type/list/list_test.go:
##########
@@ -237,3 +237,664 @@ func TestZipList(t *testing.T) {
 		}
 	})
 }
+
+func TestList(t *testing.T) {
+	srv := util.StartServer(t, map[string]string{})
+	defer srv.Close()
+	ctx := context.Background()
+	rdb := srv.NewClient()
+	defer func() { require.NoError(t, rdb.Close()) }()
+	rd := srv.NewTCPClient()
+	defer func() { require.NoError(t, rd.Close()) }()
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist", func(t *testing.T) {
+
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "myziplist1", "aa").Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "myziplist1", "bb").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "myziplist1", "cc").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LIndex(ctx, "myziplist1", 0).Val())
+		require.Equal(t, "bb", rdb.LIndex(ctx, "myziplist1", 1).Val())
+		require.Equal(t, "cc", rdb.LIndex(ctx, "myziplist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist1", 3).Val())
+		require.Equal(t, "cc", rdb.RPop(ctx, "myziplist1").Val())
+		require.Equal(t, "aa", rdb.LPop(ctx, "myziplist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "myziplist2", "a").Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "myziplist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "myziplist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "myziplist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "myziplist2", 1).Val())
+		require.Equal(t, "a", rdb.LIndex(ctx, "myziplist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "myziplist2", 3).Val())
+		require.Equal(t, "a", rdb.RPop(ctx, "myziplist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "myziplist2").Val())
+	})
+
+	t.Run("LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list", func(t *testing.T) {
+		// first lpush then rpush
+		require.Equal(t, int64(1), rdb.LPush(ctx, "mylist1", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.RPush(ctx, "mylist1", "b").Val())
+		require.Equal(t, int64(3), rdb.RPush(ctx, "mylist1", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist1", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist1", 1).Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist1", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist1", 3).Val())
+		require.Equal(t, "c", rdb.RPop(ctx, "mylist1").Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LPop(ctx, "mylist1").Val())
+
+		// first rpush then lpush
+		require.Equal(t, int64(1), rdb.RPush(ctx, "mylist2", largeValue["linkedList"]).Val())
+		require.Equal(t, int64(2), rdb.LPush(ctx, "mylist2", "b").Val())
+		require.Equal(t, int64(3), rdb.LPush(ctx, "mylist2", "c").Val())
+		require.Equal(t, int64(3), rdb.LLen(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LIndex(ctx, "mylist2", 0).Val())
+		require.Equal(t, "b", rdb.LIndex(ctx, "mylist2", 1).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.LIndex(ctx, "mylist2", 2).Val())
+		require.Equal(t, "", rdb.LIndex(ctx, "mylist2", 3).Val())
+		require.Equal(t, largeValue["linkedList"], rdb.RPop(ctx, "mylist2").Val())
+		require.Equal(t, "c", rdb.LPop(ctx, "mylist2").Val())
+	})
+
+	t.Run("R/LPOP against empty list", func(t *testing.T) {
+		require.Equal(t, "", rdb.LPop(ctx, "non-existing-list").Val())
+	})
+
+	t.Run("Variadic RPUSH/LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		require.Equal(t, int64(4), rdb.LPush(ctx, "mylist", "a", "b", "c", "d").Val())
+		require.Equal(t, int64(8), rdb.RPush(ctx, "mylist", "1", "2", "3", "4").Val())
+		require.Equal(t, []string{"d", "c", "b", "a", "1", "2", "3", "4"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+	})
+
+	t.Run("DEL a list", func(t *testing.T) {
+		require.Equal(t, int64(1), rdb.Del(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "mylist2").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "mylist2").Val())
+	})
+
+	createList := func(key string, entries ...interface{}) {
+		rdb.Del(ctx, key)
+		for _, entry := range entries {
+			rdb.RPush(ctx, key, entry)
+		}
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: single existing list - %s", listType), func(t *testing.T) {
+			createList("blist", []string{"a", "b", large, "c", "d"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist d")
+			require.NoError(t, rd.WriteArgs("blpop", "blist", "1"))
+			rd.MustReadResult(t, "blist b")
+			require.NoError(t, rd.WriteArgs("brpop", "blist", "1"))
+			rd.MustReadResult(t, "blist c")
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: multiple existing lists - %s", listType), func(t *testing.T) {
+			createList("blist1", []string{"a", large, "c"})
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 a")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist1 c")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs("blpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist2", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+
+		t.Run(fmt.Sprintf("BLPOP, BRPOP: second list has an entry - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			createList("blist2", []string{"d", large, "f"})
+
+			require.NoError(t, rd.WriteArgs("blpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 d")
+			require.NoError(t, rd.WriteArgs("brpop", "blist1", "blist2", "1"))
+			rd.MustReadResult(t, "blist2 f")
+			require.Equal(t, int64(0), rdb.LLen(ctx, "blist1").Val())
+			require.Equal(t, int64(1), rdb.LLen(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("BLPOP with same key multiple times should work (issue #801)", func(t *testing.T) {
+		rdb.Del(ctx, "list1", "list2")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list1", "a")
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rdb.LPush(ctx, "list2", "b")
+		rd.MustReadResult(t, "list2 b")
+		rdb.LPush(ctx, "list1", "a")
+		rdb.LPush(ctx, "list2", "b")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list1 a")
+		require.NoError(t, rd.WriteArgs("blpop", "list1", "list2", "list2", "list1", "0"))
+		rd.MustReadResult(t, "list2 b")
+	})
+
+	t.Run("BLPOP with variadic LPUSH", func(t *testing.T) {
+		rdb.Del(ctx, "blist", "target")
+		time.Sleep(time.Millisecond * 100)
+		require.NoError(t, rd.WriteArgs("blpop", "blist", "0"))
+		time.Sleep(time.Millisecond * 100)
+		require.Equal(t, int64(2), rdb.LPush(ctx, "blist", "foo", "bar").Val())
+		time.Sleep(time.Millisecond * 100)
+		rd.MustReadResult(t, "blist bar")
+		require.Equal(t, "foo", rdb.LRange(ctx, "blist", 0, -1).Val()[0])
+	})
+
+	for _, popType := range []string{"blpop", "brpop"} {
+
+		t.Run(fmt.Sprintf("%s: with single empty list argument", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+		})
+
+		t.Run(fmt.Sprintf("%s: with negative timeout", popType), func(t *testing.T) {
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "-1"))
+			rd.MustMatch(t, ".*negative.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: with zero timeout should block indefinitely", popType), func(t *testing.T) {
+			// To test this, use a timeout of 0 and wait a second.
+			// The blocking pop should still be waiting for a push.
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "0"))
+			time.Sleep(time.Millisecond * 1000)
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+		})
+
+		t.Run(fmt.Sprintf("%s: second argument is not a list", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			rdb.Set(ctx, "blist2", "nolist", 0)
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, ".*WRONGTYPE.*")
+		})
+
+		t.Run(fmt.Sprintf("%s: timeout", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rd.MustMatch(t, "")
+		})
+
+		t.Run(fmt.Sprintf("%s: arguments are empty", popType), func(t *testing.T) {
+			rdb.Del(ctx, "blist1", "blist2")
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist1", "foo")
+			rd.MustReadResult(t, "blist1 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+			require.NoError(t, rd.WriteArgs(popType, "blist1", "blist2", "1"))
+			rdb.RPush(ctx, "blist2", "foo")
+			rd.MustReadResult(t, "blist2 foo")
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist1").Val())
+			require.Equal(t, int64(0), rdb.Exists(ctx, "blist2").Val())
+		})
+	}
+
+	t.Run("LPUSHX, RPUSHX - generic", func(t *testing.T) {
+		rdb.Del(ctx, "xlist")
+		require.Equal(t, int64(0), rdb.LPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+		require.Equal(t, int64(0), rdb.RPushX(ctx, "xlist", "a").Val())
+		require.Equal(t, int64(0), rdb.LLen(ctx, "xlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LPUSHX, RPUSHX - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{large, "c"})
+
+			require.Equal(t, int64(3), rdb.RPushX(ctx, "xlist", "d").Val())
+			require.Equal(t, int64(4), rdb.LPushX(ctx, "xlist", "a").Val())
+			require.Equal(t, int64(6), rdb.RPushX(ctx, "xlist", "42", "x").Val())
+			require.Equal(t, int64(9), rdb.LPushX(ctx, "xlist", "y3", "y2", "y1").Val())
+			require.Equal(t, []string{"y1", "y2", "y3", "a", large, "c", "d", "42", "x"}, rdb.LRange(ctx, "xlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LINSERT - %s", listType), func(t *testing.T) {
+			createList("xlist", []string{"a", large, "c", "d"})
+
+			require.Equal(t, int64(5), rdb.LInsert(ctx, "xlist", "before", "c", "zz").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(6), rdb.LInsert(ctx, "xlist", "after", "c", "yy").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "xlist", "after", "d", "dd").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "after", "bad", "ddd").Val())
+			require.Equal(t, []string{"a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+			require.Equal(t, int64(8), rdb.LInsert(ctx, "xlist", "before", "a", "aa").Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "xlist", "before", "bad", "aaa").Val())
+			require.Equal(t, []string{"aa", "a", large, "zz", "c", "yy", "d", "dd"}, rdb.LRange(ctx, "xlist", 0, 10).Val())
+
+			// check inserting integer encoded value
+			require.Equal(t, int64(9), rdb.LInsert(ctx, "xlist", "before", "aa", "42").Val())
+			require.Equal(t, "42", rdb.LRange(ctx, "xlist", 0, 0).Val()[0])
+		})
+
+	}
+
+	t.Run("LINSERT raise error on bad syntax", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LInsert(ctx, "xlist", "aft3r", "aa", "42").Err(), ".*syntax.*error.*")
+	})
+
+	listType := "quicklist"
+	for _, num := range []int{250, 500} {
+
+		checkNumberedListConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				require.Equal(t, strconv.Itoa(i), rdb.LIndex(ctx, key, int64(i)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-i-1), rdb.LIndex(ctx, key, int64(int(l)-i-1)).Val())
+			}
+		}
+
+		checkRandomAccessConsistency := func(key string) {
+			l := rdb.LLen(ctx, key).Val()
+			for i := 0; i < int(l); i++ {
+				rint := rand.Intn(int(l))
+				require.Equal(t, strconv.Itoa(rint), rdb.LIndex(ctx, key, int64(rint)).Val())
+				require.Equal(t, strconv.Itoa(int(l)-rint-1), rdb.LIndex(ctx, key, int64(int(l)-rint-1)).Val())
+			}
+		}
+
+		t.Run(fmt.Sprintf("LINDEX consistency test - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			for i := 0; i < num; i++ {
+				rdb.RPush(ctx, "mylist", strconv.Itoa(i))
+			}
+			checkNumberedListConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("LINDEX random access - %s", listType), func(t *testing.T) {
+			checkRandomAccessConsistency("mylist")
+		})
+
+		t.Run(fmt.Sprintf("Check if list is still ok after a DEBUG RELOAD - %s", listType), func(t *testing.T) {
+			checkNumberedListConsistency("mylist")
+			checkRandomAccessConsistency("mylist")
+		})
+	}
+	t.Run("LLEN against non-list value error", func(t *testing.T) {
+		rdb.Del(ctx, "mylist")
+		rdb.Set(ctx, "mylist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LLen(ctx, "mylist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LLEN against non existing key", func(t *testing.T) {
+		require.Equal(t, int64(0), rdb.LLen(ctx, "not-a-key").Val())
+	})
+
+	t.Run("LINDEX against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LIndex(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LINDEX against non existing key", func(t *testing.T) {
+		require.Equal(t, "", rdb.LIndex(ctx, "not-a-key", 10).Val())
+	})
+
+	t.Run("LPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("RPUSH against non-list value error", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.RPush(ctx, "mylist", 0).Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("RPOPLPUSH base case - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist1", "mylist2")
+			createList("mylist1", []string{"a", large, "c", "d"})
+			require.Equal(t, "d", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist1", "mylist2").Val())
+			require.Equal(t, []string{"a", large}, rdb.LRange(ctx, "mylist1", 0, -1).Val())
+			require.Equal(t, []string{"c", "d"}, rdb.LRange(ctx, "mylist2", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("RPOPLPUSH with the same list as src and dst - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"a", large, "c"})
+			require.Equal(t, []string{"a", large, "c"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+			require.Equal(t, "c", rdb.RPopLPush(ctx, "mylist", "mylist").Val())
+			require.Equal(t, []string{"c", "a", large}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		for otherListType, otherLarge := range largeValue {
+
+			t.Run(fmt.Sprintf("RPOPLPUSH with %s source and existing target %s", listType, otherListType), func(t *testing.T) {
+				createList("srclist", []string{"a", "b", "c", large})
+				createList("dstlist", []string{otherLarge})
+				require.Equal(t, large, rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, "c", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+				require.Equal(t, []string{"a", "b"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+				require.Equal(t, []string{"c", large, otherLarge}, rdb.LRange(ctx, "dstlist", 0, -1).Val())
+			})
+
+		}
+	}
+
+	t.Run("RPOPLPUSH against non existing key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "dstlist").Val())
+	})
+
+	t.Run("RPOPLPUSH against non list src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		rdb.Set(ctx, "srclist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "srclist").Val())
+		require.Equal(t, int64(0), rdb.Exists(ctx, "newlist").Val())
+
+	})
+
+	t.Run("RPOPLPUSH against non list dst key", func(t *testing.T) {
+		createList("srclist", []string{"a", "b", "c", "d"})
+
+		rdb.Set(ctx, "dstlist", "x", 0)
+		util.ErrorRegexp(t, rdb.RPopLPush(ctx, "srclist", "dstlist").Err(), ".*WRONGTYPE.*")
+		require.Equal(t, "string", rdb.Type(ctx, "dstlist").Val())
+		require.Equal(t, []string{"a", "b", "c", "d"}, rdb.LRange(ctx, "srclist", 0, -1).Val())
+	})
+
+	t.Run("RPOPLPUSH against non existing src key", func(t *testing.T) {
+		rdb.Del(ctx, "srclist", "dstlist")
+		require.Equal(t, "", rdb.RPopLPush(ctx, "srclist", "dstlist").Val())
+	})
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("Basic LPOP/RPOP - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2"})
+
+			require.Equal(t, large, rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "2", rdb.RPop(ctx, "mylist").Val())
+			require.Equal(t, "1", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, int64(0), rdb.LLen(ctx, "mylist").Val())
+
+			// pop on empty list
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+			require.Equal(t, "", rdb.LPop(ctx, "mylist").Val())
+		})
+	}
+
+	t.Run("LPOP/RPOP against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "notalist", "foo", 0)
+		util.ErrorRegexp(t, rdb.LPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+		util.ErrorRegexp(t, rdb.RPop(ctx, "notalist").Err(), ".*WRONGTYPE.*")
+	})
+
+	t.Run("LPOP/RPOP with wrong number of arguments", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "1", "1").Err(), ".*wrong number of arguments.*")
+		util.ErrorRegexp(t, rdb.Do(ctx, "lpop", "key", "2", "2").Err(), ".*wrong number of arguments.*")
+	})
+
+	t.Run("RPOP/LPOP with the optional count argument", func(t *testing.T) {
+		require.Equal(t, int64(7), rdb.LPush(ctx, "listcount", "aa", "bb", "cc", "dd", "ee", "ff", "gg").Val())
+		require.Equal(t, []string{"gg"}, rdb.LPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"ff", "ee"}, rdb.LPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"aa", "bb"}, rdb.RPopCount(ctx, "listcount", 2).Val())
+		require.Equal(t, []string{"cc"}, rdb.RPopCount(ctx, "listcount", 1).Val())
+		require.Equal(t, []string{"dd"}, rdb.LPopCount(ctx, "listcount", 123).Val())
+		util.ErrorRegexp(t, rdb.LPopCount(ctx, "forbatqaz", -123).Err(), ".*ERR.*range.*")
+	})
+
+	t.Run("LPOP/RPOP with the count 0 returns an empty array", func(t *testing.T) {
+		// Make sure we can distinguish between an empty array and a null response
+		rdb.Do(ctx, "readraw", "1")
+		rdb.LPush(ctx, "listcount", "zoro")
+		require.Equal(t, []string{}, rdb.LPopCount(ctx, "listcount", 0).Val())
+		require.Equal(t, []string{}, rdb.RPopCount(ctx, "listcount", 0).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, "", rdb.LPop(ctx, "non_existing_key").Val())
+		require.Equal(t, "", rdb.RPop(ctx, "non_existing_key").Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	t.Run("LPOP/RPOP with <count> against non existing key", func(t *testing.T) {
+		rdb.Do(ctx, "readraw", "1")
+		rdb.Del(ctx, "non_existing_key")
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.LPopCount(ctx, "non_existing_key", 1).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 0).Val())
+		require.Equal(t, []string(nil), rdb.RPopCount(ctx, "non_existing_key", 1).Val())
+		rdb.Do(ctx, "readraw", "0")
+	})
+
+	listType = "quicklist"
+	for _, num := range []int{250, 500} {
+		t.Run(fmt.Sprintf("Mass RPOP/LPOP - %s", listType), func(t *testing.T) {
+			rdb.Del(ctx, "mylist")
+			sum1 := 0
+			for i := 0; i < num; i++ {
+				rdb.LPush(ctx, "mylist", strconv.Itoa(i))
+				sum1 += i
+			}
+			sum2 := 0
+			for i := 0; i < num/2; i++ {
+				if v1, err := strconv.Atoi(rdb.LPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v1
+				}
+				if v2, err := strconv.Atoi(rdb.RPop(ctx, "mylist").Val()); err == nil {
+					sum2 += v2
+				}
+			}
+			require.Equal(t, sum1, sum2)
+		})
+	}
+
+	for listType, large := range largeValue {
+
+		t.Run(fmt.Sprintf("LRANGE basics - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{"1", "2", "3", "4", "5", "6", "7", "8"}, rdb.LRange(ctx, "mylist", 1, -2).Val())
+			require.Equal(t, []string{"7", "8", "9"}, rdb.LRange(ctx, "mylist", -3, -1).Val())
+			require.Equal(t, []string{"4"}, rdb.LRange(ctx, "mylist", 4, 4).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE inverted indexes - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 6, 2).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range indexes including the full list - $type - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large, "1", "2", "3"}, rdb.LRange(ctx, "mylist", -1000, 1000).Val())
+		})
+
+		t.Run(fmt.Sprintf("LRANGE out of range negative end index - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{large, "1", "2", "3"})
+
+			require.Equal(t, []string{large}, rdb.LRange(ctx, "mylist", 0, -4).Val())
+			require.Equal(t, []string{}, rdb.LRange(ctx, "mylist", 0, -5).Val())
+		})
+	}
+
+	t.Run("LRANGE against non existing key", func(t *testing.T) {
+		require.Equal(t, []string{}, rdb.LRange(ctx, "nosuchkey", 0, 1).Val())
+	})
+
+	for listType, large := range largeValue {
+
+		trimList := func(listType string, min, max int64) []string {
+			rdb.Del(ctx, "mylist")
+			createList("mylist", []string{"1", "2", "3", "4", large})
+			rdb.LTrim(ctx, "mylist", min, max)
+			return rdb.LRange(ctx, "mylist", 0, -1).Val()
+		}
+
+		t.Run(fmt.Sprintf("LTRIM basics - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, 0))
+			require.Equal(t, []string{"1", "2"}, trimList(listType, 0, 1))
+			require.Equal(t, []string{"1", "2", "3"}, trimList(listType, 0, 2))
+			require.Equal(t, []string{"2", "3"}, trimList(listType, 1, 2))
+			require.Equal(t, []string{"2", "3", "4", large}, trimList(listType, 1, -1))
+			require.Equal(t, []string{"2", "3", "4"}, trimList(listType, 1, -2))
+			require.Equal(t, []string{"4", large}, trimList(listType, -2, -1))
+			require.Equal(t, []string{large}, trimList(listType, -1, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -5, -1))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, -10, 10))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 5))
+			require.Equal(t, []string{"1", "2", "3", "4", large}, trimList(listType, 0, 10))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM out of range negative end index - %s", listType), func(t *testing.T) {
+			require.Equal(t, []string{"1"}, trimList(listType, 0, -5))
+			require.Equal(t, []string{}, trimList(listType, 0, -6))
+		})
+
+		t.Run(fmt.Sprintf("LTRIM lrem elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+			require.Equal(t, int64(2), rdb.LRem(ctx, "myotherlist", 4, "3").Val())
+			require.Equal(t, []string{"2", "4", "6", "7"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LTRIM linsert elements after ltrim list - %s", listType), func(t *testing.T) {
+			createList("myotherlist1", []string{"0", "1", "2", "3", "4", "3", "6", "7", "3", "9"})
+
+			require.Equal(t, "OK", rdb.LTrim(ctx, "myotherlist1", 2, -3).Val())
+			require.Equal(t, []string{"2", "3", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist1", 0, -1).Val())
+			require.Equal(t, int64(-1), rdb.LInsert(ctx, "myotherlist1", "before", "9", "0").Val())
+			require.Equal(t, int64(7), rdb.LInsert(ctx, "myotherlist1", "before", "4", "0").Val())
+			require.Equal(t, []string{"2", "3", "0", "4", "3", "6", "7"}, rdb.LRange(ctx, "myotherlist1", 0, -1).Val())
+		})
+
+	}
+
+	for listType, large := range largeValue {
+		t.Run(fmt.Sprintf("LSET - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{"99", "98", large, "96", "95"})
+
+			rdb.LSet(ctx, "mylist", 1, "foo")
+			rdb.LSet(ctx, "mylist", -1, "bar")
+			require.Equal(t, []string{"99", "foo", large, "96", "bar"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LSET out of range index - %s", listType), func(t *testing.T) {
+			util.ErrorRegexp(t, rdb.LSet(ctx, "mylist", 10, "foo").Err(), "ERR.*range.*")
+		})
+	}
+
+	t.Run("LSET against non existing key", func(t *testing.T) {
+		util.ErrorRegexp(t, rdb.LSet(ctx, "nosuchkey", 10, "foo").Err(), "ERR.*NotFound.*")
+	})
+
+	t.Run("LSET against non list value", func(t *testing.T) {
+		rdb.Set(ctx, "nolist", "foobar", 0)
+		util.ErrorRegexp(t, rdb.LSet(ctx, "nolist", 0, "foo").Err(), ".*WRONGTYPE.*")
+	})
+
+	for listType, e := range largeValue {
+		t.Run(fmt.Sprintf("LREM remove all the occurrences - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{e, "foo", "bar", "foobar", "foobared", "zap", "bar", "test", "foo"})
+
+			require.Equal(t, int64(2), rdb.LRem(ctx, "mylist", 0, "bar").Val())
+			require.Equal(t, []string{e, "foo", "foobar", "foobared", "zap", "test", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM remove the first occurrence - %s", listType), func(t *testing.T) {
+			require.Equal(t, int64(1), rdb.LRem(ctx, "mylist", 1, "foo").Val())
+			require.Equal(t, []string{e, "foobar", "foobared", "zap", "test", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM remove non existing element - %s", listType), func(t *testing.T) {
+			require.Equal(t, int64(0), rdb.LRem(ctx, "mylist", 1, "nosuchelement").Val())
+			require.Equal(t, []string{e, "foobar", "foobared", "zap", "test", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM starting from tail with negative count - %s", listType), func(t *testing.T) {
+			createList("mylist", []string{e, "foo", "bar", "foobar", "foobared", "zap", "bar", "test", "foo", "foo"})
+
+			require.Equal(t, int64(1), rdb.LRem(ctx, "mylist", -1, "bar").Val())
+			require.Equal(t, []string{e, "foo", "bar", "foobar", "foobared", "zap", "test", "foo", "foo"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM starting from tail with negative count (2) - %s", listType), func(t *testing.T) {
+			require.Equal(t, int64(2), rdb.LRem(ctx, "mylist", -2, "foo").Val())
+			require.Equal(t, []string{e, "foo", "bar", "foobar", "foobared", "zap", "test"}, rdb.LRange(ctx, "mylist", 0, -1).Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM deleting objects that may be int encoded - %s", listType), func(t *testing.T) {
+			createList("myotherlist", e, 1, 2, 3)
+
+			require.Equal(t, int64(1), rdb.LRem(ctx, "myotherlist", 1, 2).Val())
+			require.Equal(t, int64(3), rdb.LLen(ctx, "myotherlist").Val())
+		})
+
+		t.Run(fmt.Sprintf("LREM remove elements in repeating list - %s", listType), func(t *testing.T) {
+			createList("myotherlist", e, "a", "b", "c", "d", "e", "f", "a", "f", "a", "f")
+
+			require.Equal(t, int64(1), rdb.LRem(ctx, "myotherlist", 1, "f").Val())
+			require.Equal(t, []string{e, "a", "b", "c", "d", "e", "a", "f", "a", "f"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
+			require.Equal(t, int64(2), rdb.LRem(ctx, "myotherlist", 0, "f").Val())

Review Comment:
   ```suggestion
   			require.EqualValues(t, 1, rdb.LRem(ctx, "myotherlist", 1, "f").Val())
   			require.Equal(t, []string{e, "a", "b", "c", "d", "e", "a", "f", "a", "f"}, rdb.LRange(ctx, "myotherlist", 0, -1).Val())
   			require.EqualValues(t, 2, rdb.LRem(ctx, "myotherlist", 0, "f").Val())
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@kvrocks.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org