test(relay): red for #40 — Quill relays owner asks as nest requests

This commit is contained in:
Smokey committed 2026-10-28 08:57:26 -06:00
1 parent 4494a4ac19
commit bfffb04a87
2 files changed
+164

No files matched your search

+162
View File
@@ -0,0 +1,162 @@
package main
import (
"context"
"net/http"
"net/http/httptest"
"sync"
"testing"
"time"
)
// relayBrain records every (system, prompt) pair and answers with scripted
// replies in call order, falling back to plain chatter once they run out.
type relayBrain struct {
mu sync.Mutex
pairs [][2]string
replies []string
calls int
}
func (b *relayBrain) Name() string { return "relay-rec" }
func (b *relayBrain) Ask(ctx context.Context, system, prompt string) (string, int, error) {
b.mu.Lock()
defer b.mu.Unlock()
b.pairs = append(b.pairs, [2]string{system, prompt})
reply := "just chatter"
if b.calls < len(b.replies) {
reply = b.replies[b.calls]
}
b.calls++
return reply, 10, nil
}
func newRelayServer(t *testing.T, replies ...string) (*httptest.Server, *Server, *relayBrain) {
t.Helper()
ts, s := newTestNestServer(t)
rb := &relayBrain{replies: replies}
s.p.Flock.brain = rb
return ts, s, rb
}
// askQuill posts the owner's request addressed to Quill and returns the
// stored record of that message.
func askQuill(t *testing.T, ts *httptest.Server, id, text string) map[string]any {
t.Helper()
code, req := post(t, ts.URL+"/api/nests/"+id+"/messages",
`{"author":"owner","to":"Quill","kind":"request","text":"`+text+`"}`)
if code != http.StatusCreated {
t.Fatalf("owner request: %d %v", code, req)
}
return req
}
func countBy(t *testing.T, ts *httptest.Server, id, author string, kinds ...string) (total int, matched int) {
t.Helper()
for _, m := range nestMessages(t, ts, id) {
if m["author"] != author {
continue
}
total++
for _, k := range kinds {
if m["kind"] == k {
matched++
}
}
}
return
}
func TestQuillRelayPostsRequest(t *testing.T) {
ts, _, _ := newRelayServer(t,
`RELAY {"crow":"Hugin","task":"draft the tagline"}`,
"KIND: deliver\nSo many crows, one banner.")
id := newNest(t, ts, "Lab", `"Hugin","Munin"`)
req := askQuill(t, ts, id, "get Hugin to draft the tagline")
msgs := awaitMessages(t, ts, id, 3)
var relay map[string]any
for _, m := range msgs {
if m["author"] == "Quill" && m["kind"] == "request" {
relay = m
}
}
if relay == nil {
t.Fatalf("Quill never posted the relay request: %v", msgs)
}
if relay["to"] != "Hugin" {
t.Fatalf("relay must address Hugin, got %v", relay["to"])
}
if relay["refers_to"] != req["msg_id"] {
t.Fatalf("relay must refer to the owner message %v, got %v", req["msg_id"], relay["refers_to"])
}
if relay["text"] != "draft the tagline" {
t.Fatalf("relay text must carry the task, got %v", relay["text"])
}
hugin := false
for _, m := range msgs {
if m["author"] == "Hugin" {
hugin = true
}
}
if !hugin {
t.Fatalf("the addressed crow must answer the relay through must-respond: %v", msgs)
}
}
func TestQuillRelayUnknownCrowStalls(t *testing.T) {
ts, _, _ := newRelayServer(t, `RELAY {"crow":"Soot","task":"blacken the roof"}`)
id := newNest(t, ts, "Lab", `"Hugin","Munin"`)
askQuill(t, ts, id, "get Soot to blacken the roof")
awaitMessages(t, ts, id, 2)
time.Sleep(200 * time.Millisecond)
msgs := nestMessages(t, ts, id)
total, requests := countBy(t, ts, id, "Quill", "request")
if total != 1 || requests != 0 {
t.Fatalf("want exactly one Quill message and zero Quill requests, got %v in %v", msgs, total)
}
var stall map[string]any
for _, m := range msgs {
if m["author"] == "Quill" {
stall = m
}
}
if stall == nil || stall["kind"] != "say" {
t.Fatalf("the unknown-crow reply must be a Quill say, got %v", stall)
}
if stall["to"] != "owner" {
t.Fatalf("the say must go back to the requester, got %v", stall["to"])
}
}
func TestQuillRelayNotAnsweredTwice(t *testing.T) {
ts, _, _ := newRelayServer(t,
`RELAY {"crow":"Hugin","task":"draft the tagline"}`,
"KIND: deliver\nSo many crows, one banner.")
id := newNest(t, ts, "Lab", `"Hugin","Munin"`)
askQuill(t, ts, id, "get Hugin to draft the tagline")
awaitMessages(t, ts, id, 3)
time.Sleep(200 * time.Millisecond)
total, delivers := countBy(t, ts, id, "Quill", "deliver")
if total != 1 {
t.Fatalf("a successful relay must leave Quill with exactly one message, got %d", total)
}
if delivers != 0 {
t.Fatalf("a relay must not also deliver, got %d delivers", delivers)
}
}
func TestQuillRelayGoesThroughFlock(t *testing.T) {
ts, s := newTestNestServer(t)
id := newNest(t, ts, "Lab", `"Hugin","Munin"`)
s.p.Flock.brain = &relayBrain{replies: []string{`RELAY {"crow":"Hugin","task":"draft the tagline"}`}}
s.p.Flock.sem = make(chan struct{}, 0)
askQuill(t, ts, id, "get Hugin to draft the tagline")
time.Sleep(300 * time.Millisecond)
if got := nestMessages(t, ts, id); len(got) != 1 {
t.Fatalf("a saturated flock means ask fails and Quill posts nothing: %v", got)
}
}