diff --git a/store/store_test.go b/store/store_test.go index ba398cf88..aa86f5206 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -782,6 +782,18 @@ func TestStoreWatchExpireWithHiddenKey(t *testing.T) { assert.Equal(t, e.Node.Key, "/foofoo", "") } +// Ensure that the store does see hidden key creates if watching deeper than a hidden key in recursive mode. +func TestStoreWatchRecursiveCreateDeeperThanHiddenKey(t *testing.T) { + s := newStore() + w, _ := s.Watch("/_foo/bar", true, false, 0) + s.Create("/_foo/bar/baz", false, "baz", false, Permanent) + + e := nbselect(w.EventChan) + assert.NotNil(t, e, "") + assert.Equal(t, e.Action, "create", "") + assert.Equal(t, e.Node.Key, "/_foo/bar/baz", "") +} + // Performs a non-blocking select on an event channel. func nbselect(c <-chan *Event) *Event { select { diff --git a/store/watcher_hub.go b/store/watcher_hub.go index 3ed8d2697..737fab028 100644 --- a/store/watcher_hub.go +++ b/store/watcher_hub.go @@ -127,7 +127,7 @@ func (wh *watcherHub) notifyWatchers(e *Event, nodePath string, deleted bool) { w, _ := curr.Value.(*Watcher) originalPath := (e.Node.Key == nodePath) - if (originalPath || !isHidden(e.Node.Key)) && w.notify(e, originalPath, deleted) { + if (originalPath || !isHidden(nodePath, e.Node.Key)) && w.notify(e, originalPath, deleted) { if !w.stream { // do not remove the stream watcher // if we successfully notify a watcher // we need to remove the watcher from the list @@ -158,8 +158,11 @@ func (wh *watcherHub) clone() *watcherHub { } } -// isHidden checks to see if this path is considered hidden i.e. the +// isHidden checks to see if key path is considered hidden to watch path i.e. the // last element is hidden or it's within a hidden directory -func isHidden(nodePath string) bool { - return strings.Contains(nodePath, "/_") +func isHidden(watchPath, keyPath string) bool { + // if watch path is just a "/", after path will start without "/" + // add a "/" to deal with the special case when watchPath is "/" + afterPath := path.Clean("/" + keyPath[len(watchPath):]) + return strings.Contains(afterPath, "/_") } diff --git a/store/watcher_hub_test.go b/store/watcher_hub_test.go new file mode 100644 index 000000000..08a30f71b --- /dev/null +++ b/store/watcher_hub_test.go @@ -0,0 +1,43 @@ +package store + +import ( + "testing" +) + +// TestIsHidden tests isHidden functions. +func TestIsHidden(t *testing.T) { + // watch at "/" + // key is "/_foo", hidden to "/" + // expected: hidden = true + watch := "/" + key := "/_foo" + hidden := isHidden(watch, key) + if !hidden { + t.Fatalf("%v should be hidden to %v\n", key, watch) + } + + // watch at "/_foo" + // key is "/_foo", not hidden to "/_foo" + // expected: hidden = false + watch = "/_foo" + hidden = isHidden(watch, key) + if hidden { + t.Fatalf("%v should not be hidden to %v\n", key, watch) + } + + // watch at "/_foo/" + // key is "/_foo/foo", not hidden to "/_foo" + key = "/_foo/foo" + hidden = isHidden(watch, key) + if hidden { + t.Fatalf("%v should not be hidden to %v\n", key, watch) + } + + // watch at "/_foo/" + // key is "/_foo/_foo", hidden to "/_foo" + key = "/_foo/_foo" + hidden = isHidden(watch, key) + if !hidden { + t.Fatalf("%v should be hidden to %v\n", key, watch) + } +}