Files
byre/internal/agents/health_internal_test.go
T

107 lines
3.4 KiB
Go

package agents
import (
"fmt"
"strings"
"testing"
"time"
"g.fc.run/misc/byre/internal/api"
)
func TestAssessStuck(t *testing.T) {
now := time.Now()
procs := []procInfo{
{sessionID: "a", pid: 10, state: api.AgentSessionStateRunning, lastLine: now.Add(-StuckAfter - time.Second)},
{sessionID: "b", pid: 11, state: api.AgentSessionStateRunning, lastLine: now.Add(-StuckAfter + time.Second)},
{sessionID: "c", pid: 12, state: api.AgentSessionStateIdle, lastLine: now.Add(-time.Hour)},
}
running := []api.AgentSession{{Id: "a"}, {Id: "b"}}
h := assess(now, procs, running, []int{10, 11, 12})
want := map[string]api.AgentProcessHealth{"a": api.HealthStuck, "b": api.HealthOk, "c": api.HealthOk}
for _, p := range h.Processes {
if p.Health != want[p.SessionId] {
t.Errorf("session %s health = %s, want %s", p.SessionId, p.Health, want[p.SessionId])
}
}
if len(h.Zombies) != 0 {
t.Errorf("zombies = %+v, want none", h.Zombies)
}
}
func TestAssessZombies(t *testing.T) {
now := time.Now()
procs := []procInfo{
{sessionID: "exited", pid: 20, state: api.AgentSessionStateRunning, lastLine: now, exited: true},
{sessionID: "done", pid: 21, state: api.AgentSessionStateCompleted, lastLine: now},
{sessionID: "idle", pid: 22, state: api.AgentSessionStateIdle, lastLine: now},
}
running := []api.AgentSession{{Id: "exited", TaskId: "t1"}, {Id: "gone", TaskId: "t2"}}
h := assess(now, procs, running, []int{21, 22, 99})
got := []string{}
for _, z := range h.Zombies {
key := string(z.Kind)
if z.SessionId != nil {
key += " " + *z.SessionId
}
if z.Pid != nil {
key += fmt.Sprintf(" %d", *z.Pid)
}
got = append(got, key)
}
want := []string{
"process_without_session done 21",
"running_without_process exited",
"running_without_process gone",
"process_without_session 99",
}
if strings.Join(got, "\n") != strings.Join(want, "\n") {
t.Errorf("zombies:\n%s\nwant:\n%s", strings.Join(got, "\n"), strings.Join(want, "\n"))
}
for _, p := range h.Processes {
if p.SessionId == "exited" && p.Health != api.HealthExited {
t.Errorf("exited process health = %s", p.Health)
}
}
}
func TestBadLinesRing(t *testing.T) {
b := newBadLines()
at := time.Now()
for i := range keptBadLines + 5 {
b.add("claude", "s", fmt.Sprintf("reason %d", i), []byte(fmt.Sprintf("line %d", i)), at)
}
b.add("other", "s", "big", []byte(strings.Repeat("x", badLineBytes*2)), at)
snap := b.snapshot()
if snap.Counts["claude"] != keptBadLines+5 || snap.Counts["other"] != 1 {
t.Errorf("counts = %v", snap.Counts)
}
if len(snap.Recent) != keptBadLines {
t.Fatalf("kept %d, want %d", len(snap.Recent), keptBadLines)
}
if snap.Recent[0].Reason != "big" || len(snap.Recent[0].Raw) != badLineBytes || snap.Recent[0].Size != badLineBytes*2 {
t.Errorf("newest = %q, %d raw bytes, size %d", snap.Recent[0].Reason, len(snap.Recent[0].Raw), snap.Recent[0].Size)
}
if snap.Recent[1].Raw != fmt.Sprintf("line %d", keptBadLines+4) {
t.Errorf("second newest = %q", snap.Recent[1].Raw)
}
oldest := snap.Recent[len(snap.Recent)-1].Raw
if oldest != "line 6" {
t.Errorf("oldest = %q, want line 6", oldest)
}
}
func TestParseChildren(t *testing.T) {
out := []byte(" 100 1 launchd\n 200 100 claude\n 201 100 /Users/x/.local/bin/claude\n 202 100 git\n 203 999 claude\n")
got := parseChildren(out, 100)
if fmt.Sprint(got) != "[200 201]" {
t.Errorf("children = %v", got)
}
}