1// Copyright 2015 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package store
16
17import (
18	"testing"
19	"time"
20
21	etcdErr "github.com/coreos/etcd/error"
22	"github.com/coreos/etcd/pkg/testutil"
23	"github.com/jonboulle/clockwork"
24)
25
26func TestNewStoreWithNamespaces(t *testing.T) {
27	s := newStore("/0", "/1")
28
29	_, err := s.Get("/0", false, false)
30	testutil.AssertNil(t, err)
31	_, err = s.Get("/1", false, false)
32	testutil.AssertNil(t, err)
33}
34
35// Ensure that the store can retrieve an existing value.
36func TestStoreGetValue(t *testing.T) {
37	s := newStore()
38	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
39	var eidx uint64 = 1
40	e, err := s.Get("/foo", false, false)
41	testutil.AssertNil(t, err)
42	testutil.AssertEqual(t, e.EtcdIndex, eidx)
43	testutil.AssertEqual(t, e.Action, "get")
44	testutil.AssertEqual(t, e.Node.Key, "/foo")
45	testutil.AssertEqual(t, *e.Node.Value, "bar")
46}
47
48// Ensure that any TTL <= minExpireTime becomes Permanent
49func TestMinExpireTime(t *testing.T) {
50	s := newStore()
51	fc := clockwork.NewFakeClock()
52	s.clock = fc
53	// FakeClock starts at 0, so minExpireTime should be far in the future.. but just in case
54	testutil.AssertTrue(t, minExpireTime.After(fc.Now()), "minExpireTime should be ahead of FakeClock!")
55	s.Create("/foo", false, "Y", false, TTLOptionSet{ExpireTime: fc.Now().Add(3 * time.Second)})
56	fc.Advance(5 * time.Second)
57	// Ensure it hasn't expired
58	s.DeleteExpiredKeys(fc.Now())
59	var eidx uint64 = 1
60	e, err := s.Get("/foo", true, false)
61	testutil.AssertNil(t, err)
62	testutil.AssertEqual(t, e.EtcdIndex, eidx)
63	testutil.AssertEqual(t, e.Action, "get")
64	testutil.AssertEqual(t, e.Node.Key, "/foo")
65	testutil.AssertEqual(t, e.Node.TTL, int64(0))
66}
67
68// Ensure that the store can recursively retrieve a directory listing.
69// Note that hidden files should not be returned.
70func TestStoreGetDirectory(t *testing.T) {
71	s := newStore()
72	fc := newFakeClock()
73	s.clock = fc
74	s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
75	s.Create("/foo/bar", false, "X", false, TTLOptionSet{ExpireTime: Permanent})
76	s.Create("/foo/_hidden", false, "*", false, TTLOptionSet{ExpireTime: Permanent})
77	s.Create("/foo/baz", true, "", false, TTLOptionSet{ExpireTime: Permanent})
78	s.Create("/foo/baz/bat", false, "Y", false, TTLOptionSet{ExpireTime: Permanent})
79	s.Create("/foo/baz/_hidden", false, "*", false, TTLOptionSet{ExpireTime: Permanent})
80	s.Create("/foo/baz/ttl", false, "Y", false, TTLOptionSet{ExpireTime: fc.Now().Add(time.Second * 3)})
81	var eidx uint64 = 7
82	e, err := s.Get("/foo", true, false)
83	testutil.AssertNil(t, err)
84	testutil.AssertEqual(t, e.EtcdIndex, eidx)
85	testutil.AssertEqual(t, e.Action, "get")
86	testutil.AssertEqual(t, e.Node.Key, "/foo")
87	testutil.AssertEqual(t, len(e.Node.Nodes), 2)
88	var bazNodes NodeExterns
89	for _, node := range e.Node.Nodes {
90		switch node.Key {
91		case "/foo/bar":
92			testutil.AssertEqual(t, *node.Value, "X")
93			testutil.AssertEqual(t, node.Dir, false)
94		case "/foo/baz":
95			testutil.AssertEqual(t, node.Dir, true)
96			testutil.AssertEqual(t, len(node.Nodes), 2)
97			bazNodes = node.Nodes
98		default:
99			t.Errorf("key = %s, not matched", node.Key)
100		}
101	}
102	for _, node := range bazNodes {
103		switch node.Key {
104		case "/foo/baz/bat":
105			testutil.AssertEqual(t, *node.Value, "Y")
106			testutil.AssertEqual(t, node.Dir, false)
107		case "/foo/baz/ttl":
108			testutil.AssertEqual(t, *node.Value, "Y")
109			testutil.AssertEqual(t, node.Dir, false)
110			testutil.AssertEqual(t, node.TTL, int64(3))
111		default:
112			t.Errorf("key = %s, not matched", node.Key)
113		}
114	}
115}
116
117// Ensure that the store can retrieve a directory in sorted order.
118func TestStoreGetSorted(t *testing.T) {
119	s := newStore()
120	s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
121	s.Create("/foo/x", false, "0", false, TTLOptionSet{ExpireTime: Permanent})
122	s.Create("/foo/z", false, "0", false, TTLOptionSet{ExpireTime: Permanent})
123	s.Create("/foo/y", true, "", false, TTLOptionSet{ExpireTime: Permanent})
124	s.Create("/foo/y/a", false, "0", false, TTLOptionSet{ExpireTime: Permanent})
125	s.Create("/foo/y/b", false, "0", false, TTLOptionSet{ExpireTime: Permanent})
126	var eidx uint64 = 6
127	e, err := s.Get("/foo", true, true)
128	testutil.AssertNil(t, err)
129	testutil.AssertEqual(t, e.EtcdIndex, eidx)
130
131	var yNodes NodeExterns
132	sortedStrings := []string{"/foo/x", "/foo/y", "/foo/z"}
133	for i := range e.Node.Nodes {
134		node := e.Node.Nodes[i]
135		if node.Key != sortedStrings[i] {
136			t.Errorf("expect key = %s, got key = %s", sortedStrings[i], node.Key)
137		}
138		if node.Key == "/foo/y" {
139			yNodes = node.Nodes
140		}
141	}
142
143	sortedStrings = []string{"/foo/y/a", "/foo/y/b"}
144	for i := range yNodes {
145		node := yNodes[i]
146		if node.Key != sortedStrings[i] {
147			t.Errorf("expect key = %s, got key = %s", sortedStrings[i], node.Key)
148		}
149	}
150}
151
152func TestSet(t *testing.T) {
153	s := newStore()
154
155	// Set /foo=""
156	var eidx uint64 = 1
157	e, err := s.Set("/foo", false, "", TTLOptionSet{ExpireTime: Permanent})
158	testutil.AssertNil(t, err)
159	testutil.AssertEqual(t, e.EtcdIndex, eidx)
160	testutil.AssertEqual(t, e.Action, "set")
161	testutil.AssertEqual(t, e.Node.Key, "/foo")
162	testutil.AssertFalse(t, e.Node.Dir)
163	testutil.AssertEqual(t, *e.Node.Value, "")
164	testutil.AssertNil(t, e.Node.Nodes)
165	testutil.AssertNil(t, e.Node.Expiration)
166	testutil.AssertEqual(t, e.Node.TTL, int64(0))
167	testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(1))
168
169	// Set /foo="bar"
170	eidx = 2
171	e, err = s.Set("/foo", false, "bar", TTLOptionSet{ExpireTime: Permanent})
172	testutil.AssertNil(t, err)
173	testutil.AssertEqual(t, e.EtcdIndex, eidx)
174	testutil.AssertEqual(t, e.Action, "set")
175	testutil.AssertEqual(t, e.Node.Key, "/foo")
176	testutil.AssertFalse(t, e.Node.Dir)
177	testutil.AssertEqual(t, *e.Node.Value, "bar")
178	testutil.AssertNil(t, e.Node.Nodes)
179	testutil.AssertNil(t, e.Node.Expiration)
180	testutil.AssertEqual(t, e.Node.TTL, int64(0))
181	testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(2))
182	// check prevNode
183	testutil.AssertNotNil(t, e.PrevNode)
184	testutil.AssertEqual(t, e.PrevNode.Key, "/foo")
185	testutil.AssertEqual(t, *e.PrevNode.Value, "")
186	testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(1))
187	// Set /foo="baz" (for testing prevNode)
188	eidx = 3
189	e, err = s.Set("/foo", false, "baz", TTLOptionSet{ExpireTime: Permanent})
190	testutil.AssertNil(t, err)
191	testutil.AssertEqual(t, e.EtcdIndex, eidx)
192	testutil.AssertEqual(t, e.Action, "set")
193	testutil.AssertEqual(t, e.Node.Key, "/foo")
194	testutil.AssertFalse(t, e.Node.Dir)
195	testutil.AssertEqual(t, *e.Node.Value, "baz")
196	testutil.AssertNil(t, e.Node.Nodes)
197	testutil.AssertNil(t, e.Node.Expiration)
198	testutil.AssertEqual(t, e.Node.TTL, int64(0))
199	testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(3))
200	// check prevNode
201	testutil.AssertNotNil(t, e.PrevNode)
202	testutil.AssertEqual(t, e.PrevNode.Key, "/foo")
203	testutil.AssertEqual(t, *e.PrevNode.Value, "bar")
204	testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(2))
205
206	// Set /dir as a directory
207	eidx = 4
208	e, err = s.Set("/dir", true, "", TTLOptionSet{ExpireTime: Permanent})
209	testutil.AssertNil(t, err)
210	testutil.AssertEqual(t, e.EtcdIndex, eidx)
211	testutil.AssertEqual(t, e.Action, "set")
212	testutil.AssertEqual(t, e.Node.Key, "/dir")
213	testutil.AssertTrue(t, e.Node.Dir)
214	testutil.AssertNil(t, e.Node.Value)
215	testutil.AssertNil(t, e.Node.Nodes)
216	testutil.AssertNil(t, e.Node.Expiration)
217	testutil.AssertEqual(t, e.Node.TTL, int64(0))
218	testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(4))
219}
220
221// Ensure that the store can create a new key if it doesn't already exist.
222func TestStoreCreateValue(t *testing.T) {
223	s := newStore()
224	// Create /foo=bar
225	var eidx uint64 = 1
226	e, err := s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
227	testutil.AssertNil(t, err)
228	testutil.AssertEqual(t, e.EtcdIndex, eidx)
229	testutil.AssertEqual(t, e.Action, "create")
230	testutil.AssertEqual(t, e.Node.Key, "/foo")
231	testutil.AssertFalse(t, e.Node.Dir)
232	testutil.AssertEqual(t, *e.Node.Value, "bar")
233	testutil.AssertNil(t, e.Node.Nodes)
234	testutil.AssertNil(t, e.Node.Expiration)
235	testutil.AssertEqual(t, e.Node.TTL, int64(0))
236	testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(1))
237
238	// Create /empty=""
239	eidx = 2
240	e, err = s.Create("/empty", false, "", false, TTLOptionSet{ExpireTime: Permanent})
241	testutil.AssertNil(t, err)
242	testutil.AssertEqual(t, e.EtcdIndex, eidx)
243	testutil.AssertEqual(t, e.Action, "create")
244	testutil.AssertEqual(t, e.Node.Key, "/empty")
245	testutil.AssertFalse(t, e.Node.Dir)
246	testutil.AssertEqual(t, *e.Node.Value, "")
247	testutil.AssertNil(t, e.Node.Nodes)
248	testutil.AssertNil(t, e.Node.Expiration)
249	testutil.AssertEqual(t, e.Node.TTL, int64(0))
250	testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(2))
251
252}
253
254// Ensure that the store can create a new directory if it doesn't already exist.
255func TestStoreCreateDirectory(t *testing.T) {
256	s := newStore()
257	var eidx uint64 = 1
258	e, err := s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
259	testutil.AssertNil(t, err)
260	testutil.AssertEqual(t, e.EtcdIndex, eidx)
261	testutil.AssertEqual(t, e.Action, "create")
262	testutil.AssertEqual(t, e.Node.Key, "/foo")
263	testutil.AssertTrue(t, e.Node.Dir)
264}
265
266// Ensure that the store fails to create a key if it already exists.
267func TestStoreCreateFailsIfExists(t *testing.T) {
268	s := newStore()
269	// create /foo as dir
270	s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
271
272	// create /foo as dir again
273	e, _err := s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
274	err := _err.(*etcdErr.Error)
275	testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeNodeExist)
276	testutil.AssertEqual(t, err.Message, "Key already exists")
277	testutil.AssertEqual(t, err.Cause, "/foo")
278	testutil.AssertEqual(t, err.Index, uint64(1))
279	testutil.AssertNil(t, e)
280}
281
282// Ensure that the store can update a key if it already exists.
283func TestStoreUpdateValue(t *testing.T) {
284	s := newStore()
285	// create /foo=bar
286	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
287	// update /foo="bzr"
288	var eidx uint64 = 2
289	e, err := s.Update("/foo", "baz", TTLOptionSet{ExpireTime: Permanent})
290	testutil.AssertNil(t, err)
291	testutil.AssertEqual(t, e.EtcdIndex, eidx)
292	testutil.AssertEqual(t, e.Action, "update")
293	testutil.AssertEqual(t, e.Node.Key, "/foo")
294	testutil.AssertFalse(t, e.Node.Dir)
295	testutil.AssertEqual(t, *e.Node.Value, "baz")
296	testutil.AssertEqual(t, e.Node.TTL, int64(0))
297	testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(2))
298	// check prevNode
299	testutil.AssertEqual(t, e.PrevNode.Key, "/foo")
300	testutil.AssertEqual(t, *e.PrevNode.Value, "bar")
301	testutil.AssertEqual(t, e.PrevNode.TTL, int64(0))
302	testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(1))
303
304	e, _ = s.Get("/foo", false, false)
305	testutil.AssertEqual(t, *e.Node.Value, "baz")
306	testutil.AssertEqual(t, e.EtcdIndex, eidx)
307
308	// update /foo=""
309	eidx = 3
310	e, err = s.Update("/foo", "", TTLOptionSet{ExpireTime: Permanent})
311	testutil.AssertNil(t, err)
312	testutil.AssertEqual(t, e.EtcdIndex, eidx)
313	testutil.AssertEqual(t, e.Action, "update")
314	testutil.AssertEqual(t, e.Node.Key, "/foo")
315	testutil.AssertFalse(t, e.Node.Dir)
316	testutil.AssertEqual(t, *e.Node.Value, "")
317	testutil.AssertEqual(t, e.Node.TTL, int64(0))
318	testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(3))
319	// check prevNode
320	testutil.AssertEqual(t, e.PrevNode.Key, "/foo")
321	testutil.AssertEqual(t, *e.PrevNode.Value, "baz")
322	testutil.AssertEqual(t, e.PrevNode.TTL, int64(0))
323	testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(2))
324
325	e, _ = s.Get("/foo", false, false)
326	testutil.AssertEqual(t, e.EtcdIndex, eidx)
327	testutil.AssertEqual(t, *e.Node.Value, "")
328}
329
330// Ensure that the store cannot update a directory.
331func TestStoreUpdateFailsIfDirectory(t *testing.T) {
332	s := newStore()
333	s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
334	e, _err := s.Update("/foo", "baz", TTLOptionSet{ExpireTime: Permanent})
335	err := _err.(*etcdErr.Error)
336	testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeNotFile)
337	testutil.AssertEqual(t, err.Message, "Not a file")
338	testutil.AssertEqual(t, err.Cause, "/foo")
339	testutil.AssertNil(t, e)
340}
341
342// Ensure that the store can update the TTL on a value.
343func TestStoreUpdateValueTTL(t *testing.T) {
344	s := newStore()
345	fc := newFakeClock()
346	s.clock = fc
347
348	var eidx uint64 = 2
349	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
350	_, err := s.Update("/foo", "baz", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
351	testutil.AssertNil(t, err)
352	e, _ := s.Get("/foo", false, false)
353	testutil.AssertEqual(t, *e.Node.Value, "baz")
354	testutil.AssertEqual(t, e.EtcdIndex, eidx)
355	fc.Advance(600 * time.Millisecond)
356	s.DeleteExpiredKeys(fc.Now())
357	e, err = s.Get("/foo", false, false)
358	testutil.AssertNil(t, e)
359	testutil.AssertEqual(t, err.(*etcdErr.Error).ErrorCode, etcdErr.EcodeKeyNotFound)
360}
361
362// Ensure that the store can update the TTL on a directory.
363func TestStoreUpdateDirTTL(t *testing.T) {
364	s := newStore()
365	fc := newFakeClock()
366	s.clock = fc
367
368	var eidx uint64 = 3
369	s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
370	s.Create("/foo/bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
371	e, err := s.Update("/foo", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
372	testutil.AssertNil(t, err)
373	testutil.AssertEqual(t, e.Node.Dir, true)
374	testutil.AssertEqual(t, e.EtcdIndex, eidx)
375	e, _ = s.Get("/foo/bar", false, false)
376	testutil.AssertEqual(t, *e.Node.Value, "baz")
377	testutil.AssertEqual(t, e.EtcdIndex, eidx)
378
379	fc.Advance(600 * time.Millisecond)
380	s.DeleteExpiredKeys(fc.Now())
381	e, err = s.Get("/foo/bar", false, false)
382	testutil.AssertNil(t, e)
383	testutil.AssertEqual(t, err.(*etcdErr.Error).ErrorCode, etcdErr.EcodeKeyNotFound)
384}
385
386// Ensure that the store can delete a value.
387func TestStoreDeleteValue(t *testing.T) {
388	s := newStore()
389	var eidx uint64 = 2
390	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
391	e, err := s.Delete("/foo", false, false)
392	testutil.AssertNil(t, err)
393	testutil.AssertEqual(t, e.EtcdIndex, eidx)
394	testutil.AssertEqual(t, e.Action, "delete")
395	// check prevNode
396	testutil.AssertNotNil(t, e.PrevNode)
397	testutil.AssertEqual(t, e.PrevNode.Key, "/foo")
398	testutil.AssertEqual(t, *e.PrevNode.Value, "bar")
399}
400
401// Ensure that the store can delete a directory if recursive is specified.
402func TestStoreDeleteDiretory(t *testing.T) {
403	s := newStore()
404	// create directory /foo
405	var eidx uint64 = 2
406	s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
407	// delete /foo with dir = true and recursive = false
408	// this should succeed, since the directory is empty
409	e, err := s.Delete("/foo", true, false)
410	testutil.AssertNil(t, err)
411	testutil.AssertEqual(t, e.EtcdIndex, eidx)
412	testutil.AssertEqual(t, e.Action, "delete")
413	// check prevNode
414	testutil.AssertNotNil(t, e.PrevNode)
415	testutil.AssertEqual(t, e.PrevNode.Key, "/foo")
416	testutil.AssertEqual(t, e.PrevNode.Dir, true)
417
418	// create directory /foo and directory /foo/bar
419	s.Create("/foo/bar", true, "", false, TTLOptionSet{ExpireTime: Permanent})
420	// delete /foo with dir = true and recursive = false
421	// this should fail, since the directory is not empty
422	_, err = s.Delete("/foo", true, false)
423	testutil.AssertNotNil(t, err)
424
425	// delete /foo with dir=false and recursive = true
426	// this should succeed, since recursive implies dir=true
427	// and recursively delete should be able to delete all
428	// items under the given directory
429	e, err = s.Delete("/foo", false, true)
430	testutil.AssertNil(t, err)
431	testutil.AssertEqual(t, e.Action, "delete")
432
433}
434
435// Ensure that the store cannot delete a directory if both of recursive
436// and dir are not specified.
437func TestStoreDeleteDiretoryFailsIfNonRecursiveAndDir(t *testing.T) {
438	s := newStore()
439	s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
440	e, _err := s.Delete("/foo", false, false)
441	err := _err.(*etcdErr.Error)
442	testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeNotFile)
443	testutil.AssertEqual(t, err.Message, "Not a file")
444	testutil.AssertNil(t, e)
445}
446
447func TestRootRdOnly(t *testing.T) {
448	s := newStore("/0")
449
450	for _, tt := range []string{"/", "/0"} {
451		_, err := s.Set(tt, true, "", TTLOptionSet{ExpireTime: Permanent})
452		testutil.AssertNotNil(t, err)
453
454		_, err = s.Delete(tt, true, true)
455		testutil.AssertNotNil(t, err)
456
457		_, err = s.Create(tt, true, "", false, TTLOptionSet{ExpireTime: Permanent})
458		testutil.AssertNotNil(t, err)
459
460		_, err = s.Update(tt, "", TTLOptionSet{ExpireTime: Permanent})
461		testutil.AssertNotNil(t, err)
462
463		_, err = s.CompareAndSwap(tt, "", 0, "", TTLOptionSet{ExpireTime: Permanent})
464		testutil.AssertNotNil(t, err)
465	}
466}
467
468func TestStoreCompareAndDeletePrevValue(t *testing.T) {
469	s := newStore()
470	var eidx uint64 = 2
471	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
472	e, err := s.CompareAndDelete("/foo", "bar", 0)
473	testutil.AssertNil(t, err)
474	testutil.AssertEqual(t, e.EtcdIndex, eidx)
475	testutil.AssertEqual(t, e.Action, "compareAndDelete")
476	testutil.AssertEqual(t, e.Node.Key, "/foo")
477
478	// check prevNode
479	testutil.AssertNotNil(t, e.PrevNode)
480	testutil.AssertEqual(t, e.PrevNode.Key, "/foo")
481	testutil.AssertEqual(t, *e.PrevNode.Value, "bar")
482	testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(1))
483	testutil.AssertEqual(t, e.PrevNode.CreatedIndex, uint64(1))
484}
485
486func TestStoreCompareAndDeletePrevValueFailsIfNotMatch(t *testing.T) {
487	s := newStore()
488	var eidx uint64 = 1
489	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
490	e, _err := s.CompareAndDelete("/foo", "baz", 0)
491	err := _err.(*etcdErr.Error)
492	testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeTestFailed)
493	testutil.AssertEqual(t, err.Message, "Compare failed")
494	testutil.AssertNil(t, e)
495	e, _ = s.Get("/foo", false, false)
496	testutil.AssertEqual(t, e.EtcdIndex, eidx)
497	testutil.AssertEqual(t, *e.Node.Value, "bar")
498}
499
500func TestStoreCompareAndDeletePrevIndex(t *testing.T) {
501	s := newStore()
502	var eidx uint64 = 2
503	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
504	e, err := s.CompareAndDelete("/foo", "", 1)
505	testutil.AssertNil(t, err)
506	testutil.AssertEqual(t, e.EtcdIndex, eidx)
507	testutil.AssertEqual(t, e.Action, "compareAndDelete")
508	// check prevNode
509	testutil.AssertNotNil(t, e.PrevNode)
510	testutil.AssertEqual(t, e.PrevNode.Key, "/foo")
511	testutil.AssertEqual(t, *e.PrevNode.Value, "bar")
512	testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(1))
513	testutil.AssertEqual(t, e.PrevNode.CreatedIndex, uint64(1))
514}
515
516func TestStoreCompareAndDeletePrevIndexFailsIfNotMatch(t *testing.T) {
517	s := newStore()
518	var eidx uint64 = 1
519	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
520	e, _err := s.CompareAndDelete("/foo", "", 100)
521	testutil.AssertNotNil(t, _err)
522	err := _err.(*etcdErr.Error)
523	testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeTestFailed)
524	testutil.AssertEqual(t, err.Message, "Compare failed")
525	testutil.AssertNil(t, e)
526	e, _ = s.Get("/foo", false, false)
527	testutil.AssertEqual(t, e.EtcdIndex, eidx)
528	testutil.AssertEqual(t, *e.Node.Value, "bar")
529}
530
531// Ensure that the store cannot delete a directory.
532func TestStoreCompareAndDeleteDiretoryFail(t *testing.T) {
533	s := newStore()
534	s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
535	_, _err := s.CompareAndDelete("/foo", "", 0)
536	testutil.AssertNotNil(t, _err)
537	err := _err.(*etcdErr.Error)
538	testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeNotFile)
539}
540
541// Ensure that the store can conditionally update a key if it has a previous value.
542func TestStoreCompareAndSwapPrevValue(t *testing.T) {
543	s := newStore()
544	var eidx uint64 = 2
545	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
546	e, err := s.CompareAndSwap("/foo", "bar", 0, "baz", TTLOptionSet{ExpireTime: Permanent})
547	testutil.AssertNil(t, err)
548	testutil.AssertEqual(t, e.EtcdIndex, eidx)
549	testutil.AssertEqual(t, e.Action, "compareAndSwap")
550	testutil.AssertEqual(t, *e.Node.Value, "baz")
551	// check prevNode
552	testutil.AssertNotNil(t, e.PrevNode)
553	testutil.AssertEqual(t, e.PrevNode.Key, "/foo")
554	testutil.AssertEqual(t, *e.PrevNode.Value, "bar")
555	testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(1))
556	testutil.AssertEqual(t, e.PrevNode.CreatedIndex, uint64(1))
557
558	e, _ = s.Get("/foo", false, false)
559	testutil.AssertEqual(t, *e.Node.Value, "baz")
560}
561
562// Ensure that the store cannot conditionally update a key if it has the wrong previous value.
563func TestStoreCompareAndSwapPrevValueFailsIfNotMatch(t *testing.T) {
564	s := newStore()
565	var eidx uint64 = 1
566	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
567	e, _err := s.CompareAndSwap("/foo", "wrong_value", 0, "baz", TTLOptionSet{ExpireTime: Permanent})
568	err := _err.(*etcdErr.Error)
569	testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeTestFailed)
570	testutil.AssertEqual(t, err.Message, "Compare failed")
571	testutil.AssertNil(t, e)
572	e, _ = s.Get("/foo", false, false)
573	testutil.AssertEqual(t, *e.Node.Value, "bar")
574	testutil.AssertEqual(t, e.EtcdIndex, eidx)
575}
576
577// Ensure that the store can conditionally update a key if it has a previous index.
578func TestStoreCompareAndSwapPrevIndex(t *testing.T) {
579	s := newStore()
580	var eidx uint64 = 2
581	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
582	e, err := s.CompareAndSwap("/foo", "", 1, "baz", TTLOptionSet{ExpireTime: Permanent})
583	testutil.AssertNil(t, err)
584	testutil.AssertEqual(t, e.EtcdIndex, eidx)
585	testutil.AssertEqual(t, e.Action, "compareAndSwap")
586	testutil.AssertEqual(t, *e.Node.Value, "baz")
587	// check prevNode
588	testutil.AssertNotNil(t, e.PrevNode)
589	testutil.AssertEqual(t, e.PrevNode.Key, "/foo")
590	testutil.AssertEqual(t, *e.PrevNode.Value, "bar")
591	testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(1))
592	testutil.AssertEqual(t, e.PrevNode.CreatedIndex, uint64(1))
593
594	e, _ = s.Get("/foo", false, false)
595	testutil.AssertEqual(t, *e.Node.Value, "baz")
596	testutil.AssertEqual(t, e.EtcdIndex, eidx)
597}
598
599// Ensure that the store cannot conditionally update a key if it has the wrong previous index.
600func TestStoreCompareAndSwapPrevIndexFailsIfNotMatch(t *testing.T) {
601	s := newStore()
602	var eidx uint64 = 1
603	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
604	e, _err := s.CompareAndSwap("/foo", "", 100, "baz", TTLOptionSet{ExpireTime: Permanent})
605	err := _err.(*etcdErr.Error)
606	testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeTestFailed)
607	testutil.AssertEqual(t, err.Message, "Compare failed")
608	testutil.AssertNil(t, e)
609	e, _ = s.Get("/foo", false, false)
610	testutil.AssertEqual(t, e.EtcdIndex, eidx)
611	testutil.AssertEqual(t, *e.Node.Value, "bar")
612}
613
614// Ensure that the store can watch for key creation.
615func TestStoreWatchCreate(t *testing.T) {
616	s := newStore()
617	var eidx uint64 = 0
618	w, _ := s.Watch("/foo", false, false, 0)
619	c := w.EventChan()
620	testutil.AssertEqual(t, w.StartIndex(), eidx)
621	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
622	eidx = 1
623	e := nbselect(c)
624	testutil.AssertEqual(t, e.EtcdIndex, eidx)
625	testutil.AssertEqual(t, e.Action, "create")
626	testutil.AssertEqual(t, e.Node.Key, "/foo")
627	e = nbselect(c)
628	testutil.AssertNil(t, e)
629}
630
631// Ensure that the store can watch for recursive key creation.
632func TestStoreWatchRecursiveCreate(t *testing.T) {
633	s := newStore()
634	var eidx uint64 = 0
635	w, _ := s.Watch("/foo", true, false, 0)
636	testutil.AssertEqual(t, w.StartIndex(), eidx)
637	eidx = 1
638	s.Create("/foo/bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
639	e := nbselect(w.EventChan())
640	testutil.AssertEqual(t, e.EtcdIndex, eidx)
641	testutil.AssertEqual(t, e.Action, "create")
642	testutil.AssertEqual(t, e.Node.Key, "/foo/bar")
643}
644
645// Ensure that the store can watch for key updates.
646func TestStoreWatchUpdate(t *testing.T) {
647	s := newStore()
648	var eidx uint64 = 1
649	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
650	w, _ := s.Watch("/foo", false, false, 0)
651	testutil.AssertEqual(t, w.StartIndex(), eidx)
652	eidx = 2
653	s.Update("/foo", "baz", TTLOptionSet{ExpireTime: Permanent})
654	e := nbselect(w.EventChan())
655	testutil.AssertEqual(t, e.EtcdIndex, eidx)
656	testutil.AssertEqual(t, e.Action, "update")
657	testutil.AssertEqual(t, e.Node.Key, "/foo")
658}
659
660// Ensure that the store can watch for recursive key updates.
661func TestStoreWatchRecursiveUpdate(t *testing.T) {
662	s := newStore()
663	var eidx uint64 = 1
664	s.Create("/foo/bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
665	w, _ := s.Watch("/foo", true, false, 0)
666	testutil.AssertEqual(t, w.StartIndex(), eidx)
667	eidx = 2
668	s.Update("/foo/bar", "baz", TTLOptionSet{ExpireTime: Permanent})
669	e := nbselect(w.EventChan())
670	testutil.AssertEqual(t, e.EtcdIndex, eidx)
671	testutil.AssertEqual(t, e.Action, "update")
672	testutil.AssertEqual(t, e.Node.Key, "/foo/bar")
673}
674
675// Ensure that the store can watch for key deletions.
676func TestStoreWatchDelete(t *testing.T) {
677	s := newStore()
678	var eidx uint64 = 1
679	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
680	w, _ := s.Watch("/foo", false, false, 0)
681	testutil.AssertEqual(t, w.StartIndex(), eidx)
682	eidx = 2
683	s.Delete("/foo", false, false)
684	e := nbselect(w.EventChan())
685	testutil.AssertEqual(t, e.EtcdIndex, eidx)
686	testutil.AssertEqual(t, e.Action, "delete")
687	testutil.AssertEqual(t, e.Node.Key, "/foo")
688}
689
690// Ensure that the store can watch for recursive key deletions.
691func TestStoreWatchRecursiveDelete(t *testing.T) {
692	s := newStore()
693	var eidx uint64 = 1
694	s.Create("/foo/bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
695	w, _ := s.Watch("/foo", true, false, 0)
696	testutil.AssertEqual(t, w.StartIndex(), eidx)
697	eidx = 2
698	s.Delete("/foo/bar", false, false)
699	e := nbselect(w.EventChan())
700	testutil.AssertEqual(t, e.EtcdIndex, eidx)
701	testutil.AssertEqual(t, e.Action, "delete")
702	testutil.AssertEqual(t, e.Node.Key, "/foo/bar")
703}
704
705// Ensure that the store can watch for CAS updates.
706func TestStoreWatchCompareAndSwap(t *testing.T) {
707	s := newStore()
708	var eidx uint64 = 1
709	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
710	w, _ := s.Watch("/foo", false, false, 0)
711	testutil.AssertEqual(t, w.StartIndex(), eidx)
712	eidx = 2
713	s.CompareAndSwap("/foo", "bar", 0, "baz", TTLOptionSet{ExpireTime: Permanent})
714	e := nbselect(w.EventChan())
715	testutil.AssertEqual(t, e.EtcdIndex, eidx)
716	testutil.AssertEqual(t, e.Action, "compareAndSwap")
717	testutil.AssertEqual(t, e.Node.Key, "/foo")
718}
719
720// Ensure that the store can watch for recursive CAS updates.
721func TestStoreWatchRecursiveCompareAndSwap(t *testing.T) {
722	s := newStore()
723	var eidx uint64 = 1
724	s.Create("/foo/bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
725	w, _ := s.Watch("/foo", true, false, 0)
726	testutil.AssertEqual(t, w.StartIndex(), eidx)
727	eidx = 2
728	s.CompareAndSwap("/foo/bar", "baz", 0, "bat", TTLOptionSet{ExpireTime: Permanent})
729	e := nbselect(w.EventChan())
730	testutil.AssertEqual(t, e.EtcdIndex, eidx)
731	testutil.AssertEqual(t, e.Action, "compareAndSwap")
732	testutil.AssertEqual(t, e.Node.Key, "/foo/bar")
733}
734
735// Ensure that the store can watch for key expiration.
736func TestStoreWatchExpire(t *testing.T) {
737	s := newStore()
738	fc := newFakeClock()
739	s.clock = fc
740
741	var eidx uint64 = 3
742	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(400 * time.Millisecond)})
743	s.Create("/foofoo", false, "barbarbar", false, TTLOptionSet{ExpireTime: fc.Now().Add(450 * time.Millisecond)})
744	s.Create("/foodir", true, "", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
745
746	w, _ := s.Watch("/", true, false, 0)
747	testutil.AssertEqual(t, w.StartIndex(), eidx)
748	c := w.EventChan()
749	e := nbselect(c)
750	testutil.AssertNil(t, e)
751	fc.Advance(600 * time.Millisecond)
752	s.DeleteExpiredKeys(fc.Now())
753	eidx = 4
754	e = nbselect(c)
755	testutil.AssertEqual(t, e.EtcdIndex, eidx)
756	testutil.AssertEqual(t, e.Action, "expire")
757	testutil.AssertEqual(t, e.Node.Key, "/foo")
758	w, _ = s.Watch("/", true, false, 5)
759	eidx = 6
760	testutil.AssertEqual(t, w.StartIndex(), eidx)
761	e = nbselect(w.EventChan())
762	testutil.AssertEqual(t, e.EtcdIndex, eidx)
763	testutil.AssertEqual(t, e.Action, "expire")
764	testutil.AssertEqual(t, e.Node.Key, "/foofoo")
765	w, _ = s.Watch("/", true, false, 6)
766	e = nbselect(w.EventChan())
767	testutil.AssertEqual(t, e.EtcdIndex, eidx)
768	testutil.AssertEqual(t, e.Action, "expire")
769	testutil.AssertEqual(t, e.Node.Key, "/foodir")
770	testutil.AssertEqual(t, e.Node.Dir, true)
771}
772
773// Ensure that the store can watch for key expiration when refreshing.
774func TestStoreWatchExpireRefresh(t *testing.T) {
775	s := newStore()
776	fc := newFakeClock()
777	s.clock = fc
778
779	var eidx uint64 = 2
780	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
781	s.Create("/foofoo", false, "barbarbar", false, TTLOptionSet{ExpireTime: fc.Now().Add(1200 * time.Millisecond), Refresh: true})
782
783	// Make sure we set watch updates when Refresh is true for newly created keys
784	w, _ := s.Watch("/", true, false, 0)
785	testutil.AssertEqual(t, w.StartIndex(), eidx)
786	c := w.EventChan()
787	e := nbselect(c)
788	testutil.AssertNil(t, e)
789	fc.Advance(600 * time.Millisecond)
790	s.DeleteExpiredKeys(fc.Now())
791	eidx = 3
792	e = nbselect(c)
793	testutil.AssertEqual(t, e.EtcdIndex, eidx)
794	testutil.AssertEqual(t, e.Action, "expire")
795	testutil.AssertEqual(t, e.Node.Key, "/foo")
796
797	s.Update("/foofoo", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
798	w, _ = s.Watch("/", true, false, 4)
799	fc.Advance(700 * time.Millisecond)
800	s.DeleteExpiredKeys(fc.Now())
801	eidx = 5 // We should skip 4 because a TTL update should occur with no watch notification if set `TTLOptionSet.Refresh` to true
802	testutil.AssertEqual(t, w.StartIndex(), eidx-1)
803	e = nbselect(w.EventChan())
804	testutil.AssertEqual(t, e.EtcdIndex, eidx)
805	testutil.AssertEqual(t, e.Action, "expire")
806	testutil.AssertEqual(t, e.Node.Key, "/foofoo")
807}
808
809// Ensure that the store can watch for key expiration when refreshing with an empty value.
810func TestStoreWatchExpireEmptyRefresh(t *testing.T) {
811	s := newStore()
812	fc := newFakeClock()
813	s.clock = fc
814
815	var eidx uint64 = 1
816	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
817	// Should be no-op
818	fc.Advance(200 * time.Millisecond)
819	s.DeleteExpiredKeys(fc.Now())
820
821	s.Update("/foo", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
822	w, _ := s.Watch("/", true, false, 2)
823	fc.Advance(700 * time.Millisecond)
824	s.DeleteExpiredKeys(fc.Now())
825	eidx = 3 // We should skip 2 because a TTL update should occur with no watch notification if set `TTLOptionSet.Refresh` to true
826	testutil.AssertEqual(t, w.StartIndex(), eidx-1)
827	e := nbselect(w.EventChan())
828	testutil.AssertEqual(t, e.EtcdIndex, eidx)
829	testutil.AssertEqual(t, e.Action, "expire")
830	testutil.AssertEqual(t, e.Node.Key, "/foo")
831	testutil.AssertEqual(t, *e.PrevNode.Value, "bar")
832}
833
834// Update TTL of a key (set TTLOptionSet.Refresh to false) and send notification
835func TestStoreWatchNoRefresh(t *testing.T) {
836	s := newStore()
837	fc := newFakeClock()
838	s.clock = fc
839
840	var eidx uint64 = 1
841	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
842	// Should be no-op
843	fc.Advance(200 * time.Millisecond)
844	s.DeleteExpiredKeys(fc.Now())
845
846	// Update key's TTL with setting `TTLOptionSet.Refresh` to false will cause an update event
847	s.Update("/foo", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: false})
848	w, _ := s.Watch("/", true, false, 2)
849	fc.Advance(700 * time.Millisecond)
850	s.DeleteExpiredKeys(fc.Now())
851	eidx = 2
852	testutil.AssertEqual(t, w.StartIndex(), eidx)
853	e := nbselect(w.EventChan())
854	testutil.AssertEqual(t, e.EtcdIndex, eidx)
855	testutil.AssertEqual(t, e.Action, "update")
856	testutil.AssertEqual(t, e.Node.Key, "/foo")
857	testutil.AssertEqual(t, *e.PrevNode.Value, "bar")
858}
859
860// Ensure that the store can update the TTL on a value with refresh.
861func TestStoreRefresh(t *testing.T) {
862	s := newStore()
863	fc := newFakeClock()
864	s.clock = fc
865
866	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
867	s.Create("/bar", true, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
868	_, err := s.Update("/foo", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
869	testutil.AssertNil(t, err)
870
871	_, err = s.Set("/foo", false, "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
872	testutil.AssertNil(t, err)
873
874	_, err = s.Update("/bar", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
875	testutil.AssertNil(t, err)
876
877	_, err = s.CompareAndSwap("/foo", "bar", 0, "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
878	testutil.AssertNil(t, err)
879}
880
881// Ensure that the store can watch in streaming mode.
882func TestStoreWatchStream(t *testing.T) {
883	s := newStore()
884	var eidx uint64 = 1
885	w, _ := s.Watch("/foo", false, true, 0)
886	// first modification
887	s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
888	e := nbselect(w.EventChan())
889	testutil.AssertEqual(t, e.EtcdIndex, eidx)
890	testutil.AssertEqual(t, e.Action, "create")
891	testutil.AssertEqual(t, e.Node.Key, "/foo")
892	testutil.AssertEqual(t, *e.Node.Value, "bar")
893	e = nbselect(w.EventChan())
894	testutil.AssertNil(t, e)
895	// second modification
896	eidx = 2
897	s.Update("/foo", "baz", TTLOptionSet{ExpireTime: Permanent})
898	e = nbselect(w.EventChan())
899	testutil.AssertEqual(t, e.EtcdIndex, eidx)
900	testutil.AssertEqual(t, e.Action, "update")
901	testutil.AssertEqual(t, e.Node.Key, "/foo")
902	testutil.AssertEqual(t, *e.Node.Value, "baz")
903	e = nbselect(w.EventChan())
904	testutil.AssertNil(t, e)
905}
906
907// Ensure that the store can recover from a previously saved state.
908func TestStoreRecover(t *testing.T) {
909	s := newStore()
910	var eidx uint64 = 4
911	s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
912	s.Create("/foo/x", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
913	s.Update("/foo/x", "barbar", TTLOptionSet{ExpireTime: Permanent})
914	s.Create("/foo/y", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
915	b, err := s.Save()
916	testutil.AssertNil(t, err)
917
918	s2 := newStore()
919	s2.Recovery(b)
920
921	e, err := s.Get("/foo/x", false, false)
922	testutil.AssertEqual(t, e.Node.CreatedIndex, uint64(2))
923	testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(3))
924	testutil.AssertEqual(t, e.EtcdIndex, eidx)
925	testutil.AssertNil(t, err)
926	testutil.AssertEqual(t, *e.Node.Value, "barbar")
927
928	e, err = s.Get("/foo/y", false, false)
929	testutil.AssertEqual(t, e.EtcdIndex, eidx)
930	testutil.AssertNil(t, err)
931	testutil.AssertEqual(t, *e.Node.Value, "baz")
932}
933
934// Ensure that the store can recover from a previously saved state that includes an expiring key.
935func TestStoreRecoverWithExpiration(t *testing.T) {
936	s := newStore()
937	s.clock = newFakeClock()
938
939	fc := newFakeClock()
940
941	var eidx uint64 = 4
942	s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
943	s.Create("/foo/x", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
944	s.Create("/foo/y", false, "baz", false, TTLOptionSet{ExpireTime: fc.Now().Add(5 * time.Millisecond)})
945	b, err := s.Save()
946	testutil.AssertNil(t, err)
947
948	time.Sleep(10 * time.Millisecond)
949
950	s2 := newStore()
951	s2.clock = fc
952
953	s2.Recovery(b)
954
955	fc.Advance(600 * time.Millisecond)
956	s.DeleteExpiredKeys(fc.Now())
957
958	e, err := s.Get("/foo/x", false, false)
959	testutil.AssertNil(t, err)
960	testutil.AssertEqual(t, e.EtcdIndex, eidx)
961	testutil.AssertEqual(t, *e.Node.Value, "bar")
962
963	e, err = s.Get("/foo/y", false, false)
964	testutil.AssertNotNil(t, err)
965	testutil.AssertNil(t, e)
966}
967
968// Ensure that the store can watch for hidden keys as long as it's an exact path match.
969func TestStoreWatchCreateWithHiddenKey(t *testing.T) {
970	s := newStore()
971	var eidx uint64 = 1
972	w, _ := s.Watch("/_foo", false, false, 0)
973	s.Create("/_foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
974	e := nbselect(w.EventChan())
975	testutil.AssertEqual(t, e.EtcdIndex, eidx)
976	testutil.AssertEqual(t, e.Action, "create")
977	testutil.AssertEqual(t, e.Node.Key, "/_foo")
978	e = nbselect(w.EventChan())
979	testutil.AssertNil(t, e)
980}
981
982// Ensure that the store doesn't see hidden key creates without an exact path match in recursive mode.
983func TestStoreWatchRecursiveCreateWithHiddenKey(t *testing.T) {
984	s := newStore()
985	w, _ := s.Watch("/foo", true, false, 0)
986	s.Create("/foo/_bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
987	e := nbselect(w.EventChan())
988	testutil.AssertNil(t, e)
989	w, _ = s.Watch("/foo", true, false, 0)
990	s.Create("/foo/_baz", true, "", false, TTLOptionSet{ExpireTime: Permanent})
991	e = nbselect(w.EventChan())
992	testutil.AssertNil(t, e)
993	s.Create("/foo/_baz/quux", false, "quux", false, TTLOptionSet{ExpireTime: Permanent})
994	e = nbselect(w.EventChan())
995	testutil.AssertNil(t, e)
996}
997
998// Ensure that the store doesn't see hidden key updates.
999func TestStoreWatchUpdateWithHiddenKey(t *testing.T) {
1000	s := newStore()
1001	s.Create("/_foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
1002	w, _ := s.Watch("/_foo", false, false, 0)
1003	s.Update("/_foo", "baz", TTLOptionSet{ExpireTime: Permanent})
1004	e := nbselect(w.EventChan())
1005	testutil.AssertEqual(t, e.Action, "update")
1006	testutil.AssertEqual(t, e.Node.Key, "/_foo")
1007	e = nbselect(w.EventChan())
1008	testutil.AssertNil(t, e)
1009}
1010
1011// Ensure that the store doesn't see hidden key updates without an exact path match in recursive mode.
1012func TestStoreWatchRecursiveUpdateWithHiddenKey(t *testing.T) {
1013	s := newStore()
1014	s.Create("/foo/_bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
1015	w, _ := s.Watch("/foo", true, false, 0)
1016	s.Update("/foo/_bar", "baz", TTLOptionSet{ExpireTime: Permanent})
1017	e := nbselect(w.EventChan())
1018	testutil.AssertNil(t, e)
1019}
1020
1021// Ensure that the store can watch for key deletions.
1022func TestStoreWatchDeleteWithHiddenKey(t *testing.T) {
1023	s := newStore()
1024	var eidx uint64 = 2
1025	s.Create("/_foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
1026	w, _ := s.Watch("/_foo", false, false, 0)
1027	s.Delete("/_foo", false, false)
1028	e := nbselect(w.EventChan())
1029	testutil.AssertEqual(t, e.EtcdIndex, eidx)
1030	testutil.AssertEqual(t, e.Action, "delete")
1031	testutil.AssertEqual(t, e.Node.Key, "/_foo")
1032	e = nbselect(w.EventChan())
1033	testutil.AssertNil(t, e)
1034}
1035
1036// Ensure that the store doesn't see hidden key deletes without an exact path match in recursive mode.
1037func TestStoreWatchRecursiveDeleteWithHiddenKey(t *testing.T) {
1038	s := newStore()
1039	s.Create("/foo/_bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
1040	w, _ := s.Watch("/foo", true, false, 0)
1041	s.Delete("/foo/_bar", false, false)
1042	e := nbselect(w.EventChan())
1043	testutil.AssertNil(t, e)
1044}
1045
1046// Ensure that the store doesn't see expirations of hidden keys.
1047func TestStoreWatchExpireWithHiddenKey(t *testing.T) {
1048	s := newStore()
1049	fc := newFakeClock()
1050	s.clock = fc
1051
1052	s.Create("/_foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
1053	s.Create("/foofoo", false, "barbarbar", false, TTLOptionSet{ExpireTime: fc.Now().Add(1000 * time.Millisecond)})
1054
1055	w, _ := s.Watch("/", true, false, 0)
1056	c := w.EventChan()
1057	e := nbselect(c)
1058	testutil.AssertNil(t, e)
1059	fc.Advance(600 * time.Millisecond)
1060	s.DeleteExpiredKeys(fc.Now())
1061	e = nbselect(c)
1062	testutil.AssertNil(t, e)
1063	fc.Advance(600 * time.Millisecond)
1064	s.DeleteExpiredKeys(fc.Now())
1065	e = nbselect(c)
1066	testutil.AssertEqual(t, e.Action, "expire")
1067	testutil.AssertEqual(t, e.Node.Key, "/foofoo")
1068}
1069
1070// Ensure that the store does see hidden key creates if watching deeper than a hidden key in recursive mode.
1071func TestStoreWatchRecursiveCreateDeeperThanHiddenKey(t *testing.T) {
1072	s := newStore()
1073	var eidx uint64 = 1
1074	w, _ := s.Watch("/_foo/bar", true, false, 0)
1075	s.Create("/_foo/bar/baz", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
1076
1077	e := nbselect(w.EventChan())
1078	testutil.AssertNotNil(t, e)
1079	testutil.AssertEqual(t, e.EtcdIndex, eidx)
1080	testutil.AssertEqual(t, e.Action, "create")
1081	testutil.AssertEqual(t, e.Node.Key, "/_foo/bar/baz")
1082}
1083
1084// Ensure that slow consumers are handled properly.
1085//
1086// Since Watcher.EventChan() has a buffer of size 100 we can only queue 100
1087// event per watcher. If the consumer cannot consume the event on time and
1088// another event arrives, the channel is closed and event is discarded.
1089// This test ensures that after closing the channel, the store can continue
1090// to operate correctly.
1091func TestStoreWatchSlowConsumer(t *testing.T) {
1092	s := newStore()
1093	s.Watch("/foo", true, true, 0) // stream must be true
1094	// Fill watch channel with 100 events
1095	for i := 1; i <= 100; i++ {
1096		s.Set("/foo", false, string(i), TTLOptionSet{ExpireTime: Permanent}) // ok
1097	}
1098	testutil.AssertEqual(t, s.WatcherHub.count, int64(1))
1099	s.Set("/foo", false, "101", TTLOptionSet{ExpireTime: Permanent}) // ok
1100	// remove watcher
1101	testutil.AssertEqual(t, s.WatcherHub.count, int64(0))
1102	s.Set("/foo", false, "102", TTLOptionSet{ExpireTime: Permanent}) // must not panic
1103}
1104
1105// Performs a non-blocking select on an event channel.
1106func nbselect(c <-chan *Event) *Event {
1107	select {
1108	case e := <-c:
1109		return e
1110	default:
1111		return nil
1112	}
1113}
1114
1115// newFakeClock creates a new FakeClock that has been advanced to at least minExpireTime
1116func newFakeClock() clockwork.FakeClock {
1117	fc := clockwork.NewFakeClock()
1118	for minExpireTime.After(fc.Now()) {
1119		fc.Advance((0x1 << 62) * time.Nanosecond)
1120	}
1121	return fc
1122}
1123