github.com/macb/etcd@v0.3.1-0.20140227003422-a60481c6b1a0/store/store_test.go (about) 1 /* 2 Copyright 2013 CoreOS Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package store 18 19 import ( 20 "testing" 21 "time" 22 23 etcdErr "github.com/coreos/etcd/error" 24 "github.com/coreos/etcd/third_party/github.com/stretchr/testify/assert" 25 ) 26 27 // Ensure that the store can retrieve an existing value. 28 func TestStoreGetValue(t *testing.T) { 29 s := newStore() 30 s.Create("/foo", false, "bar", false, Permanent) 31 e, err := s.Get("/foo", false, false) 32 assert.Nil(t, err, "") 33 assert.Equal(t, e.Action, "get", "") 34 assert.Equal(t, e.Node.Key, "/foo", "") 35 assert.Equal(t, *e.Node.Value, "bar", "") 36 } 37 38 // Ensure that the store can recrusively retrieve a directory listing. 39 // Note that hidden files should not be returned. 40 func TestStoreGetDirectory(t *testing.T) { 41 s := newStore() 42 s.Create("/foo", true, "", false, Permanent) 43 s.Create("/foo/bar", false, "X", false, Permanent) 44 s.Create("/foo/_hidden", false, "*", false, Permanent) 45 s.Create("/foo/baz", true, "", false, Permanent) 46 s.Create("/foo/baz/bat", false, "Y", false, Permanent) 47 s.Create("/foo/baz/_hidden", false, "*", false, Permanent) 48 s.Create("/foo/baz/ttl", false, "Y", false, time.Now().Add(time.Second*3)) 49 e, err := s.Get("/foo", true, false) 50 assert.Nil(t, err, "") 51 assert.Equal(t, e.Action, "get", "") 52 assert.Equal(t, e.Node.Key, "/foo", "") 53 assert.Equal(t, len(e.Node.Nodes), 2, "") 54 assert.Equal(t, e.Node.Nodes[0].Key, "/foo/bar", "") 55 assert.Equal(t, *e.Node.Nodes[0].Value, "X", "") 56 assert.Equal(t, e.Node.Nodes[0].Dir, false, "") 57 assert.Equal(t, e.Node.Nodes[1].Key, "/foo/baz", "") 58 assert.Equal(t, e.Node.Nodes[1].Dir, true, "") 59 assert.Equal(t, len(e.Node.Nodes[1].Nodes), 2, "") 60 assert.Equal(t, e.Node.Nodes[1].Nodes[0].Key, "/foo/baz/bat", "") 61 assert.Equal(t, *e.Node.Nodes[1].Nodes[0].Value, "Y", "") 62 assert.Equal(t, e.Node.Nodes[1].Nodes[0].Dir, false, "") 63 assert.Equal(t, e.Node.Nodes[1].Nodes[1].Key, "/foo/baz/ttl", "") 64 assert.Equal(t, *e.Node.Nodes[1].Nodes[1].Value, "Y", "") 65 assert.Equal(t, e.Node.Nodes[1].Nodes[1].Dir, false, "") 66 assert.Equal(t, e.Node.Nodes[1].Nodes[1].TTL, 3, "") 67 } 68 69 // Ensure that the store can retrieve a directory in sorted order. 70 func TestStoreGetSorted(t *testing.T) { 71 s := newStore() 72 s.Create("/foo", true, "", false, Permanent) 73 s.Create("/foo/x", false, "0", false, Permanent) 74 s.Create("/foo/z", false, "0", false, Permanent) 75 s.Create("/foo/y", true, "", false, Permanent) 76 s.Create("/foo/y/a", false, "0", false, Permanent) 77 s.Create("/foo/y/b", false, "0", false, Permanent) 78 e, err := s.Get("/foo", true, true) 79 assert.Nil(t, err, "") 80 assert.Equal(t, e.Node.Nodes[0].Key, "/foo/x", "") 81 assert.Equal(t, e.Node.Nodes[1].Key, "/foo/y", "") 82 assert.Equal(t, e.Node.Nodes[1].Nodes[0].Key, "/foo/y/a", "") 83 assert.Equal(t, e.Node.Nodes[1].Nodes[1].Key, "/foo/y/b", "") 84 assert.Equal(t, e.Node.Nodes[2].Key, "/foo/z", "") 85 } 86 87 func TestSet(t *testing.T) { 88 s := newStore() 89 90 // Set /foo="" 91 e, err := s.Set("/foo", false, "", Permanent) 92 assert.Nil(t, err, "") 93 assert.Equal(t, e.Action, "set", "") 94 assert.Equal(t, e.Node.Key, "/foo", "") 95 assert.False(t, e.Node.Dir, "") 96 assert.Equal(t, *e.Node.Value, "", "") 97 assert.Nil(t, e.Node.Nodes, "") 98 assert.Nil(t, e.Node.Expiration, "") 99 assert.Equal(t, e.Node.TTL, 0, "") 100 assert.Equal(t, e.Node.ModifiedIndex, uint64(1), "") 101 102 // Set /foo="bar" 103 e, err = s.Set("/foo", false, "bar", Permanent) 104 assert.Nil(t, err, "") 105 assert.Equal(t, e.Action, "set", "") 106 assert.Equal(t, e.Node.Key, "/foo", "") 107 assert.False(t, e.Node.Dir, "") 108 assert.Equal(t, *e.Node.Value, "bar", "") 109 assert.Nil(t, e.Node.Nodes, "") 110 assert.Nil(t, e.Node.Expiration, "") 111 assert.Equal(t, e.Node.TTL, 0, "") 112 assert.Equal(t, e.Node.ModifiedIndex, uint64(2), "") 113 // check prevNode 114 assert.NotNil(t, e.PrevNode, "") 115 assert.Equal(t, e.PrevNode.Key, "/foo", "") 116 assert.Equal(t, *e.PrevNode.Value, "", "") 117 assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "") 118 // Set /foo="baz" (for testing prevNode) 119 e, err = s.Set("/foo", false, "baz", Permanent) 120 assert.Nil(t, err, "") 121 assert.Equal(t, e.Action, "set", "") 122 assert.Equal(t, e.Node.Key, "/foo", "") 123 assert.False(t, e.Node.Dir, "") 124 assert.Equal(t, *e.Node.Value, "baz", "") 125 assert.Nil(t, e.Node.Nodes, "") 126 assert.Nil(t, e.Node.Expiration, "") 127 assert.Equal(t, e.Node.TTL, 0, "") 128 assert.Equal(t, e.Node.ModifiedIndex, uint64(3), "") 129 // check prevNode 130 assert.NotNil(t, e.PrevNode, "") 131 assert.Equal(t, e.PrevNode.Key, "/foo", "") 132 assert.Equal(t, *e.PrevNode.Value, "bar", "") 133 assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(2), "") 134 135 // Set /dir as a directory 136 e, err = s.Set("/dir", true, "", Permanent) 137 assert.Nil(t, err, "") 138 assert.Equal(t, e.Action, "set", "") 139 assert.Equal(t, e.Node.Key, "/dir", "") 140 assert.True(t, e.Node.Dir, "") 141 assert.Nil(t, e.Node.Value) 142 assert.Nil(t, e.Node.Nodes, "") 143 assert.Nil(t, e.Node.Expiration, "") 144 assert.Equal(t, e.Node.TTL, 0, "") 145 assert.Equal(t, e.Node.ModifiedIndex, uint64(4), "") 146 } 147 148 // Ensure that the store can create a new key if it doesn't already exist. 149 func TestStoreCreateValue(t *testing.T) { 150 s := newStore() 151 // Create /foo=bar 152 e, err := s.Create("/foo", false, "bar", false, Permanent) 153 assert.Nil(t, err, "") 154 assert.Equal(t, e.Action, "create", "") 155 assert.Equal(t, e.Node.Key, "/foo", "") 156 assert.False(t, e.Node.Dir, "") 157 assert.Equal(t, *e.Node.Value, "bar", "") 158 assert.Nil(t, e.Node.Nodes, "") 159 assert.Nil(t, e.Node.Expiration, "") 160 assert.Equal(t, e.Node.TTL, 0, "") 161 assert.Equal(t, e.Node.ModifiedIndex, uint64(1), "") 162 163 // Create /empty="" 164 e, err = s.Create("/empty", false, "", false, Permanent) 165 assert.Nil(t, err, "") 166 assert.Equal(t, e.Action, "create", "") 167 assert.Equal(t, e.Node.Key, "/empty", "") 168 assert.False(t, e.Node.Dir, "") 169 assert.Equal(t, *e.Node.Value, "", "") 170 assert.Nil(t, e.Node.Nodes, "") 171 assert.Nil(t, e.Node.Expiration, "") 172 assert.Equal(t, e.Node.TTL, 0, "") 173 assert.Equal(t, e.Node.ModifiedIndex, uint64(2), "") 174 175 } 176 177 // Ensure that the store can create a new directory if it doesn't already exist. 178 func TestStoreCreateDirectory(t *testing.T) { 179 s := newStore() 180 e, err := s.Create("/foo", true, "", false, Permanent) 181 assert.Nil(t, err, "") 182 assert.Equal(t, e.Action, "create", "") 183 assert.Equal(t, e.Node.Key, "/foo", "") 184 assert.True(t, e.Node.Dir, "") 185 } 186 187 // Ensure that the store fails to create a key if it already exists. 188 func TestStoreCreateFailsIfExists(t *testing.T) { 189 s := newStore() 190 // create /foo as dir 191 s.Create("/foo", true, "", false, Permanent) 192 193 // create /foo as dir again 194 e, _err := s.Create("/foo", true, "", false, Permanent) 195 err := _err.(*etcdErr.Error) 196 assert.Equal(t, err.ErrorCode, etcdErr.EcodeNodeExist, "") 197 assert.Equal(t, err.Message, "Key already exists", "") 198 assert.Equal(t, err.Cause, "/foo", "") 199 assert.Equal(t, err.Index, uint64(1), "") 200 assert.Nil(t, e, 0, "") 201 } 202 203 // Ensure that the store can update a key if it already exists. 204 func TestStoreUpdateValue(t *testing.T) { 205 s := newStore() 206 // create /foo=bar 207 s.Create("/foo", false, "bar", false, Permanent) 208 // update /foo="bzr" 209 e, err := s.Update("/foo", "baz", Permanent) 210 assert.Nil(t, err, "") 211 assert.Equal(t, e.Action, "update", "") 212 assert.Equal(t, e.Node.Key, "/foo", "") 213 assert.False(t, e.Node.Dir, "") 214 assert.Equal(t, *e.Node.Value, "baz", "") 215 assert.Equal(t, e.Node.TTL, 0, "") 216 assert.Equal(t, e.Node.ModifiedIndex, uint64(2), "") 217 // check prevNode 218 assert.Equal(t, e.PrevNode.Key, "/foo", "") 219 assert.Equal(t, *e.PrevNode.Value, "bar", "") 220 assert.Equal(t, e.PrevNode.TTL, 0, "") 221 assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "") 222 223 e, _ = s.Get("/foo", false, false) 224 assert.Equal(t, *e.Node.Value, "baz", "") 225 226 // update /foo="" 227 e, err = s.Update("/foo", "", Permanent) 228 assert.Nil(t, err, "") 229 assert.Equal(t, e.Action, "update", "") 230 assert.Equal(t, e.Node.Key, "/foo", "") 231 assert.False(t, e.Node.Dir, "") 232 assert.Equal(t, *e.Node.Value, "", "") 233 assert.Equal(t, e.Node.TTL, 0, "") 234 assert.Equal(t, e.Node.ModifiedIndex, uint64(3), "") 235 // check prevNode 236 assert.Equal(t, e.PrevNode.Key, "/foo", "") 237 assert.Equal(t, *e.PrevNode.Value, "baz", "") 238 assert.Equal(t, e.PrevNode.TTL, 0, "") 239 assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(2), "") 240 241 e, _ = s.Get("/foo", false, false) 242 assert.Equal(t, *e.Node.Value, "", "") 243 } 244 245 // Ensure that the store cannot update a directory. 246 func TestStoreUpdateFailsIfDirectory(t *testing.T) { 247 s := newStore() 248 s.Create("/foo", true, "", false, Permanent) 249 e, _err := s.Update("/foo", "baz", Permanent) 250 err := _err.(*etcdErr.Error) 251 assert.Equal(t, err.ErrorCode, etcdErr.EcodeNotFile, "") 252 assert.Equal(t, err.Message, "Not a file", "") 253 assert.Equal(t, err.Cause, "/foo", "") 254 assert.Nil(t, e, "") 255 } 256 257 // Ensure that the store can update the TTL on a value. 258 func TestStoreUpdateValueTTL(t *testing.T) { 259 s := newStore() 260 261 c := make(chan bool) 262 defer func() { 263 c <- true 264 }() 265 go mockSyncService(s.DeleteExpiredKeys, c) 266 267 s.Create("/foo", false, "bar", false, Permanent) 268 _, err := s.Update("/foo", "baz", time.Now().Add(500*time.Millisecond)) 269 e, _ := s.Get("/foo", false, false) 270 assert.Equal(t, *e.Node.Value, "baz", "") 271 272 time.Sleep(600 * time.Millisecond) 273 e, err = s.Get("/foo", false, false) 274 assert.Nil(t, e, "") 275 assert.Equal(t, err.(*etcdErr.Error).ErrorCode, etcdErr.EcodeKeyNotFound, "") 276 } 277 278 // Ensure that the store can update the TTL on a directory. 279 func TestStoreUpdateDirTTL(t *testing.T) { 280 s := newStore() 281 282 c := make(chan bool) 283 defer func() { 284 c <- true 285 }() 286 go mockSyncService(s.DeleteExpiredKeys, c) 287 288 s.Create("/foo", true, "", false, Permanent) 289 s.Create("/foo/bar", false, "baz", false, Permanent) 290 _, err := s.Update("/foo", "", time.Now().Add(500*time.Millisecond)) 291 e, _ := s.Get("/foo/bar", false, false) 292 assert.Equal(t, *e.Node.Value, "baz", "") 293 294 time.Sleep(600 * time.Millisecond) 295 e, err = s.Get("/foo/bar", false, false) 296 assert.Nil(t, e, "") 297 assert.Equal(t, err.(*etcdErr.Error).ErrorCode, etcdErr.EcodeKeyNotFound, "") 298 } 299 300 // Ensure that the store can delete a value. 301 func TestStoreDeleteValue(t *testing.T) { 302 s := newStore() 303 s.Create("/foo", false, "bar", false, Permanent) 304 e, err := s.Delete("/foo", false, false) 305 assert.Nil(t, err, "") 306 assert.Equal(t, e.Action, "delete", "") 307 // check pervNode 308 assert.NotNil(t, e.PrevNode, "") 309 assert.Equal(t, e.PrevNode.Key, "/foo", "") 310 assert.Equal(t, *e.PrevNode.Value, "bar", "") 311 } 312 313 // Ensure that the store can delete a directory if recursive is specified. 314 func TestStoreDeleteDiretory(t *testing.T) { 315 s := newStore() 316 // create directory /foo 317 s.Create("/foo", true, "", false, Permanent) 318 // delete /foo with dir = true and recursive = false 319 // this should succeed, since the directory is empty 320 e, err := s.Delete("/foo", true, false) 321 assert.Nil(t, err, "") 322 assert.Equal(t, e.Action, "delete", "") 323 // check pervNode 324 assert.NotNil(t, e.PrevNode, "") 325 assert.Equal(t, e.PrevNode.Key, "/foo", "") 326 assert.Equal(t, e.PrevNode.Dir, true, "") 327 328 // create directory /foo and directory /foo/bar 329 s.Create("/foo/bar", true, "", false, Permanent) 330 // delete /foo with dir = true and recursive = false 331 // this should fail, since the directory is not empty 332 _, err = s.Delete("/foo", true, false) 333 assert.NotNil(t, err, "") 334 335 // delete /foo with dir=false and recursive = true 336 // this should succeed, since recursive implies dir=true 337 // and recursively delete should be able to delete all 338 // items under the given directory 339 e, err = s.Delete("/foo", false, true) 340 assert.Nil(t, err, "") 341 assert.Equal(t, e.Action, "delete", "") 342 343 } 344 345 // Ensure that the store cannot delete a directory if both of recursive 346 // and dir are not specified. 347 func TestStoreDeleteDiretoryFailsIfNonRecursiveAndDir(t *testing.T) { 348 s := newStore() 349 s.Create("/foo", true, "", false, Permanent) 350 e, _err := s.Delete("/foo", false, false) 351 err := _err.(*etcdErr.Error) 352 assert.Equal(t, err.ErrorCode, etcdErr.EcodeNotFile, "") 353 assert.Equal(t, err.Message, "Not a file", "") 354 assert.Nil(t, e, "") 355 } 356 357 func TestRootRdOnly(t *testing.T) { 358 s := newStore() 359 360 _, err := s.Set("/", true, "", Permanent) 361 assert.NotNil(t, err, "") 362 363 _, err = s.Delete("/", true, true) 364 assert.NotNil(t, err, "") 365 366 _, err = s.Create("/", true, "", false, Permanent) 367 assert.NotNil(t, err, "") 368 369 _, err = s.Update("/", "", Permanent) 370 assert.NotNil(t, err, "") 371 372 _, err = s.CompareAndSwap("/", "", 0, "", Permanent) 373 assert.NotNil(t, err, "") 374 } 375 376 func TestStoreCompareAndDeletePrevValue(t *testing.T) { 377 s := newStore() 378 s.Create("/foo", false, "bar", false, Permanent) 379 e, err := s.CompareAndDelete("/foo", "bar", 0) 380 assert.Nil(t, err, "") 381 assert.Equal(t, e.Action, "compareAndDelete", "") 382 assert.Equal(t, e.Node.Key, "/foo", "") 383 384 // check pervNode 385 assert.NotNil(t, e.PrevNode, "") 386 assert.Equal(t, e.PrevNode.Key, "/foo", "") 387 assert.Equal(t, *e.PrevNode.Value, "bar", "") 388 assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "") 389 assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "") 390 } 391 392 func TestStoreCompareAndDeletePrevValueFailsIfNotMatch(t *testing.T) { 393 s := newStore() 394 s.Create("/foo", false, "bar", false, Permanent) 395 e, _err := s.CompareAndDelete("/foo", "baz", 0) 396 err := _err.(*etcdErr.Error) 397 assert.Equal(t, err.ErrorCode, etcdErr.EcodeTestFailed, "") 398 assert.Equal(t, err.Message, "Compare failed", "") 399 assert.Nil(t, e, "") 400 e, _ = s.Get("/foo", false, false) 401 assert.Equal(t, *e.Node.Value, "bar", "") 402 } 403 404 func TestStoreCompareAndDeletePrevIndex(t *testing.T) { 405 s := newStore() 406 s.Create("/foo", false, "bar", false, Permanent) 407 e, err := s.CompareAndDelete("/foo", "", 1) 408 assert.Nil(t, err, "") 409 assert.Equal(t, e.Action, "compareAndDelete", "") 410 // check pervNode 411 assert.NotNil(t, e.PrevNode, "") 412 assert.Equal(t, e.PrevNode.Key, "/foo", "") 413 assert.Equal(t, *e.PrevNode.Value, "bar", "") 414 assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "") 415 assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "") 416 } 417 418 func TestStoreCompareAndDeletePrevIndexFailsIfNotMatch(t *testing.T) { 419 s := newStore() 420 s.Create("/foo", false, "bar", false, Permanent) 421 e, _err := s.CompareAndDelete("/foo", "", 100) 422 assert.NotNil(t, _err, "") 423 err := _err.(*etcdErr.Error) 424 assert.Equal(t, err.ErrorCode, etcdErr.EcodeTestFailed, "") 425 assert.Equal(t, err.Message, "Compare failed", "") 426 assert.Nil(t, e, "") 427 e, _ = s.Get("/foo", false, false) 428 assert.Equal(t, *e.Node.Value, "bar", "") 429 } 430 431 // Ensure that the store cannot delete a directory. 432 func TestStoreCompareAndDeleteDiretoryFail(t *testing.T) { 433 s := newStore() 434 s.Create("/foo", true, "", false, Permanent) 435 _, _err := s.CompareAndDelete("/foo", "", 0) 436 assert.NotNil(t, _err, "") 437 err := _err.(*etcdErr.Error) 438 assert.Equal(t, err.ErrorCode, etcdErr.EcodeNotFile, "") 439 } 440 441 // Ensure that the store can conditionally update a key if it has a previous value. 442 func TestStoreCompareAndSwapPrevValue(t *testing.T) { 443 s := newStore() 444 s.Create("/foo", false, "bar", false, Permanent) 445 e, err := s.CompareAndSwap("/foo", "bar", 0, "baz", Permanent) 446 assert.Nil(t, err, "") 447 assert.Equal(t, e.Action, "compareAndSwap", "") 448 assert.Equal(t, *e.Node.Value, "baz", "") 449 // check pervNode 450 assert.NotNil(t, e.PrevNode, "") 451 assert.Equal(t, e.PrevNode.Key, "/foo", "") 452 assert.Equal(t, *e.PrevNode.Value, "bar", "") 453 assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "") 454 assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "") 455 456 e, _ = s.Get("/foo", false, false) 457 assert.Equal(t, *e.Node.Value, "baz", "") 458 } 459 460 // Ensure that the store cannot conditionally update a key if it has the wrong previous value. 461 func TestStoreCompareAndSwapPrevValueFailsIfNotMatch(t *testing.T) { 462 s := newStore() 463 s.Create("/foo", false, "bar", false, Permanent) 464 e, _err := s.CompareAndSwap("/foo", "wrong_value", 0, "baz", Permanent) 465 err := _err.(*etcdErr.Error) 466 assert.Equal(t, err.ErrorCode, etcdErr.EcodeTestFailed, "") 467 assert.Equal(t, err.Message, "Compare failed", "") 468 assert.Nil(t, e, "") 469 e, _ = s.Get("/foo", false, false) 470 assert.Equal(t, *e.Node.Value, "bar", "") 471 } 472 473 // Ensure that the store can conditionally update a key if it has a previous index. 474 func TestStoreCompareAndSwapPrevIndex(t *testing.T) { 475 s := newStore() 476 s.Create("/foo", false, "bar", false, Permanent) 477 e, err := s.CompareAndSwap("/foo", "", 1, "baz", Permanent) 478 assert.Nil(t, err, "") 479 assert.Equal(t, e.Action, "compareAndSwap", "") 480 assert.Equal(t, *e.Node.Value, "baz", "") 481 // check pervNode 482 assert.NotNil(t, e.PrevNode, "") 483 assert.Equal(t, e.PrevNode.Key, "/foo", "") 484 assert.Equal(t, *e.PrevNode.Value, "bar", "") 485 assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "") 486 assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "") 487 488 e, _ = s.Get("/foo", false, false) 489 assert.Equal(t, *e.Node.Value, "baz", "") 490 } 491 492 // Ensure that the store cannot conditionally update a key if it has the wrong previous index. 493 func TestStoreCompareAndSwapPrevIndexFailsIfNotMatch(t *testing.T) { 494 s := newStore() 495 s.Create("/foo", false, "bar", false, Permanent) 496 e, _err := s.CompareAndSwap("/foo", "", 100, "baz", Permanent) 497 err := _err.(*etcdErr.Error) 498 assert.Equal(t, err.ErrorCode, etcdErr.EcodeTestFailed, "") 499 assert.Equal(t, err.Message, "Compare failed", "") 500 assert.Nil(t, e, "") 501 e, _ = s.Get("/foo", false, false) 502 assert.Equal(t, *e.Node.Value, "bar", "") 503 } 504 505 // Ensure that the store can watch for key creation. 506 func TestStoreWatchCreate(t *testing.T) { 507 s := newStore() 508 w, _ := s.Watch("/foo", false, false, 0) 509 c := w.EventChan 510 s.Create("/foo", false, "bar", false, Permanent) 511 e := nbselect(c) 512 assert.Equal(t, e.Action, "create", "") 513 assert.Equal(t, e.Node.Key, "/foo", "") 514 e = nbselect(c) 515 assert.Nil(t, e, "") 516 } 517 518 // Ensure that the store can watch for recursive key creation. 519 func TestStoreWatchRecursiveCreate(t *testing.T) { 520 s := newStore() 521 w, _ := s.Watch("/foo", true, false, 0) 522 s.Create("/foo/bar", false, "baz", false, Permanent) 523 e := nbselect(w.EventChan) 524 assert.Equal(t, e.Action, "create", "") 525 assert.Equal(t, e.Node.Key, "/foo/bar", "") 526 } 527 528 // Ensure that the store can watch for key updates. 529 func TestStoreWatchUpdate(t *testing.T) { 530 s := newStore() 531 s.Create("/foo", false, "bar", false, Permanent) 532 w, _ := s.Watch("/foo", false, false, 0) 533 s.Update("/foo", "baz", Permanent) 534 e := nbselect(w.EventChan) 535 assert.Equal(t, e.Action, "update", "") 536 assert.Equal(t, e.Node.Key, "/foo", "") 537 } 538 539 // Ensure that the store can watch for recursive key updates. 540 func TestStoreWatchRecursiveUpdate(t *testing.T) { 541 s := newStore() 542 s.Create("/foo/bar", false, "baz", false, Permanent) 543 w, _ := s.Watch("/foo", true, false, 0) 544 s.Update("/foo/bar", "baz", Permanent) 545 e := nbselect(w.EventChan) 546 assert.Equal(t, e.Action, "update", "") 547 assert.Equal(t, e.Node.Key, "/foo/bar", "") 548 } 549 550 // Ensure that the store can watch for key deletions. 551 func TestStoreWatchDelete(t *testing.T) { 552 s := newStore() 553 s.Create("/foo", false, "bar", false, Permanent) 554 w, _ := s.Watch("/foo", false, false, 0) 555 s.Delete("/foo", false, false) 556 e := nbselect(w.EventChan) 557 assert.Equal(t, e.Action, "delete", "") 558 assert.Equal(t, e.Node.Key, "/foo", "") 559 } 560 561 // Ensure that the store can watch for recursive key deletions. 562 func TestStoreWatchRecursiveDelete(t *testing.T) { 563 s := newStore() 564 s.Create("/foo/bar", false, "baz", false, Permanent) 565 w, _ := s.Watch("/foo", true, false, 0) 566 s.Delete("/foo/bar", false, false) 567 e := nbselect(w.EventChan) 568 assert.Equal(t, e.Action, "delete", "") 569 assert.Equal(t, e.Node.Key, "/foo/bar", "") 570 } 571 572 // Ensure that the store can watch for CAS updates. 573 func TestStoreWatchCompareAndSwap(t *testing.T) { 574 s := newStore() 575 s.Create("/foo", false, "bar", false, Permanent) 576 w, _ := s.Watch("/foo", false, false, 0) 577 s.CompareAndSwap("/foo", "bar", 0, "baz", Permanent) 578 e := nbselect(w.EventChan) 579 assert.Equal(t, e.Action, "compareAndSwap", "") 580 assert.Equal(t, e.Node.Key, "/foo", "") 581 } 582 583 // Ensure that the store can watch for recursive CAS updates. 584 func TestStoreWatchRecursiveCompareAndSwap(t *testing.T) { 585 s := newStore() 586 s.Create("/foo/bar", false, "baz", false, Permanent) 587 w, _ := s.Watch("/foo", true, false, 0) 588 s.CompareAndSwap("/foo/bar", "baz", 0, "bat", Permanent) 589 e := nbselect(w.EventChan) 590 assert.Equal(t, e.Action, "compareAndSwap", "") 591 assert.Equal(t, e.Node.Key, "/foo/bar", "") 592 } 593 594 // Ensure that the store can watch for key expiration. 595 func TestStoreWatchExpire(t *testing.T) { 596 s := newStore() 597 598 stopChan := make(chan bool) 599 defer func() { 600 stopChan <- true 601 }() 602 go mockSyncService(s.DeleteExpiredKeys, stopChan) 603 604 s.Create("/foo", false, "bar", false, time.Now().Add(500*time.Millisecond)) 605 s.Create("/foofoo", false, "barbarbar", false, time.Now().Add(500*time.Millisecond)) 606 607 w, _ := s.Watch("/", true, false, 0) 608 c := w.EventChan 609 e := nbselect(c) 610 assert.Nil(t, e, "") 611 time.Sleep(600 * time.Millisecond) 612 e = nbselect(c) 613 assert.Equal(t, e.Action, "expire", "") 614 assert.Equal(t, e.Node.Key, "/foo", "") 615 w, _ = s.Watch("/", true, false, 4) 616 e = nbselect(w.EventChan) 617 assert.Equal(t, e.Action, "expire", "") 618 assert.Equal(t, e.Node.Key, "/foofoo", "") 619 } 620 621 // Ensure that the store can watch in streaming mode. 622 func TestStoreWatchStream(t *testing.T) { 623 s := newStore() 624 w, _ := s.Watch("/foo", false, true, 0) 625 // first modification 626 s.Create("/foo", false, "bar", false, Permanent) 627 e := nbselect(w.EventChan) 628 assert.Equal(t, e.Action, "create", "") 629 assert.Equal(t, e.Node.Key, "/foo", "") 630 assert.Equal(t, *e.Node.Value, "bar", "") 631 e = nbselect(w.EventChan) 632 assert.Nil(t, e, "") 633 // second modification 634 s.Update("/foo", "baz", Permanent) 635 e = nbselect(w.EventChan) 636 assert.Equal(t, e.Action, "update", "") 637 assert.Equal(t, e.Node.Key, "/foo", "") 638 assert.Equal(t, *e.Node.Value, "baz", "") 639 e = nbselect(w.EventChan) 640 assert.Nil(t, e, "") 641 } 642 643 // Ensure that the store can recover from a previously saved state. 644 func TestStoreRecover(t *testing.T) { 645 s := newStore() 646 s.Create("/foo", true, "", false, Permanent) 647 s.Create("/foo/x", false, "bar", false, Permanent) 648 s.Create("/foo/y", false, "baz", false, Permanent) 649 b, err := s.Save() 650 651 s2 := newStore() 652 s2.Recovery(b) 653 654 e, err := s.Get("/foo/x", false, false) 655 assert.Nil(t, err, "") 656 assert.Equal(t, *e.Node.Value, "bar", "") 657 658 e, err = s.Get("/foo/y", false, false) 659 assert.Nil(t, err, "") 660 assert.Equal(t, *e.Node.Value, "baz", "") 661 } 662 663 // Ensure that the store can recover from a previously saved state that includes an expiring key. 664 func TestStoreRecoverWithExpiration(t *testing.T) { 665 s := newStore() 666 667 c := make(chan bool) 668 defer func() { 669 c <- true 670 }() 671 go mockSyncService(s.DeleteExpiredKeys, c) 672 673 s.Create("/foo", true, "", false, Permanent) 674 s.Create("/foo/x", false, "bar", false, Permanent) 675 s.Create("/foo/y", false, "baz", false, time.Now().Add(5*time.Millisecond)) 676 b, err := s.Save() 677 678 time.Sleep(10 * time.Millisecond) 679 680 s2 := newStore() 681 682 c2 := make(chan bool) 683 defer func() { 684 c2 <- true 685 }() 686 go mockSyncService(s2.DeleteExpiredKeys, c2) 687 688 s2.Recovery(b) 689 690 time.Sleep(600 * time.Millisecond) 691 692 e, err := s.Get("/foo/x", false, false) 693 assert.Nil(t, err, "") 694 assert.Equal(t, *e.Node.Value, "bar", "") 695 696 e, err = s.Get("/foo/y", false, false) 697 assert.NotNil(t, err, "") 698 assert.Nil(t, e, "") 699 } 700 701 // Ensure that the store can watch for hidden keys as long as it's an exact path match. 702 func TestStoreWatchCreateWithHiddenKey(t *testing.T) { 703 s := newStore() 704 w, _ := s.Watch("/_foo", false, false, 0) 705 s.Create("/_foo", false, "bar", false, Permanent) 706 e := nbselect(w.EventChan) 707 assert.Equal(t, e.Action, "create", "") 708 assert.Equal(t, e.Node.Key, "/_foo", "") 709 e = nbselect(w.EventChan) 710 assert.Nil(t, e, "") 711 } 712 713 // Ensure that the store doesn't see hidden key creates without an exact path match in recursive mode. 714 func TestStoreWatchRecursiveCreateWithHiddenKey(t *testing.T) { 715 s := newStore() 716 w, _ := s.Watch("/foo", true, false, 0) 717 s.Create("/foo/_bar", false, "baz", false, Permanent) 718 e := nbselect(w.EventChan) 719 assert.Nil(t, e, "") 720 w, _ = s.Watch("/foo", true, false, 0) 721 s.Create("/foo/_baz", true, "", false, Permanent) 722 e = nbselect(w.EventChan) 723 assert.Nil(t, e, "") 724 s.Create("/foo/_baz/quux", false, "quux", false, Permanent) 725 e = nbselect(w.EventChan) 726 assert.Nil(t, e, "") 727 } 728 729 // Ensure that the store doesn't see hidden key updates. 730 func TestStoreWatchUpdateWithHiddenKey(t *testing.T) { 731 s := newStore() 732 s.Create("/_foo", false, "bar", false, Permanent) 733 w, _ := s.Watch("/_foo", false, false, 0) 734 s.Update("/_foo", "baz", Permanent) 735 e := nbselect(w.EventChan) 736 assert.Equal(t, e.Action, "update", "") 737 assert.Equal(t, e.Node.Key, "/_foo", "") 738 e = nbselect(w.EventChan) 739 assert.Nil(t, e, "") 740 } 741 742 // Ensure that the store doesn't see hidden key updates without an exact path match in recursive mode. 743 func TestStoreWatchRecursiveUpdateWithHiddenKey(t *testing.T) { 744 s := newStore() 745 s.Create("/foo/_bar", false, "baz", false, Permanent) 746 w, _ := s.Watch("/foo", true, false, 0) 747 s.Update("/foo/_bar", "baz", Permanent) 748 e := nbselect(w.EventChan) 749 assert.Nil(t, e, "") 750 } 751 752 // Ensure that the store can watch for key deletions. 753 func TestStoreWatchDeleteWithHiddenKey(t *testing.T) { 754 s := newStore() 755 s.Create("/_foo", false, "bar", false, Permanent) 756 w, _ := s.Watch("/_foo", false, false, 0) 757 s.Delete("/_foo", false, false) 758 e := nbselect(w.EventChan) 759 assert.Equal(t, e.Action, "delete", "") 760 assert.Equal(t, e.Node.Key, "/_foo", "") 761 e = nbselect(w.EventChan) 762 assert.Nil(t, e, "") 763 } 764 765 // Ensure that the store doesn't see hidden key deletes without an exact path match in recursive mode. 766 func TestStoreWatchRecursiveDeleteWithHiddenKey(t *testing.T) { 767 s := newStore() 768 s.Create("/foo/_bar", false, "baz", false, Permanent) 769 w, _ := s.Watch("/foo", true, false, 0) 770 s.Delete("/foo/_bar", false, false) 771 e := nbselect(w.EventChan) 772 assert.Nil(t, e, "") 773 } 774 775 // Ensure that the store doesn't see expirations of hidden keys. 776 func TestStoreWatchExpireWithHiddenKey(t *testing.T) { 777 s := newStore() 778 779 stopChan := make(chan bool) 780 defer func() { 781 stopChan <- true 782 }() 783 go mockSyncService(s.DeleteExpiredKeys, stopChan) 784 785 s.Create("/_foo", false, "bar", false, time.Now().Add(500*time.Millisecond)) 786 s.Create("/foofoo", false, "barbarbar", false, time.Now().Add(1000*time.Millisecond)) 787 788 w, _ := s.Watch("/", true, false, 0) 789 c := w.EventChan 790 e := nbselect(c) 791 assert.Nil(t, e, "") 792 time.Sleep(600 * time.Millisecond) 793 e = nbselect(c) 794 assert.Nil(t, e, "") 795 time.Sleep(600 * time.Millisecond) 796 e = nbselect(c) 797 assert.Equal(t, e.Action, "expire", "") 798 assert.Equal(t, e.Node.Key, "/foofoo", "") 799 } 800 801 // Ensure that the store does see hidden key creates if watching deeper than a hidden key in recursive mode. 802 func TestStoreWatchRecursiveCreateDeeperThanHiddenKey(t *testing.T) { 803 s := newStore() 804 w, _ := s.Watch("/_foo/bar", true, false, 0) 805 s.Create("/_foo/bar/baz", false, "baz", false, Permanent) 806 807 e := nbselect(w.EventChan) 808 assert.NotNil(t, e, "") 809 assert.Equal(t, e.Action, "create", "") 810 assert.Equal(t, e.Node.Key, "/_foo/bar/baz", "") 811 } 812 813 // Ensure that slow consumers are handled properly. 814 // 815 // Since Watcher.EventChan has a buffer of size 1 we can only queue 1 816 // event per watcher. If the consumer cannot consume the event on time and 817 // another event arrives, the channel is closed and event is discarded. 818 // This test ensures that after closing the channel, the store can continue 819 // to operate correctly. 820 func TestStoreWatchSlowConsumer(t *testing.T) { 821 s := newStore() 822 s.Watch("/foo", true, true, 0) // stream must be true 823 s.Set("/foo", false, "1", Permanent) // ok 824 s.Set("/foo", false, "2", Permanent) // ok 825 s.Set("/foo", false, "3", Permanent) // must not panic 826 } 827 828 // Performs a non-blocking select on an event channel. 829 func nbselect(c <-chan *Event) *Event { 830 select { 831 case e := <-c: 832 return e 833 default: 834 return nil 835 } 836 } 837 838 func mockSyncService(f func(now time.Time), c chan bool) { 839 ticker := time.Tick(time.Millisecond * 500) 840 for { 841 select { 842 case <-c: 843 return 844 case now := <-ticker: 845 f(now) 846 } 847 } 848 }