package cli import ( "bytes" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "github.com/spf13/cobra" "gitea.mynodes.xyz/ilker/llmgw/internal/control/adminapi" "gitea.mynodes.xyz/ilker/llmgw/internal/store" ) func TestStatusSaysWhichControlPlaneLeadsAndWhetherTheOthersStandBy(t *testing.T) { render := func(s adminapi.Summary) string { cmd := &cobra.Command{} var b bytes.Buffer cmd.SetOut(&b) printSummary(cmd, s) return b.String() } out := render(adminapi.Summary{ControlPlanes: adminapi.ControlPlanes{ Members: []adminapi.ControlPlane{ {Name: "llmgw-ctl", State: "leading", This: true}, {Name: "llmgw-ctl02", State: "standing by"}, }, Floating: "10.0.0.161", FloatingUp: true, }}) if !strings.Contains(out, "CONTROL llmgw-ctl leads (this one), llmgw-ctl02 standing by ยท 10.0.0.161 on the leader") { t.Errorf("status:\n%s", out) } out = render(adminapi.Summary{ControlPlanes: adminapi.ControlPlanes{ Members: []adminapi.ControlPlane{ {Name: "llmgw-ctl", State: "not answering"}, {Name: "llmgw-ctl02", State: "standing by", This: true}, }, Floating: "10.0.0.161", }}) for _, want := range []string{"NO LEADER", "llmgw-ctl not answering", "10.0.0.161 NOT ANSWERING"} { if !strings.Contains(out, want) { t.Errorf("%q missing:\n%s", want, out) } } if out := render(adminapi.Summary{}); strings.Contains(out, "CONTROL") { t.Error("a single control plane got a control line") } } func TestTheProbeAsksEachControlPlaneWhetherItLeads(t *testing.T) { leading := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})) defer leading.Close() standby := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusServiceUnavailable) })) defer standby.Close() gone := httptest.NewServer(http.NotFoundHandler()) gone.Close() st, err := store.Open(t.Context(), filepath.Join(t.TempDir(), "s.db")) if err != nil { t.Fatal(err) } defer st.Close() for _, cp := range []store.ControlPlane{ {URL: "https://10.0.0.1:7443", Name: "one"}, {URL: "https://10.0.0.2:7443", Name: "two"}, {URL: "https://10.0.0.3:7443", Name: "three"}, } { if err := st.PutControlPlane(t.Context(), cp); err != nil { t.Fatal(err) } } at := map[string]string{"10.0.0.1": leading.URL, "10.0.0.2": standby.URL, "10.0.0.3": gone.URL, "10.0.0.161": leading.URL} p := newControlPlaneProbe(st, ":8080") floating := filepath.Join(t.TempDir(), "floating") if err := os.WriteFile(floating, []byte("10.0.0.161/24\n"), 0o644); err != nil { t.Fatal(err) } p.self, p.floating = "two", floating p.leaderURL = func(host string) string { return at[host] + "/leader" } s := p.Status() got := map[string]adminapi.ControlPlane{} for _, m := range s.Members { got[m.Name] = m } if got["one"].State != "leading" || got["two"].State != "standing by" || got["three"].State != "not answering" { t.Errorf("states: %+v", got) } if !got["two"].This || got["one"].This || got["one"].Address != "10.0.0.1" { t.Errorf("which and where: %+v", got) } if !s.FloatingUp { t.Error("a floating address on the leader read as down") } } func TestOneControlPlaneIsNotASetOfThem(t *testing.T) { st, err := store.Open(t.Context(), filepath.Join(t.TempDir(), "s.db")) if err != nil { t.Fatal(err) } defer st.Close() if err := st.PutControlPlane(t.Context(), store.ControlPlane{URL: "https://10.0.0.1:7443", Name: "one"}); err != nil { t.Fatal(err) } if s := newControlPlaneProbe(st, ":8080").Status(); len(s.Members) != 0 { t.Errorf("one control plane reported as a set: %+v", s) } } func TestTheFloatingAddressIsReadFromWhatServerAddWrote(t *testing.T) { dir := t.TempDir() file := filepath.Join(dir, "floating") if err := os.WriteFile(file, []byte("10.0.0.161/24\n"), 0o644); err != nil { t.Fatal(err) } if got := floatingAddress(file); got != "10.0.0.161" { t.Errorf("read %q", got) } if err := os.WriteFile(file, []byte("not an address\n"), 0o644); err != nil { t.Fatal(err) } if got := floatingAddress(file); got != "" { t.Errorf("nonsense read as %q", got) } if got := floatingAddress(filepath.Join(dir, "none")); got != "" { t.Errorf("no file read as %q", got) } }