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