Files
BlackBean/pigo/internal/cli/status/status_test.go
T
2026-08-14 23:41:57 +08:00

255 lines
7.7 KiB
Go

package status
import (
"bytes"
"strings"
"testing"
"github.com/smallnest/pigo/internal/agentcore"
"github.com/smallnest/pigo/internal/cli"
"github.com/smallnest/pigo/internal/compaction"
"github.com/smallnest/pigo/internal/runtime"
)
func TestRunStatus(t *testing.T) {
host := newFakeHost()
host.live.Model = "test-model"
host.live.ProviderName = "test-provider"
host.live.BaseURL = "https://api.example.com"
host.live.Protocol = "anthropic"
host.live.ContextWindow = 128000
host.agentCtx.Messages = append(host.agentCtx.Messages,
agentcore.UserMessage{RoleField: agentcore.RoleUser, Content: agentcore.ContentList{agentcore.NewTextContent("hello")}},
)
var buf bytes.Buffer
RunStatus(&buf, host)
output := buf.String()
if !strings.Contains(output, "runtime config:") {
t.Error("expected output to contain 'runtime config:'")
}
if !strings.Contains(output, "model: test-model") {
t.Error("expected output to contain 'model: test-model'")
}
if !strings.Contains(output, "provider: test-provider") {
t.Error("expected output to contain 'provider: test-provider'")
}
if !strings.Contains(output, "base URL: https://api.example.com") {
t.Error("expected output to contain 'base URL: https://api.example.com'")
}
if !strings.Contains(output, "protocol: anthropic") {
t.Error("expected output to contain 'protocol: anthropic'")
}
if !strings.Contains(output, "context window: 128000 tokens") {
t.Error("expected output to contain 'context window: 128000 tokens'")
}
if !strings.Contains(output, "context:") {
t.Error("expected output to contain 'context:'")
}
if !strings.Contains(output, "current:") {
t.Error("expected output to contain 'current:'")
}
if !strings.Contains(output, "utilization:") {
t.Error("expected output to contain 'utilization:'")
}
if !strings.Contains(output, "compactions: 0") {
t.Error("expected output to contain 'compactions: 0'")
}
if !strings.Contains(output, "before compact:") {
t.Error("expected output to contain 'before compact:'")
}
}
func TestRunStatusWithCompaction(t *testing.T) {
host := newFakeHost()
host.live.ContextWindow = 128000
host.agentCtx.Messages = append(host.agentCtx.Messages,
agentcore.UserMessage{RoleField: agentcore.RoleUser, Content: agentcore.ContentList{agentcore.NewTextContent("hello")}},
agentcore.CompactionMessage{Summary: "compacted history"},
agentcore.UserMessage{RoleField: agentcore.RoleUser, Content: agentcore.ContentList{agentcore.NewTextContent("more")}},
)
var buf bytes.Buffer
RunStatus(&buf, host)
output := buf.String()
if !strings.Contains(output, "compactions: 1") {
t.Error("expected output to contain 'compactions: 1'")
}
}
func TestRunStatusUnknownContextWindow(t *testing.T) {
host := newFakeHost()
host.live.ContextWindow = 0 // unknown
var buf bytes.Buffer
RunStatus(&buf, host)
output := buf.String()
if !strings.Contains(output, "context window: unknown") {
t.Error("expected output to contain 'context window: unknown'")
}
if !strings.Contains(output, "auto-compaction disabled") {
t.Error("expected output to contain 'auto-compaction disabled'")
}
}
func TestBeforeCompactCalculation(t *testing.T) {
reserve := compaction.DefaultCompactionSettings.ReserveTokens
if reserve != 16384 {
t.Errorf("expected reserve tokens to be 16384, got %d", reserve)
}
contextWindow := 128000
threshold := contextWindow - reserve
if threshold != 128000-16384 {
t.Errorf("expected threshold to be 128000-16384=%d, got %d", 128000-16384, threshold)
}
}
func TestRunStatusEnvAndCreds(t *testing.T) {
host := newFakeHost()
host.cwd = "/tmp/test-cwd"
host.live.ProviderName = "test-provider"
host.live.BaseURL = "https://api.example.com"
host.slash.AddSkill(runtime.SlashCommand{Name: "my-skill", Expand: func(string) string { return "" }})
host.slash.AddPlugin(runtime.SlashCommand{Name: "my-plugin", Run: func(string) (string, string) { return "", "" }})
host.creds.SetOverride("test-provider", "sk-secretkey-wxyz")
var buf bytes.Buffer
RunStatus(&buf, host)
output := buf.String()
if !strings.Contains(output, "project & environment:") {
t.Error("expected 'project & environment:' section")
}
if !strings.Contains(output, "cwd: /tmp/test-cwd") {
t.Error("expected 'cwd: /tmp/test-cwd'")
}
if !strings.Contains(output, "trust: disabled") {
t.Error("expected 'trust: disabled' when trust manager is nil")
}
if !strings.Contains(output, "skills: 1 (my-skill)") {
t.Error("expected 'skills: 1 (my-skill)'")
}
if !strings.Contains(output, "plugins: 1 (my-plugin)") {
t.Error("expected 'plugins: 1 (my-plugin)'")
}
if !strings.Contains(output, "credentials & connectivity:") {
t.Error("expected 'credentials & connectivity:' section")
}
if !strings.Contains(output, "api key: set") {
t.Error("expected 'api key: set'")
}
if !strings.Contains(output, "••••wxyz") {
t.Error("expected masked key '••••wxyz'")
}
if strings.Contains(output, "sk-secretkey-wxyz") {
t.Error("full API key leaked into /status output")
}
if !strings.Contains(output, "endpoint: https://api.example.com") {
t.Error("expected 'endpoint: https://api.example.com'")
}
}
func TestRunStatusTelemetryNoData(t *testing.T) {
host := newFakeHost()
// host.telemetry is nil.
var buf bytes.Buffer
RunStatus(&buf, host)
output := buf.String()
if !strings.Contains(output, "telemetry:") {
t.Error("expected 'telemetry:' section")
}
if n := strings.Count(output, "no telemetry yet"); n != 2 {
t.Errorf("expected 2 'no telemetry yet' (cumulative + last run), got %d", n)
}
}
func TestRunStatusTelemetryPopulated(t *testing.T) {
host := newFakeHost()
host.live.ContextWindow = 128000
holder := cli.NewTelemetryHolder()
holder.Fold(agentcore.TelemetryEvent{
Turns: 3,
TruncationCount: 1,
CompactionCount: 0,
ContextUtilization: 0.42,
ContextTokens: 53760,
ContextWindow: 128000,
ToolDurationsMs: map[string]agentcore.ToolTiming{
"bash": {Count: 2, TotalMs: 150},
"read": {Count: 4, TotalMs: 80},
},
})
host.telemetry = holder
var buf bytes.Buffer
RunStatus(&buf, host)
output := buf.String()
if strings.Contains(output, "no telemetry yet") {
t.Error("did not expect 'no telemetry yet' when telemetry is populated")
}
if !strings.Contains(output, "since session start:") {
t.Error("expected 'since session start:' cumulative block")
}
if !strings.Contains(output, "last run:") {
t.Error("expected 'last run:' block")
}
if !strings.Contains(output, "turns: 3") {
t.Error("expected 'turns: 3' (last run == cumulative after one run)")
}
if !strings.Contains(output, "bash") || !strings.Contains(output, "2 calls") || !strings.Contains(output, "150ms") {
t.Error("expected bash tool row with '2 calls' / '150ms'")
}
if !strings.Contains(output, "utilization: 42%") {
t.Error("expected 'utilization: 42%'")
}
holder.Fold(agentcore.TelemetryEvent{
Turns: 2,
TruncationCount: 0,
CompactionCount: 1,
ContextUtilization: 0.5,
ContextTokens: 64000,
ContextWindow: 128000,
ToolDurationsMs: map[string]agentcore.ToolTiming{
"bash": {Count: 1, TotalMs: 40},
},
})
buf.Reset()
RunStatus(&buf, host)
output = buf.String()
if !strings.Contains(output, "turns: 5") {
t.Error("expected cumulative 'turns: 5' after two runs")
}
if !strings.Contains(output, "turns: 2") {
t.Error("expected last-run 'turns: 2' after two runs")
}
}
func TestMaskKey(t *testing.T) {
cases := []struct{ in, want string }{
{"sk-secretkey-wxyz", "••••wxyz"},
{"abcd", "••••"}, // exactly 4 -> masked entirely
{"ab", "••"},
{"", ""},
}
for _, c := range cases {
if got := maskKey(c.in); got != c.want {
t.Errorf("maskKey(%q) = %q, want %q", c.in, got, c.want)
}
}
}