Files
multica/server/internal/handler/squad_member_status_test.go
YYClaw a6b83fef41 fix(agents): surface archived status for retired agents (#3608)
Retired agents (agent.archived_at set) previously read as offline across
the agent dot, hover card, detail badge, and squad member list — a
leftover online runtime row could even make them look reachable. Add a
dedicated archived presence/status that wins over every runtime/task
signal so a retired agent never reads as live or merely offline.

- Add archived to AgentAvailability and SquadMemberStatusValue unions
- Short-circuit deriveAgentPresenceDetail before runtime/task scan
- Backend deriveSquadMemberStatus returns archived instead of offline
- Render gray Archive dot/label; skip workload + reassign affordances
- en/ko/zh-Hans locale strings
2026-06-02 13:03:15 +08:00

53 lines
2.0 KiB
Go

package handler
import (
"testing"
"time"
"github.com/jackc/pgx/v5/pgtype"
)
func TestDeriveSquadMemberStatus(t *testing.T) {
now := time.Date(2026, 5, 18, 12, 0, 0, 0, time.UTC)
online := pgtype.Text{String: "online", Valid: true}
offline := pgtype.Text{String: "offline", Valid: true}
missing := pgtype.Text{}
tsAgo := func(d time.Duration) pgtype.Timestamptz {
return pgtype.Timestamptz{Time: now.Add(-d), Valid: true}
}
tsNone := pgtype.Timestamptz{}
cases := []struct {
name string
archived bool
runtimeStatus pgtype.Text
lastSeen pgtype.Timestamptz
hasActiveTask bool
want string
}{
{"active wins over offline runtime", false, offline, tsAgo(time.Hour), true, "working"},
{"active wins over missing runtime", false, missing, tsNone, true, "working"},
{"online runtime, no task", false, online, tsAgo(2 * time.Second), false, "idle"},
{"offline runtime, recent heartbeat", false, offline, tsAgo(2 * time.Minute), false, "unstable"},
{"offline runtime, stale heartbeat", false, offline, tsAgo(2 * time.Hour), false, "offline"},
{"offline runtime, no heartbeat", false, offline, tsNone, false, "offline"},
{"no runtime row", false, missing, tsNone, false, "offline"},
// Archived agents always report archived regardless of any leftover
// runtime row or task — they should appear in the squad listing
// but never look like they're still working or merely offline.
{"archived agent with active task", true, online, tsAgo(time.Second), true, "archived"},
{"archived agent with online runtime", true, online, tsAgo(time.Second), false, "archived"},
{"archived agent already offline", true, offline, tsAgo(time.Hour), false, "archived"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := deriveSquadMemberStatus(tc.archived, tc.runtimeStatus, tc.lastSeen, tc.hasActiveTask, now)
if got != tc.want {
t.Fatalf("deriveSquadMemberStatus = %q, want %q", got, tc.want)
}
})
}
}