start_server {tags {"keyspace"}} {
    test {DEL against a single item} {
        r set x foo
        assert {[r get x] eq "foo"}
        r del x
        r get x
    } {}

    test {Vararg DEL} {
        r set foo1 a
        r set foo2 b
        r set foo3 c
        list [r del foo1 foo2 foo3 foo4] [r mget foo1 foo2 foo3]
    } {3 {{} {} {}}}

    test {KEYS with pattern} {
        foreach key {key_x key_y key_z foo_a foo_b foo_c} {
            r set $key hello
        }
        lsort [r keys foo*]
    } {foo_a foo_b foo_c}

    test {KEYS to get all keys} {
        lsort [r keys *]
    } {foo_a foo_b foo_c key_x key_y key_z}

    test {DBSIZE} {
        r dbsize
    } {6}

    test {DEL all keys} {
        foreach key [r keys *] {r del $key}
        r dbsize
    } {0}

    test "DEL against expired key" {
        r debug set-active-expire 0
        r setex keyExpire 1 valExpire
        after 1100
        assert_equal 0 [r del keyExpire]
        r debug set-active-expire 1
    }

    test {EXISTS} {
        set res {}
        r set newkey test
        append res [r exists newkey]
        r del newkey
        append res [r exists newkey]
    } {10}

    test {Zero length value in key. SET/GET/EXISTS} {
        r set emptykey {}
        set res [r get emptykey]
        append res [r exists emptykey]
        r del emptykey
        append res [r exists emptykey]
    } {10}

    test {Commands pipelining} {
        set fd [r channel]
        puts -nonewline $fd "SET k1 xyzk\r\nGET k1\r\nPING\r\n"
        flush $fd
        set res {}
        append res [string match OK* [r read]]
        append res [r read]
        append res [string match PONG* [r read]]
        format $res
    } {1xyzk1}

    test {Non existing command} {
        catch {r foobaredcommand} err
        string match ERR* $err
    } {1}

    test {RENAME basic usage} {
        r set mykey hello
        r rename mykey mykey1
        r rename mykey1 mykey2
        r get mykey2
    } {hello}

    test {RENAME source key should no longer exist} {
        r exists mykey
    } {0}

    test {RENAME against already existing key} {
        r set mykey a
        r set mykey2 b
        r rename mykey2 mykey
        set res [r get mykey]
        append res [r exists mykey2]
    } {b0}

    test {RENAMENX basic usage} {
        r del mykey
        r del mykey2
        r set mykey foobar
        r renamenx mykey mykey2
        set res [r get mykey2]
        append res [r exists mykey]
    } {foobar0}

    test {RENAMENX against already existing key} {
        r set mykey foo
        r set mykey2 bar
        r renamenx mykey mykey2
    } {0}

    test {RENAMENX against already existing key (2)} {
        set res [r get mykey]
        append res [r get mykey2]
    } {foobar}

    test {RENAME against non existing source key} {
        catch {r rename nokey foobar} err
        format $err
    } {ERR*}

    test {RENAME where source and dest key are the same (existing)} {
        r set mykey foo
        r rename mykey mykey
    } {OK}

    test {RENAMENX where source and dest key are the same (existing)} {
        r set mykey foo
        r renamenx mykey mykey
    } {0}

    test {RENAME where source and dest key are the same (non existing)} {
        r del mykey
        catch {r rename mykey mykey} err
        format $err
    } {ERR*}

    test {RENAME with volatile key, should move the TTL as well} {
        r del mykey mykey2
        r set mykey foo
        r expire mykey 100
        assert {[r ttl mykey] > 95 && [r ttl mykey] <= 100}
        r rename mykey mykey2
        assert {[r ttl mykey2] > 95 && [r ttl mykey2] <= 100}
    }

    test {RENAME with volatile key, should not inherit TTL of target key} {
        r del mykey mykey2
        r set mykey foo
        r set mykey2 bar
        r expire mykey2 100
        assert {[r ttl mykey] == -1 && [r ttl mykey2] > 0}
        r rename mykey mykey2
        r ttl mykey2
    } {-1}

    test {DEL all keys again (DB 0)} {
        foreach key [r keys *] {
            r del $key
        }
        r dbsize
    } {0}

    test {DEL all keys again (DB 1)} {
        r select 10
        foreach key [r keys *] {
            r del $key
        }
        set res [r dbsize]
        r select 9
        format $res
    } {0}

    test {COPY basic usage for string} {
        r set mykey foobar
        set res {}
        r copy mykey mynewkey
        lappend res [r get mynewkey]
        lappend res [r dbsize]
        r copy mykey mynewkey DB 10
        r select 10
        lappend res [r get mynewkey]
        lappend res [r dbsize]
        r select 9
        format $res
    } [list foobar 2 foobar 1]

    test {COPY for string does not replace an existing key without REPLACE option} {
        r set mykey2 hello
        catch {r copy mykey2 mynewkey DB 10} e
        set e
    } {0}

    test {COPY for string can replace an existing key with REPLACE option} {
        r copy mykey2 mynewkey DB 10 REPLACE
        r select 10
        r get mynewkey
    } {hello}

    test {COPY for string ensures that copied data is independent of copying data} {
        r flushdb
        r select 9
        r set mykey foobar
        set res {}
        r copy mykey mynewkey DB 10
        r select 10
        lappend res [r get mynewkey]
        r set mynewkey hoge
        lappend res [r get mynewkey]
        r select 9
        lappend res [r get mykey]
        r select 10
        r flushdb
        r select 9
        format $res
    } [list foobar hoge foobar]

    test {COPY for string does not copy data to no-integer DB} {
        r set mykey foobar
        catch {r copy mykey mynewkey DB notanumber} e
        set e
    } {ERR value is not an integer or out of range}

    test {COPY can copy key expire metadata as well} {
        r set mykey foobar ex 100
        r copy mykey mynewkey REPLACE
        assert {[r ttl mynewkey] > 0 && [r ttl mynewkey] <= 100}
        assert {[r get mynewkey] eq "foobar"}
    }

    test {COPY does not create an expire if it does not exist} {
        r set mykey foobar
        assert {[r ttl mykey] == -1}
        r copy mykey mynewkey REPLACE
        assert {[r ttl mynewkey] == -1}
        assert {[r get mynewkey] eq "foobar"}
    }

    test {COPY basic usage for list} {
        r del mylist mynewlist
        r lpush mylist a b c d
        r copy mylist mynewlist
        set digest [r debug digest-value mylist]
        assert_equal $digest [r debug digest-value mynewlist]
        assert_equal 1 [r object refcount mylist]
        assert_equal 1 [r object refcount mynewlist]
        r del mylist
        assert_equal $digest [r debug digest-value mynewlist]
    }

    test {COPY basic usage for intset set} {
        r del set1 newset1 
        r sadd set1 1 2 3
        assert_encoding intset set1
        r copy set1 newset1
        set digest [r debug digest-value set1]
        assert_equal $digest [r debug digest-value newset1]
        assert_equal 1 [r object refcount set1]
        assert_equal 1 [r object refcount newset1]
        r del set1
        assert_equal $digest [r debug digest-value newset1]
    }

    test {COPY basic usage for hashtable set} {
        r del set2 newset2
        r sadd set2 1 2 3 a
        assert_encoding hashtable set2
        r copy set2 newset2
        set digest [r debug digest-value set2]
        assert_equal $digest [r debug digest-value newset2]
        assert_equal 1 [r object refcount set2]
        assert_equal 1 [r object refcount newset2]
        r del set2
        assert_equal $digest [r debug digest-value newset2]
    }

    test {COPY basic usage for ziplist sorted set} {
        r del zset1 newzset1
        r zadd zset1 123 foobar
        assert_encoding ziplist zset1
        r copy zset1 newzset1
        set digest [r debug digest-value zset1]
        assert_equal $digest [r debug digest-value newzset1]
        assert_equal 1 [r object refcount zset1]
        assert_equal 1 [r object refcount newzset1]
        r del zset1
        assert_equal $digest [r debug digest-value newzset1]
    }

     test {COPY basic usage for skiplist sorted set} {
        r del zset2 newzset2
        set original_max [lindex [r config get zset-max-ziplist-entries] 1]
        r config set zset-max-ziplist-entries 0
        for {set j 0} {$j < 130} {incr j} {
            r zadd zset2 [randomInt 50] ele-[randomInt 10]
        }
        assert_encoding skiplist zset2
        r copy zset2 newzset2
        set digest [r debug digest-value zset2]
        assert_equal $digest [r debug digest-value newzset2]
        assert_equal 1 [r object refcount zset2]
        assert_equal 1 [r object refcount newzset2]
        r del zset2
        assert_equal $digest [r debug digest-value newzset2]
        r config set zset-max-ziplist-entries $original_max
    }

    test {COPY basic usage for ziplist hash} {
        r del hash1 newhash1
        r hset hash1 tmp 17179869184
        assert_encoding ziplist hash1
        r copy hash1 newhash1
        set digest [r debug digest-value hash1]
        assert_equal $digest [r debug digest-value newhash1]
        assert_equal 1 [r object refcount hash1]
        assert_equal 1 [r object refcount newhash1]
        r del hash1
        assert_equal $digest [r debug digest-value newhash1]
    }

    test {COPY basic usage for hashtable hash} {
        r del hash2 newhash2
        set original_max [lindex [r config get hash-max-ziplist-entries] 1]
        r config set hash-max-ziplist-entries 0
        for {set i 0} {$i < 64} {incr i} {
            r hset hash2 [randomValue] [randomValue]
        }
        assert_encoding hashtable hash2
        r copy hash2 newhash2
        set digest [r debug digest-value hash2]
        assert_equal $digest [r debug digest-value newhash2]
        assert_equal 1 [r object refcount hash2]
        assert_equal 1 [r object refcount newhash2]
        r del hash2
        assert_equal $digest [r debug digest-value newhash2]
        r config set hash-max-ziplist-entries $original_max
    }

    test {COPY basic usage for stream} {
        r del mystream mynewstream
        for {set i 0} {$i < 1000} {incr i} {
            r XADD mystream * item 2 value b
        }
        r copy mystream mynewstream
        set digest [r debug digest-value mystream]
        assert_equal $digest [r debug digest-value mynewstream]
        assert_equal 1 [r object refcount mystream]
        assert_equal 1 [r object refcount mynewstream]
        r del mystream
        assert_equal $digest [r debug digest-value mynewstream]
    }

    test {COPY basic usage for stream-cgroups} {
        r del x
        r XADD x 100 a 1
        set id [r XADD x 101 b 1]
        r XADD x 102 c 1
        r XADD x 103 e 1
        r XADD x 104 f 1
        r XADD x 105 g 1
        r XGROUP CREATE x g1 0
        r XGROUP CREATE x g2 0
        r XREADGROUP GROUP g1 Alice COUNT 1 STREAMS x >
        r XREADGROUP GROUP g1 Bob COUNT 1 STREAMS x >
        r XREADGROUP GROUP g1 Bob NOACK COUNT 1 STREAMS x >
        r XREADGROUP GROUP g2 Charlie COUNT 4 STREAMS x >
        r XGROUP SETID x g1 $id
        r XREADGROUP GROUP g1 Dave COUNT 3 STREAMS x >
        r XDEL x 103

        r copy x newx
        set info [r xinfo stream x full]
        assert_equal $info [r xinfo stream newx full]
        assert_equal 1 [r object refcount x]
        assert_equal 1 [r object refcount newx]
        r del x
        assert_equal $info [r xinfo stream newx full]
        r flushdb
    }

    test {MOVE basic usage} {
        r set mykey foobar
        r move mykey 10
        set res {}
        lappend res [r exists mykey]
        lappend res [r dbsize]
        r select 10
        lappend res [r get mykey]
        lappend res [r dbsize]
        r select 9
        format $res
    } [list 0 0 foobar 1]

    test {MOVE against key existing in the target DB} {
        r set mykey hello
        r move mykey 10
    } {0}

    test {MOVE against non-integer DB (#1428)} {
        r set mykey hello
        catch {r move mykey notanumber} e
        set e
    } {ERR value is not an integer or out of range}

    test {MOVE can move key expire metadata as well} {
        r select 10
        r flushdb
        r select 9
        r set mykey foo ex 100
        r move mykey 10
        assert {[r ttl mykey] == -2}
        r select 10
        assert {[r ttl mykey] > 0 && [r ttl mykey] <= 100}
        assert {[r get mykey] eq "foo"}
        r select 9
    }

    test {MOVE does not create an expire if it does not exist} {
        r select 10
        r flushdb
        r select 9
        r set mykey foo
        r move mykey 10
        assert {[r ttl mykey] == -2}
        r select 10
        assert {[r ttl mykey] == -1}
        assert {[r get mykey] eq "foo"}
        r select 9
    }

    test {SET/GET keys in different DBs} {
        r set a hello
        r set b world
        r select 10
        r set a foo
        r set b bared
        r select 9
        set res {}
        lappend res [r get a]
        lappend res [r get b]
        r select 10
        lappend res [r get a]
        lappend res [r get b]
        r select 9
        format $res
    } {hello world foo bared}

    test {RANDOMKEY} {
        r flushdb
        r set foo x
        r set bar y
        set foo_seen 0
        set bar_seen 0
        for {set i 0} {$i < 100} {incr i} {
            set rkey [r randomkey]
            if {$rkey eq {foo}} {
                set foo_seen 1
            }
            if {$rkey eq {bar}} {
                set bar_seen 1
            }
        }
        list $foo_seen $bar_seen
    } {1 1}

    test {RANDOMKEY against empty DB} {
        r flushdb
        r randomkey
    } {}

    test {RANDOMKEY regression 1} {
        r flushdb
        r set x 10
        r del x
        r randomkey
    } {}

    test {KEYS * two times with long key, Github issue #1208} {
        r flushdb
        r set dlskeriewrioeuwqoirueioqwrueoqwrueqw test
        r keys *
        r keys *
    } {dlskeriewrioeuwqoirueioqwrueoqwrueqw}

    test {Regression for pattern matching long nested loops} {
        r flushdb
        r SET aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 1
        r KEYS "a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*b"
    } {}
}
