ajhahn.de
← eeco
Go 77 lines
package ai

import (
	"os"
	"path/filepath"
	"testing"
)

// writeLedger drops a crafted ai-calls.json into stateDir. The body is the
// frozen on-disk shape, so hand-authoring it is safe.
func writeLedger(t *testing.T, stateDir, body string) {
	t.Helper()
	if err := os.MkdirAll(stateDir, 0o755); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(stateDir, AICallsFilename), []byte(body), 0o644); err != nil {
		t.Fatal(err)
	}
}

func TestSummarize_Aggregates(t *testing.T) {
	dir := t.TempDir()
	writeLedger(t, dir, `{"records":[
		{"label":"a","provider":"anthropic","ran":true,"parked":false,
		 "tokens":{"input":1000,"cached_input":200,"output":500},"ts":"2026-05-21T10:00:00Z"},
		{"label":"b","provider":"cli","ran":true,"parked":false,
		 "tokens":{"input":2000,"cached_input":0,"output":800},"ts":"2026-05-30T12:00:00Z"},
		{"label":"c","provider":"anthropic","ran":false,"parked":true,
		 "tokens":{"input":0,"cached_input":0,"output":0},"ts":"2026-05-25T09:00:00Z"}
	]}`)

	s := Summarize(dir)
	if s.TotalCalls != 3 {
		t.Errorf("TotalCalls = %d, want 3", s.TotalCalls)
	}
	if s.Ran != 2 {
		t.Errorf("Ran = %d, want 2", s.Ran)
	}
	if s.Parked != 1 {
		t.Errorf("Parked = %d, want 1", s.Parked)
	}
	if s.Tokens.Input != 3000 || s.Tokens.CachedInput != 200 || s.Tokens.Output != 1300 {
		t.Errorf("Tokens = %+v, want {3000 200 1300}", s.Tokens)
	}
	if got := s.Tokens.Total(); got != 4500 {
		t.Errorf("Tokens.Total() = %d, want 4500", got)
	}
	if s.ByProvider["anthropic"] != 2 || s.ByProvider["cli"] != 1 {
		t.Errorf("ByProvider = %v, want anthropic:2 cli:1", s.ByProvider)
	}
	if s.FirstTS != "2026-05-21T10:00:00Z" {
		t.Errorf("FirstTS = %q, want 2026-05-21T10:00:00Z", s.FirstTS)
	}
	if s.LastTS != "2026-05-30T12:00:00Z" {
		t.Errorf("LastTS = %q, want 2026-05-30T12:00:00Z", s.LastTS)
	}
}

func TestSummarize_MissingFile(t *testing.T) {
	s := Summarize(t.TempDir())
	if s.TotalCalls != 0 || s.Tokens.Total() != 0 || len(s.ByProvider) != 0 {
		t.Errorf("missing ledger should be the zero summary, got %+v", s)
	}
	if s.FirstTS != "" || s.LastTS != "" {
		t.Errorf("missing ledger should have empty range, got first=%q last=%q", s.FirstTS, s.LastTS)
	}
}

func TestSummarize_EmptyRecords(t *testing.T) {
	dir := t.TempDir()
	writeLedger(t, dir, `{"records":[]}`)
	s := Summarize(dir)
	if s.TotalCalls != 0 || s.Tokens.Total() != 0 {
		t.Errorf("empty ledger should be the zero summary, got %+v", s)
	}
}