package main import ( "context" "net/http" "net/http/httptest" "strings" "testing" "time" ) // echoBrain returns the prompt itself, so an Elder merge contains the reports. type echoBrain struct{} func (echoBrain) Name() string { return "echo" } func (echoBrain) Ask(ctx context.Context, system, prompt string) (string, int, error) { return prompt, 10, nil } func newEchoServer(t *testing.T) (*httptest.Server, *Server) { t.Helper() ts, s := newTestNestServer(t) s.p.Flock.brain = echoBrain{} return ts, s } func TestElderAddressableInNest(t *testing.T) { ts, s := newEchoServer(t) id := newNest(t, ts, "Lab", `"Hugin","Munin"`) r := postRequest(t, ts, id, "conspiracy", "is this plan risky") postDeliver(t, ts, id, "Hugin", r, "the mint Condition is fragile") postDeliver(t, ts, id, "Munin", r, "the audit trail says otherwise") code, _ := postMsg(t, ts, id, `{"author":"owner","kind":"request","to":"Elder","text":"merge this","refers_to":"`+r+`"}`) if code != http.StatusCreated { t.Fatalf("elder request: %d", code) } elders := awaitMsgsBy(t, ts, id, "Elder") if len(elders) != 1 { t.Fatalf("want exactly one Elder message, got %v", elders) } m := elders[0] text, _ := m["text"].(string) for _, want := range []string{"fragile", "otherwise"} { if !strings.Contains(text, want) { t.Fatalf("Elder merge must carry both reports (missing %q): %q", want, text) } } if m["refers_to"] != r || m["to"] != "owner" || m["kind"] != "deliver" { t.Fatalf("Elder reply shape wrong: %v", m) } _ = s } func TestElderOnlyMergesOnRequest(t *testing.T) { ts, s := newEchoServer(t) id := newNest(t, ts, "Lab", `"Hugin","Munin"`) r := postRequest(t, ts, id, "conspiracy", "is this plan risky") postDeliver(t, ts, id, "Hugin", r, "ship it") postDeliver(t, ts, id, "Munin", r, "ship it too") time.Sleep(300 * time.Millisecond) if got := awaitMsgsBy(t, ts, id, "Elder"); len(got) != 0 { t.Fatalf("Elder must stay silent unless asked, got %v", got) } _ = s } func TestParseAddressingServiceRole(t *testing.T) { ts, s := newTestNestServer(t) id := newNest(t, ts, "Lab", `"Hugin"`) if got := parseAddressing("please @Elder merge this", nestMembers(nestOf(s, id))); got != "Elder" { t.Fatalf("Elder must be an addressable service handle, got %q", got) } } func awaitMsgsBy(t *testing.T, ts *httptest.Server, id, author string) []map[string]any { t.Helper() deadline := time.Now().Add(2 * time.Second) for { var out []map[string]any for _, m := range nestMessages(t, ts, id) { if m["author"] == author { out = append(out, m) } } if len(out) > 0 || time.Now().After(deadline) { return out } time.Sleep(10 * time.Millisecond) } }