func TestMemberSimple(t *testing.T) { st := store.New() defer close(st.Ops) fp := &test.FakeProposer{Store: st} c := make(chan string) go Clean(c, fp.Store, fp) fp.Propose([]byte(store.MustEncodeSet("/ctl/node/a/x", "a", store.Missing))) fp.Propose([]byte(store.MustEncodeSet("/ctl/node/a/y", "b", store.Missing))) fp.Propose([]byte(store.MustEncodeSet("/ctl/node/a/addr", "1.2.3.4", store.Missing))) fp.Propose([]byte(store.MustEncodeSet("/ctl/cal/0", "a", store.Missing))) calCh := fp.Watch(store.MustCompileGlob("/ctl/cal/0")) nodeCh := fp.Watch(store.MustCompileGlob("/ctl/node/a/?")) // indicate that this peer is inactive go func() { c <- "1.2.3.4" }() ev := <-calCh assert.T(t, ev.IsSet()) assert.Equal(t, "", ev.Body) cs := []int{} ev = <-nodeCh assert.T(t, ev.IsDel()) cs = append(cs, int(ev.Path[len(ev.Path)-1])) ev = <-nodeCh assert.T(t, ev.IsDel()) cs = append(cs, int(ev.Path[len(ev.Path)-1])) sort.SortInts(cs) assert.Equal(t, []int{'x', 'y'}, cs) }
func TestManagerFilterPropSeqn(t *testing.T) { ps := make(chan int64, 100) st := store.New() defer close(st.Ops) m := &Manager{ DefRev: 2, Alpha: 1, Self: "b", PSeqn: ps, Store: st, } go m.Run() st.Ops <- store.Op{1, store.MustEncodeSet("/ctl/cal/0", "a", 0)} st.Ops <- store.Op{2, store.MustEncodeSet("/ctl/cal/1", "b", 0)} st.Ops <- store.Op{3, store.Nop} st.Ops <- store.Op{4, store.Nop} assert.Equal(t, int64(3), <-ps) assert.Equal(t, int64(5), <-ps) st.Ops <- store.Op{5, store.Nop} st.Ops <- store.Op{6, store.Nop} assert.Equal(t, int64(7), <-ps) }
func TestManagerEvent(t *testing.T) { const alpha = 2 runs := make(map[int64]*run) st := store.New() defer close(st.Ops) st.Ops <- store.Op{ Seqn: 1, Mut: store.MustEncodeSet(node+"/a/addr", "1.2.3.4:5", 0), } st.Ops <- store.Op{ Seqn: 2, Mut: store.MustEncodeSet(cal+"/1", "a", 0), } ch, err := st.Wait(store.Any, 2) if err != nil { panic(err) } x, _ := net.ResolveUDPAddr("udp", "1.2.3.4:5") pseqn := make(chan int64, 1) m := &Manager{ Alpha: alpha, Self: "a", PSeqn: pseqn, Ops: st.Ops, Out: make(chan Packet), run: runs, } m.event(<-ch) exp := &run{ self: "a", seqn: 2 + alpha, cals: []string{"a"}, addr: []*net.UDPAddr{x}, ops: st.Ops, out: m.Out, bound: initialWaitBound, } exp.c = coordinator{ crnd: 1, size: 1, quor: exp.quorum(), } exp.l = learner{ round: 1, size: 1, quorum: int64(exp.quorum()), votes: map[string]int64{}, voted: []bool{false}, } assert.Equal(t, 1, len(runs)) assert.Equal(t, exp, runs[exp.seqn]) assert.Equal(t, exp.seqn, <-pseqn) assert.Equal(t, exp.seqn+1, m.next) }
func TestAddRun(t *testing.T) { const alpha = 2 runs := make(map[int64]*run) st := store.New() defer close(st.Ops) st.Ops <- store.Op{ Seqn: 1, Mut: store.MustEncodeSet(node+"/a/addr", "x", 0), } st.Ops <- store.Op{ Seqn: 2, Mut: store.MustEncodeSet(cal+"/1", "a", 0), } ch, err := st.Wait(store.Any, 2) if err != nil { panic(err) } pseqn := make(chan int64, 1) c := &Config{ Alpha: alpha, Self: "a", PSeqn: pseqn, Ops: st.Ops, Out: make(chan Packet), } m := &Manager{cfg: *c, run: runs} got := m.addRun(<-ch) exp := &run{ self: "a", seqn: 2 + alpha, cals: []string{"a"}, addr: []string{"x"}, ops: st.Ops, out: c.Out, bound: initialWaitBound, } exp.c = coordinator{ crnd: 1, size: 1, quor: exp.quorum(), } exp.l = learner{ round: 1, quorum: int64(exp.quorum()), votes: map[string]int64{}, voted: map[string]bool{}, } assert.Equal(t, exp, got) assert.Equal(t, exp, runs[got.seqn]) assert.Equal(t, 1, len(runs)) assert.Equal(t, exp.seqn, <-pseqn) }
func TestConsensusOne(t *testing.T) { self := "test" const alpha = 1 st := store.New() st.Ops <- store.Op{1, store.MustEncodeSet("/ctl/node/"+self+"/addr", "1.2.3.4:5", 0)} st.Ops <- store.Op{2, store.MustEncodeSet("/ctl/cal/1", self, 0)} <-st.Seqns in := make(chan Packet) out := make(chan Packet) seqns := make(chan int64, alpha) props := make(chan *Prop) m := &Manager{ Self: self, DefRev: 2, Alpha: alpha, In: in, Out: out, Ops: st.Ops, PSeqn: seqns, Props: props, TFill: 10e9, Store: st, Ticker: time.Tick(10e6), } go m.Run() go func() { for o := range out { in <- o } }() n := <-seqns w, err := st.Wait(store.Any, n) if err != nil { panic(err) } props <- &Prop{n, []byte("foo")} e := <-w exp := store.Event{ Seqn: 3, Path: "/ctl/err", Body: "bad mutation", Rev: 3, Mut: "foo", Err: errors.New("bad mutation"), } e.Getter = nil assert.Equal(t, exp, e) }
func TestConsensusOne(t *testing.T) { self := "test" const alpha = 1 st := store.New() st.Ops <- store.Op{1, store.MustEncodeSet("/ctl/node/"+self+"/addr", "x", 0)} st.Ops <- store.Op{2, store.MustEncodeSet("/ctl/cal/1", self, 0)} <-st.Seqns in := make(chan Packet) out := make(chan Packet) seqns := make(chan int64, alpha) props := make(chan *Prop) cfg := &Config{ self, 2, alpha, in, out, st.Ops, seqns, props, 10e9, st, time.Tick(10e6), } NewManager(cfg) go func() { for o := range out { in <- o } }() n := <-seqns w, err := st.Wait(store.Any, n) if err != nil { panic(err) } props <- &Prop{n, []byte("foo")} e := <-w exp := store.Event{ Seqn: 3, Path: "/ctl/err", Body: "bad mutation", Rev: 3, Mut: "foo", Err: os.NewError("bad mutation"), } e.Getter = nil assert.Equal(t, exp, e) }
func TestGetAddrs(t *testing.T) { st := store.New() defer close(st.Ops) st.Ops <- store.Op{1, store.MustEncodeSet(node+"/1/addr", "x", 0)} st.Ops <- store.Op{2, store.MustEncodeSet(node+"/2/addr", "y", 0)} st.Ops <- store.Op{3, store.MustEncodeSet(node+"/3/addr", "z", 0)} <-st.Seqns assert.Equal(t, map[string]bool{"x": true, "y": true, "z": true}, getAddrs(st)) }
func TestGetCalsPartial(t *testing.T) { st := store.New() defer close(st.Ops) st.Ops <- store.Op{Seqn: 1, Mut: store.MustEncodeSet(cal+"/1", "a", 0)} st.Ops <- store.Op{Seqn: 2, Mut: store.MustEncodeSet(cal+"/2", "", 0)} st.Ops <- store.Op{Seqn: 3, Mut: store.MustEncodeSet(cal+"/3", "", 0)} <-st.Seqns assert.Equal(t, []string{"a"}, getCals(st)) }
func alphaTest(t *testing.T, alpha int64) { runs := make(chan *run) st := store.New() defer close(st.Ops) st.Ops <- store.Op{ Seqn: 1, Mut: store.MustEncodeSet(node+"/a/addr", "x", 0), } st.Ops <- store.Op{ Seqn: 2, Mut: store.MustEncodeSet(cal+"/1", "a", 0), } for 2 != <-st.Seqns { } tr := run{ self: "a", ops: st.Ops, out: make(chan Packet), bound: initialWaitBound, } go generateRuns(alpha, st.Watch(store.Any), runs, tr) // The only way to generate a run is on an event. // Send a nop here to get things started. st.Ops <- store.Op{3, store.Nop} exp := &run{ self: "a", seqn: 3 + alpha, cals: []string{"a"}, addrs: map[string]bool{"x": true}, ops: st.Ops, out: tr.out, bound: initialWaitBound, } exp.c = coordinator{ crnd: 1, size: 1, quor: exp.quorum(), } exp.l = learner{ round: 1, quorum: int64(exp.quorum()), votes: map[string]int64{}, voted: map[string]bool{}, } assert.Equal(t, exp, <-runs) }
func TestGetAddrs(t *testing.T) { st := store.New() defer close(st.Ops) st.Ops <- store.Op{1, store.MustEncodeSet(node+"/1/addr", "x", 0)} st.Ops <- store.Op{2, store.MustEncodeSet(node+"/2/addr", "y", 0)} st.Ops <- store.Op{3, store.MustEncodeSet(node+"/3/addr", "z", 0)} <-st.Seqns addrs := getAddrs(st, []string{"1", "2", "3"}) assert.Equal(t, []string{"x", "y", "z"}, addrs) }
func TestManagerPumpDropsOldPackets(t *testing.T) { st := store.New() defer close(st.Ops) st.Ops <- store.Op{1, store.MustEncodeSet(node+"/a/addr", "x", 0)} st.Ops <- store.Op{2, store.MustEncodeSet("/ctl/cal/0", "a", 0)} var m Manager m.run = make(map[int64]*run) m.event(<-mustWait(st, 2)) m.pump() recvPacket(&m.packet, Packet{"x", mustMarshal(&msg{Seqn: proto.Int64(1)})}) m.pump() assert.Equal(t, 0, m.Stats.WaitPackets) }
func TestRunAfterWatch(t *testing.T) { alpha := int64(3) runs := make(chan *run) st := store.New() defer close(st.Ops) st.Ops <- store.Op{ Seqn: 1, Mut: store.MustEncodeSet(node+"/b/addr", "y", 0), } for 1 != <-st.Seqns { } tr := run{ self: "b", ops: st.Ops, out: make(chan Packet), bound: initialWaitBound, } go generateRuns(alpha, st.Watch(store.Any), runs, tr) st.Ops <- store.Op{ Seqn: 2, Mut: store.MustEncodeSet(cal+"/1", "b", 0), } exp := &run{ self: "b", seqn: 2 + alpha, cals: []string{"b"}, addrs: map[string]bool{"y": true}, ops: st.Ops, out: tr.out, bound: initialWaitBound, } exp.c = coordinator{ crnd: 1, size: 1, quor: exp.quorum(), } exp.l = learner{ round: 1, quorum: int64(exp.quorum()), votes: map[string]int64{}, voted: map[string]bool{}, } assert.Equal(t, exp, <-runs) }
func TestGetAddrs(t *testing.T) { st := store.New() defer close(st.Ops) st.Ops <- store.Op{1, store.MustEncodeSet(node+"/1/addr", "1.2.3.4:5", 0)} st.Ops <- store.Op{2, store.MustEncodeSet(node+"/2/addr", "2.3.4.5:6", 0)} st.Ops <- store.Op{3, store.MustEncodeSet(node+"/3/addr", "3.4.5.6:7", 0)} <-st.Seqns x, _ := net.ResolveUDPAddr("udp", "1.2.3.4:5") y, _ := net.ResolveUDPAddr("udp", "2.3.4.5:6") z, _ := net.ResolveUDPAddr("udp", "3.4.5.6:7") addrs := getAddrs(st, []string{"1", "2", "3"}) assert.Equal(t, []*net.UDPAddr{x, y, z}, addrs) }
func TestManagerDeletesSuccessfulRun(t *testing.T) { st := store.New() defer close(st.Ops) in := make(chan Packet) out := make(chan Packet, 100) cfg := &Config{ Alpha: 1, Store: st, In: in, Out: out, Ops: st.Ops, } m := NewManager(cfg) st.Ops <- store.Op{ Seqn: 1, Mut: store.MustEncodeSet(node+"/a/addr", "x", 0), } for (<-m.Stats).TotalRuns < 1 { } in <- Packet{ Data: mustMarshal(&msg{Seqn: proto.Int64(2), Cmd: learn, Value: []byte("foo")}), Addr: "127.0.0.1:9999", } for (<-m.Stats).TotalRuns < 2 { } assert.Equal(t, 1, (<-m.Stats).Runs) }
func TestManagerTickQueue(t *testing.T) { ticker := make(chan int64) st := store.New() defer close(st.Ops) in := make(chan Packet) cfg := &Config{ Alpha: 1, Store: st, In: in, Ticker: ticker, Out: make(chan Packet, 100), } m := NewManager(cfg) st.Ops <- store.Op{ Seqn: 1, Mut: store.MustEncodeSet(node+"/a/addr", "x", 0), } for (<-m.Stats).Runs < 1 { } // get it to tick for seqn 2 in <- Packet{Data: mustMarshal(&msg{Seqn: proto.Int64(2), Cmd: propose})} assert.Equal(t, 1, (<-m.Stats).WaitTicks) ticker <- time.Nanoseconds() + initialWaitBound*2 assert.Equal(t, int64(1), (<-m.Stats).TotalTicks) }
func TestSession(t *testing.T) { st := store.New() defer close(st.Ops) fp := &test.FakeProposer{Store: st} go Clean(st, fp) ch := make(chan store.Event, 100) go func(c <-chan store.Event) { for e := range c { ch <- e } close(ch) }(st.Watch("/session/*")) // check-in with less than a nanosecond to live body := strconv.Itoa64(time.Nanoseconds() + 1) fp.Propose(store.MustEncodeSet("/session/a", body, store.Clobber)) // Throw away the set assert.T(t, (<-ch).IsSet()) ev := <-ch assert.T(t, ev.IsDel()) assert.Equal(t, "/session/a", ev.Path) }
func TestConsensusOne(t *testing.T) { self := "test" alpha := int64(1) st := store.New() st.Ops <- store.Op{1, store.MustEncodeSet("/ctl/node/"+self+"/addr", "x", 0)} st.Ops <- store.Op{2, store.MustEncodeSet("/ctl/cal/1", self, 0)} <-st.Seqns cmw := st.Watch(store.Any) in := make(chan Packet) out := make(chan Packet) seqns := make(chan int64, int(alpha)) props := make(chan *Prop) NewManager(self, 0, alpha, in, out, st.Ops, seqns, props, cmw, 10e9, st) go func() { for o := range out { in <- o } }() for i := int64(3); i <= alpha+2; i++ { st.Ops <- store.Op{Seqn: i, Mut: store.Nop} } n := <-seqns w, err := st.Wait(n) if err != nil { panic(err) } props <- &Prop{n, []byte("foo")} e := <-w exp := store.Event{ Seqn: 4, Path: "/ctl/err", Body: "bad mutation", Rev: 4, Mut: "foo", Err: os.NewError("bad mutation"), } e.Getter = nil assert.Equal(t, exp, e) }
func TestManagerRemoveLastCal(t *testing.T) { const alpha = 2 runs := make(map[int64]*run) st := store.New() defer close(st.Ops) st.Ops <- store.Op{1, store.MustEncodeSet(node+"/a/addr", "x", 0)} st.Ops <- store.Op{2, store.MustEncodeSet(cal+"/1", "a", 0)} st.Ops <- store.Op{3, store.MustEncodeSet(cal+"/1", "", -1)} pseqn := make(chan int64, 100) m := &Manager{ Alpha: alpha, Self: "a", PSeqn: pseqn, Ops: st.Ops, Out: make(chan Packet), run: runs, } m.event(<-mustWait(st, 2)) m.event(<-mustWait(st, 3)) exp := &run{ self: "a", seqn: 3 + alpha, cals: []string{"a"}, addr: []string{"x"}, ops: st.Ops, out: m.Out, bound: initialWaitBound, } exp.c = coordinator{ crnd: 1, size: 1, quor: exp.quorum(), } exp.l = learner{ round: 1, quorum: int64(exp.quorum()), votes: map[string]int64{}, voted: map[string]bool{}, } assert.Equal(t, 2, len(runs)) assert.Equal(t, exp, runs[exp.seqn]) assert.Equal(t, exp.seqn+1, m.next) }
func follow(ops chan<- store.Op, ch <-chan *doozer.Event) { for ev := range ch { // store.Clobber is okay here because the event // has already passed through another store mut := store.MustEncodeSet(ev.Path, string(ev.Body), store.Clobber) ops <- store.Op{ev.Rev, mut} } }
func alphaTest(t *testing.T, alpha int64) { runs := make(chan *run) st := store.New() defer close(st.Ops) st.Ops <- store.Op{ Seqn: 1, Mut: store.MustEncodeSet(node+"/a/addr", "x", 0), } st.Ops <- store.Op{ Seqn: 2, Mut: store.MustEncodeSet(cal+"/1", "a", 0), } tr := run{ self: "a", ops: st.Ops, out: make(chan Packet), bound: initialWaitBound, } go generateRuns(2, alpha, st, runs, tr) exp := &run{ self: "a", seqn: 2 + alpha, cals: []string{"a"}, addrs: map[string]bool{"x": true}, ops: st.Ops, out: tr.out, bound: initialWaitBound, } exp.c = coordinator{ crnd: 1, size: 1, quor: exp.quorum(), } exp.l = learner{ round: 1, quorum: int64(exp.quorum()), votes: map[string]int64{}, voted: map[string]bool{}, } assert.Equal(t, exp, <-runs) }
func encodeTimer(path string, offset int64) string { future := time.Nanoseconds() + offset muta := store.MustEncodeSet( path, strconv.Itoa64(future), store.Clobber, ) return muta }
func (c cloner) VisitFile(path string, f *doozer.FileInfo) { // store.Clobber is okay here because the event // has already passed through another store body, _, err := c.cl.Get(path, &f.Rev) if err != nil { panic(err) } mut := store.MustEncodeSet(path, string(body), store.Clobber) c.ch <- store.Op{f.Rev, mut} }
func TestDelRun(t *testing.T) { const alpha = 2 runs := make(map[int64]*run) st := store.New() defer close(st.Ops) st.Ops <- store.Op{1, store.MustEncodeSet(node+"/a/addr", "x", 0)} st.Ops <- store.Op{2, store.MustEncodeSet(cal+"/1", "a", 0)} st.Ops <- store.Op{3, store.Nop} st.Ops <- store.Op{4, store.Nop} c2, err := st.Wait(store.Any, 2) if err != nil { panic(err) } c3, err := st.Wait(store.Any, 3) if err != nil { panic(err) } c4, err := st.Wait(store.Any, 4) if err != nil { panic(err) } pseqn := make(chan int64, 100) m := &Manager{ Alpha: alpha, Self: "a", PSeqn: pseqn, Ops: st.Ops, Out: make(chan Packet), run: runs, } m.event(<-c2) assert.Equal(t, 1, len(m.run)) m.event(<-c3) assert.Equal(t, 2, len(m.run)) m.event(<-c4) assert.Equal(t, 2, len(m.run)) }
func TestManagerTickQueue(t *testing.T) { st := store.New() defer close(st.Ops) st.Ops <- store.Op{1, store.MustEncodeSet(node+"/a/addr", "1.2.3.4:5", 0)} st.Ops <- store.Op{2, store.MustEncodeSet("/ctl/cal/0", "a", 0)} var m Manager m.run = make(map[int64]*run) m.Alpha = 1 m.Store = st m.Out = make(chan Packet, 100) m.event(<-mustWait(st, 2)) // get it to tick for seqn 3 recvPacket(&m.packet, Packet{Data: mustMarshal(&msg{Seqn: proto.Int64(3), Cmd: propose})}) m.pump() assert.Equal(t, 1, m.tick.Len()) m.doTick(time.Nanoseconds() + initialWaitBound*2) assert.Equal(t, int64(1), m.Stats.TotalTicks) }
func TestGcClean(t *testing.T) { st := store.New() defer close(st.Ops) go Clean(st) for <-st.Watches < 1 { } // Wait for Clean()'s Watch to take st.Ops <- store.Op{1, store.Nop} st.Ops <- store.Op{2, store.MustEncodeSet("/doozer/slot/1", "a", store.Missing)} st.Ops <- store.Op{3, store.MustEncodeSet("/doozer/info/a/applied", "2", store.Missing)} st.Ops <- store.Op{4, store.MustEncodeSet("/doozer/info/X/applied", "0", store.Missing)} ch := st.Watch("/x") st.Ops <- store.Op{5, store.MustEncodeSet("/x", "", store.Missing)} <-ch close(ch) ev := <-st.Wait(1) assert.Equal(t, store.ErrTooLate, (ev).Err) }
func TestLearnedValueIsLearned(t *testing.T) { self := "test" alpha := int64(1) st := store.New() st.Ops <- store.Op{1, store.MustEncodeSet("/ctl/node/"+self+"/addr", "x", 0)} st.Ops <- store.Op{2, store.MustEncodeSet("/ctl/cal/1", self, 0)} <-st.Seqns cmw := st.Watch(store.Any) in := make(chan Packet) out := make(chan Packet) seqns := make(chan int64, int(alpha)) props := make(chan *Prop) NewManager(self, 0, alpha, in, out, st.Ops, seqns, props, cmw, 10e9, st) v := store.MustEncodeSet("/foo", "bar", -1) st.Ops <- store.Op{Seqn: 3, Mut: v} in <- Packet{"x", mustMarshal(&msg{Seqn: proto.Int64(3), Cmd: rsvp})} in <- Packet{"x", mustMarshal(&msg{Seqn: proto.Int64(3), Cmd: nominate})} in <- Packet{"x", mustMarshal(&msg{Seqn: proto.Int64(3), Cmd: vote})} in <- Packet{"x", mustMarshal(&msg{Seqn: proto.Int64(3), Cmd: nop})} in <- Packet{"x", mustMarshal(&msg{Seqn: proto.Int64(3), Cmd: tick})} in <- Packet{"x", mustMarshal(&msg{Seqn: proto.Int64(3), Cmd: learn})} in <- Packet{"x", mustMarshal(&msg{Seqn: proto.Int64(3), Cmd: propose})} in <- Packet{"x", mustMarshal(&msg{Seqn: proto.Int64(3), Cmd: invite})} exp := Packet{"x", mustMarshal(&msg{ Cmd: learn, Seqn: proto.Int64(3), Value: []byte(v), })} assert.Equal(t, exp, <-out) }
func TestMemberSimple(t *testing.T) { st := store.New() defer close(st.Ops) fp := &test.FakeProposer{Store: st} go Clean(fp.Store, fp) // start our session fp.Propose(store.MustEncodeSet("/session/a", "foo", store.Missing)) keys := map[string]string{ "/doozer/slot/0": "a", "/doozer/members/a": "addr", "/doozer/info/a/x": "a", "/doozer/info/a/y": "b", } // join the cluster for k, p := range keys { fp.Propose(store.MustEncodeSet(k, p, store.Missing)) } // watch the keys to be deleted ch := fp.Watch("/doozer/**") // end the session fp.Propose(store.MustEncodeDel("/session/a", store.Clobber)) // now that the session has ended, check its membership is cleaned up for i := 0; i < len(keys); i++ { ev := <-ch _, ok := keys[ev.Path] keys[ev.Path] = "", false assert.T(t, ok) assert.Equal(t, "", ev.Body) } }
func TestManagerPacketProcessing(t *testing.T) { st := store.New() defer close(st.Ops) in := make(chan Packet) out := make(chan Packet, 100) var m Manager m.run = make(map[int64]*run) m.Alpha = 1 m.Store = st m.In = in m.Out = out m.Ops = st.Ops st.Ops <- store.Op{1, store.MustEncodeSet(node+"/a/addr", "1.2.3.4:5", 0)} st.Ops <- store.Op{2, store.MustEncodeSet("/ctl/cal/0", "a", 0)} m.event(<-mustWait(st, 2)) recvPacket(&m.packet, Packet{ Data: mustMarshal(&msg{Seqn: proto.Int64(2), Cmd: learn, Value: []byte("foo")}), Addr: &net.UDPAddr{net.IP{127, 0, 0, 1}, 9999}, }) m.pump() assert.Equal(t, 0, m.packet.Len()) }
func TestLockSimple(t *testing.T) { st := store.New() defer close(st.Ops) fp := &test.FakeProposer{Store: st} go Clean(fp.Store, fp) // start our session fp.Propose(store.MustEncodeSet("/session/a", "1.2.3.4:55", store.Clobber)) // lock something for a fp.Propose(store.MustEncodeSet("/lock/x", "a", store.Missing)) fp.Propose(store.MustEncodeSet("/lock/y", "b", store.Missing)) fp.Propose(store.MustEncodeSet("/lock/z", "a", store.Missing)) // watch the locks to be deleted ch := fp.Watch("/lock/*") // end the session fp.Propose(store.MustEncodeDel("/session/a", store.Clobber)) // now that the session has ended, check all locks it owned are released assert.Equal(t, "/lock/x", (<-ch).Path) assert.Equal(t, "/lock/z", (<-ch).Path) }
func TestManagerReply(t *testing.T) { st := store.New() st.Ops <- store.Op{1, mustEncodeSet(membersDir+"a", "x")} st.Ops <- store.Op{2, mustEncodeSet(slotDir+"0", "a")} ch := make(ChanPutCloserTo) mg := NewManager("a", 1, st, ch) mut := store.MustEncodeSet("/foo", "bar", store.Clobber) st.Ops <- store.Op{3, mut} <-st.Seqns // give mg a chance to get the store.Event for seqn 3 msg := newInvite(1) msg.SetSeqn(3) it := mg.getInstance(3) assert.Equal(t, instance(nil), it) mg.PutFrom("x", msg) exp := newLearn(mut) exp.SetSeqn(3) assert.Equal(t, Packet{exp, "x"}, <-ch) }