blob: 1dc7da471f7972bd350aa1367bebb1a1728e0ca6 [file] [log] [blame]
// Copyright 2014 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package memcache
import (
"fmt"
"testing"
"google.golang.org/appengine"
"google.golang.org/appengine/internal/aetesting"
pb "google.golang.org/appengine/internal/memcache"
)
var errRPC = fmt.Errorf("RPC error")
func TestGetRequest(t *testing.T) {
serviceCalled := false
apiKey := "lyric"
c := aetesting.FakeSingleContext(t, "memcache", "Get", func(req *pb.MemcacheGetRequest, _ *pb.MemcacheGetResponse) error {
// Test request.
if n := len(req.Key); n != 1 {
t.Errorf("got %d want 1", n)
return nil
}
if k := string(req.Key[0]); k != apiKey {
t.Errorf("got %q want %q", k, apiKey)
}
serviceCalled = true
return nil
})
// Test the "forward" path from the API call parameters to the
// protobuf request object. (The "backward" path from the
// protobuf response object to the API call response,
// including the error response, are handled in the next few
// tests).
Get(c, apiKey)
if !serviceCalled {
t.Error("Service was not called as expected")
}
}
func TestGetResponseHit(t *testing.T) {
key := "lyric"
value := "Where the buffalo roam"
c := aetesting.FakeSingleContext(t, "memcache", "Get", func(_ *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error {
res.Item = []*pb.MemcacheGetResponse_Item{
{Key: []byte(key), Value: []byte(value)},
}
return nil
})
apiItem, err := Get(c, key)
if apiItem == nil || apiItem.Key != key || string(apiItem.Value) != value {
t.Errorf("got %q, %q want {%q,%q}, nil", apiItem, err, key, value)
}
}
func TestGetResponseMiss(t *testing.T) {
c := aetesting.FakeSingleContext(t, "memcache", "Get", func(_ *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error {
// don't fill in any of the response
return nil
})
_, err := Get(c, "something")
if err != ErrCacheMiss {
t.Errorf("got %v want ErrCacheMiss", err)
}
}
func TestGetResponseRPCError(t *testing.T) {
c := aetesting.FakeSingleContext(t, "memcache", "Get", func(_ *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error {
return errRPC
})
if _, err := Get(c, "something"); err != errRPC {
t.Errorf("got %v want errRPC", err)
}
}
func TestAddRequest(t *testing.T) {
var apiItem = &Item{
Key: "lyric",
Value: []byte("Oh, give me a home"),
}
serviceCalled := false
c := aetesting.FakeSingleContext(t, "memcache", "Set", func(req *pb.MemcacheSetRequest, _ *pb.MemcacheSetResponse) error {
// Test request.
pbItem := req.Item[0]
if k := string(pbItem.Key); k != apiItem.Key {
t.Errorf("got %q want %q", k, apiItem.Key)
}
if v := string(apiItem.Value); v != string(pbItem.Value) {
t.Errorf("got %q want %q", v, string(pbItem.Value))
}
if p := *pbItem.SetPolicy; p != pb.MemcacheSetRequest_ADD {
t.Errorf("got %v want %v", p, pb.MemcacheSetRequest_ADD)
}
serviceCalled = true
return nil
})
Add(c, apiItem)
if !serviceCalled {
t.Error("Service was not called as expected")
}
}
func TestAddResponseStored(t *testing.T) {
c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error {
res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_STORED}
return nil
})
if err := Add(c, &Item{}); err != nil {
t.Errorf("got %v want nil", err)
}
}
func TestAddResponseNotStored(t *testing.T) {
c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error {
res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_NOT_STORED}
return nil
})
if err := Add(c, &Item{}); err != ErrNotStored {
t.Errorf("got %v want ErrNotStored", err)
}
}
func TestAddResponseError(t *testing.T) {
c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error {
res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_ERROR}
return nil
})
if err := Add(c, &Item{}); err != ErrServerError {
t.Errorf("got %v want ErrServerError", err)
}
}
func TestAddResponseRPCError(t *testing.T) {
c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error {
return errRPC
})
if err := Add(c, &Item{}); err != errRPC {
t.Errorf("got %v want errRPC", err)
}
}
func TestSetRequest(t *testing.T) {
var apiItem = &Item{
Key: "lyric",
Value: []byte("Where the buffalo roam"),
}
serviceCalled := false
c := aetesting.FakeSingleContext(t, "memcache", "Set", func(req *pb.MemcacheSetRequest, _ *pb.MemcacheSetResponse) error {
// Test request.
if n := len(req.Item); n != 1 {
t.Errorf("got %d want 1", n)
return nil
}
pbItem := req.Item[0]
if k := string(pbItem.Key); k != apiItem.Key {
t.Errorf("got %q want %q", k, apiItem.Key)
}
if v := string(pbItem.Value); v != string(apiItem.Value) {
t.Errorf("got %q want %q", v, string(apiItem.Value))
}
if p := *pbItem.SetPolicy; p != pb.MemcacheSetRequest_SET {
t.Errorf("got %v want %v", p, pb.MemcacheSetRequest_SET)
}
serviceCalled = true
return nil
})
Set(c, apiItem)
if !serviceCalled {
t.Error("Service was not called as expected")
}
}
func TestSetResponse(t *testing.T) {
c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error {
res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_STORED}
return nil
})
if err := Set(c, &Item{}); err != nil {
t.Errorf("got %v want nil", err)
}
}
func TestSetResponseError(t *testing.T) {
c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error {
res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_ERROR}
return nil
})
if err := Set(c, &Item{}); err != ErrServerError {
t.Errorf("got %v want ErrServerError", err)
}
}
func TestNamespaceResetting(t *testing.T) {
namec := make(chan *string, 1)
c0 := aetesting.FakeSingleContext(t, "memcache", "Get", func(req *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error {
namec <- req.NameSpace
return errRPC
})
// Check that wrapping c0 in a namespace twice works correctly.
c1, err := appengine.Namespace(c0, "A")
if err != nil {
t.Fatalf("appengine.Namespace: %v", err)
}
c2, err := appengine.Namespace(c1, "") // should act as the original context
if err != nil {
t.Fatalf("appengine.Namespace: %v", err)
}
Get(c0, "key")
if ns := <-namec; ns != nil {
t.Errorf(`Get with c0: ns = %q, want nil`, *ns)
}
Get(c1, "key")
if ns := <-namec; ns == nil {
t.Error(`Get with c1: ns = nil, want "A"`)
} else if *ns != "A" {
t.Errorf(`Get with c1: ns = %q, want "A"`, *ns)
}
Get(c2, "key")
if ns := <-namec; ns != nil {
t.Errorf(`Get with c2: ns = %q, want nil`, *ns)
}
}
func TestGetMultiEmpty(t *testing.T) {
serviceCalled := false
c := aetesting.FakeSingleContext(t, "memcache", "Get", func(req *pb.MemcacheGetRequest, _ *pb.MemcacheGetResponse) error {
serviceCalled = true
return nil
})
// Test that the Memcache service is not called when
// GetMulti is passed an empty slice of keys.
GetMulti(c, []string{})
if serviceCalled {
t.Error("Service was called but should not have been")
}
}