210 lines
6.9 KiB
Go
210 lines
6.9 KiB
Go
package cli
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/smallnest/pigo/internal/agentcore"
|
|
)
|
|
|
|
// TestTelemetryHolderReset verifies that Reset clears the holder to "no telemetry yet".
|
|
func TestTelemetryHolderReset(t *testing.T) {
|
|
h := NewTelemetryHolder()
|
|
if h.HasTelemetry() {
|
|
t.Fatal("expected HasTelemetry() = false on new holder")
|
|
}
|
|
|
|
// Fold an event and verify it's present.
|
|
h.Fold(agentcore.TelemetryEvent{
|
|
Turns: 1,
|
|
TruncationCount: 1,
|
|
CompactionCount: 1,
|
|
ContextTokens: 100,
|
|
ContextWindow: 1000,
|
|
})
|
|
if !h.HasTelemetry() {
|
|
t.Fatal("expected HasTelemetry() = true after Fold")
|
|
}
|
|
if h.Last() == nil {
|
|
t.Fatal("expected Last() != nil after Fold")
|
|
}
|
|
if h.CumulativeTurns() != 1 {
|
|
t.Fatalf("expected CumulativeTurns() = 1, got %d", h.CumulativeTurns())
|
|
}
|
|
|
|
// Reset and verify it's cleared.
|
|
h.Reset()
|
|
if h.HasTelemetry() {
|
|
t.Fatal("expected HasTelemetry() = false after Reset")
|
|
}
|
|
if h.Last() != nil {
|
|
t.Fatal("expected Last() = nil after Reset")
|
|
}
|
|
if h.CumulativeTurns() != 0 {
|
|
t.Fatalf("expected CumulativeTurns() = 0 after Reset, got %d", h.CumulativeTurns())
|
|
}
|
|
if h.CumulativeTruncationCount() != 0 {
|
|
t.Fatalf("expected CumulativeTruncationCount() = 0 after Reset, got %d", h.CumulativeTruncationCount())
|
|
}
|
|
if h.CumulativeCompactionCount() != 0 {
|
|
t.Fatalf("expected CumulativeCompactionCount() = 0 after Reset, got %d", h.CumulativeCompactionCount())
|
|
}
|
|
if h.LatestContextTokens() != 0 {
|
|
t.Fatalf("expected LatestContextTokens() = 0 after Reset, got %d", h.LatestContextTokens())
|
|
}
|
|
if h.LatestContextWindow() != 0 {
|
|
t.Fatalf("expected LatestContextWindow() = 0 after Reset, got %d", h.LatestContextWindow())
|
|
}
|
|
}
|
|
|
|
// TestTelemetryHolderFoldTwoEvents verifies that folding two synthetic TelemetryEvents
|
|
// sums the metrics correctly.
|
|
func TestTelemetryHolderFoldTwoEvents(t *testing.T) {
|
|
h := NewTelemetryHolder()
|
|
|
|
// Fold first event.
|
|
h.Fold(agentcore.TelemetryEvent{
|
|
Turns: 2,
|
|
TruncationCount: 1,
|
|
CompactionCount: 3,
|
|
ContextTokens: 100,
|
|
ContextWindow: 1000,
|
|
ToolDurationsMs: map[string]agentcore.ToolTiming{
|
|
"foo": {Count: 1, TotalMs: 100},
|
|
"bar": {Count: 2, TotalMs: 200},
|
|
},
|
|
})
|
|
|
|
// Verify the first event is the last one.
|
|
if !h.HasTelemetry() {
|
|
t.Fatal("expected HasTelemetry() = true after first Fold")
|
|
}
|
|
last1 := h.Last()
|
|
if last1 == nil {
|
|
t.Fatal("expected Last() != nil after first Fold")
|
|
}
|
|
if last1.Turns != 2 {
|
|
t.Fatalf("expected Last().Turns = 2, got %d", last1.Turns)
|
|
}
|
|
|
|
// Verify cumulative after first event.
|
|
if h.CumulativeTurns() != 2 {
|
|
t.Fatalf("expected CumulativeTurns() = 2 after first Fold, got %d", h.CumulativeTurns())
|
|
}
|
|
if h.CumulativeTruncationCount() != 1 {
|
|
t.Fatalf("expected CumulativeTruncationCount() = 1 after first Fold, got %d", h.CumulativeTruncationCount())
|
|
}
|
|
if h.CumulativeCompactionCount() != 3 {
|
|
t.Fatalf("expected CumulativeCompactionCount() = 3 after first Fold, got %d", h.CumulativeCompactionCount())
|
|
}
|
|
if h.LatestContextTokens() != 100 {
|
|
t.Fatalf("expected LatestContextTokens() = 100 after first Fold, got %d", h.LatestContextTokens())
|
|
}
|
|
if h.LatestContextWindow() != 1000 {
|
|
t.Fatalf("expected LatestContextWindow() = 1000 after first Fold, got %d", h.LatestContextWindow())
|
|
}
|
|
tools1 := h.CumulativeToolDurations()
|
|
if tools1["foo"].Count != 1 || tools1["foo"].TotalMs != 100 {
|
|
t.Fatalf("expected foo tool timing {1, 100}, got %+v", tools1["foo"])
|
|
}
|
|
if tools1["bar"].Count != 2 || tools1["bar"].TotalMs != 200 {
|
|
t.Fatalf("expected bar tool timing {2, 200}, got %+v", tools1["bar"])
|
|
}
|
|
|
|
// Fold second event.
|
|
h.Fold(agentcore.TelemetryEvent{
|
|
Turns: 3,
|
|
TruncationCount: 2,
|
|
CompactionCount: 1,
|
|
ContextTokens: 150,
|
|
ContextWindow: 1000,
|
|
ToolDurationsMs: map[string]agentcore.ToolTiming{
|
|
"foo": {Count: 2, TotalMs: 250},
|
|
"baz": {Count: 1, TotalMs: 50},
|
|
},
|
|
})
|
|
|
|
// Verify the second event is now the last one.
|
|
last2 := h.Last()
|
|
if last2 == nil {
|
|
t.Fatal("expected Last() != nil after second Fold")
|
|
}
|
|
if last2.Turns != 3 {
|
|
t.Fatalf("expected Last().Turns = 3, got %d", last2.Turns)
|
|
}
|
|
|
|
// Verify cumulative after second event (sums of both).
|
|
if h.CumulativeTurns() != 5 {
|
|
t.Fatalf("expected CumulativeTurns() = 5 after second Fold, got %d", h.CumulativeTurns())
|
|
}
|
|
if h.CumulativeTruncationCount() != 3 {
|
|
t.Fatalf("expected CumulativeTruncationCount() = 3 after second Fold, got %d", h.CumulativeTruncationCount())
|
|
}
|
|
if h.CumulativeCompactionCount() != 4 {
|
|
t.Fatalf("expected CumulativeCompactionCount() = 4 after second Fold, got %d", h.CumulativeCompactionCount())
|
|
}
|
|
if h.LatestContextTokens() != 150 {
|
|
t.Fatalf("expected LatestContextTokens() = 150 after second Fold, got %d", h.LatestContextTokens())
|
|
}
|
|
if h.LatestContextWindow() != 1000 {
|
|
t.Fatalf("expected LatestContextWindow() = 1000 after second Fold, got %d", h.LatestContextWindow())
|
|
}
|
|
tools2 := h.CumulativeToolDurations()
|
|
if tools2["foo"].Count != 3 || tools2["foo"].TotalMs != 350 {
|
|
t.Fatalf("expected foo tool timing {3, 350} after sum, got %+v", tools2["foo"])
|
|
}
|
|
if tools2["bar"].Count != 2 || tools2["bar"].TotalMs != 200 {
|
|
t.Fatalf("expected bar tool timing {2, 200} unchanged, got %+v", tools2["bar"])
|
|
}
|
|
if tools2["baz"].Count != 1 || tools2["baz"].TotalMs != 50 {
|
|
t.Fatalf("expected baz tool timing {1, 50} after sum, got %+v", tools2["baz"])
|
|
}
|
|
}
|
|
|
|
// TestTelemetryHolderCumulativeContextUtilization verifies context utilization calculation.
|
|
func TestTelemetryHolderCumulativeContextUtilization(t *testing.T) {
|
|
h := NewTelemetryHolder()
|
|
|
|
// With unknown window (0), utilization should be 0.
|
|
h.Fold(agentcore.TelemetryEvent{
|
|
ContextTokens: 100,
|
|
ContextWindow: 0,
|
|
})
|
|
if h.CumulativeContextUtilization() != 0 {
|
|
t.Fatalf("expected utilization = 0 with unknown window, got %f", h.CumulativeContextUtilization())
|
|
}
|
|
|
|
// With known window, utilization should be tokens/window.
|
|
h.Fold(agentcore.TelemetryEvent{
|
|
ContextTokens: 500,
|
|
ContextWindow: 1000,
|
|
})
|
|
if h.CumulativeContextUtilization() != 0.5 {
|
|
t.Fatalf("expected utilization = 0.5, got %f", h.CumulativeContextUtilization())
|
|
}
|
|
}
|
|
|
|
// TestTelemetryHolderToolDurationsImmutable verifies that CumulativeToolDurations returns
|
|
// a copy to prevent external mutation.
|
|
func TestTelemetryHolderToolDurationsImmutable(t *testing.T) {
|
|
h := NewTelemetryHolder()
|
|
h.Fold(agentcore.TelemetryEvent{
|
|
ToolDurationsMs: map[string]agentcore.ToolTiming{
|
|
"test": {Count: 1, TotalMs: 100},
|
|
},
|
|
})
|
|
|
|
// Get the tool durations and mutate the returned map.
|
|
tools := h.CumulativeToolDurations()
|
|
tools["test"] = agentcore.ToolTiming{Count: 999, TotalMs: 9999}
|
|
tools["new"] = agentcore.ToolTiming{Count: 1, TotalMs: 1}
|
|
|
|
// Verify the internal state was not modified.
|
|
tools2 := h.CumulativeToolDurations()
|
|
if tools2["test"].Count != 1 || tools2["test"].TotalMs != 100 {
|
|
t.Fatalf("expected internal tool timing to remain {1, 100}, got %+v", tools2["test"])
|
|
}
|
|
if _, ok := tools2["new"]; ok {
|
|
t.Fatal("expected 'new' tool not to be present in internal state")
|
|
}
|
|
}
|