Files
so-many-crows/quill_test.go
T

98 lines
2.8 KiB
Go

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")
}
}