Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

205 lines
5.6 KiB
Go

package main
import (
"bytes"
"io"
"os"
"strings"
"sync"
"testing"
"time"
"github.com/zzet/gortex/internal/savings"
)
// captureStdout runs fn with os.Stdout redirected to a pipe and returns
// whatever fn wrote. Tests use this to assert against the dashboard
// output without touching the real terminal.
func captureStdout(t *testing.T, fn func()) string {
t.Helper()
orig := os.Stdout
r, w, err := os.Pipe()
if err != nil {
t.Fatalf("pipe: %v", err)
}
os.Stdout = w
var buf bytes.Buffer
var wg sync.WaitGroup
wg.Go(func() {
_, _ = io.Copy(&buf, r)
})
defer func() {
os.Stdout = orig
}()
fn()
_ = w.Close()
wg.Wait()
return buf.String()
}
func TestPickHeadlineCost_RespectsModelFlag(t *testing.T) {
costs := map[string]float64{
"claude-opus-4": 0.50,
"claude-sonnet-4": 0.10,
"claude-haiku-4.5": 0.03,
}
val, name := pickHeadlineCost(costs, "claude-sonnet-4")
if name != "claude-sonnet-4" || val != 0.10 {
t.Errorf("pickHeadlineCost(preferred=sonnet) = (%.2f, %q), want (0.10, claude-sonnet-4)", val, name)
}
}
func TestPickHeadlineCost_DefaultsToMostExpensive(t *testing.T) {
costs := map[string]float64{
"claude-opus-4": 0.50,
"claude-sonnet-4": 0.10,
"gpt-4o-mini": 0.005,
}
val, name := pickHeadlineCost(costs, "")
if name != "claude-opus-4" || val != 0.50 {
t.Errorf("pickHeadlineCost(default) = (%.2f, %q), want (0.50, claude-opus-4)", val, name)
}
}
func TestPickHeadlineCost_AllZeros(t *testing.T) {
costs := map[string]float64{"a": 0, "b": 0, "c": 0}
val, name := pickHeadlineCost(costs, "")
if val != 0 {
t.Errorf("expected 0 cost for all-zero map, got %.4f", val)
}
if name == "" || name == "n/a" {
t.Errorf("expected a deterministic model name for all-zero map, got %q", name)
}
}
func TestPickHeadlineCost_Empty(t *testing.T) {
val, name := pickHeadlineCost(nil, "")
if val != 0 || name != "n/a" {
t.Errorf("pickHeadlineCost(nil) = (%.2f, %q), want (0, n/a)", val, name)
}
}
func TestFormatUSD(t *testing.T) {
cases := []struct {
in float64
want string
}{
{0, "$0.0000"},
{0.00125, "$0.0013"},
{0.99, "$0.9900"},
{1.00, "$1.00"},
{167.808, "$167.81"},
}
for _, c := range cases {
got := formatUSD(c.in)
if got != c.want {
t.Errorf("formatUSD(%.4f) = %q, want %q", c.in, got, c.want)
}
}
}
func TestHumanInt(t *testing.T) {
cases := []struct {
in int64
want string
}{
{0, "0"},
{42, "42"},
{999, "999"},
{1000, "1,000"},
{12345, "12,345"},
{1234567, "1,234,567"},
{-1234, "-1,234"},
}
for _, c := range cases {
got := humanInt(c.in)
if got != c.want {
t.Errorf("humanInt(%d) = %q, want %q", c.in, got, c.want)
}
}
}
// TestEmitSavingsDashboard_RendersThreeBuckets is a smoke test for the
// dashboard layout: it captures stdout and asserts each bucket label and a
// representative bar cell show up, so silent regressions in the renderer
// (e.g. a missing bucket, no bar cells written) get caught.
func TestEmitSavingsDashboard_RendersThreeBuckets(t *testing.T) {
now := time.Now().UTC()
snap := savings.File{
FirstSeen: now.Add(-72 * time.Hour),
LastUpdated: now,
Totals: savings.Totals{TokensSaved: 1_000_000, TokensReturned: 200_000, CallsCounted: 50},
PerRepo: map[string]*savings.Totals{
"/repo-a": {TokensSaved: 600_000, TokensReturned: 100_000, CallsCounted: 30},
"/repo-b": {TokensSaved: 400_000, TokensReturned: 100_000, CallsCounted: 20},
},
}
buckets := []savings.Bucket{
{Label: "Today", Totals: savings.Totals{TokensSaved: 5_000, TokensReturned: 1_000, CallsCounted: 4},
PerTool: []savings.ToolTotal{
{Tool: "get_symbol_source", Totals: savings.Totals{TokensSaved: 4_000, TokensReturned: 800, CallsCounted: 3}},
{Tool: "smart_context", Totals: savings.Totals{TokensSaved: 1_000, TokensReturned: 200, CallsCounted: 1}},
}},
{Label: "Last 7 days", Totals: savings.Totals{TokensSaved: 50_000, TokensReturned: 10_000, CallsCounted: 12}},
{Label: "All time", Totals: snap.Totals},
}
out := captureStdout(t, func() {
// Force a known cell width and verbose for both code paths.
oldVerbose := savingsVerbose
oldCells := savingsBarCells
savingsVerbose = true
savingsBarCells = 16
defer func() {
savingsVerbose = oldVerbose
savingsBarCells = oldCells
}()
emitSavingsDashboard(snap, buckets, nil, nil, "/tmp/sidecar.sqlite")
})
for _, want := range []string{
"Gortex Token Savings",
"Cost avoided per model (all time):",
"Today",
"Last 7 days",
"All time",
"█", // at least one filled bar cell
"░", // at least one empty bar cell
"get_symbol_source", // verbose per-tool table
"smart_context",
"Cost avoided per model (all time):",
"Per-repo totals (all time):",
"/repo-a",
} {
if !strings.Contains(out, want) {
t.Errorf("dashboard output missing %q\n----\n%s\n----", want, out)
}
}
}
func TestEmitSavingsDashboard_EmptyTotals(t *testing.T) {
snap := savings.File{}
buckets := []savings.Bucket{
{Label: "Today"},
{Label: "Last 7 days"},
{Label: "All time"},
}
out := captureStdout(t, func() {
emitSavingsDashboard(snap, buckets, nil, nil, "/tmp/sidecar.sqlite")
})
if !strings.Contains(out, "No source-reading tool calls recorded yet") {
t.Errorf("empty totals should show the no-data hint, got:\n%s", out)
}
// Should not print any bucket rows when there's no data.
if strings.Contains(out, "█") {
t.Errorf("empty totals should not render bars, got:\n%s", out)
}
// An empty ledger has never tracked anything — the dashboard must not
// claim a "tracking since" moment (the zero FirstSeen stays hidden).
if strings.Contains(out, "Tracking since") {
t.Errorf("empty ledger must not print a tracking-since line, got:\n%s", out)
}
}