diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index f12976b44..67350eb98 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -87,11 +87,6 @@ "ImportPath": "github.com/fd/go-nat", "Rev": "50e7633d5f27d81490026a13e5b92d2e42d8c6bb" }, - { - "ImportPath": "github.com/fzzy/radix/redis", - "Comment": "v0.5.1", - "Rev": "27a863cdffdb0998d13e1e11992b18489aeeaa25" - }, { "ImportPath": "github.com/gogo/protobuf/io", "Rev": "0ac967c269268f1af7d9bcc7927ccc9a589b2b36" diff --git a/Godeps/_workspace/src/github.com/fzzy/radix/redis/client.go b/Godeps/_workspace/src/github.com/fzzy/radix/redis/client.go deleted file mode 100644 index 21a3dace8..000000000 --- a/Godeps/_workspace/src/github.com/fzzy/radix/redis/client.go +++ /dev/null @@ -1,244 +0,0 @@ -package redis - -import ( - "bufio" - "errors" - "net" - "strings" - "time" - - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/fzzy/radix/redis/resp" -) - -const ( - bufSize int = 4096 -) - -//* Common errors - -var LoadingError error = errors.New("server is busy loading dataset in memory") -var PipelineQueueEmptyError error = errors.New("pipeline queue empty") - -//* Client - -// Client describes a Redis client. -type Client struct { - // The connection the client talks to redis over. Don't touch this unless - // you know what you're doing. - Conn net.Conn - timeout time.Duration - reader *bufio.Reader - pending []*request - completed []*Reply -} - -// request describes a client's request to the redis server -type request struct { - cmd string - args []interface{} -} - -// Dial connects to the given Redis server with the given timeout, which will be -// used as the read/write timeout when communicating with redis -func DialTimeout(network, addr string, timeout time.Duration) (*Client, error) { - // establish a connection - conn, err := net.Dial(network, addr) - if err != nil { - return nil, err - } - - c := new(Client) - c.Conn = conn - c.timeout = timeout - c.reader = bufio.NewReaderSize(conn, bufSize) - return c, nil -} - -// Dial connects to the given Redis server. -func Dial(network, addr string) (*Client, error) { - return DialTimeout(network, addr, time.Duration(0)) -} - -//* Public methods - -// Close closes the connection. -func (c *Client) Close() error { - return c.Conn.Close() -} - -// Cmd calls the given Redis command. -func (c *Client) Cmd(cmd string, args ...interface{}) *Reply { - err := c.writeRequest(&request{cmd, args}) - if err != nil { - return &Reply{Type: ErrorReply, Err: err} - } - return c.ReadReply() -} - -// Append adds the given call to the pipeline queue. -// Use GetReply() to read the reply. -func (c *Client) Append(cmd string, args ...interface{}) { - c.pending = append(c.pending, &request{cmd, args}) -} - -// GetReply returns the reply for the next request in the pipeline queue. -// Error reply with PipelineQueueEmptyError is returned, -// if the pipeline queue is empty. -func (c *Client) GetReply() *Reply { - if len(c.completed) > 0 { - r := c.completed[0] - c.completed = c.completed[1:] - return r - } - c.completed = nil - - if len(c.pending) == 0 { - return &Reply{Type: ErrorReply, Err: PipelineQueueEmptyError} - } - - nreqs := len(c.pending) - err := c.writeRequest(c.pending...) - c.pending = nil - if err != nil { - return &Reply{Type: ErrorReply, Err: err} - } - r := c.ReadReply() - c.completed = make([]*Reply, nreqs-1) - for i := 0; i < nreqs-1; i++ { - c.completed[i] = c.ReadReply() - } - - return r -} - -//* Private methods - -func (c *Client) setReadTimeout() { - if c.timeout != 0 { - c.Conn.SetReadDeadline(time.Now().Add(c.timeout)) - } -} - -func (c *Client) setWriteTimeout() { - if c.timeout != 0 { - c.Conn.SetWriteDeadline(time.Now().Add(c.timeout)) - } -} - -// This will read a redis reply off of the connection without sending anything -// first (useful after you've sent a SUSBSCRIBE command). This will block until -// a reply is received or the timeout is reached. On timeout an ErrorReply will -// be returned, you can check if it's a timeout like so: -// -// r := conn.ReadReply() -// if r.Err != nil { -// if t, ok := r.Err.(*net.OpError); ok && t.Timeout() { -// // Is timeout -// } else { -// // Not timeout -// } -// } -// -// Note: this is a more low-level function, you really shouldn't have to -// actually use it unless you're writing your own pub/sub code -func (c *Client) ReadReply() *Reply { - c.setReadTimeout() - return c.parse() -} - -func (c *Client) writeRequest(requests ...*request) error { - c.setWriteTimeout() - for i := range requests { - req := make([]interface{}, 0, len(requests[i].args)+1) - req = append(req, requests[i].cmd) - req = append(req, requests[i].args...) - err := resp.WriteArbitraryAsFlattenedStrings(c.Conn, req) - if err != nil { - c.Close() - return err - } - } - return nil -} - -func (c *Client) parse() *Reply { - m, err := resp.ReadMessage(c.reader) - if err != nil { - if t, ok := err.(*net.OpError); !ok || !t.Timeout() { - // close connection except timeout - c.Close() - } - return &Reply{Type: ErrorReply, Err: err} - } - r, err := messageToReply(m) - if err != nil { - return &Reply{Type: ErrorReply, Err: err} - } - return r -} - -// The error return parameter is for bubbling up parse errors and the like, if -// the error is sent by redis itself as an Err message type, then it will be -// sent back as an actual Reply (wrapped in a CmdError) -func messageToReply(m *resp.Message) (*Reply, error) { - r := &Reply{} - - switch m.Type { - case resp.Err: - errMsg, err := m.Err() - if err != nil { - return nil, err - } - if strings.HasPrefix(errMsg.Error(), "LOADING") { - err = LoadingError - } else { - err = &CmdError{errMsg} - } - r.Type = ErrorReply - r.Err = err - - case resp.SimpleStr: - status, err := m.Bytes() - if err != nil { - return nil, err - } - r.Type = StatusReply - r.buf = status - - case resp.Int: - i, err := m.Int() - if err != nil { - return nil, err - } - r.Type = IntegerReply - r.int = i - - case resp.BulkStr: - b, err := m.Bytes() - if err != nil { - return nil, err - } - r.Type = BulkReply - r.buf = b - - case resp.Nil: - r.Type = NilReply - - case resp.Array: - ms, err := m.Array() - if err != nil { - return nil, err - } - r.Type = MultiReply - r.Elems = make([]*Reply, len(ms)) - for i := range ms { - r.Elems[i], err = messageToReply(ms[i]) - if err != nil { - return nil, err - } - } - } - - return r, nil - -} diff --git a/Godeps/_workspace/src/github.com/fzzy/radix/redis/client_test.go b/Godeps/_workspace/src/github.com/fzzy/radix/redis/client_test.go deleted file mode 100644 index afbe830a0..000000000 --- a/Godeps/_workspace/src/github.com/fzzy/radix/redis/client_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package redis - -import ( - "bufio" - "bytes" - "github.com/stretchr/testify/assert" - . "testing" - "time" -) - -func dial(t *T) *Client { - client, err := DialTimeout("tcp", "127.0.0.1:6379", 10*time.Second) - assert.Nil(t, err) - return client -} - -func TestCmd(t *T) { - c := dial(t) - v, _ := c.Cmd("echo", "Hello, World!").Str() - assert.Equal(t, "Hello, World!", v) - - // Test that a bad command properly returns a *CmdError - err := c.Cmd("non-existant-cmd").Err - assert.NotEqual(t, "", err.(*CmdError).Error()) - - // Test that application level errors propagate correctly - c.Cmd("sadd", "foo", "bar") - _, err = c.Cmd("get", "foo").Str() - assert.NotEqual(t, "", err.(*CmdError).Error()) -} - -func TestPipeline(t *T) { - c := dial(t) - c.Append("echo", "foo") - c.Append("echo", "bar") - c.Append("echo", "zot") - - v, _ := c.GetReply().Str() - assert.Equal(t, "foo", v) - - v, _ = c.GetReply().Str() - assert.Equal(t, "bar", v) - - v, _ = c.GetReply().Str() - assert.Equal(t, "zot", v) - - r := c.GetReply() - assert.Equal(t, ErrorReply, r.Type) - assert.Equal(t, PipelineQueueEmptyError, r.Err) -} - -func TestParse(t *T) { - c := dial(t) - - parseString := func(b string) *Reply { - c.reader = bufio.NewReader(bytes.NewBufferString(b)) - return c.parse() - } - - // missing \n trailing - r := parseString("foo") - assert.Equal(t, ErrorReply, r.Type) - assert.NotNil(t, r.Err) - - // error reply - r = parseString("-ERR unknown command 'foobar'\r\n") - assert.Equal(t, ErrorReply, r.Type) - assert.Equal(t, "ERR unknown command 'foobar'", r.Err.Error()) - - // LOADING error - r = parseString("-LOADING Redis is loading the dataset in memory\r\n") - assert.Equal(t, ErrorReply, r.Type) - assert.Equal(t, LoadingError, r.Err) - - // status reply - r = parseString("+OK\r\n") - assert.Equal(t, StatusReply, r.Type) - assert.Equal(t, []byte("OK"), r.buf) - - // integer reply - r = parseString(":1337\r\n") - assert.Equal(t, IntegerReply, r.Type) - assert.Equal(t, int64(1337), r.int) - - // null bulk reply - r = parseString("$-1\r\n") - assert.Equal(t, NilReply, r.Type) - - // bulk reply - r = parseString("$6\r\nfoobar\r\n") - assert.Equal(t, BulkReply, r.Type) - assert.Equal(t, []byte("foobar"), r.buf) - - // null multi bulk reply - r = parseString("*-1\r\n") - assert.Equal(t, NilReply, r.Type) - - // multi bulk reply - r = parseString("*5\r\n:0\r\n:1\r\n:2\r\n:3\r\n$6\r\nfoobar\r\n") - assert.Equal(t, MultiReply, r.Type) - assert.Equal(t, 5, len(r.Elems)) - for i := 0; i < 4; i++ { - assert.Equal(t, int64(i), r.Elems[i].int) - } - assert.Equal(t, []byte("foobar"), r.Elems[4].buf) -} diff --git a/Godeps/_workspace/src/github.com/fzzy/radix/redis/doc.go b/Godeps/_workspace/src/github.com/fzzy/radix/redis/doc.go deleted file mode 100644 index 37c2527db..000000000 --- a/Godeps/_workspace/src/github.com/fzzy/radix/redis/doc.go +++ /dev/null @@ -1,87 +0,0 @@ -// A simple client for connecting and interacting with redis. -// -// To import inside your package do: -// -// import "github.com/fzzy/radix/redis" -// -// Connecting -// -// Use either Dial or DialTimeout: -// -// client, err := redis.Dial("tcp", "localhost:6379") -// if err != nil { -// // handle err -// } -// -// Make sure to call Close on the client if you want to clean it up before the -// end of the program. -// -// Cmd and Reply -// -// The Cmd method returns a Reply, which has methods for converting to various -// types. Each of these methods returns an error which can either be a -// connection error (e.g. timeout), an application error (e.g. key is wrong -// type), or a conversion error (e.g. cannot convert to integer). You can also -// directly check the error using the Err field: -// -// foo, err := client.Cmd("GET", "foo").Str() -// if err != nil { -// // handle err -// } -// -// // Checking Err field directly -// -// err = client.Cmd("PING").Err -// if err != nil { -// // handle err -// } -// -// Multi Replies -// -// The elements to Multi replies can be accessed as strings using List or -// ListBytes, or you can use the Elems field for more low-level access: -// -// r := client.Cmd("MGET", "foo", "bar", "baz") -// -// // This: -// for _, elemStr := range r.List() { -// fmt.Println(elemStr) -// } -// -// // is equivalent to this: -// for i := range r.Elems { -// elemStr, _ := r.Elems[i].Str() -// fmt.Println(elemStr) -// } -// -// Pipelining -// -// Pipelining is when the client sends a bunch of commands to the server at -// once, and only once all the commands have been sent does it start reading the -// replies off the socket. This is supported using the Append and GetReply -// methods. Append will simply append the command to a buffer without sending -// it, the first time GetReply is called it will send all the commands in the -// buffer and return the Reply for the first command that was sent. Subsequent -// calls to GetReply return Replys for subsequent commands: -// -// client.Append("GET", "foo") -// client.Append("SET", "bar", "foo") -// client.Append("DEL", "baz") -// -// // Read GET foo reply -// foo, err := client.GetReply().Str() -// if err != nil { -// // handle err -// } -// -// // Read SET bar foo reply -// if err := client.GetReply().Err; err != nil { -// // handle err -// } -// -// // Read DEL baz reply -// if err := client.GetReply().Err; err != nil { -// // handle err -// } -// -package redis diff --git a/Godeps/_workspace/src/github.com/fzzy/radix/redis/reply.go b/Godeps/_workspace/src/github.com/fzzy/radix/redis/reply.go deleted file mode 100644 index c4ad7c2d3..000000000 --- a/Godeps/_workspace/src/github.com/fzzy/radix/redis/reply.go +++ /dev/null @@ -1,275 +0,0 @@ -package redis - -import ( - "errors" - "strconv" - "strings" -) - -// A CmdError implements the error interface and is what is returned when the -// server returns an error on the application level (e.g. key doesn't exist or -// is the wrong type), as opposed to a connection/transport error. -// -// You can test if a reply is a CmdError like so: -// -// r := conn.Cmd("GET", "key-which-isnt-a-string") -// if r.Err != nil { -// if cerr, ok := r.Err.(*redis.CmdError); ok { -// // Is CmdError -// } else { -// // Is other error -// } -// } -type CmdError struct { - Err error -} - -func (cerr *CmdError) Error() string { - return cerr.Err.Error() -} - -// Returns true if error returned was due to the redis server being read only -func (cerr *CmdError) Readonly() bool { - return strings.HasPrefix(cerr.Err.Error(), "READONLY") -} - -//* Reply - -/* -ReplyType describes type of a reply. - -Possible values are: - -StatusReply -- status reply -ErrorReply -- error reply -IntegerReply -- integer reply -NilReply -- nil reply -BulkReply -- bulk reply -MultiReply -- multi bulk reply -*/ -type ReplyType uint8 - -const ( - StatusReply ReplyType = iota - ErrorReply - IntegerReply - NilReply - BulkReply - MultiReply -) - -// Reply holds a Redis reply. -type Reply struct { - Type ReplyType // Reply type - Elems []*Reply // Sub-replies - Err error // Reply error - buf []byte - int int64 -} - -// Bytes returns the reply value as a byte string or -// an error, if the reply type is not StatusReply or BulkReply. -func (r *Reply) Bytes() ([]byte, error) { - if r.Type == ErrorReply { - return nil, r.Err - } - if !(r.Type == StatusReply || r.Type == BulkReply) { - return nil, errors.New("string value is not available for this reply type") - } - - return r.buf, nil -} - -// Str is a convenience method for calling Reply.Bytes() and converting it to string -func (r *Reply) Str() (string, error) { - b, err := r.Bytes() - if err != nil { - return "", err - } - - return string(b), nil -} - -// Int64 returns the reply value as a int64 or an error, -// if the reply type is not IntegerReply or the reply type -// BulkReply could not be parsed to an int64. -func (r *Reply) Int64() (int64, error) { - if r.Type == ErrorReply { - return 0, r.Err - } - if r.Type != IntegerReply { - s, err := r.Str() - if err == nil { - i64, err := strconv.ParseInt(s, 10, 64) - if err != nil { - return 0, errors.New("failed to parse integer value from string value") - } else { - return i64, nil - } - } - - return 0, errors.New("integer value is not available for this reply type") - } - - return r.int, nil -} - -// Int is a convenience method for calling Reply.Int64() and converting it to int. -func (r *Reply) Int() (int, error) { - i64, err := r.Int64() - if err != nil { - return 0, err - } - - return int(i64), nil -} - -// Bool returns false, if the reply value equals to 0 or "0", otherwise true; or -// an error, if the reply type is not IntegerReply or BulkReply. -func (r *Reply) Bool() (bool, error) { - if r.Type == ErrorReply { - return false, r.Err - } - i, err := r.Int() - if err == nil { - if i == 0 { - return false, nil - } - - return true, nil - } - - s, err := r.Str() - if err == nil { - if s == "0" { - return false, nil - } - - return true, nil - } - - return false, errors.New("boolean value is not available for this reply type") -} - -// List returns a multi bulk reply as a slice of strings or an error. -// The reply type must be MultiReply and its elements' types must all be either BulkReply or NilReply. -// Nil elements are returned as empty strings. -// Useful for list commands. -func (r *Reply) List() ([]string, error) { - // Doing all this in two places instead of just calling ListBytes() so we don't have - // to iterate twice - if r.Type == ErrorReply { - return nil, r.Err - } - if r.Type != MultiReply { - return nil, errors.New("reply type is not MultiReply") - } - - strings := make([]string, len(r.Elems)) - for i, v := range r.Elems { - if v.Type == BulkReply { - strings[i] = string(v.buf) - } else if v.Type == NilReply { - strings[i] = "" - } else { - return nil, errors.New("element type is not BulkReply or NilReply") - } - } - - return strings, nil -} - -// ListBytes returns a multi bulk reply as a slice of bytes slices or an error. -// The reply type must be MultiReply and its elements' types must all be either BulkReply or NilReply. -// Nil elements are returned as nil. -// Useful for list commands. -func (r *Reply) ListBytes() ([][]byte, error) { - if r.Type == ErrorReply { - return nil, r.Err - } - if r.Type != MultiReply { - return nil, errors.New("reply type is not MultiReply") - } - - bufs := make([][]byte, len(r.Elems)) - for i, v := range r.Elems { - if v.Type == BulkReply { - bufs[i] = v.buf - } else if v.Type == NilReply { - bufs[i] = nil - } else { - return nil, errors.New("element type is not BulkReply or NilReply") - } - } - - return bufs, nil -} - -// Hash returns a multi bulk reply as a map[string]string or an error. -// The reply type must be MultiReply, -// it must have an even number of elements, -// they must be in a "key value key value..." order and -// values must all be either BulkReply or NilReply. -// Nil values are returned as empty strings. -// Useful for hash commands. -func (r *Reply) Hash() (map[string]string, error) { - if r.Type == ErrorReply { - return nil, r.Err - } - rmap := map[string]string{} - - if r.Type != MultiReply { - return nil, errors.New("reply type is not MultiReply") - } - - if len(r.Elems)%2 != 0 { - return nil, errors.New("reply has odd number of elements") - } - - for i := 0; i < len(r.Elems)/2; i++ { - var val string - - key, err := r.Elems[i*2].Str() - if err != nil { - return nil, errors.New("key element has no string reply") - } - - v := r.Elems[i*2+1] - if v.Type == BulkReply { - val = string(v.buf) - rmap[key] = val - } else if v.Type == NilReply { - } else { - return nil, errors.New("value element type is not BulkReply or NilReply") - } - } - - return rmap, nil -} - -// String returns a string representation of the reply and its sub-replies. -// This method is for debugging. -// Use method Reply.Str() for reading string reply. -func (r *Reply) String() string { - switch r.Type { - case ErrorReply: - return r.Err.Error() - case StatusReply: - fallthrough - case BulkReply: - return string(r.buf) - case IntegerReply: - return strconv.FormatInt(r.int, 10) - case NilReply: - return "" - case MultiReply: - s := "[ " - for _, e := range r.Elems { - s = s + e.String() + " " - } - return s + "]" - } - - // This should never execute - return "" -} diff --git a/Godeps/_workspace/src/github.com/fzzy/radix/redis/reply_test.go b/Godeps/_workspace/src/github.com/fzzy/radix/redis/reply_test.go deleted file mode 100644 index 0002966ab..000000000 --- a/Godeps/_workspace/src/github.com/fzzy/radix/redis/reply_test.go +++ /dev/null @@ -1,125 +0,0 @@ -package redis - -import ( - "github.com/stretchr/testify/assert" - . "testing" -) - -func TestStr(t *T) { - r := &Reply{Type: ErrorReply, Err: LoadingError} - _, err := r.Str() - assert.Equal(t, LoadingError, err) - - r = &Reply{Type: IntegerReply} - _, err = r.Str() - assert.NotNil(t, err) - - r = &Reply{Type: StatusReply, buf: []byte("foo")} - b, err := r.Str() - assert.Nil(t, err) - assert.Equal(t, "foo", b) - - r = &Reply{Type: BulkReply, buf: []byte("foo")} - b, err = r.Str() - assert.Nil(t, err) - assert.Equal(t, "foo", b) -} - -func TestBytes(t *T) { - r := &Reply{Type: BulkReply, buf: []byte("foo")} - b, err := r.Bytes() - assert.Nil(t, err) - assert.Equal(t, []byte("foo"), b) -} - -func TestInt64(t *T) { - r := &Reply{Type: ErrorReply, Err: LoadingError} - _, err := r.Int64() - assert.Equal(t, LoadingError, err) - - r = &Reply{Type: IntegerReply, int: 5} - b, err := r.Int64() - assert.Nil(t, err) - assert.Equal(t, int64(5), b) - - r = &Reply{Type: BulkReply, buf: []byte("5")} - b, err = r.Int64() - assert.Nil(t, err) - assert.Equal(t, int64(5), b) - - r = &Reply{Type: BulkReply, buf: []byte("foo")} - _, err = r.Int64() - assert.NotNil(t, err) -} - -func TestInt(t *T) { - r := &Reply{Type: IntegerReply, int: 5} - b, err := r.Int() - assert.Nil(t, err) - assert.Equal(t, 5, b) -} - -func TestBool(t *T) { - r := &Reply{Type: IntegerReply, int: 0} - b, err := r.Bool() - assert.Nil(t, err) - assert.Equal(t, false, b) - - r = &Reply{Type: StatusReply, buf: []byte("0")} - b, err = r.Bool() - assert.Nil(t, err) - assert.Equal(t, false, b) - - r = &Reply{Type: IntegerReply, int: 2} - b, err = r.Bool() - assert.Nil(t, err) - assert.Equal(t, true, b) - - r = &Reply{Type: NilReply} - _, err = r.Bool() - assert.NotNil(t, err) -} - -func TestList(t *T) { - r := &Reply{Type: MultiReply} - r.Elems = make([]*Reply, 3) - r.Elems[0] = &Reply{Type: BulkReply, buf: []byte("0")} - r.Elems[1] = &Reply{Type: NilReply} - r.Elems[2] = &Reply{Type: BulkReply, buf: []byte("2")} - l, err := r.List() - assert.Nil(t, err) - assert.Equal(t, 3, len(l)) - assert.Equal(t, "0", l[0]) - assert.Equal(t, "", l[1]) - assert.Equal(t, "2", l[2]) -} - -func TestBytesList(t *T) { - r := &Reply{Type: MultiReply} - r.Elems = make([]*Reply, 3) - r.Elems[0] = &Reply{Type: BulkReply, buf: []byte("0")} - r.Elems[1] = &Reply{Type: NilReply} - r.Elems[2] = &Reply{Type: BulkReply, buf: []byte("2")} - l, err := r.ListBytes() - assert.Nil(t, err) - assert.Equal(t, 3, len(l)) - assert.Equal(t, []byte("0"), l[0]) - assert.Nil(t, l[1]) - assert.Equal(t, []byte("2"), l[2]) -} - -func TestHash(t *T) { - r := &Reply{Type: MultiReply} - r.Elems = make([]*Reply, 6) - r.Elems[0] = &Reply{Type: BulkReply, buf: []byte("a")} - r.Elems[1] = &Reply{Type: BulkReply, buf: []byte("0")} - r.Elems[2] = &Reply{Type: BulkReply, buf: []byte("b")} - r.Elems[3] = &Reply{Type: NilReply} - r.Elems[4] = &Reply{Type: BulkReply, buf: []byte("c")} - r.Elems[5] = &Reply{Type: BulkReply, buf: []byte("2")} - h, err := r.Hash() - assert.Nil(t, err) - assert.Equal(t, "0", h["a"]) - assert.Equal(t, "", h["b"]) - assert.Equal(t, "2", h["c"]) -} diff --git a/Godeps/_workspace/src/github.com/fzzy/radix/redis/resp/resp.go b/Godeps/_workspace/src/github.com/fzzy/radix/redis/resp/resp.go deleted file mode 100644 index 5afac97ca..000000000 --- a/Godeps/_workspace/src/github.com/fzzy/radix/redis/resp/resp.go +++ /dev/null @@ -1,466 +0,0 @@ -// This package provides an easy to use interface for creating and parsing -// messages encoded in the REdis Serialization Protocol (RESP). You can check -// out more details about the protocol here: http://redis.io/topics/protocol -package resp - -import ( - "bufio" - "bytes" - "errors" - "fmt" - "io" - "reflect" - "strconv" -) - -var ( - delim = []byte{'\r', '\n'} - delimEnd = delim[len(delim)-1] -) - -type Type int - -const ( - SimpleStr Type = iota - Err - Int - BulkStr - Array - Nil -) - -const ( - simpleStrPrefix = '+' - errPrefix = '-' - intPrefix = ':' - bulkStrPrefix = '$' - arrayPrefix = '*' -) - -// Parse errors -var ( - badType = errors.New("wrong type") - parseErr = errors.New("parse error") -) - -type Message struct { - Type - val interface{} - raw []byte -} - -// NewMessagePParses the given raw message and returns a Message struct -// representing it -func NewMessage(b []byte) (*Message, error) { - return ReadMessage(bytes.NewReader(b)) -} - -// Can be used when writing to a resp stream to write a simple-string-style -// stream (e.g. +OK\r\n) instead of the default bulk-string-style strings. -// -// foo := NewSimpleString("foo") -// bar := NewSimpleString("bar") -// baz := NewSimpleString("baz") -// resp.WriteArbitrary(w, foo) -// resp.WriteArbitrary(w, []interface{}{bar, baz}) -// -func NewSimpleString(s string) *Message { - b := append(make([]byte, 0, len(s) + 3), '+') - b = append(b, []byte(s)...) - b = append(b, '\r', '\n') - return &Message{ - Type: SimpleStr, - val: s, - raw: b, - } -} - -// ReadMessage attempts to read a message object from the given io.Reader, parse -// it, and return a Message struct representing it -func ReadMessage(reader io.Reader) (*Message, error) { - r := bufio.NewReader(reader) - return bufioReadMessage(r) -} - -func bufioReadMessage(r *bufio.Reader) (*Message, error) { - b, err := r.Peek(1) - if err != nil { - return nil, err - } - switch b[0] { - case simpleStrPrefix: - return readSimpleStr(r) - case errPrefix: - return readError(r) - case intPrefix: - return readInt(r) - case bulkStrPrefix: - return readBulkStr(r) - case arrayPrefix: - return readArray(r) - default: - return nil, badType - } -} - -func readSimpleStr(r *bufio.Reader) (*Message, error) { - b, err := r.ReadBytes(delimEnd) - if err != nil { - return nil, err - } - return &Message{Type: SimpleStr, val: b[1 : len(b)-2], raw: b}, nil -} - -func readError(r *bufio.Reader) (*Message, error) { - b, err := r.ReadBytes(delimEnd) - if err != nil { - return nil, err - } - return &Message{Type: Err, val: b[1 : len(b)-2], raw: b}, nil -} - -func readInt(r *bufio.Reader) (*Message, error) { - b, err := r.ReadBytes(delimEnd) - if err != nil { - return nil, err - } - i, err := strconv.ParseInt(string(b[1:len(b)-2]), 10, 64) - if err != nil { - return nil, parseErr - } - return &Message{Type: Int, val: i, raw: b}, nil -} - -func readBulkStr(r *bufio.Reader) (*Message, error) { - b, err := r.ReadBytes(delimEnd) - if err != nil { - return nil, err - } - size, err := strconv.ParseInt(string(b[1:len(b)-2]), 10, 64) - if err != nil { - return nil, parseErr - } - if size < 0 { - return &Message{Type: Nil, raw: b}, nil - } - total := make([]byte, size) - b2 := total - var n int - for len(b2) > 0 { - n, err = r.Read(b2) - if err != nil { - return nil, err - } - b2 = b2[n:] - } - - // There's a hanging \r\n there, gotta read past it - trail := make([]byte, 2) - for i := 0; i < 2; i++ { - if c, err := r.ReadByte(); err != nil { - return nil, err - } else { - trail[i] = c - } - } - - blens := len(b) + len(total) - raw := make([]byte, 0, blens+2) - raw = append(raw, b...) - raw = append(raw, total...) - raw = append(raw, trail...) - return &Message{Type: BulkStr, val: total, raw: raw}, nil -} - -func readArray(r *bufio.Reader) (*Message, error) { - b, err := r.ReadBytes(delimEnd) - if err != nil { - return nil, err - } - size, err := strconv.ParseInt(string(b[1:len(b)-2]), 10, 64) - if err != nil { - return nil, parseErr - } - if size < 0 { - return &Message{Type: Nil, raw: b}, nil - } - - arr := make([]*Message, size) - for i := range arr { - m, err := bufioReadMessage(r) - if err != nil { - return nil, err - } - arr[i] = m - b = append(b, m.raw...) - } - return &Message{Type: Array, val: arr, raw: b}, nil -} - -// Bytes returns a byte slice representing the value of the Message. Only valid -// for a Message of type SimpleStr, Err, and BulkStr. Others will return an -// error -func (m *Message) Bytes() ([]byte, error) { - if b, ok := m.val.([]byte); ok { - return b, nil - } - return nil, badType -} - -// Str is a Convenience method around Bytes which converts the output to a -// string -func (m *Message) Str() (string, error) { - b, err := m.Bytes() - if err != nil { - return "", err - } - return string(b), nil -} - -// Int returns an int64 representing the value of the Message. Only valid for -// Int messages -func (m *Message) Int() (int64, error) { - if i, ok := m.val.(int64); ok { - return i, nil - } - return 0, badType -} - -// Err returns an error representing the value of the Message. Only valid for -// Err messages -func (m *Message) Err() (error, error) { - if m.Type != Err { - return nil, badType - } - s, err := m.Str() - if err != nil { - return nil, err - } - return errors.New(s), nil -} - -// Array returns the Message slice encompassed by this Messsage, assuming the -// Message is of type Array -func (m *Message) Array() ([]*Message, error) { - if a, ok := m.val.([]*Message); ok { - return a, nil - } - return nil, badType -} - -// WriteMessage takes in the given Message and writes its encoded form to the -// given io.Writer -func WriteMessage(w io.Writer, m *Message) error { - _, err := w.Write(m.raw) - return err -} - -// WriteArbitrary takes in any primitive golang value, or Message, and writes -// its encoded form to the given io.Writer, inferring types where appropriate. -func WriteArbitrary(w io.Writer, m interface{}) error { - b := format(m, false) - _, err := w.Write(b) - return err -} - -// WriteArbitraryAsString is similar to WriteArbitraryAsFlattenedString except -// that it won't flatten any embedded arrays. -func WriteArbitraryAsString(w io.Writer, m interface{}) error { - b := format(m, true) - _, err := w.Write(b) - return err -} - -// WriteArbitraryAsFlattenedStrings is similar to WriteArbitrary except that it -// will encode all types except Array as a BulkStr, converting the argument into -// a string first as necessary. It will also flatten any embedded arrays into a -// single long array. This is useful because commands to a redis server must be -// given as an array of bulk strings. If the argument isn't already in a slice -// or map it will be wrapped so that it is written as an Array of size one. -// -// Note that if a Message type is found it will *not* be encoded to a BulkStr, -// but will simply be passed through as whatever type it already represents. -func WriteArbitraryAsFlattenedStrings(w io.Writer, m interface{}) error { - fm := flatten(m) - return WriteArbitraryAsString(w, fm) -} - -func format(m interface{}, forceString bool) []byte { - switch mt := m.(type) { - case []byte: - return formatStr(mt) - case string: - return formatStr([]byte(mt)) - case bool: - if mt { - return formatStr([]byte("1")) - } else { - return formatStr([]byte("0")) - } - case nil: - if forceString { - return formatStr([]byte{}) - } else { - return formatNil() - } - case int: - return formatInt(int64(mt), forceString) - case int8: - return formatInt(int64(mt), forceString) - case int16: - return formatInt(int64(mt), forceString) - case int32: - return formatInt(int64(mt), forceString) - case int64: - return formatInt(mt, forceString) - case uint: - return formatInt(int64(mt), forceString) - case uint8: - return formatInt(int64(mt), forceString) - case uint16: - return formatInt(int64(mt), forceString) - case uint32: - return formatInt(int64(mt), forceString) - case uint64: - return formatInt(int64(mt), forceString) - case float32: - ft := strconv.FormatFloat(float64(mt), 'f', -1, 32) - return formatStr([]byte(ft)) - case float64: - ft := strconv.FormatFloat(mt, 'f', -1, 64) - return formatStr([]byte(ft)) - case error: - if forceString { - return formatStr([]byte(mt.Error())) - } else { - return formatErr(mt) - } - - // We duplicate the below code here a bit, since this is the common case and - // it'd be better to not get the reflect package involved here - case []interface{}: - l := len(mt) - b := make([]byte, 0, l*1024) - b = append(b, '*') - b = append(b, []byte(strconv.Itoa(l))...) - b = append(b, []byte("\r\n")...) - for i := 0; i < l; i++ { - b = append(b, format(mt[i], forceString)...) - } - return b - - case *Message: - return mt.raw - - default: - // Fallback to reflect-based. - switch reflect.TypeOf(m).Kind() { - case reflect.Slice: - rm := reflect.ValueOf(mt) - l := rm.Len() - b := make([]byte, 0, l*1024) - b = append(b, '*') - b = append(b, []byte(strconv.Itoa(l))...) - b = append(b, []byte("\r\n")...) - for i := 0; i < l; i++ { - vv := rm.Index(i).Interface() - b = append(b, format(vv, forceString)...) - } - - return b - case reflect.Map: - rm := reflect.ValueOf(mt) - l := rm.Len() * 2 - b := make([]byte, 0, l*1024) - b = append(b, '*') - b = append(b, []byte(strconv.Itoa(l))...) - b = append(b, []byte("\r\n")...) - keys := rm.MapKeys() - for _, k := range keys { - kv := k.Interface() - vv := rm.MapIndex(k).Interface() - b = append(b, format(kv, forceString)...) - b = append(b, format(vv, forceString)...) - } - return b - default: - return formatStr([]byte(fmt.Sprint(m))) - } - } -} - -var typeOfBytes = reflect.TypeOf([]byte(nil)) - -func flatten(m interface{}) []interface{} { - t := reflect.TypeOf(m) - - // If it's a byte-slice we don't want to flatten - if t == typeOfBytes { - return []interface{}{m} - } - - switch t.Kind() { - case reflect.Slice: - rm := reflect.ValueOf(m) - l := rm.Len() - ret := make([]interface{}, 0, l) - for i := 0; i < l; i++ { - ret = append(ret, flatten(rm.Index(i).Interface())...) - } - return ret - - case reflect.Map: - rm := reflect.ValueOf(m) - l := rm.Len() * 2 - keys := rm.MapKeys() - ret := make([]interface{}, 0, l) - for _, k := range keys { - kv := k.Interface() - vv := rm.MapIndex(k).Interface() - ret = append(ret, flatten(kv)...) - ret = append(ret, flatten(vv)...) - } - return ret - - default: - return []interface{}{m} - } -} - -func formatStr(b []byte) []byte { - l := strconv.Itoa(len(b)) - bs := make([]byte, 0, len(l)+len(b)+5) - bs = append(bs, bulkStrPrefix) - bs = append(bs, []byte(l)...) - bs = append(bs, delim...) - bs = append(bs, b...) - bs = append(bs, delim...) - return bs -} - -func formatErr(ierr error) []byte { - ierrstr := []byte(ierr.Error()) - bs := make([]byte, 0, len(ierrstr)+3) - bs = append(bs, errPrefix) - bs = append(bs, ierrstr...) - bs = append(bs, delim...) - return bs -} - -func formatInt(i int64, forceString bool) []byte { - istr := strconv.FormatInt(i, 10) - if forceString { - return formatStr([]byte(istr)) - } - bs := make([]byte, 0, len(istr)+3) - bs = append(bs, intPrefix) - bs = append(bs, istr...) - bs = append(bs, delim...) - return bs -} - -var nilFormatted = []byte("$-1\r\n") - -func formatNil() []byte { - return nilFormatted -} diff --git a/Godeps/_workspace/src/github.com/fzzy/radix/redis/resp/resp_test.go b/Godeps/_workspace/src/github.com/fzzy/radix/redis/resp/resp_test.go deleted file mode 100644 index 20112c0b9..000000000 --- a/Godeps/_workspace/src/github.com/fzzy/radix/redis/resp/resp_test.go +++ /dev/null @@ -1,209 +0,0 @@ -package resp - -import ( - "bytes" - "errors" - "github.com/stretchr/testify/assert" - . "testing" -) - -func TestRead(t *T) { - var m *Message - var err error - - _, err = NewMessage(nil) - assert.NotNil(t, err) - - _, err = NewMessage([]byte{}) - assert.NotNil(t, err) - - // Simple string - m, _ = NewMessage([]byte("+ohey\r\n")) - assert.Equal(t, SimpleStr, m.Type) - assert.Equal(t, []byte("ohey"), m.val) - - // Empty simple string - m, _ = NewMessage([]byte("+\r\n")) - assert.Equal(t, SimpleStr, m.Type) - assert.Equal(t, []byte(""), m.val.([]byte)) - - // Error - m, _ = NewMessage([]byte("-ohey\r\n")) - assert.Equal(t, Err, m.Type) - assert.Equal(t, []byte("ohey"), m.val.([]byte)) - - // Empty error - m, _ = NewMessage([]byte("-\r\n")) - assert.Equal(t, Err, m.Type) - assert.Equal(t, []byte(""), m.val.([]byte)) - - // Int - m, _ = NewMessage([]byte(":1024\r\n")) - assert.Equal(t, Int, m.Type) - assert.Equal(t, int64(1024), m.val.(int64)) - - // Bulk string - m, _ = NewMessage([]byte("$3\r\nfoo\r\n")) - assert.Equal(t, BulkStr, m.Type) - assert.Equal(t, []byte("foo"), m.val.([]byte)) - - // Empty bulk string - m, _ = NewMessage([]byte("$0\r\n\r\n")) - assert.Equal(t, BulkStr, m.Type) - assert.Equal(t, []byte(""), m.val.([]byte)) - - // Nil bulk string - m, _ = NewMessage([]byte("$-1\r\n")) - assert.Equal(t, Nil, m.Type) - - // Array - m, _ = NewMessage([]byte("*2\r\n+foo\r\n+bar\r\n")) - assert.Equal(t, Array, m.Type) - assert.Equal(t, 2, len(m.val.([]*Message))) - assert.Equal(t, SimpleStr, m.val.([]*Message)[0].Type) - assert.Equal(t, []byte("foo"), m.val.([]*Message)[0].val.([]byte)) - assert.Equal(t, SimpleStr, m.val.([]*Message)[1].Type) - assert.Equal(t, []byte("bar"), m.val.([]*Message)[1].val.([]byte)) - - // Empty array - m, _ = NewMessage([]byte("*0\r\n")) - assert.Equal(t, Array, m.Type) - assert.Equal(t, 0, len(m.val.([]*Message))) - - // Nil Array - m, _ = NewMessage([]byte("*-1\r\n")) - assert.Equal(t, Nil, m.Type) - - // Embedded Array - m, _ = NewMessage([]byte("*3\r\n+foo\r\n+bar\r\n*2\r\n+foo\r\n+bar\r\n")) - assert.Equal(t, Array, m.Type) - assert.Equal(t, 3, len(m.val.([]*Message))) - assert.Equal(t, SimpleStr, m.val.([]*Message)[0].Type) - assert.Equal(t, []byte("foo"), m.val.([]*Message)[0].val.([]byte)) - assert.Equal(t, SimpleStr, m.val.([]*Message)[1].Type) - assert.Equal(t, []byte("bar"), m.val.([]*Message)[1].val.([]byte)) - m = m.val.([]*Message)[2] - assert.Equal(t, 2, len(m.val.([]*Message))) - assert.Equal(t, SimpleStr, m.val.([]*Message)[0].Type) - assert.Equal(t, []byte("foo"), m.val.([]*Message)[0].val.([]byte)) - assert.Equal(t, SimpleStr, m.val.([]*Message)[1].Type) - assert.Equal(t, []byte("bar"), m.val.([]*Message)[1].val.([]byte)) - - // Test that two bulks in a row read correctly - m, _ = NewMessage([]byte("*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n")) - assert.Equal(t, Array, m.Type) - assert.Equal(t, 2, len(m.val.([]*Message))) - assert.Equal(t, BulkStr, m.val.([]*Message)[0].Type) - assert.Equal(t, []byte("foo"), m.val.([]*Message)[0].val.([]byte)) - assert.Equal(t, BulkStr, m.val.([]*Message)[1].Type) - assert.Equal(t, []byte("bar"), m.val.([]*Message)[1].val.([]byte)) -} - -type arbitraryTest struct { - val interface{} - expect []byte -} - -var nilMessage, _ = NewMessage([]byte("$-1\r\n")) - -var arbitraryTests = []arbitraryTest{ - {[]byte("OHAI"), []byte("$4\r\nOHAI\r\n")}, - {"OHAI", []byte("$4\r\nOHAI\r\n")}, - {true, []byte("$1\r\n1\r\n")}, - {false, []byte("$1\r\n0\r\n")}, - {nil, []byte("$-1\r\n")}, - {80, []byte(":80\r\n")}, - {int64(-80), []byte(":-80\r\n")}, - {uint64(80), []byte(":80\r\n")}, - {float32(0.1234), []byte("$6\r\n0.1234\r\n")}, - {float64(0.1234), []byte("$6\r\n0.1234\r\n")}, - {errors.New("hi"), []byte("-hi\r\n")}, - - {nilMessage, []byte("$-1\r\n")}, - - {[]int{1, 2, 3}, []byte("*3\r\n:1\r\n:2\r\n:3\r\n")}, - {map[int]int{1: 2}, []byte("*2\r\n:1\r\n:2\r\n")}, - - {NewSimpleString("OK"), []byte("+OK\r\n")}, -} - -var arbitraryAsStringTests = []arbitraryTest{ - {[]byte("OHAI"), []byte("$4\r\nOHAI\r\n")}, - {"OHAI", []byte("$4\r\nOHAI\r\n")}, - {true, []byte("$1\r\n1\r\n")}, - {false, []byte("$1\r\n0\r\n")}, - {nil, []byte("$0\r\n\r\n")}, - {80, []byte("$2\r\n80\r\n")}, - {int64(-80), []byte("$3\r\n-80\r\n")}, - {uint64(80), []byte("$2\r\n80\r\n")}, - {float32(0.1234), []byte("$6\r\n0.1234\r\n")}, - {float64(0.1234), []byte("$6\r\n0.1234\r\n")}, - {errors.New("hi"), []byte("$2\r\nhi\r\n")}, - - {nilMessage, []byte("$-1\r\n")}, - - {[]int{1, 2, 3}, []byte("*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n")}, - {map[int]int{1: 2}, []byte("*2\r\n$1\r\n1\r\n$1\r\n2\r\n")}, - - {NewSimpleString("OK"), []byte("+OK\r\n")}, -} - -var arbitraryAsFlattenedStringsTests = []arbitraryTest{ - { - []interface{}{"wat", map[string]interface{}{ - "foo": 1, - }}, - []byte("*3\r\n$3\r\nwat\r\n$3\r\nfoo\r\n$1\r\n1\r\n"), - }, -} - -func TestWriteArbitrary(t *T) { - var err error - buf := bytes.NewBuffer([]byte{}) - for _, test := range arbitraryTests { - t.Logf("Checking test %v", test) - buf.Reset() - err = WriteArbitrary(buf, test.val) - assert.Nil(t, err) - assert.Equal(t, test.expect, buf.Bytes()) - } -} - -func TestWriteArbitraryAsString(t *T) { - var err error - buf := bytes.NewBuffer([]byte{}) - for _, test := range arbitraryAsStringTests { - t.Logf("Checking test %v", test) - buf.Reset() - err = WriteArbitraryAsString(buf, test.val) - assert.Nil(t, err) - assert.Equal(t, test.expect, buf.Bytes()) - } -} - -func TestWriteArbitraryAsFlattenedStrings(t *T) { - var err error - buf := bytes.NewBuffer([]byte{}) - for _, test := range arbitraryAsFlattenedStringsTests { - t.Logf("Checking test %v", test) - buf.Reset() - err = WriteArbitraryAsFlattenedStrings(buf, test.val) - assert.Nil(t, err) - assert.Equal(t, test.expect, buf.Bytes()) - } -} - -func TestMessageWrite(t *T) { - var err error - var m *Message - buf := bytes.NewBuffer([]byte{}) - for _, test := range arbitraryTests { - t.Logf("Checking test; %v", test) - buf.Reset() - m, err = NewMessage(test.expect) - assert.Nil(t, err) - err = WriteMessage(buf, m) - assert.Nil(t, err) - assert.Equal(t, test.expect, buf.Bytes()) - } -} diff --git a/thirdparty/redis-datastore/datastore.go b/thirdparty/redis-datastore/datastore.go deleted file mode 100644 index cc3161818..000000000 --- a/thirdparty/redis-datastore/datastore.go +++ /dev/null @@ -1,84 +0,0 @@ -package redis - -import ( - "errors" - "fmt" - "sync" - "time" - - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/fzzy/radix/redis" - datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - query "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" -) - -var _ datastore.Datastore = &Datastore{} -var _ datastore.ThreadSafeDatastore = &Datastore{} - -var ErrInvalidType = errors.New("redis datastore: invalid type error. this datastore only supports []byte values") - -func NewExpiringDatastore(client *redis.Client, ttl time.Duration) (datastore.ThreadSafeDatastore, error) { - return &Datastore{ - client: client, - ttl: ttl, - }, nil -} - -func NewDatastore(client *redis.Client) (datastore.ThreadSafeDatastore, error) { - return &Datastore{ - client: client, - }, nil -} - -type Datastore struct { - mu sync.Mutex - client *redis.Client - ttl time.Duration -} - -func (ds *Datastore) Put(key datastore.Key, value interface{}) error { - ds.mu.Lock() - defer ds.mu.Unlock() - - data, ok := value.([]byte) - if !ok { - return ErrInvalidType - } - - ds.client.Append("SET", key.String(), data) - if ds.ttl != 0 { - ds.client.Append("EXPIRE", key.String(), ds.ttl.Seconds()) - } - if err := ds.client.GetReply().Err; err != nil { - return fmt.Errorf("failed to put value: %s", err) - } - if ds.ttl != 0 { - if err := ds.client.GetReply().Err; err != nil { - return fmt.Errorf("failed to set expiration: %s", err) - } - } - return nil -} - -func (ds *Datastore) Get(key datastore.Key) (value interface{}, err error) { - ds.mu.Lock() - defer ds.mu.Unlock() - return ds.client.Cmd("GET", key.String()).Bytes() -} - -func (ds *Datastore) Has(key datastore.Key) (exists bool, err error) { - ds.mu.Lock() - defer ds.mu.Unlock() - return ds.client.Cmd("EXISTS", key.String()).Bool() -} - -func (ds *Datastore) Delete(key datastore.Key) (err error) { - ds.mu.Lock() - defer ds.mu.Unlock() - return ds.client.Cmd("DEL", key.String()).Err -} - -func (ds *Datastore) Query(q query.Query) (query.Results, error) { - return nil, errors.New("TODO implement query for redis datastore?") -} - -func (ds *Datastore) IsThreadSafe() {} diff --git a/thirdparty/redis-datastore/datastore_test.go b/thirdparty/redis-datastore/datastore_test.go deleted file mode 100644 index 005f330af..000000000 --- a/thirdparty/redis-datastore/datastore_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package redis - -import ( - "bytes" - "os" - "testing" - "time" - - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/fzzy/radix/redis" - datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - "github.com/ipfs/go-ipfs/thirdparty/assert" -) - -const RedisEnv = "REDIS_DATASTORE_TEST_HOST" - -func TestPutGetBytes(t *testing.T) { - client := clientOrAbort(t) - ds, err := NewDatastore(client) - if err != nil { - t.Fatal(err) - } - key, val := datastore.NewKey("foo"), []byte("bar") - assert.Nil(ds.Put(key, val), t) - v, err := ds.Get(key) - if err != nil { - t.Fatal(err) - } - if bytes.Compare(v.([]byte), val) != 0 { - t.Fail() - } -} - -func TestHasBytes(t *testing.T) { - client := clientOrAbort(t) - ds, err := NewDatastore(client) - if err != nil { - t.Fatal(err) - } - key, val := datastore.NewKey("foo"), []byte("bar") - has, err := ds.Has(key) - if err != nil { - t.Fatal(err) - } - if has { - t.Fail() - } - - assert.Nil(ds.Put(key, val), t) - hasAfterPut, err := ds.Has(key) - if err != nil { - t.Fatal(err) - } - if !hasAfterPut { - t.Fail() - } -} - -func TestDelete(t *testing.T) { - client := clientOrAbort(t) - ds, err := NewDatastore(client) - if err != nil { - t.Fatal(err) - } - key, val := datastore.NewKey("foo"), []byte("bar") - assert.Nil(ds.Put(key, val), t) - assert.Nil(ds.Delete(key), t) - - hasAfterDelete, err := ds.Has(key) - if err != nil { - t.Fatal(err) - } - if hasAfterDelete { - t.Fail() - } -} - -func TestExpiry(t *testing.T) { - ttl := 1 * time.Second - client := clientOrAbort(t) - ds, err := NewExpiringDatastore(client, ttl) - if err != nil { - t.Fatal(err) - } - key, val := datastore.NewKey("foo"), []byte("bar") - assert.Nil(ds.Put(key, val), t) - time.Sleep(ttl + 1*time.Second) - assert.Nil(ds.Delete(key), t) - - hasAfterExpiration, err := ds.Has(key) - if err != nil { - t.Fatal(err) - } - if hasAfterExpiration { - t.Fail() - } -} - -func clientOrAbort(t *testing.T) *redis.Client { - c, err := redis.Dial("tcp", os.Getenv(RedisEnv)) - if err != nil { - t.Log("could not connect to a redis instance") - t.SkipNow() - } - if err := c.Cmd("FLUSHALL").Err; err != nil { - t.Fatal(err) - } - return c -}