package main // store_stats_test.go — SessionStats/StatsTotals aggregation over agent_end. import ( "testing" ) func appendAgentEnd(t *testing.T, s *Store, sessionID string, seq int64, payload string) { t.Helper() if err := s.AppendEvent(Event{ SessionID: sessionID, Seq: seq, TS: 1, Type: evAgentEnd, Payload: []byte(payload), }); err != nil { t.Fatalf("append agent_end %s/%d: %v", sessionID, seq, err) } } func TestStoreSessionStats(t *testing.T) { store := openTestStore(t) appendAgentEnd(t, store, "s1", 1, `{"usage":{"inputTokens":100,"outputTokens":50,"totalCost":0.25}}`) appendAgentEnd(t, store, "s1", 2, `{"usage":{"inputTokens":30,"outputTokens":20,"totalCost":0.5}}`) appendAgentEnd(t, store, "s1", 3, `{}`) // no usage → zeros appendAgentEnd(t, store, "s1", 4, `{"usage":{"inputTokens":7}}`) // partial usage if err := store.AppendEvent(Event{SessionID: "s1", Seq: 5, TS: 1, Type: evMessageEnd, Payload: []byte(`{}`)}); err != nil { t.Fatalf("append message_end: %v", err) } stats, err := store.SessionStats("s1") if err != nil { t.Fatalf("session stats: %v", err) } if stats.Turns != 4 || stats.InputTokens != 137 || stats.OutputTokens != 70 || stats.TotalCost != 0.75 { t.Fatalf("stats = %+v, want turns 4, input 137, output 70, cost 0.75", stats) } unknown, err := store.SessionStats("nope") if err != nil { t.Fatalf("unknown session stats: %v", err) } if unknown != (UsageStats{}) { t.Fatalf("unknown session stats = %+v, want zeros", unknown) } } func TestStoreSessionStatsCorruptPayloadCountsTurn(t *testing.T) { store := openTestStore(t) appendAgentEnd(t, store, "s1", 1, `{"usage":{"inputTokens":5,"outputTokens":5,"totalCost":0.1}}`) if err := store.AppendEvent(Event{SessionID: "s1", Seq: 2, TS: 1, Type: evAgentEnd, Payload: []byte(`not-json`)}); err != nil { t.Fatalf("append corrupt: %v", err) } stats, err := store.SessionStats("s1") if err != nil { t.Fatalf("stats: %v", err) } if stats.Turns != 2 || stats.InputTokens != 5 || stats.OutputTokens != 5 || stats.TotalCost != 0.1 { t.Fatalf("stats = %+v, want turns 2 with usage only from row 1", stats) } } func TestStoreStatsTotals(t *testing.T) { store := openTestStore(t) empty, err := store.StatsTotals() if err != nil { t.Fatalf("empty totals: %v", err) } if empty != (StatsTotals{}) { t.Fatalf("empty totals = %+v, want zeros", empty) } appendAgentEnd(t, store, "s1", 1, `{"usage":{"inputTokens":1000,"outputTokens":200,"totalCost":1.5}}`) appendAgentEnd(t, store, "s1", 2, `{}`) appendAgentEnd(t, store, "s2", 1, `{"usage":{"inputTokens":40,"outputTokens":60,"totalCost":0.25}}`) if err := store.AppendEvent(Event{SessionID: "s2", Seq: 2, TS: 1, Type: evAgentSettled, Payload: []byte(`{}`)}); err != nil { t.Fatalf("append settled: %v", err) } totals, err := store.StatsTotals() if err != nil { t.Fatalf("totals: %v", err) } want := StatsTotals{UsageStats: UsageStats{Turns: 3, InputTokens: 1040, OutputTokens: 260, TotalCost: 1.75}, SessionsCount: 2} if totals != want { t.Fatalf("totals = %+v, want %+v", totals, want) } }