Files
byre/internal/agents/lead_test.go
T

170 lines
6.2 KiB
Go

package agents_test
import (
"fmt"
"slices"
"strings"
"testing"
"time"
"g.fc.run/misc/byre/internal/api"
"g.fc.run/misc/byre/internal/store"
)
func leadIdle(ev api.Event) bool {
return ev.Kind == "session.state" && ev.Owner != nil && ev.Payload["state"] == string(api.AgentSessionStateIdle)
}
func TestLeadSignalsAndShow(t *testing.T) {
t.Parallel()
e := newEnv(t)
m := e.manager()
lead, err := m.MessageLead(e.ctx, e.user, "Reply with the single word ready.", testModel, nil)
if err != nil {
t.Fatalf("message lead: %v", err)
}
if lead.Owner != login || lead.TaskId != "" || lead.Role != api.Lead {
t.Fatalf("lead session %+v", lead)
}
e.waitFor("lead idle", leadIdle)
worker := api.AgentSession{
Id: store.NewID(), TaskId: e.task.Id, Vendor: api.Claude, VendorSessionId: store.NewID(),
State: api.AgentSessionStateIdle, Role: api.Implementer, CreatedBy: login, CreatedAt: time.Now().UTC(),
}
err = e.store.CreateSession(e.ctx, worker, e.project.Id)
if err != nil {
t.Fatalf("create worker session: %v", err)
}
err = e.store.CreateInboxItem(e.ctx, api.InboxItem{
Id: store.NewID(), ProjectId: e.project.Id, TaskId: e.task.Id, AgentSessionId: worker.Id, ThreadId: "t1",
Kind: api.Report, Priority: api.Fyi, Payload: map[string]any{"text": "Finished the parser. The tests pass."},
State: api.Open, CreatedAt: time.Now().UTC(), Responses: []api.InboxResponse{},
})
if err != nil {
t.Fatalf("file report: %v", err)
}
created := e.waitFor("inbox.created", func(ev api.Event) bool { return ev.Kind == "inbox.created" })
m.Observe(created[len(created)-1])
signalled := e.waitFor("signal delivered", func(ev api.Event) bool {
return ev.Kind == "user.message" && ev.Owner != nil && ev.Actor == "byre"
})
text := signalled[len(signalled)-1].Payload["text"].(string)
if !strings.Contains(text, "Finished the parser") || !strings.Contains(text, e.task.Id) {
t.Fatalf("signal text %q", text)
}
e.waitFor("lead idle after signal", leadIdle)
_, err = m.MessageLead(e.ctx, e.user, "Call the show tool with task_id "+e.task.Id+", tabs [\"inbox\"] and tab \"inbox\". Then say done.", "", nil)
if err != nil {
t.Fatalf("message lead: %v", err)
}
e.waitFor("focus.updated by the lead", func(ev api.Event) bool {
return ev.Kind == "focus.updated" && ev.Actor == login+" (lead)"
})
f, err := e.store.GetFocus(e.ctx, login)
if err != nil {
t.Fatalf("get focus: %v", err)
}
if f.TaskId == nil || *f.TaskId != e.task.Id || f.ProjectId == nil || *f.ProjectId != e.project.Id || f.Tab != api.TabInbox {
t.Fatalf("focus %+v", f)
}
e.waitFor("lead idle after show", leadIdle)
m.SetPaneFocus(login, true)
_, err = m.MessageLead(e.ctx, e.user, "Call the show tool with task_id "+e.task.Id+", tabs [\"changes\"] and tab \"changes\". Then say done.", "", nil)
if err != nil {
t.Fatalf("message lead: %v", err)
}
e.waitFor("show refused", func(ev api.Event) bool {
return ev.Kind == "agent.tool_result" && ev.Owner != nil && strings.Contains(fmt.Sprint(ev.Payload), "working in the right pane")
})
e.waitFor("lead idle after refused show", leadIdle)
f, err = e.store.GetFocus(e.ctx, login)
if err != nil {
t.Fatalf("get focus: %v", err)
}
if f.Tab != api.TabInbox {
t.Fatalf("focus moved while the pane was focused: %+v", f)
}
_, err = m.MessageLead(e.ctx, e.user, "Call the show tool with task_id "+e.task.Id+", tabs [\"inbox\", \"changes\"] and tab \"inbox\". Then say done.", "", nil)
if err != nil {
t.Fatalf("message lead: %v", err)
}
e.waitFor("show of what is already shown", func(ev api.Event) bool {
return ev.Kind == "agent.tool_result" && ev.Owner != nil && strings.Contains(fmt.Sprint(ev.Payload), "Already shown")
})
e.waitFor("lead idle after already-shown show", leadIdle)
f, err = e.store.GetFocus(e.ctx, login)
if err != nil {
t.Fatalf("get focus: %v", err)
}
if f.Tab != api.TabInbox || !slices.Equal(f.Tabs, []api.TaskTab{api.TabInbox, api.TabChanges}) {
t.Fatalf("tabs not reordered on the shown tab: %+v", f)
}
err = e.store.WithdrawItem(e.ctx, created[len(created)-1].Payload["id"].(string))
if err != nil {
t.Fatalf("withdraw report: %v", err)
}
_, err = m.MessageLead(e.ctx, e.user, "Call the show tool with task_id "+e.task.Id+", tabs [\"changes\"] and tab \"changes\". Then say done.", "", nil)
if err != nil {
t.Fatalf("message lead: %v", err)
}
e.waitFor("show allowed on an emptied inbox", func(ev api.Event) bool {
return ev.Kind == "focus.updated" && ev.Actor == login+" (lead)" && ev.Payload["tab"] == string(api.TabChanges)
})
e.waitFor("lead idle after show on an emptied inbox", leadIdle)
err = m.SetFocus(e.ctx, login, api.Focus{ProjectId: &e.project.Id, Tabs: []api.TaskTab{api.TabProject}, Tab: api.TabProject}, login)
if err != nil {
t.Fatalf("focus project: %v", err)
}
_, err = m.MessageLead(e.ctx, e.user, "Call the show tool with task_id "+e.task.Id+", tabs [\"transcript\"] and tab \"transcript\". Then say done.", "", nil)
if err != nil {
t.Fatalf("message lead: %v", err)
}
e.waitFor("show allowed over the project tab", func(ev api.Event) bool {
return ev.Kind == "focus.updated" && ev.Actor == login+" (lead)" && ev.Payload["tab"] == string(api.TabTranscript)
})
}
func TestFocusProject(t *testing.T) {
t.Parallel()
e := newEnv(t)
m := e.manager()
err := m.SetFocus(e.ctx, login, api.Focus{ProjectId: &e.project.Id, Tabs: []api.TaskTab{api.TabProject}, Tab: api.TabProject}, login)
if err != nil {
t.Fatalf("focus project: %v", err)
}
f, err := e.store.GetFocus(e.ctx, login)
if err != nil {
t.Fatalf("get focus: %v", err)
}
if f.TaskId != nil || f.ProjectId == nil || *f.ProjectId != e.project.Id || f.Tab != api.TabProject {
t.Fatalf("focus %+v", f)
}
other := "nope"
err = m.SetFocus(e.ctx, login, api.Focus{TaskId: &e.task.Id, ProjectId: &other, Tabs: []api.TaskTab{}, Tab: api.TabTranscript}, login)
if err != nil {
t.Fatalf("focus task: %v", err)
}
f, err = e.store.GetFocus(e.ctx, login)
if err != nil {
t.Fatalf("get focus: %v", err)
}
if f.TaskId == nil || *f.TaskId != e.task.Id || f.ProjectId == nil || *f.ProjectId != e.project.Id {
t.Fatalf("focus %+v", f)
}
err = m.SetFocus(e.ctx, login, api.Focus{ProjectId: &other, Tabs: []api.TaskTab{}, Tab: api.TabProject}, login)
if err == nil {
t.Fatal("focus on an unknown project was accepted")
}
}