first commit
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
// This file holds the end-to-end and edge-case tests for /status (US-005, #295):
|
||||
// fresh-session behavior, model-switch reflection, telemetry reset on session
|
||||
// switch, and render timing, exercised through direct RunStatus calls with a
|
||||
// fake host. The REPL-intercept and headless-flag tests live in package main.
|
||||
package status
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/smallnest/pigo/internal/agentcore"
|
||||
"github.com/smallnest/pigo/internal/cli"
|
||||
)
|
||||
|
||||
// TestStatusFreshSessionAllSections verifies /status renders every section on a
|
||||
// brand-new session before any model turn, with "no telemetry yet", and does not
|
||||
// panic.
|
||||
func TestStatusFreshSessionAllSections(t *testing.T) {
|
||||
host := newFakeHost()
|
||||
host.cwd = "/tmp/e2e-fresh"
|
||||
|
||||
var buf bytes.Buffer
|
||||
RunStatus(&buf, host)
|
||||
output := buf.String()
|
||||
|
||||
for _, want := range []string{
|
||||
"runtime config:",
|
||||
"context:",
|
||||
"project & environment:",
|
||||
"credentials & connectivity:",
|
||||
"telemetry:",
|
||||
"no telemetry yet",
|
||||
} {
|
||||
if !strings.Contains(output, want) {
|
||||
t.Errorf("fresh-session /status: expected output to contain %q", want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestStatusReflectsModelSwitch verifies /status reads the live run config each
|
||||
// invocation, so a /model switch (which mutates live.Model/providerName) is
|
||||
// reflected on the next /status without a restart.
|
||||
func TestStatusReflectsModelSwitch(t *testing.T) {
|
||||
host := newFakeHost()
|
||||
host.live.Model = "model-a"
|
||||
host.live.ProviderName = "prov-a"
|
||||
|
||||
var buf bytes.Buffer
|
||||
RunStatus(&buf, host)
|
||||
if out := buf.String(); !strings.Contains(out, "model: model-a") || !strings.Contains(out, "provider: prov-a") {
|
||||
t.Errorf("expected model-a/prov-a, got:\n%s", out)
|
||||
}
|
||||
|
||||
// Simulate a /model switch mutating the live config.
|
||||
host.live.Model = "model-b"
|
||||
host.live.ProviderName = "prov-b"
|
||||
buf.Reset()
|
||||
RunStatus(&buf, host)
|
||||
out := buf.String()
|
||||
if !strings.Contains(out, "model: model-b") || !strings.Contains(out, "provider: prov-b") {
|
||||
t.Errorf("expected model-b/prov-b after switch, got:\n%s", out)
|
||||
}
|
||||
if strings.Contains(out, "model: model-a") {
|
||||
t.Errorf("stale model-a still present after switch:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
// TestStatusTelemetryResetOnFork verifies that after the telemetry holder is
|
||||
// reset (as runForkClone/runImport do on /fork, /clone, /import - wired in
|
||||
// #291), /status shows "no telemetry yet" again, so cumulative stats do not
|
||||
// bleed across conversations.
|
||||
func TestStatusTelemetryResetOnFork(t *testing.T) {
|
||||
host := newFakeHost()
|
||||
|
||||
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}},
|
||||
})
|
||||
host.telemetry = holder
|
||||
|
||||
var buf bytes.Buffer
|
||||
RunStatus(&buf, host)
|
||||
if out := buf.String(); !strings.Contains(out, "turns: 3") {
|
||||
t.Errorf("expected 'turns: 3' before reset, got:\n%s", out)
|
||||
}
|
||||
|
||||
// /fork, /clone, /import all call holder.Reset() (runForkClone/runImport).
|
||||
holder.Reset()
|
||||
buf.Reset()
|
||||
RunStatus(&buf, host)
|
||||
out := buf.String()
|
||||
if n := strings.Count(out, "no telemetry yet"); n != 2 {
|
||||
t.Errorf("expected 2 'no telemetry yet' after reset (cumulative + last run), got %d:\n%s", n, out)
|
||||
}
|
||||
if strings.Contains(out, "turns: 3") {
|
||||
t.Errorf("stale telemetry after reset:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
// TestStatusTiming verifies /status renders fast (target <50ms; assert <100ms
|
||||
// to absorb CI runner variance). It is pure in-memory rendering - no disk or
|
||||
// network I/O on the hot path.
|
||||
func TestStatusTiming(t *testing.T) {
|
||||
host := newFakeHost()
|
||||
host.cwd = "/tmp/e2e-timing"
|
||||
host.live.ContextWindow = 128000
|
||||
host.agentCtx.Messages = append(host.agentCtx.Messages,
|
||||
agentcore.UserMessage{RoleField: agentcore.RoleUser, Content: agentcore.ContentList{agentcore.NewTextContent("hello")}},
|
||||
)
|
||||
holder := cli.NewTelemetryHolder()
|
||||
holder.Fold(agentcore.TelemetryEvent{
|
||||
Turns: 5,
|
||||
ContextUtilization: 0.5,
|
||||
ContextTokens: 64000,
|
||||
ContextWindow: 128000,
|
||||
ToolDurationsMs: map[string]agentcore.ToolTiming{"bash": {Count: 3, TotalMs: 210}, "read": {Count: 6, TotalMs: 90}},
|
||||
})
|
||||
host.telemetry = holder
|
||||
|
||||
// Warm once (allocs), then measure.
|
||||
var buf bytes.Buffer
|
||||
RunStatus(&buf, host)
|
||||
buf.Reset()
|
||||
|
||||
start := time.Now()
|
||||
RunStatus(&buf, host)
|
||||
elapsed := time.Since(start)
|
||||
if elapsed >= 100*time.Millisecond {
|
||||
t.Errorf("/status render took %v, want <100ms (target <50ms)", elapsed)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user