mirror of https://github.com/ledisdb/ledisdb.git
348 lines
10 KiB
Go
348 lines
10 KiB
Go
|
package rdb_test
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/cupcake/rdb"
|
||
|
. "gopkg.in/check.v1"
|
||
|
)
|
||
|
|
||
|
// Hook gocheck into the gotest runner.
|
||
|
func Test(t *testing.T) { TestingT(t) }
|
||
|
|
||
|
type DecoderSuite struct{}
|
||
|
|
||
|
var _ = Suite(&DecoderSuite{})
|
||
|
|
||
|
func (s *DecoderSuite) TestEmptyRDB(c *C) {
|
||
|
r := decodeRDB("empty_database")
|
||
|
c.Assert(r.started, Equals, 1)
|
||
|
c.Assert(r.ended, Equals, 1)
|
||
|
c.Assert(len(r.dbs), Equals, 0)
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestMultipleDatabases(c *C) {
|
||
|
r := decodeRDB("multiple_databases")
|
||
|
c.Assert(len(r.dbs), Equals, 2)
|
||
|
_, ok := r.dbs[1]
|
||
|
c.Assert(ok, Equals, false)
|
||
|
c.Assert(r.dbs[0]["key_in_zeroth_database"], Equals, "zero")
|
||
|
c.Assert(r.dbs[2]["key_in_second_database"], Equals, "second")
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestExpiry(c *C) {
|
||
|
r := decodeRDB("keys_with_expiry")
|
||
|
c.Assert(r.expiries[0]["expires_ms_precision"], Equals, int64(1671963072573))
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestMixedExpiry(c *C) {
|
||
|
r := decodeRDB("keys_with_mixed_expiry")
|
||
|
c.Assert(r.expiries[0]["key01"], Not(Equals), int64(0))
|
||
|
c.Assert(r.expiries[0]["key04"], Not(Equals), int64(0))
|
||
|
|
||
|
c.Assert(r.expiries[0]["key02"], Equals, int64(0))
|
||
|
c.Assert(r.expiries[0]["key03"], Equals, int64(0))
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestIntegerKeys(c *C) {
|
||
|
r := decodeRDB("integer_keys")
|
||
|
c.Assert(r.dbs[0]["125"], Equals, "Positive 8 bit integer")
|
||
|
c.Assert(r.dbs[0]["43947"], Equals, "Positive 16 bit integer")
|
||
|
c.Assert(r.dbs[0]["183358245"], Equals, "Positive 32 bit integer")
|
||
|
c.Assert(r.dbs[0]["-123"], Equals, "Negative 8 bit integer")
|
||
|
c.Assert(r.dbs[0]["-29477"], Equals, "Negative 16 bit integer")
|
||
|
c.Assert(r.dbs[0]["-183358245"], Equals, "Negative 32 bit integer")
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestStringKeyWithCompression(c *C) {
|
||
|
r := decodeRDB("easily_compressible_string_key")
|
||
|
c.Assert(r.dbs[0][strings.Repeat("a", 200)], Equals, "Key that redis should compress easily")
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestZipmapWithCompression(c *C) {
|
||
|
r := decodeRDB("zipmap_that_compresses_easily")
|
||
|
zm := r.dbs[0]["zipmap_compresses_easily"].(map[string]string)
|
||
|
c.Assert(zm["a"], Equals, "aa")
|
||
|
c.Assert(zm["aa"], Equals, "aaaa")
|
||
|
c.Assert(zm["aaaaa"], Equals, "aaaaaaaaaaaaaa")
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestZipmap(c *C) {
|
||
|
r := decodeRDB("zipmap_that_doesnt_compress")
|
||
|
zm := r.dbs[0]["zimap_doesnt_compress"].(map[string]string)
|
||
|
c.Assert(zm["MKD1G6"], Equals, "2")
|
||
|
c.Assert(zm["YNNXK"], Equals, "F7TI")
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestZipmapWitBigValues(c *C) {
|
||
|
r := decodeRDB("zipmap_with_big_values")
|
||
|
zm := r.dbs[0]["zipmap_with_big_values"].(map[string]string)
|
||
|
c.Assert(len(zm["253bytes"]), Equals, 253)
|
||
|
c.Assert(len(zm["254bytes"]), Equals, 254)
|
||
|
c.Assert(len(zm["255bytes"]), Equals, 255)
|
||
|
c.Assert(len(zm["300bytes"]), Equals, 300)
|
||
|
c.Assert(len(zm["20kbytes"]), Equals, 20000)
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestHashZiplist(c *C) {
|
||
|
r := decodeRDB("hash_as_ziplist")
|
||
|
zm := r.dbs[0]["zipmap_compresses_easily"].(map[string]string)
|
||
|
c.Assert(zm["a"], Equals, "aa")
|
||
|
c.Assert(zm["aa"], Equals, "aaaa")
|
||
|
c.Assert(zm["aaaaa"], Equals, "aaaaaaaaaaaaaa")
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestDictionary(c *C) {
|
||
|
r := decodeRDB("dictionary")
|
||
|
d := r.dbs[0]["force_dictionary"].(map[string]string)
|
||
|
c.Assert(len(d), Equals, 1000)
|
||
|
c.Assert(d["ZMU5WEJDG7KU89AOG5LJT6K7HMNB3DEI43M6EYTJ83VRJ6XNXQ"], Equals, "T63SOS8DQJF0Q0VJEZ0D1IQFCYTIPSBOUIAI9SB0OV57MQR1FI")
|
||
|
c.Assert(d["UHS5ESW4HLK8XOGTM39IK1SJEUGVV9WOPK6JYA5QBZSJU84491"], Equals, "6VULTCV52FXJ8MGVSFTZVAGK2JXZMGQ5F8OVJI0X6GEDDR27RZ")
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestZiplistWithCompression(c *C) {
|
||
|
r := decodeRDB("ziplist_that_compresses_easily")
|
||
|
for i, length := range []int{6, 12, 18, 24, 30, 36} {
|
||
|
c.Assert(r.dbs[0]["ziplist_compresses_easily"].([]string)[i], Equals, strings.Repeat("a", length))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestZiplist(c *C) {
|
||
|
r := decodeRDB("ziplist_that_doesnt_compress")
|
||
|
l := r.dbs[0]["ziplist_doesnt_compress"].([]string)
|
||
|
c.Assert(l[0], Equals, "aj2410")
|
||
|
c.Assert(l[1], Equals, "cc953a17a8e096e76a44169ad3f9ac87c5f8248a403274416179aa9fbd852344")
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestZiplistWithInts(c *C) {
|
||
|
r := decodeRDB("ziplist_with_integers")
|
||
|
expected := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "-2", "13", "25", "-61", "63", "16380", "-16000", "65535", "-65523", "4194304", "9223372036854775807"}
|
||
|
for i, x := range expected {
|
||
|
c.Assert(r.dbs[0]["ziplist_with_integers"].([]string)[i], Equals, x)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestIntSet16(c *C) {
|
||
|
r := decodeRDB("intset_16")
|
||
|
for i, x := range []string{"32764", "32765", "32766"} {
|
||
|
c.Assert(r.dbs[0]["intset_16"].([]string)[i], Equals, x)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestIntSet32(c *C) {
|
||
|
r := decodeRDB("intset_32")
|
||
|
for i, x := range []string{"2147418108", "2147418109", "2147418110"} {
|
||
|
c.Assert(r.dbs[0]["intset_32"].([]string)[i], Equals, x)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestIntSet64(c *C) {
|
||
|
r := decodeRDB("intset_64")
|
||
|
for i, x := range []string{"9223090557583032316", "9223090557583032317", "9223090557583032318"} {
|
||
|
c.Assert(r.dbs[0]["intset_64"].([]string)[i], Equals, x)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestSet(c *C) {
|
||
|
r := decodeRDB("regular_set")
|
||
|
for i, x := range []string{"beta", "delta", "alpha", "phi", "gamma", "kappa"} {
|
||
|
c.Assert(r.dbs[0]["regular_set"].([]string)[i], Equals, x)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestZSetZiplist(c *C) {
|
||
|
r := decodeRDB("sorted_set_as_ziplist")
|
||
|
z := r.dbs[0]["sorted_set_as_ziplist"].(map[string]float64)
|
||
|
c.Assert(z["8b6ba6718a786daefa69438148361901"], Equals, float64(1))
|
||
|
c.Assert(z["cb7a24bb7528f934b841b34c3a73e0c7"], Equals, float64(2.37))
|
||
|
c.Assert(z["523af537946b79c4f8369ed39ba78605"], Equals, float64(3.423))
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestRDBv5(c *C) {
|
||
|
r := decodeRDB("rdb_version_5_with_checksum")
|
||
|
c.Assert(r.dbs[0]["abcd"], Equals, "efgh")
|
||
|
c.Assert(r.dbs[0]["foo"], Equals, "bar")
|
||
|
c.Assert(r.dbs[0]["bar"], Equals, "baz")
|
||
|
c.Assert(r.dbs[0]["abcdef"], Equals, "abcdef")
|
||
|
c.Assert(r.dbs[0]["longerstring"], Equals, "thisisalongerstring.idontknowwhatitmeans")
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestRDBv7(c *C) {
|
||
|
r := decodeRDB("rdb_v7_list_quicklist")
|
||
|
c.Assert(r.aux["redis-ver"], Equals, "3.2.0")
|
||
|
c.Assert(r.dbSize[0], Equals, uint32(1))
|
||
|
c.Assert(r.expiresSize[0], Equals, uint32(0))
|
||
|
z := r.dbs[0]["foo"].([]string)
|
||
|
c.Assert(z[0], Equals, "bar")
|
||
|
c.Assert(z[1], Equals, "baz")
|
||
|
c.Assert(z[2], Equals, "boo")
|
||
|
}
|
||
|
|
||
|
func (s *DecoderSuite) TestDumpDecoder(c *C) {
|
||
|
r := &FakeRedis{}
|
||
|
err := rdb.DecodeDump([]byte("\u0000\xC0\n\u0006\u0000\xF8r?\xC5\xFB\xFB_("), 1, []byte("test"), 123, r)
|
||
|
if err != nil {
|
||
|
c.Error(err)
|
||
|
}
|
||
|
c.Assert(r.dbs[1]["test"], Equals, "10")
|
||
|
}
|
||
|
|
||
|
func decodeRDB(name string) *FakeRedis {
|
||
|
r := &FakeRedis{}
|
||
|
f, err := os.Open("fixtures/" + name + ".rdb")
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
err = rdb.Decode(f, r)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
return r
|
||
|
}
|
||
|
|
||
|
type FakeRedis struct {
|
||
|
dbs map[int]map[string]interface{}
|
||
|
lengths map[int]map[string]int
|
||
|
expiries map[int]map[string]int64
|
||
|
dbSize map[int]uint32
|
||
|
expiresSize map[int]uint32
|
||
|
|
||
|
cdb int
|
||
|
started int
|
||
|
ended int
|
||
|
|
||
|
aux map[string]string
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) setExpiry(key []byte, expiry int64) {
|
||
|
r.expiries[r.cdb][string(key)] = expiry
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) setLength(key []byte, length int64) {
|
||
|
r.lengths[r.cdb][string(key)] = int(length)
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) getLength(key []byte) int {
|
||
|
return int(r.lengths[r.cdb][string(key)])
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) db() map[string]interface{} {
|
||
|
return r.dbs[r.cdb]
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) StartRDB() {
|
||
|
r.started++
|
||
|
r.dbs = make(map[int]map[string]interface{})
|
||
|
r.expiries = make(map[int]map[string]int64)
|
||
|
r.lengths = make(map[int]map[string]int)
|
||
|
r.aux = make(map[string]string)
|
||
|
r.dbSize = make(map[int]uint32)
|
||
|
r.expiresSize = make(map[int]uint32)
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) StartDatabase(n int) {
|
||
|
r.dbs[n] = make(map[string]interface{})
|
||
|
r.expiries[n] = make(map[string]int64)
|
||
|
r.lengths[n] = make(map[string]int)
|
||
|
r.cdb = n
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) Set(key, value []byte, expiry int64) {
|
||
|
r.setExpiry(key, expiry)
|
||
|
r.db()[string(key)] = string(value)
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) StartHash(key []byte, length, expiry int64) {
|
||
|
r.setExpiry(key, expiry)
|
||
|
r.setLength(key, length)
|
||
|
r.db()[string(key)] = make(map[string]string)
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) Hset(key, field, value []byte) {
|
||
|
r.db()[string(key)].(map[string]string)[string(field)] = string(value)
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) EndHash(key []byte) {
|
||
|
actual := len(r.db()[string(key)].(map[string]string))
|
||
|
if actual != r.getLength(key) {
|
||
|
panic(fmt.Sprintf("wrong length for key %s got %d, expected %d", key, actual, r.getLength(key)))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) StartSet(key []byte, cardinality, expiry int64) {
|
||
|
r.setExpiry(key, expiry)
|
||
|
r.setLength(key, cardinality)
|
||
|
r.db()[string(key)] = make([]string, 0, cardinality)
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) Sadd(key, member []byte) {
|
||
|
r.db()[string(key)] = append(r.db()[string(key)].([]string), string(member))
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) EndSet(key []byte) {
|
||
|
actual := len(r.db()[string(key)].([]string))
|
||
|
if actual != r.getLength(key) {
|
||
|
panic(fmt.Sprintf("wrong length for key %s got %d, expected %d", key, actual, r.getLength(key)))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) StartList(key []byte, length, expiry int64) {
|
||
|
r.setExpiry(key, expiry)
|
||
|
r.setLength(key, length)
|
||
|
cap := length
|
||
|
if length < 0 {
|
||
|
cap = 1
|
||
|
}
|
||
|
r.db()[string(key)] = make([]string, 0, cap)
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) Rpush(key, value []byte) {
|
||
|
r.db()[string(key)] = append(r.db()[string(key)].([]string), string(value))
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) EndList(key []byte) {
|
||
|
actual := len(r.db()[string(key)].([]string))
|
||
|
if actual != r.getLength(key) && r.getLength(key) >= 0 {
|
||
|
panic(fmt.Sprintf("wrong length for key %s got %d, expected %d", key, actual, r.getLength(key)))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) StartZSet(key []byte, cardinality, expiry int64) {
|
||
|
r.setExpiry(key, expiry)
|
||
|
r.setLength(key, cardinality)
|
||
|
r.db()[string(key)] = make(map[string]float64)
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) Zadd(key []byte, score float64, member []byte) {
|
||
|
r.db()[string(key)].(map[string]float64)[string(member)] = score
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) EndZSet(key []byte) {
|
||
|
actual := len(r.db()[string(key)].(map[string]float64))
|
||
|
if actual != r.getLength(key) {
|
||
|
panic(fmt.Sprintf("wrong length for key %s got %d, expected %d", key, actual, r.getLength(key)))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) EndDatabase(n int) {
|
||
|
if n != r.cdb {
|
||
|
panic(fmt.Sprintf("database end called with %d, expected %d", n, r.cdb))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) EndRDB() {
|
||
|
r.ended++
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) Aux(key, value []byte) {
|
||
|
r.aux[string(key)] = string(value)
|
||
|
}
|
||
|
|
||
|
func (r *FakeRedis) ResizeDatabase(dbSize, expiresSize uint32) {
|
||
|
r.dbSize[r.cdb] = dbSize
|
||
|
r.expiresSize[r.cdb] = expiresSize
|
||
|
}
|