mesh/repo-check head 954728b39042 merged into trunk 8ae804fe6450 beside mesh-controller#388@ac79e293 — pass: its merge-check.sh passed
mesh/merge-gate head 954728b39042 merged into trunk 8ae804fe6450 — pass: group feat/delivery-watch composed: every machine composes with the change as it d…
mesh/delivery delivered
mesh/delivery-group group feat/delivery-watch delivered: every member is delivered
154 lines
4.5 KiB
Go
154 lines
4.5 KiB
Go
package view
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
func served(t *testing.T) (string, *Model) {
|
|
t.Helper()
|
|
m := New()
|
|
fill(m, base(t)...)
|
|
fill(m, haveE(t, "a", "w", have(t, "b9", delivery.PhaseRunning, delivery.HealthOK, ""), t0))
|
|
dir, err := os.MkdirTemp(os.Getenv("TMPDIR"), "s")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { os.RemoveAll(dir) })
|
|
path := filepath.Join(dir, "run", "cli.sock")
|
|
l, err := ListenSocket(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
done := make(chan struct{})
|
|
go func() { ServeSocket(ctx, l, m); close(done) }()
|
|
t.Cleanup(func() { cancel(); l.Close(); <-done })
|
|
return path, m
|
|
}
|
|
|
|
func TestSocketAnswersRows(t *testing.T) { // 1134/AC6
|
|
path, m := served(t)
|
|
got, err := Ask(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := m.Snapshot()
|
|
b1, _ := json.Marshal(got)
|
|
b2, _ := json.Marshal(want)
|
|
if len(want.Rows) == 0 || string(b1) != string(b2) {
|
|
t.Fatalf("socket answered %s, want %s", b1, b2)
|
|
}
|
|
st, err := os.Stat(path)
|
|
if err != nil || st.Mode().Perm() != 0o666 {
|
|
t.Fatalf("socket mode = %v, %v; want 0666", st.Mode().Perm(), err)
|
|
}
|
|
dst, _ := os.Stat(filepath.Dir(path))
|
|
if dst.Mode().Perm() != 0o755 {
|
|
t.Fatalf("directory mode = %v, want 0755", dst.Mode().Perm())
|
|
}
|
|
}
|
|
|
|
func TestSocketRefusesAnyOtherAsk(t *testing.T) { // 1134/AC6
|
|
path, m := served(t)
|
|
before, _ := json.Marshal(m.Snapshot())
|
|
for _, ask := range []string{`{"ask":"start","module":"x"}`, `{"ask":"retry"}`, `{"ask":""}`, `not json`, `{}`} {
|
|
c, err := dial(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
c.Write([]byte(ask + "\n"))
|
|
line, err := bufio.NewReader(c).ReadString('\n')
|
|
c.Close()
|
|
if err != nil || !strings.Contains(line, `"error"`) || strings.Contains(line, `"rows"`) {
|
|
t.Errorf("ask %q answered %q, %v; want a refusal", ask, line, err)
|
|
}
|
|
}
|
|
after, _ := json.Marshal(m.Snapshot())
|
|
if string(before) != string(after) {
|
|
t.Fatal("a refused ask changed the rows")
|
|
}
|
|
}
|
|
|
|
func TestTableShowsEveryRow(t *testing.T) { // 1134/AC6
|
|
_, m := served(t)
|
|
var out strings.Builder
|
|
s := m.Snapshot()
|
|
Table(&out, s)
|
|
for _, r := range s.Rows {
|
|
if !strings.Contains(out.String(), r.Module) {
|
|
t.Fatalf("table lacks %s:\n%s", r.Module, out.String())
|
|
}
|
|
}
|
|
for _, w := range []string{"NODE", "MODULE", "ASSIGNED", "HAVE", "PHASE", "HEALTH", "SINCE", "WAITS", "no have yet", "left behind"} {
|
|
if !strings.Contains(out.String(), w) {
|
|
t.Errorf("table lacks %q:\n%s", w, out.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWebIsTheSameRowsAndReadOnly(t *testing.T) { // 1134/AC6
|
|
_, m := served(t)
|
|
h := Web(m)
|
|
rec := httptest.NewRecorder()
|
|
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", nil))
|
|
if rec.Code != 200 || !strings.Contains(rec.Body.String(), "no have yet") || !strings.Contains(rec.Header().Get("Content-Type"), "text/html") {
|
|
t.Fatalf("web = %d %q", rec.Code, rec.Body.String())
|
|
}
|
|
rec = httptest.NewRecorder()
|
|
h.ServeHTTP(rec, httptest.NewRequest(http.MethodPost, "/", strings.NewReader("x")))
|
|
if rec.Code != http.StatusMethodNotAllowed {
|
|
t.Fatalf("POST = %d, want 405", rec.Code)
|
|
}
|
|
}
|
|
|
|
func watchmanSnapshot(t *testing.T) *Model {
|
|
t.Helper()
|
|
m := New()
|
|
fill(m, presenceE(t, "a", false, t0), stuckE(t, "node-engine_have", "a.x", "a report from a", t0, false),
|
|
Entry{Bucket: delivery.PresenceBucket, Key: "c", Value: []byte(`{"v":9}`), At: t0})
|
|
return m
|
|
}
|
|
|
|
func TestTableAndWebShowTheWatchmansLists(t *testing.T) { // 1168/AC4
|
|
m := watchmanSnapshot(t)
|
|
var out strings.Builder
|
|
Table(&out, m.Snapshot())
|
|
rec := httptest.NewRecorder()
|
|
Web(m).ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", nil))
|
|
for name, text := range map[string]string{"table": out.String(), "web": rec.Body.String()} {
|
|
for _, w := range []string{"OFFLINE", "STUCK", "UNREADABLE", "node-engine_have", "a.x", "a report from a", "offline", "c"} {
|
|
if !strings.Contains(text, w) {
|
|
t.Errorf("%s lacks %q:\n%s", name, w, text)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSocketCarriesTheWatchmansLists(t *testing.T) { // 1168/AC4
|
|
m := watchmanSnapshot(t)
|
|
dir, _ := os.MkdirTemp(os.Getenv("TMPDIR"), "w")
|
|
t.Cleanup(func() { os.RemoveAll(dir) })
|
|
path := filepath.Join(dir, "cli.sock")
|
|
l, err := ListenSocket(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
t.Cleanup(cancel)
|
|
go ServeSocket(ctx, l, m)
|
|
got, err := Ask(path)
|
|
if err != nil || len(got.Offline) != 1 || len(got.Stuck) != 1 || len(got.Unreadable) != 1 {
|
|
t.Fatalf("socket = %+v, %v", got, err)
|
|
}
|
|
}
|