test(nest): live-model crow delivery in the nest; ReadEvents log helper (#13)
This commit is contained in:
1 parent
ca22c16a53
commit
0da8cf2bb6
2 files changed
+118
No files matched your search
@@ -0,0 +1,97 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// newLiveNestServer builds the server over the real brain from env (NewBrain)
|
||||
// instead of the mock. Requires an API key; callers must skip first.
|
||||
func newLiveNestServer(t *testing.T) (*Server, string) {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
ledger, _ := LoadLedger(filepath.Join(dir, "ledger.json"))
|
||||
flog, err := OpenFlightLog(filepath.Join(dir, "flight.jsonl"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { flog.Close() })
|
||||
nests, _ := LoadNests(filepath.Join(dir, "nests.json"))
|
||||
msgs, _ := LoadMessageStore(filepath.Join(dir, "messages"))
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
approver := NewWebApprover(time.Minute)
|
||||
p := &Pipeline{Flock: NewFlock(NewBrain(), DefaultGuardrails(), flog), Ledger: ledger, Log: flog, G: DefaultGuardrails(), Approver: approver}
|
||||
s := NewServer(ctx, p, approver, nests).WithMessages(msgs)
|
||||
ts := httptest.NewServer(s.Handler())
|
||||
t.Cleanup(func() { cancel(); ts.Close() })
|
||||
return s, filepath.Join(dir, "flight.jsonl")
|
||||
}
|
||||
|
||||
func TestLiveCrowDeliversInNest(t *testing.T) {
|
||||
if os.Getenv("OPENROUTER_API_KEY") == "" && os.Getenv("ANTHROPIC_API_KEY") == "" {
|
||||
t.Skip("no model key")
|
||||
}
|
||||
s, logPath := newLiveNestServer(t)
|
||||
nest, err := s.nests.Create("Live Lab", []string{"Hugin"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req, err := s.msgs.Append(Message{NestID: nest.ID, Kind: "request", Author: "owner", To: "Hugin", Text: "What is the plural of crow?", Timestamp: time.Now().UTC().Format(time.RFC3339)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Synchronous call, like the endpoint's goroutine would.
|
||||
s.respondToRequest(nest, req)
|
||||
|
||||
msgs := s.msgs.All(nest.ID)
|
||||
var reply *Message
|
||||
for i := range msgs {
|
||||
if msgs[i].Author == "Hugin" {
|
||||
reply = &msgs[i]
|
||||
}
|
||||
}
|
||||
events, err := ReadEvents(logPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if reply == nil {
|
||||
// No crow message at all: either a flight failure (429/timeout: the
|
||||
// known free-model instability -> skip) or nothing was even attempted.
|
||||
flightFailed := false
|
||||
for _, e := range events {
|
||||
if e.Kind == "flight" && e.Error != "" {
|
||||
flightFailed = true
|
||||
}
|
||||
}
|
||||
if flightFailed {
|
||||
t.Skip("upstream model failed (known free-model instability); the flag path is covered by #11")
|
||||
}
|
||||
t.Fatalf("no Hugin reply after a request, log: %+v", msgs)
|
||||
}
|
||||
kind := reply.Kind
|
||||
if kind != "deliver" && kind != "claim" && kind != "decline" {
|
||||
t.Fatalf("live crow reply must be responsive, got %q (%s)", kind, reply.Text)
|
||||
}
|
||||
if reply.To != "owner" {
|
||||
t.Fatalf("deliver must answer the requester, got to=%q", reply.To)
|
||||
}
|
||||
if reply.RefersTo != req.MsgID() {
|
||||
t.Fatalf("reply must point at the request, got %q", reply.RefersTo)
|
||||
}
|
||||
// The call went through Flock.ask, so the flight log has Hugin's flight.
|
||||
flights := 0
|
||||
for _, e := range events {
|
||||
if e.Crow == "Hugin" && e.Model != "" && !strings.Contains(strings.ToLower(e.Model), "mock") {
|
||||
flights++
|
||||
}
|
||||
}
|
||||
if flights == 0 {
|
||||
t.Fatalf("want a logged non-mock Hugin flight, events: %+v", events)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user