test(nest): red for #11 — parse @-address, must-respond flag, deliver-to-requester

This commit is contained in:
Smokey committed 2026-10-28 04:53:53 -06:00
1 parent c9a6a64605
commit a37e34cc9a
1 file changed
+85 -3
+85 -3
View File
@@ -1,6 +1,7 @@
package main
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
@@ -9,7 +10,6 @@ import (
"time"
)
// newTestNestServer is newTestServer plus a message store.
func newTestNestServer(t *testing.T) (*httptest.Server, *Server) {
t.Helper()
ts, s := newTestServer(t, DefaultGuardrails(), time.Minute)
@@ -21,6 +21,34 @@ func newTestNestServer(t *testing.T) (*httptest.Server, *Server) {
return ts, s
}
// scriptedBrain answers nest requests with a canned reply per call.
type scriptedBrain struct {
replies []string
calls int
}
func (b *scriptedBrain) Name() string { return "scripted" }
func (b *scriptedBrain) Ask(ctx context.Context, system, prompt string) (string, int, error) {
r := "[mock] fallback"
if b.calls < len(b.replies) {
r = b.replies[b.calls]
}
b.calls++
return r, 10, nil
}
func newScriptedServer(t *testing.T, replies ...string) (*httptest.Server, *Server) {
t.Helper()
ts, s := newTestNestServer(t)
s.p.Flock.brain = &scriptedBrain{replies: replies}
return ts, s
}
func nestOf(s *Server, id string) Nest {
n, _ := s.nests.Get(id)
return n
}
func newNest(t *testing.T, ts *httptest.Server, name, crows string) string {
t.Helper()
code, out := post(t, ts.URL+"/api/nests", `{"name":"`+name+`","crows":[`+crows+`]}`)
@@ -99,8 +127,8 @@ func TestPostMessageTriggersOneResponse(t *testing.T) {
for _, m := range msgs {
if m["author"] == "Hugin" {
replies++
if m["kind"] != "say" && m["kind"] != "claim" && m["kind"] != "deliver" {
t.Fatalf("crow reply has a bad kind: %v", m["kind"])
if m["kind"] != "claim" && m["kind"] != "deliver" && m["kind"] != "decline" {
t.Fatalf("crow reply must be a responsive kind, got %v", m["kind"])
}
if m["refers_to"] != req["msg_id"] {
t.Fatalf("crow reply must point at the request, got %v", m["refers_to"])
@@ -124,3 +152,57 @@ func TestNoIdleResponse(t *testing.T) {
t.Fatalf("an unaddressed say must not summon a crow, log: %v", got)
}
}
func TestParseAddressing(t *testing.T) {
members := []string{"Hugin", "Munin", "owner"}
if got := parseAddressing("Taking it, @Hugin", members); got != "Hugin" {
t.Fatalf("want Hugin, got %q", got)
}
if got := parseAddressing("hello @Nobody", members); got != "" {
t.Fatalf("unknown handle must not resolve, got %q", got)
}
if got := parseAddressing("ask @Munin.", members); got != "Munin" {
t.Fatalf("want trailing period stripped, got %q", got)
}
}
func TestAddressedCrowMustRespond(t *testing.T) {
ts, s := newScriptedServer(t, "some idle chatter", "again not a reply")
id := newNest(t, ts, "Lab", `"Hugin"`)
s.msgs.Append(Message{NestID: id, Kind: "request", Author: "owner", To: "Hugin", Text: "count the eggs"})
req := s.msgs.All(id)[0]
s.respondToRequest(nestOf(s, id), req)
msgs := s.msgs.All(id)
if len(msgs) != 2 || msgs[1].Kind != "flag" || msgs[1].Author != "Sentry" || msgs[1].To != "Hugin" {
t.Fatalf("two non-responsive replies must end in a Sentry flag, got %+v", msgs)
}
if msgs[1].RefersTo != req.MsgID() {
t.Fatalf("flag must point at the ignored request, got %q", msgs[1].RefersTo)
}
}
func TestRespondedRequestNoFlag(t *testing.T) {
ts, s := newScriptedServer(t, "KIND: claim\nOn it, @owner.")
id := newNest(t, ts, "Lab", `"Hugin"`)
s.msgs.Append(Message{NestID: id, Kind: "request", Author: "owner", To: "Hugin", Text: "count the eggs"})
req := s.msgs.All(id)[0]
s.respondToRequest(nestOf(s, id), req)
msgs := s.msgs.All(id)
if len(msgs) != 2 || msgs[1].Kind != "claim" || msgs[1].Text != "On it, @owner." {
t.Fatalf("a responsive first reply must post the claim, got %+v", msgs)
}
}
func TestDeliverAddressesRequester(t *testing.T) {
ts, _ := newTestNestServer(t)
id := newNest(t, ts, "Lab", `"Munin"`)
_, req := post(t, ts.URL+"/api/nests/"+id+"/messages", `{"author":"owner","kind":"request","text":"count the eggs","to":"Munin"}`)
code, d := post(t, ts.URL+"/api/nests/"+id+"/messages",
`{"author":"Munin","kind":"deliver","text":"three eggs","refers_to":"`+req["msg_id"].(string)+`"}`)
if code != http.StatusCreated {
t.Fatalf("deliver: %d %v", code, d)
}
if d["to"] != "owner" {
t.Fatalf("deliver must go back to the requester, got to=%v", d["to"])
}
}