package cluster import ( "encoding/json" "errors" "io" "net/http" "net/http/httptest" "reflect" "testing" "time" ) func Test_AddressProviderString(t *testing.T) { a := []string{"a", "b", "c"} p := NewAddressProviderString(a) b, err := p.Lookup() if err != nil { t.Fatalf("failed to lookup addresses: %s", err.Error()) } if !reflect.DeepEqual(a, b) { t.Fatalf("failed to get correct addresses") } } func Test_NewBootstrapper(t *testing.T) { bs := NewBootstrapper(nil, 1, nil) if bs == nil { t.Fatalf("failed to create a simple Bootstrapper") } } func Test_BootstrapperBootDoneImmediately(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { t.Fatalf("client made HTTP request") })) done := func() bool { return true } p := NewAddressProviderString([]string{ts.URL}) bs := NewBootstrapper(p, 1, nil) if err := bs.Boot("node1", "192.168.1.1:1234", done, 10*time.Second); err != nil { t.Fatalf("failed to boot: %s", err) } } func Test_BootstrapperBootTimeout(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusServiceUnavailable) })) done := func() bool { return false } p := NewAddressProviderString([]string{ts.URL}) bs := NewBootstrapper(p, 1, nil) bs.Interval = time.Second err := bs.Boot("node1", "192.168.1.1:1234", done, 5*time.Second) if err == nil { t.Fatalf("no error returned from timed-out boot") } if !errors.Is(err, ErrBootTimeout) { t.Fatalf("wrong error returned") } } func Test_BootstrapperBootSingleNotify(t *testing.T) { tsNotified := false var body map[string]string ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/join" { w.WriteHeader(http.StatusServiceUnavailable) return } tsNotified = true b, err := io.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) return } if err := json.Unmarshal(b, &body); err != nil { w.WriteHeader(http.StatusBadRequest) return } })) n := -1 done := func() bool { n++ if n == 5 { return true } return false } p := NewAddressProviderString([]string{ts.URL}) bs := NewBootstrapper(p, 1, nil) bs.Interval = time.Second err := bs.Boot("node1", "192.168.1.1:1234", done, 60*time.Second) if err != nil { t.Fatalf("failed to boot: %s", err) } if tsNotified != true { t.Fatalf("notify target not contacted") } if got, exp := body["id"], "node1"; got != exp { t.Fatalf("wrong node ID supplied, exp %s, got %s", exp, got) } if got, exp := body["addr"], "192.168.1.1:1234"; got != exp { t.Fatalf("wrong address supplied, exp %s, got %s", exp, got) } } func Test_BootstrapperBootSingleNotifyAuth(t *testing.T) { tsNotified := false ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { username, password, ok := r.BasicAuth() if !ok { t.Fatalf("request did not have Basic Auth credentials") } if username != "username1" || password != "password1" { t.Fatalf("bad Basic Auth credentials received (%s, %s", username, password) } if r.URL.Path == "/join" { w.WriteHeader(http.StatusServiceUnavailable) return } tsNotified = true })) n := -1 done := func() bool { n++ if n == 5 { return true } return false } p := NewAddressProviderString([]string{ts.URL}) bs := NewBootstrapper(p, 1, nil) bs.SetBasicAuth("username1", "password1") bs.Interval = time.Second err := bs.Boot("node1", "192.168.1.1:1234", done, 60*time.Second) if err != nil { t.Fatalf("failed to boot: %s", err) } if tsNotified != true { t.Fatalf("notify target not contacted") } } func Test_BootstrapperBootMultiNotify(t *testing.T) { ts1Join := false ts1Notified := false ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/join" { ts1Join = true w.WriteHeader(http.StatusServiceUnavailable) return } ts1Notified = true })) ts2Join := false ts2Notified := false ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/join" { ts2Join = true w.WriteHeader(http.StatusServiceUnavailable) return } ts2Notified = true })) n := -1 done := func() bool { n++ if n == 5 { return true } return false } p := NewAddressProviderString([]string{ts1.URL, ts2.URL}) bs := NewBootstrapper(p, 2, nil) bs.Interval = time.Second err := bs.Boot("node1", "192.168.1.1:1234", done, 60*time.Second) if err != nil { t.Fatalf("failed to boot: %s", err) } if ts1Join != true || ts2Join != true { t.Fatalf("all join targets not contacted") } if ts1Notified != true || ts2Notified != true { t.Fatalf("all notify targets not contacted") } }