// newFakeClock creates a new FakeClock that has been advanced to at least minExpireTime func newFakeClock() clockwork.FakeClock { fc := clockwork.NewFakeClock() for minExpireTime.After(fc.Now()) { fc.Advance((0x1 << 62) * time.Nanosecond) } return fc }
// Ensure that any TTL <= minExpireTime becomes Permanent func TestMinExpireTime(t *testing.T) { s := newStore() fc := clockwork.NewFakeClock() s.clock = fc // FakeClock starts at 0, so minExpireTime should be far in the future.. but just in case assert.True(t, minExpireTime.After(fc.Now()), "minExpireTime should be ahead of FakeClock!") s.Create("/foo", false, "Y", false, fc.Now().Add(3*time.Second)) fc.Advance(5 * time.Second) // Ensure it hasn't expired s.DeleteExpiredKeys(fc.Now()) var eidx uint64 = 1 e, err := s.Get("/foo", true, false) assert.Nil(t, err, "") assert.Equal(t, e.EtcdIndex, eidx, "") assert.Equal(t, e.Action, "get", "") assert.Equal(t, e.Node.Key, "/foo", "") assert.Equal(t, e.Node.TTL, 0) }
func TestRetryFailure(t *testing.T) { nRetries = maxRetryInTest defer func() { nRetries = math.MaxUint32 }() cluster := "1000" c := &clientWithRetry{failTimes: 4} fc := clockwork.NewFakeClock() d := discovery{ cluster: cluster, id: 1, c: c, clock: fc, } go func() { for i := uint(1); i <= maxRetryInTest; i++ { fc.BlockUntil(1) fc.Advance(time.Second * (0x1 << i)) } }() if _, _, _, err := d.checkCluster(); err != ErrTooManyRetries { t.Errorf("err = %v, want %v", err, ErrTooManyRetries) } }
func TestCheckCluster(t *testing.T) { cluster := "/prefix/1000" self := "/1000/1" tests := []struct { nodes []*client.Node index uint64 werr error wsize int }{ { // self is in the size range []*client.Node{ {Key: "/1000/_config/size", Value: "3", CreatedIndex: 1}, {Key: "/1000/_config/"}, {Key: self, CreatedIndex: 2}, {Key: "/1000/2", CreatedIndex: 3}, {Key: "/1000/3", CreatedIndex: 4}, {Key: "/1000/4", CreatedIndex: 5}, }, 5, nil, 3, }, { // self is in the size range []*client.Node{ {Key: "/1000/_config/size", Value: "3", CreatedIndex: 1}, {Key: "/1000/_config/"}, {Key: "/1000/2", CreatedIndex: 2}, {Key: "/1000/3", CreatedIndex: 3}, {Key: self, CreatedIndex: 4}, {Key: "/1000/4", CreatedIndex: 5}, }, 5, nil, 3, }, { // self is out of the size range []*client.Node{ {Key: "/1000/_config/size", Value: "3", CreatedIndex: 1}, {Key: "/1000/_config/"}, {Key: "/1000/2", CreatedIndex: 2}, {Key: "/1000/3", CreatedIndex: 3}, {Key: "/1000/4", CreatedIndex: 4}, {Key: self, CreatedIndex: 5}, }, 5, ErrFullCluster, 3, }, { // self is not in the cluster []*client.Node{ {Key: "/1000/_config/size", Value: "3", CreatedIndex: 1}, {Key: "/1000/_config/"}, {Key: "/1000/2", CreatedIndex: 2}, {Key: "/1000/3", CreatedIndex: 3}, }, 3, nil, 3, }, { []*client.Node{ {Key: "/1000/_config/size", Value: "3", CreatedIndex: 1}, {Key: "/1000/_config/"}, {Key: "/1000/2", CreatedIndex: 2}, {Key: "/1000/3", CreatedIndex: 3}, {Key: "/1000/4", CreatedIndex: 4}, }, 3, ErrFullCluster, 3, }, { // bad size key []*client.Node{ {Key: "/1000/_config/size", Value: "bad", CreatedIndex: 1}, }, 0, ErrBadSizeKey, 0, }, { // no size key []*client.Node{}, 0, ErrSizeNotFound, 0, }, } for i, tt := range tests { rs := make([]*client.Response, 0) if len(tt.nodes) > 0 { rs = append(rs, &client.Response{Node: tt.nodes[0], Index: tt.index}) rs = append(rs, &client.Response{ Node: &client.Node{ Key: cluster, Nodes: tt.nodes[1:], }, Index: tt.index, }) } c := &clientWithResp{rs: rs} dBase := discovery{cluster: cluster, id: 1, c: c} cRetry := &clientWithRetry{failTimes: 3} cRetry.rs = rs fc := clockwork.NewFakeClock() dRetry := discovery{cluster: cluster, id: 1, c: cRetry, clock: fc} for _, d := range []discovery{dBase, dRetry} { go func() { for i := uint(1); i <= maxRetryInTest; i++ { fc.BlockUntil(1) fc.Advance(time.Second * (0x1 << i)) } }() ns, size, index, err := d.checkCluster() if err != tt.werr { t.Errorf("#%d: err = %v, want %v", i, err, tt.werr) } if reflect.DeepEqual(ns, tt.nodes) { t.Errorf("#%d: nodes = %v, want %v", i, ns, tt.nodes) } if size != tt.wsize { t.Errorf("#%d: size = %v, want %d", i, size, tt.wsize) } if index != tt.index { t.Errorf("#%d: index = %v, want %d", i, index, tt.index) } } } }
func TestWaitNodes(t *testing.T) { all := []*client.Node{ 0: {Key: "/1000/1", CreatedIndex: 2}, 1: {Key: "/1000/2", CreatedIndex: 3}, 2: {Key: "/1000/3", CreatedIndex: 4}, } tests := []struct { nodes []*client.Node rs []*client.Response }{ { all, []*client.Response{}, }, { all[:1], []*client.Response{ {Node: &client.Node{Key: "/1000/2", CreatedIndex: 3}}, {Node: &client.Node{Key: "/1000/3", CreatedIndex: 4}}, }, }, { all[:2], []*client.Response{ {Node: &client.Node{Key: "/1000/3", CreatedIndex: 4}}, }, }, { append(all, &client.Node{Key: "/1000/4", CreatedIndex: 5}), []*client.Response{ {Node: &client.Node{Key: "/1000/3", CreatedIndex: 4}}, }, }, } for i, tt := range tests { // Basic case c := &clientWithResp{rs: nil, w: &watcherWithResp{rs: tt.rs}} dBase := &discovery{cluster: "1000", c: c} // Retry case retryScanResp := make([]*client.Response, 0) if len(tt.nodes) > 0 { retryScanResp = append(retryScanResp, &client.Response{ Node: &client.Node{ Key: "1000", Value: strconv.Itoa(3), }, }) retryScanResp = append(retryScanResp, &client.Response{ Node: &client.Node{ Nodes: tt.nodes, }, }) } cRetry := &clientWithResp{ rs: retryScanResp, w: &watcherWithRetry{rs: tt.rs, failTimes: 2}, } fc := clockwork.NewFakeClock() dRetry := &discovery{ cluster: "1000", c: cRetry, clock: fc, } for _, d := range []*discovery{dBase, dRetry} { go func() { for i := uint(1); i <= maxRetryInTest; i++ { fc.BlockUntil(1) fc.Advance(time.Second * (0x1 << i)) } }() g, err := d.waitNodes(tt.nodes, 3, 0) // we do not care about index in this test if err != nil { t.Errorf("#%d: err = %v, want %v", i, err, nil) } if !reflect.DeepEqual(g, all) { t.Errorf("#%d: all = %v, want %v", i, g, all) } } } }