func TestTribeTaskAgreements(t *testing.T) { numOfTribes := 5 tribes := getTribes(numOfTribes, nil) Convey(fmt.Sprintf("%d tribes are started", numOfTribes), t, func() { for i := 0; i < numOfTribes; i++ { log.Debugf("%v is reporting %v members", i, len(tribes[i].memberlist.Members())) So(len(tribes[0].memberlist.Members()), ShouldEqual, len(tribes[i].memberlist.Members())) } Convey("the cluster agrees on membership", func() { for i := 0; i < numOfTribes; i++ { So( len(tribes[0].memberlist.Members()), ShouldEqual, len(tribes[i].memberlist.Members()), ) So(len(tribes[0].members), ShouldEqual, len(tribes[i].members)) } agreementName := "agreement1" agreementName2 := "agreement2" task1 := agreement.Task{ID: uuid.New()} task2 := agreement.Task{ID: uuid.New()} Convey("a member handles", func() { t := tribes[0] t2 := tribes[1] Convey("an out of order 'add task' message", func() { msg := &taskMsg{ LTime: t.clock.Increment(), UUID: uuid.New(), TaskID: task1.ID, AgreementName: agreementName, Type: addTaskMsgType, } b := t.handleAddTask(msg) So(b, ShouldEqual, true) t.broadcast(addTaskMsgType, msg, nil) So(len(t.intentBuffer), ShouldEqual, 1) err := t.AddTask(agreementName, task1) So(err.Error(), ShouldResemble, errAgreementDoesNotExist.Error()) err = t.AddAgreement(agreementName) So(err, ShouldBeNil) So(len(t.intentBuffer), ShouldEqual, 0) So(len(t.agreements[agreementName].TaskAgreement.Tasks), ShouldEqual, 1) ok, _ := t.agreements[agreementName].TaskAgreement.Tasks.Contains(task1) So(ok, ShouldBeTrue) Convey("adding an existing task", func() { err := t.AddTask(agreementName, task1) So(err.Error(), ShouldResemble, errTaskAlreadyExists.Error()) Convey("removing a task that doesn't exist", func() { err := t.RemoveTask(agreementName, agreement.Task{ID: uuid.New()}) So(err.Error(), ShouldResemble, errTaskDoesNotExist.Error()) err = t.RemoveTask("doesn't exist", task1) So(err.Error(), ShouldResemble, errAgreementDoesNotExist.Error()) Convey("joining an agreement with tasks", func() { err := t.AddAgreement(agreementName2) So(err, ShouldBeNil) err = t.AddTask(agreementName2, task2) So(err, ShouldBeNil) err = t.JoinAgreement(agreementName, t.memberlist.LocalNode().Name) So(err, ShouldBeNil) err = t.JoinAgreement(agreementName, t2.memberlist.LocalNode().Name) So(err, ShouldBeNil) err = t.JoinAgreement(agreementName2, t.memberlist.LocalNode().Name) So(err, ShouldBeNil) So(len(t.members[t.memberlist.LocalNode().Name].TaskAgreements), ShouldEqual, 2) err = t.canJoinAgreement(agreementName2, t.memberlist.LocalNode().Name) So(err, ShouldBeNil) So(t.members[t.memberlist.LocalNode().Name].PluginAgreement, ShouldNotBeNil) So(len(t.members[t.memberlist.LocalNode().Name].TaskAgreements), ShouldEqual, 2) Convey("all members agree on tasks", func(c C) { var wg sync.WaitGroup for _, t := range tribes { wg.Add(1) go func(t *tribe) { defer wg.Done() for { if a, ok := t.agreements[agreementName]; ok { if ok, _ := a.TaskAgreement.Tasks.Contains(task1); ok { return } } time.Sleep(50 * time.Millisecond) } }(t) } wg.Wait() Convey("the agreement is queried for the state of a given task", func() { t := tribes[rand.Intn(numOfTribes)] resp := t.taskStateQuery(agreementName, task1.ID) So(resp, ShouldNotBeNil) responses := taskStateResponses{} for r := range resp.resp { responses = append(responses, r) } So(len(responses), ShouldEqual, 2) So(responses.State(), ShouldEqual, core.TaskSpinning) Convey("a member handles removing a task", func() { t := tribes[rand.Intn(numOfTribes)] ok, _ := t.agreements[agreementName].TaskAgreement.Tasks.Contains(task1) So(ok, ShouldBeTrue) err := t.RemoveTask(agreementName, task1) ok, _ = t.agreements[agreementName].TaskAgreement.Tasks.Contains(task1) So(ok, ShouldBeFalse) So(err, ShouldBeNil) So(t.intentBuffer, ShouldBeEmpty) var wg sync.WaitGroup for _, t := range tribes { wg.Add(1) go func(t *tribe) { defer wg.Done() for { if a, ok := t.agreements[agreementName]; ok { if len(a.TaskAgreement.Tasks) == 0 { return } } time.Sleep(50 * time.Millisecond) } }(t) } wg.Wait() }) }) }) }) }) }) }) }) }) }) }