package main import ( "context" "net/http" "net/http/httptest" "strings" "sync" "testing" ) type quillBrain struct { mu sync.Mutex pairs [][2]string reply string } func (b *quillBrain) Name() string { return "quill-rec" } func (b *quillBrain) Ask(ctx context.Context, system, prompt string) (string, int, error) { b.mu.Lock() b.pairs = append(b.pairs, [2]string{system, prompt}) b.mu.Unlock() return b.reply, 10, nil } func newQuillServer(t *testing.T, reply string) (*httptest.Server, *Server, *quillBrain) { t.Helper() ts, s := newTestNestServer(t) qb := &quillBrain{reply: reply} s.p.Flock.brain = qb return ts, s, qb } func TestQuillIsServiceRole(t *testing.T) { ts, _, _ := newQuillServer(t, "hello back") id := newNest(t, ts, "Lab", `"Hugin","Munin"`) code, msg := post(t, ts.URL+"/api/nests/"+id+"/messages", `{"author":"owner","to":"Quill","kind":"say","text":"hello"}`) if code != http.StatusCreated { t.Fatalf("Quill must be addressable like Elder: got %d %v", code, msg) } } func TestQuillAnswersInCharacter(t *testing.T) { ts, _, qb := newQuillServer(t, "Already on it, boss.") id := newNest(t, ts, "Lab", `"Hugin","Munin"`) code, _ := post(t, ts.URL+"/api/nests/"+id+"/messages", `{"author":"owner","to":"Quill","kind":"request","text":"quiet the room"}`) if code != http.StatusCreated { t.Fatalf("post: %d", code) } msgs := awaitMessages(t, ts, id, 2) var reply map[string]any for _, m := range msgs { if m["author"] == "Quill" { reply = m } } if reply == nil { t.Fatalf("Quill never answered: %v", msgs) } if reply["kind"] != "deliver" || reply["to"] != "owner" { t.Fatalf("want deliver to owner, got %v", reply) } if reply["text"] != "Already on it, boss." { t.Fatalf("reply text: %v", reply["text"]) } qb.mu.Lock() defer qb.mu.Unlock() if len(qb.pairs) != 1 { t.Fatalf("want exactly one model call, got %d", len(qb.pairs)) } if !strings.Contains(qb.pairs[0][0], "Quill") || !strings.Contains(qb.pairs[0][0], "The Hand") { t.Fatalf("system prompt must carry the Quill persona: %q", qb.pairs[0][0]) } if !strings.Contains(qb.pairs[0][1], "quiet the room") { t.Fatalf("prompt must carry the request text: %q", qb.pairs[0][1]) } } func TestQuillNotInNestCrows(t *testing.T) { ts, s, _ := newQuillServer(t, "noted") id := newNest(t, ts, "Lab", `"Hugin"`) post(t, ts.URL+"/api/nests/"+id+"/messages", `{"author":"owner","to":"Quill","kind":"request","text":"status?"}`) awaitMessages(t, ts, id, 2) for _, c := range nestOf(s, id).Crows { if c == "Quill" { t.Fatal("Quill must never become a nest member") } } // No trust promotion happened for Quill either: service roles act on // address, not on trust (the test server ledger was never touched). if _, ok := s.p.Ledger.Crows["Quill|other"]; ok { t.Fatal("Quill must not need or earn a worker trust record") } }