1package api
2
3import (
4	"bytes"
5	"path"
6	"strings"
7	"testing"
8	"time"
9
10	"github.com/stretchr/testify/require"
11)
12
13func TestAPI_ClientPutGetDelete(t *testing.T) {
14	t.Parallel()
15	c, s := makeClient(t)
16	defer s.Stop()
17
18	kv := c.KV()
19
20	s.WaitForSerfCheck(t)
21	// Get a get without a key
22	key := testKey()
23	pair, _, err := kv.Get(key, nil)
24	if err != nil {
25		t.Fatalf("err: %v", err)
26	}
27	if pair != nil {
28		t.Fatalf("unexpected value: %#v", pair)
29	}
30
31	value := []byte("test")
32
33	// Put a key that begins with a '/', this should fail
34	invalidKey := "/test"
35	p := &KVPair{Key: invalidKey, Flags: 42, Value: value}
36	if _, err := kv.Put(p, nil); err == nil {
37		t.Fatalf("Invalid key not detected: %s", invalidKey)
38	}
39
40	// Put the key
41	p = &KVPair{Key: key, Flags: 42, Value: value}
42	if _, err := kv.Put(p, nil); err != nil {
43		t.Fatalf("err: %v", err)
44	}
45
46	// Get should work
47	pair, meta, err := kv.Get(key, nil)
48	if err != nil {
49		t.Fatalf("err: %v", err)
50	}
51	if pair == nil {
52		t.Fatalf("expected value: %#v", pair)
53	}
54	if !bytes.Equal(pair.Value, value) {
55		t.Fatalf("unexpected value: %#v", pair)
56	}
57	if pair.Flags != 42 {
58		t.Fatalf("unexpected value: %#v", pair)
59	}
60	if meta.LastIndex == 0 {
61		t.Fatalf("unexpected value: %#v", meta)
62	}
63
64	// Delete
65	if _, err := kv.Delete(key, nil); err != nil {
66		t.Fatalf("err: %v", err)
67	}
68
69	// Get should fail
70	pair, _, err = kv.Get(key, nil)
71	if err != nil {
72		t.Fatalf("err: %v", err)
73	}
74	if pair != nil {
75		t.Fatalf("unexpected value: %#v", pair)
76	}
77}
78
79func TestAPI_ClientList_DeleteRecurse(t *testing.T) {
80	t.Parallel()
81	c, s := makeClient(t)
82	defer s.Stop()
83
84	kv := c.KV()
85
86	// Generate some test keys
87	prefix := testKey()
88	var keys []string
89	for i := 0; i < 100; i++ {
90		keys = append(keys, path.Join(prefix, testKey()))
91	}
92
93	// Set values
94	value := []byte("test")
95	for _, key := range keys {
96		p := &KVPair{Key: key, Value: value}
97		if _, err := kv.Put(p, nil); err != nil {
98			t.Fatalf("err: %v", err)
99		}
100	}
101
102	// List the values
103	pairs, meta, err := kv.List(prefix, nil)
104	if err != nil {
105		t.Fatalf("err: %v", err)
106	}
107	if len(pairs) != len(keys) {
108		t.Fatalf("got %d keys", len(pairs))
109	}
110	for _, pair := range pairs {
111		if !bytes.Equal(pair.Value, value) {
112			t.Fatalf("unexpected value: %#v", pair)
113		}
114	}
115	if meta.LastIndex == 0 {
116		t.Fatalf("unexpected value: %#v", meta)
117	}
118
119	// Delete all
120	if _, err := kv.DeleteTree(prefix, nil); err != nil {
121		t.Fatalf("err: %v", err)
122	}
123
124	// List the values
125	pairs, _, err = kv.List(prefix, nil)
126	if err != nil {
127		t.Fatalf("err: %v", err)
128	}
129	if len(pairs) != 0 {
130		t.Fatalf("got %d keys", len(pairs))
131	}
132}
133
134func TestAPI_ClientDeleteCAS(t *testing.T) {
135	t.Parallel()
136	c, s := makeClient(t)
137	defer s.Stop()
138
139	kv := c.KV()
140
141	// Put the key
142	key := testKey()
143	value := []byte("test")
144	p := &KVPair{Key: key, Value: value}
145	if work, _, err := kv.CAS(p, nil); err != nil {
146		t.Fatalf("err: %v", err)
147	} else if !work {
148		t.Fatalf("CAS failure")
149	}
150
151	// Get should work
152	pair, meta, err := kv.Get(key, nil)
153	if err != nil {
154		t.Fatalf("err: %v", err)
155	}
156	if pair == nil {
157		t.Fatalf("expected value: %#v", pair)
158	}
159	if meta.LastIndex == 0 {
160		t.Fatalf("unexpected value: %#v", meta)
161	}
162
163	// CAS update with bad index
164	p.ModifyIndex = 1
165	if work, _, err := kv.DeleteCAS(p, nil); err != nil {
166		t.Fatalf("err: %v", err)
167	} else if work {
168		t.Fatalf("unexpected CAS")
169	}
170
171	// CAS update with valid index
172	p.ModifyIndex = meta.LastIndex
173	if work, _, err := kv.DeleteCAS(p, nil); err != nil {
174		t.Fatalf("err: %v", err)
175	} else if !work {
176		t.Fatalf("unexpected CAS failure")
177	}
178}
179
180func TestAPI_ClientCAS(t *testing.T) {
181	t.Parallel()
182	c, s := makeClient(t)
183	defer s.Stop()
184
185	kv := c.KV()
186
187	// Put the key
188	key := testKey()
189	value := []byte("test")
190	p := &KVPair{Key: key, Value: value}
191	if work, _, err := kv.CAS(p, nil); err != nil {
192		t.Fatalf("err: %v", err)
193	} else if !work {
194		t.Fatalf("CAS failure")
195	}
196
197	// Get should work
198	pair, meta, err := kv.Get(key, nil)
199	if err != nil {
200		t.Fatalf("err: %v", err)
201	}
202	if pair == nil {
203		t.Fatalf("expected value: %#v", pair)
204	}
205	if meta.LastIndex == 0 {
206		t.Fatalf("unexpected value: %#v", meta)
207	}
208
209	// CAS update with bad index
210	newVal := []byte("foo")
211	p.Value = newVal
212	p.ModifyIndex = 1
213	if work, _, err := kv.CAS(p, nil); err != nil {
214		t.Fatalf("err: %v", err)
215	} else if work {
216		t.Fatalf("unexpected CAS")
217	}
218
219	// CAS update with valid index
220	p.ModifyIndex = meta.LastIndex
221	if work, _, err := kv.CAS(p, nil); err != nil {
222		t.Fatalf("err: %v", err)
223	} else if !work {
224		t.Fatalf("unexpected CAS failure")
225	}
226}
227
228func TestAPI_ClientWatchGet(t *testing.T) {
229	t.Parallel()
230	c, s := makeClient(t)
231	defer s.Stop()
232
233	kv := c.KV()
234
235	s.WaitForSerfCheck(t)
236	// Get a get without a key
237	key := testKey()
238	pair, meta, err := kv.Get(key, nil)
239	if err != nil {
240		t.Fatalf("err: %v", err)
241	}
242	if pair != nil {
243		t.Fatalf("unexpected value: %#v", pair)
244	}
245	if meta.LastIndex == 0 {
246		t.Fatalf("unexpected value: %#v", meta)
247	}
248
249	// Put the key
250	value := []byte("test")
251	doneCh := make(chan error)
252	go func() {
253		kv := c.KV()
254
255		time.Sleep(100 * time.Millisecond)
256		p := &KVPair{Key: key, Flags: 42, Value: value}
257		_, err := kv.Put(p, nil)
258		doneCh <- err
259	}()
260
261	// Get should work
262	options := &QueryOptions{WaitIndex: meta.LastIndex}
263	pair, meta2, err := kv.Get(key, options)
264	if err != nil {
265		t.Fatalf("err: %v", err)
266	}
267	if pair == nil {
268		t.Fatalf("expected value: %#v", pair)
269	}
270	if !bytes.Equal(pair.Value, value) {
271		t.Fatalf("unexpected value: %#v", pair)
272	}
273	if pair.Flags != 42 {
274		t.Fatalf("unexpected value: %#v", pair)
275	}
276	if meta2.LastIndex <= meta.LastIndex {
277		t.Fatalf("unexpected value: %#v", meta2)
278	}
279
280	// Block until put finishes to avoid a race between it and deferred s.Stop()
281	err = <-doneCh
282	require.NoError(t, err)
283}
284
285func TestAPI_ClientWatchList(t *testing.T) {
286	t.Parallel()
287	c, s := makeClient(t)
288	defer s.Stop()
289
290	kv := c.KV()
291
292	// Get a get without a key
293	prefix := testKey()
294	key := path.Join(prefix, testKey())
295	pairs, meta, err := kv.List(prefix, nil)
296	if err != nil {
297		t.Fatalf("err: %v", err)
298	}
299	if len(pairs) != 0 {
300		t.Fatalf("unexpected value: %#v", pairs)
301	}
302	if meta.LastIndex == 0 {
303		t.Fatalf("unexpected value: %#v", meta)
304	}
305
306	// Put the key
307	value := []byte("test")
308	doneCh := make(chan error)
309	go func() {
310		kv := c.KV()
311
312		time.Sleep(100 * time.Millisecond)
313		p := &KVPair{Key: key, Flags: 42, Value: value}
314		_, err := kv.Put(p, nil)
315		doneCh <- err
316	}()
317
318	// Get should work
319	options := &QueryOptions{WaitIndex: meta.LastIndex}
320	pairs, meta2, err := kv.List(prefix, options)
321	if err != nil {
322		t.Fatalf("err: %v", err)
323	}
324	if len(pairs) != 1 {
325		t.Fatalf("expected value: %#v", pairs)
326	}
327	if !bytes.Equal(pairs[0].Value, value) {
328		t.Fatalf("unexpected value: %#v", pairs)
329	}
330	if pairs[0].Flags != 42 {
331		t.Fatalf("unexpected value: %#v", pairs)
332	}
333	if meta2.LastIndex <= meta.LastIndex {
334		t.Fatalf("unexpected value: %#v", meta2)
335	}
336
337	// Block until put finishes to avoid a race between it and deferred s.Stop()
338	err = <-doneCh
339	require.NoError(t, err)
340}
341
342func TestAPI_ClientKeys_DeleteRecurse(t *testing.T) {
343	t.Parallel()
344	c, s := makeClient(t)
345	defer s.Stop()
346
347	kv := c.KV()
348
349	// Generate some test keys
350	prefix := testKey()
351	var keys []string
352	for i := 0; i < 100; i++ {
353		keys = append(keys, path.Join(prefix, testKey()))
354	}
355
356	// Set values
357	value := []byte("test")
358	for _, key := range keys {
359		p := &KVPair{Key: key, Value: value}
360		if _, err := kv.Put(p, nil); err != nil {
361			t.Fatalf("err: %v", err)
362		}
363	}
364
365	// List the values
366	out, meta, err := kv.Keys(prefix, "", nil)
367	if err != nil {
368		t.Fatalf("err: %v", err)
369	}
370	if len(out) != len(keys) {
371		t.Fatalf("got %d keys", len(out))
372	}
373	if meta.LastIndex == 0 {
374		t.Fatalf("unexpected value: %#v", meta)
375	}
376
377	// Delete all
378	if _, err := kv.DeleteTree(prefix, nil); err != nil {
379		t.Fatalf("err: %v", err)
380	}
381
382	// List the values
383	out, _, err = kv.Keys(prefix, "", nil)
384	if err != nil {
385		t.Fatalf("err: %v", err)
386	}
387	if len(out) != 0 {
388		t.Fatalf("got %d keys", len(out))
389	}
390}
391
392func TestAPI_ClientAcquireRelease(t *testing.T) {
393	t.Parallel()
394	c, s := makeClient(t)
395	defer s.Stop()
396
397	s.WaitForSerfCheck(t)
398
399	session := c.Session()
400	kv := c.KV()
401
402	// Make a session
403	id, _, err := session.CreateNoChecks(nil, nil)
404	if err != nil {
405		t.Fatalf("err: %v", err)
406	}
407	defer session.Destroy(id, nil)
408
409	// Acquire the key
410	key := testKey()
411	value := []byte("test")
412	p := &KVPair{Key: key, Value: value, Session: id}
413	if work, _, err := kv.Acquire(p, nil); err != nil {
414		t.Fatalf("err: %v", err)
415	} else if !work {
416		t.Fatalf("Lock failure")
417	}
418
419	// Get should work
420	pair, meta, err := kv.Get(key, nil)
421	if err != nil {
422		t.Fatalf("err: %v", err)
423	}
424	if pair == nil {
425		t.Fatalf("expected value: %#v", pair)
426	}
427	if pair.LockIndex != 1 {
428		t.Fatalf("Expected lock: %v", pair)
429	}
430	if pair.Session != id {
431		t.Fatalf("Expected lock: %v", pair)
432	}
433	if meta.LastIndex == 0 {
434		t.Fatalf("unexpected value: %#v", meta)
435	}
436
437	// Release
438	if work, _, err := kv.Release(p, nil); err != nil {
439		t.Fatalf("err: %v", err)
440	} else if !work {
441		t.Fatalf("Release fail")
442	}
443
444	// Get should work
445	pair, meta, err = kv.Get(key, nil)
446	if err != nil {
447		t.Fatalf("err: %v", err)
448	}
449	if pair == nil {
450		t.Fatalf("expected value: %#v", pair)
451	}
452	if pair.LockIndex != 1 {
453		t.Fatalf("Expected lock: %v", pair)
454	}
455	if pair.Session != "" {
456		t.Fatalf("Expected unlock: %v", pair)
457	}
458	if meta.LastIndex == 0 {
459		t.Fatalf("unexpected value: %#v", meta)
460	}
461}
462
463func TestAPI_KVClientTxn(t *testing.T) {
464	t.Parallel()
465	c, s := makeClient(t)
466	defer s.Stop()
467
468	s.WaitForSerfCheck(t)
469
470	session := c.Session()
471	kv := c.KV()
472
473	// Make a session.
474	id, _, err := session.CreateNoChecks(nil, nil)
475	if err != nil {
476		t.Fatalf("err: %v", err)
477	}
478	defer session.Destroy(id, nil)
479
480	// Acquire and get the key via a transaction, but don't supply a valid
481	// session.
482	key := testKey()
483	value := []byte("test")
484	txn := KVTxnOps{
485		&KVTxnOp{
486			Verb:  KVLock,
487			Key:   key,
488			Value: value,
489		},
490		&KVTxnOp{
491			Verb: KVGet,
492			Key:  key,
493		},
494	}
495	ok, ret, _, err := kv.Txn(txn, nil)
496	if err != nil {
497		t.Fatalf("err: %v", err)
498	} else if ok {
499		t.Fatalf("transaction should have failed")
500	}
501
502	if ret == nil || len(ret.Errors) != 2 || len(ret.Results) != 0 {
503		t.Fatalf("bad: %v", ret)
504	}
505	if ret.Errors[0].OpIndex != 0 ||
506		!strings.Contains(ret.Errors[0].What, "missing session") ||
507		!strings.Contains(ret.Errors[1].What, "doesn't exist") {
508		t.Fatalf("bad: %v", ret.Errors[0])
509	}
510
511	// Now poke in a real session and try again.
512	txn[0].Session = id
513	ok, ret, _, err = kv.Txn(txn, nil)
514	if err != nil {
515		t.Fatalf("err: %v", err)
516	} else if !ok {
517		t.Fatalf("transaction failure")
518	}
519
520	if ret == nil || len(ret.Errors) != 0 || len(ret.Results) != 2 {
521		t.Fatalf("bad: %v", ret)
522	}
523	for i, result := range ret.Results {
524		var expected []byte
525		if i == 1 {
526			expected = value
527		}
528
529		if result.Key != key ||
530			!bytes.Equal(result.Value, expected) ||
531			result.Session != id ||
532			result.LockIndex != 1 {
533			t.Fatalf("bad: %v", result)
534		}
535	}
536
537	// Run a read-only transaction.
538	txn = KVTxnOps{
539		&KVTxnOp{
540			Verb: KVGet,
541			Key:  key,
542		},
543	}
544	ok, ret, _, err = kv.Txn(txn, nil)
545	if err != nil {
546		t.Fatalf("err: %v", err)
547	} else if !ok {
548		t.Fatalf("transaction failure")
549	}
550
551	if ret == nil || len(ret.Errors) != 0 || len(ret.Results) != 1 {
552		t.Fatalf("bad: %v", ret)
553	}
554	for _, result := range ret.Results {
555		if result.Key != key ||
556			!bytes.Equal(result.Value, value) ||
557			result.Session != id ||
558			result.LockIndex != 1 {
559			t.Fatalf("bad: %v", result)
560		}
561	}
562
563	// Sanity check using the regular GET API.
564	pair, meta, err := kv.Get(key, nil)
565	if err != nil {
566		t.Fatalf("err: %v", err)
567	}
568	if pair == nil {
569		t.Fatalf("expected value: %#v", pair)
570	}
571	if pair.LockIndex != 1 {
572		t.Fatalf("Expected lock: %v", pair)
573	}
574	if pair.Session != id {
575		t.Fatalf("Expected lock: %v", pair)
576	}
577	if meta.LastIndex == 0 {
578		t.Fatalf("unexpected value: %#v", meta)
579	}
580}
581