1// Copyright 2014 Google Inc. All rights reserved. 2// Use of this source code is governed by the Apache 2.0 3// license that can be found in the LICENSE file. 4 5package memcache 6 7import ( 8 "fmt" 9 "testing" 10 11 "google.golang.org/appengine" 12 "google.golang.org/appengine/internal/aetesting" 13 pb "google.golang.org/appengine/internal/memcache" 14) 15 16var errRPC = fmt.Errorf("RPC error") 17 18func TestGetRequest(t *testing.T) { 19 serviceCalled := false 20 apiKey := "lyric" 21 22 c := aetesting.FakeSingleContext(t, "memcache", "Get", func(req *pb.MemcacheGetRequest, _ *pb.MemcacheGetResponse) error { 23 // Test request. 24 if n := len(req.Key); n != 1 { 25 t.Errorf("got %d want 1", n) 26 return nil 27 } 28 if k := string(req.Key[0]); k != apiKey { 29 t.Errorf("got %q want %q", k, apiKey) 30 } 31 32 serviceCalled = true 33 return nil 34 }) 35 36 // Test the "forward" path from the API call parameters to the 37 // protobuf request object. (The "backward" path from the 38 // protobuf response object to the API call response, 39 // including the error response, are handled in the next few 40 // tests). 41 Get(c, apiKey) 42 if !serviceCalled { 43 t.Error("Service was not called as expected") 44 } 45} 46 47func TestGetResponseHit(t *testing.T) { 48 key := "lyric" 49 value := "Where the buffalo roam" 50 51 c := aetesting.FakeSingleContext(t, "memcache", "Get", func(_ *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error { 52 res.Item = []*pb.MemcacheGetResponse_Item{ 53 {Key: []byte(key), Value: []byte(value)}, 54 } 55 return nil 56 }) 57 apiItem, err := Get(c, key) 58 if apiItem == nil || apiItem.Key != key || string(apiItem.Value) != value { 59 t.Errorf("got %q, %q want {%q,%q}, nil", apiItem, err, key, value) 60 } 61} 62 63func TestGetResponseMiss(t *testing.T) { 64 c := aetesting.FakeSingleContext(t, "memcache", "Get", func(_ *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error { 65 // don't fill in any of the response 66 return nil 67 }) 68 _, err := Get(c, "something") 69 if err != ErrCacheMiss { 70 t.Errorf("got %v want ErrCacheMiss", err) 71 } 72} 73 74func TestGetResponseRPCError(t *testing.T) { 75 c := aetesting.FakeSingleContext(t, "memcache", "Get", func(_ *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error { 76 return errRPC 77 }) 78 79 if _, err := Get(c, "something"); err != errRPC { 80 t.Errorf("got %v want errRPC", err) 81 } 82} 83 84func TestAddRequest(t *testing.T) { 85 var apiItem = &Item{ 86 Key: "lyric", 87 Value: []byte("Oh, give me a home"), 88 } 89 90 serviceCalled := false 91 92 c := aetesting.FakeSingleContext(t, "memcache", "Set", func(req *pb.MemcacheSetRequest, _ *pb.MemcacheSetResponse) error { 93 // Test request. 94 pbItem := req.Item[0] 95 if k := string(pbItem.Key); k != apiItem.Key { 96 t.Errorf("got %q want %q", k, apiItem.Key) 97 } 98 if v := string(apiItem.Value); v != string(pbItem.Value) { 99 t.Errorf("got %q want %q", v, string(pbItem.Value)) 100 } 101 if p := *pbItem.SetPolicy; p != pb.MemcacheSetRequest_ADD { 102 t.Errorf("got %v want %v", p, pb.MemcacheSetRequest_ADD) 103 } 104 105 serviceCalled = true 106 return nil 107 }) 108 109 Add(c, apiItem) 110 if !serviceCalled { 111 t.Error("Service was not called as expected") 112 } 113} 114 115func TestAddResponseStored(t *testing.T) { 116 c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error { 117 res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_STORED} 118 return nil 119 }) 120 121 if err := Add(c, &Item{}); err != nil { 122 t.Errorf("got %v want nil", err) 123 } 124} 125 126func TestAddResponseNotStored(t *testing.T) { 127 c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error { 128 res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_NOT_STORED} 129 return nil 130 }) 131 132 if err := Add(c, &Item{}); err != ErrNotStored { 133 t.Errorf("got %v want ErrNotStored", err) 134 } 135} 136 137func TestAddResponseError(t *testing.T) { 138 c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error { 139 res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_ERROR} 140 return nil 141 }) 142 143 if err := Add(c, &Item{}); err != ErrServerError { 144 t.Errorf("got %v want ErrServerError", err) 145 } 146} 147 148func TestAddResponseRPCError(t *testing.T) { 149 c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error { 150 return errRPC 151 }) 152 153 if err := Add(c, &Item{}); err != errRPC { 154 t.Errorf("got %v want errRPC", err) 155 } 156} 157 158func TestSetRequest(t *testing.T) { 159 var apiItem = &Item{ 160 Key: "lyric", 161 Value: []byte("Where the buffalo roam"), 162 } 163 164 serviceCalled := false 165 166 c := aetesting.FakeSingleContext(t, "memcache", "Set", func(req *pb.MemcacheSetRequest, _ *pb.MemcacheSetResponse) error { 167 // Test request. 168 if n := len(req.Item); n != 1 { 169 t.Errorf("got %d want 1", n) 170 return nil 171 } 172 pbItem := req.Item[0] 173 if k := string(pbItem.Key); k != apiItem.Key { 174 t.Errorf("got %q want %q", k, apiItem.Key) 175 } 176 if v := string(pbItem.Value); v != string(apiItem.Value) { 177 t.Errorf("got %q want %q", v, string(apiItem.Value)) 178 } 179 if p := *pbItem.SetPolicy; p != pb.MemcacheSetRequest_SET { 180 t.Errorf("got %v want %v", p, pb.MemcacheSetRequest_SET) 181 } 182 183 serviceCalled = true 184 return nil 185 }) 186 187 Set(c, apiItem) 188 if !serviceCalled { 189 t.Error("Service was not called as expected") 190 } 191} 192 193func TestSetResponse(t *testing.T) { 194 c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error { 195 res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_STORED} 196 return nil 197 }) 198 199 if err := Set(c, &Item{}); err != nil { 200 t.Errorf("got %v want nil", err) 201 } 202} 203 204func TestSetResponseError(t *testing.T) { 205 c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error { 206 res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_ERROR} 207 return nil 208 }) 209 210 if err := Set(c, &Item{}); err != ErrServerError { 211 t.Errorf("got %v want ErrServerError", err) 212 } 213} 214 215func TestNamespaceResetting(t *testing.T) { 216 namec := make(chan *string, 1) 217 c0 := aetesting.FakeSingleContext(t, "memcache", "Get", func(req *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error { 218 namec <- req.NameSpace 219 return errRPC 220 }) 221 222 // Check that wrapping c0 in a namespace twice works correctly. 223 c1, err := appengine.Namespace(c0, "A") 224 if err != nil { 225 t.Fatalf("appengine.Namespace: %v", err) 226 } 227 c2, err := appengine.Namespace(c1, "") // should act as the original context 228 if err != nil { 229 t.Fatalf("appengine.Namespace: %v", err) 230 } 231 232 Get(c0, "key") 233 if ns := <-namec; ns != nil { 234 t.Errorf(`Get with c0: ns = %q, want nil`, *ns) 235 } 236 237 Get(c1, "key") 238 if ns := <-namec; ns == nil { 239 t.Error(`Get with c1: ns = nil, want "A"`) 240 } else if *ns != "A" { 241 t.Errorf(`Get with c1: ns = %q, want "A"`, *ns) 242 } 243 244 Get(c2, "key") 245 if ns := <-namec; ns != nil { 246 t.Errorf(`Get with c2: ns = %q, want nil`, *ns) 247 } 248} 249 250func TestGetMultiEmpty(t *testing.T) { 251 serviceCalled := false 252 c := aetesting.FakeSingleContext(t, "memcache", "Get", func(req *pb.MemcacheGetRequest, _ *pb.MemcacheGetResponse) error { 253 serviceCalled = true 254 return nil 255 }) 256 257 // Test that the Memcache service is not called when 258 // GetMulti is passed an empty slice of keys. 259 GetMulti(c, []string{}) 260 if serviceCalled { 261 t.Error("Service was called but should not have been") 262 } 263} 264