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  }