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