go.etcd.io/etcd@v3.3.27+incompatible/store/store_test.go (about)

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