diff --git a/integration/v3_leadership_test.go b/integration/v3_leadership_test.go index 0cb83539c..4053d419e 100644 --- a/integration/v3_leadership_test.go +++ b/integration/v3_leadership_test.go @@ -139,3 +139,38 @@ func TestMoveLeaderToLearnerError(t *testing.T) { t.Errorf("expecting error to contain %s, got %s", expectedErrKeywords, err.Error()) } } + +// TestTransferLeadershipWithLearner ensures TransferLeadership does not timeout due to learner is +// automatically picked by leader as transferee. +func TestTransferLeadershipWithLearner(t *testing.T) { + defer testutil.AfterTest(t) + + clus := NewClusterV3(t, &ClusterConfig{Size: 1}) + defer clus.Terminate(t) + + clus.AddAndLaunchLearnerMember(t) + + learners, err := clus.GetLearnerMembers() + if err != nil { + t.Fatalf("failed to get the learner members in cluster: %v", err) + } + if len(learners) != 1 { + t.Fatalf("added 1 learner to cluster, got %d", len(learners)) + } + + leaderIdx := clus.WaitLeader(t) + errCh := make(chan error, 1) + go func() { + // note that this cluster has 1 leader and 1 learner. TransferLeadership should return nil. + // Leadership transfer is skipped in cluster with 1 voting member. + errCh <- clus.Members[leaderIdx].s.TransferLeadership() + }() + select { + case err := <-errCh: + if err != nil { + t.Errorf("got error during leadership transfer: %v", err) + } + case <-time.After(5 * time.Second): + t.Error("timed out waiting for leader transition") + } +}