| // 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") |
| } |
| } |