func (f *Fixtures) Follower() *cluster.Gateway {
	timeout := time.Second
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	for {
		for _, gateway := range f.gateways {
			if gateway.Raft().Raft().State() == raft.Follower {
				return gateway
			}
		}
		select {
		case <-ctx.Done():
			f.t.Fatalf("no node running as follower")
		default:
		}
		time.Sleep(10 * time.Millisecond)
	}
}
Follower returns a follower gateway in the cluster.
package testing
import (
	"context"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
	dqlite "github.com/CanonicalLtd/go-dqlite"
	"github.com/hashicorp/raft"
	"github.com/spoke-d/thermionic/internal/cert"
	"github.com/spoke-d/thermionic/internal/cluster"
	"github.com/spoke-d/thermionic/internal/cluster/heartbeat"
	"github.com/spoke-d/thermionic/internal/cluster/membership"
	"github.com/spoke-d/thermionic/internal/db"
	querycluster "github.com/spoke-d/thermionic/internal/db/cluster"
	"github.com/spoke-d/thermionic/internal/node"
	"github.com/spoke-d/thermionic/internal/state"
	st "github.com/spoke-d/thermionic/internal/state"
)
type Fixtures struct {
	t        *testing.T
	gateways map[int]*cluster.Gateway               
	states   map[*cluster.Gateway]*state.State      
	servers  map[*cluster.Gateway]*httptest.Server  
	cleanups []func()
}
func NewFixtures(t *testing.T) *Fixtures {
	return &Fixtures{
		t:        t,
		gateways: make(map[int]*cluster.Gateway),
		states:   make(map[*cluster.Gateway]*state.State),
		servers:  make(map[*cluster.Gateway]*httptest.Server),
	}
}
func (f *Fixtures) Bootstrap() *cluster.Gateway {
	f.t.Logf("create bootstrap node for test cluster")
	state, gateway, _ := f.node()
	certInfo := KeyPair()
	bootstrapTask := membership.NewBootstrap(
		makeMembershipStateShim(state),
		makeMembershipGatewayShim(gateway),
		certInfo, node.ConfigSchema,
	)
	if err := bootstrapTask.Run("buzz"); err != nil {
		f.t.Fatal(err)
	}
	return gateway
}
func (f *Fixtures) Grow() *cluster.Gateway {
	f.t.Logf("adding another node to the test cluster")
	target := f.Leader()
	targetState := f.states[target]
	state, gateway, address := f.node()
	name := address
	acceptTask := membership.NewAccept(
		makeMembershipStateShim(targetState),
		makeMembershipGatewayShim(target),
	)
	nodes, err := acceptTask.Run(name, address, 2, 1)
	if err != nil {
		f.t.Fatal(err)
	}
	joinTask := membership.NewJoin(
		makeMembershipStateShim(state),
		makeMembershipGatewayShim(gateway),
		node.ConfigSchema,
	)
	if err := joinTask.Run(target.Cert(), name, nodes); err != nil {
		f.t.Fatal(err)
	}
	return gateway
}
func (f *Fixtures) Leader() *cluster.Gateway {
	timeout := time.Second
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	for {
		for _, gateway := range f.gateways {
			if gateway.Raft().Raft().State() == raft.Leader {
				return gateway
			}
		}
		select {
		case <-ctx.Done():
			f.t.Fatalf("no leader was elected within %s", timeout)
		default:
		}
		time.Sleep(10 * time.Millisecond)
	}
}