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
127 lines
4.7 KiB
Go
127 lines
4.7 KiB
Go
package review
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/zzet/gortex/internal/llm"
|
|
)
|
|
|
|
// TestCostFromUsage_TokenSplitAndUSD asserts the breakdown carries the
|
|
// exact token split and a USD figure computed from the price card.
|
|
func TestCostFromUsage_TokenSplitAndUSD(t *testing.T) {
|
|
usage := llm.TokenUsage{InputTokens: 1234, OutputTokens: 456, CacheReadTokens: 2000, CacheWriteTokens: 300}
|
|
price := llm.ProviderPricing{Input: 3.0, Output: 15.0} // USD per 1M tokens
|
|
|
|
cb := CostFromUsage(usage, price, 4210)
|
|
|
|
require.Equal(t, 1234, cb.InputTokens)
|
|
require.Equal(t, 456, cb.OutputTokens)
|
|
require.Equal(t, 2000, cb.CacheReadTokens)
|
|
require.Equal(t, 300, cb.CacheWriteTokens)
|
|
require.Equal(t, int64(4210), cb.ElapsedMs)
|
|
require.True(t, cb.Estimated, "a non-zero usage is a grounded estimate")
|
|
|
|
// 1234 * 3.0/1e6 + 456 * 15.0/1e6
|
|
wantUSD := (1234*3.0 + 456*15.0) / 1_000_000.0
|
|
require.InDelta(t, wantUSD, cb.USD, 1e-12)
|
|
require.Greater(t, cb.USD, 0.0, "a priced custom provider yields a nonzero USD")
|
|
}
|
|
|
|
// TestCostFromUsage_ZeroUsageNotEstimated asserts a no-usage provider
|
|
// still yields a (zero) cost block flagged Estimated:false.
|
|
func TestCostFromUsage_ZeroUsageNotEstimated(t *testing.T) {
|
|
cb := CostFromUsage(llm.TokenUsage{}, llm.ProviderPricing{Input: 3.0, Output: 15.0}, 1000)
|
|
require.False(t, cb.Estimated)
|
|
require.Equal(t, 0.0, cb.USD)
|
|
require.Equal(t, int64(1000), cb.ElapsedMs)
|
|
}
|
|
|
|
// TestAttributeFindingTokens_SumsToTotal asserts output tokens are split
|
|
// across findings proportionally and conserve the total (±rounding folded
|
|
// into the last finding).
|
|
func TestAttributeFindingTokens_SumsToTotal(t *testing.T) {
|
|
findings := []Finding{
|
|
{Message: "short", Body: "tiny body"},
|
|
{Message: "a much longer finding message", Body: "a considerably longer body with more tokens to weigh"},
|
|
{Message: "mid", Body: "medium body here"},
|
|
}
|
|
total := llm.TokenUsage{OutputTokens: 1000}
|
|
|
|
AttributeFindingTokens(findings, total)
|
|
|
|
sum := 0
|
|
for _, f := range findings {
|
|
require.GreaterOrEqual(t, f.GenTokens, 0)
|
|
sum += f.GenTokens
|
|
}
|
|
require.Equal(t, total.OutputTokens, sum, "per-finding shares must sum to the output total")
|
|
// The longest finding must get at least as many tokens as the shortest.
|
|
require.GreaterOrEqual(t, findings[1].GenTokens, findings[0].GenTokens)
|
|
}
|
|
|
|
func TestAttributeFindingTokens_NoOpOnEmpty(t *testing.T) {
|
|
// Nothing to attribute — must not panic and must leave GenTokens zero.
|
|
AttributeFindingTokens(nil, llm.TokenUsage{OutputTokens: 100})
|
|
f := []Finding{{Message: "x"}}
|
|
AttributeFindingTokens(f, llm.TokenUsage{OutputTokens: 0})
|
|
require.Equal(t, 0, f[0].GenTokens)
|
|
}
|
|
|
|
// TestRunWithUsage_SurfacesCostBreakdown asserts a review driven through
|
|
// the usage-aware seam attaches a CostBreakdown to the report, with the
|
|
// summed usage, USD from the price card, and per-finding GenTokens.
|
|
func TestRunWithUsage_SurfacesCostBreakdown(t *testing.T) {
|
|
reply := `[
|
|
{"file":"app/svc.go","snippet":"return p.Balance","message":"possible nil dereference of p","severity":"critical","category":"correctness"}
|
|
]`
|
|
usage := llm.TokenUsage{InputTokens: 800, OutputTokens: 200, CacheReadTokens: 100}
|
|
gen := func(_ context.Context, _ string, _ int) (string, llm.TokenUsage, error) {
|
|
return reply, usage, nil
|
|
}
|
|
price := llm.ProviderPricing{Input: 3.0, Output: 15.0}
|
|
|
|
report, err := RunWithUsage(context.Background(), nil, gen, price, Options{
|
|
RepoRoot: "/tmp/repo",
|
|
Diff: sampleDiff,
|
|
Rules: testResolver(t),
|
|
UseLLM: true,
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, report)
|
|
require.NotNil(t, report.Cost, "a usage-aware seam must surface a cost block")
|
|
|
|
require.Equal(t, 800, report.Cost.InputTokens)
|
|
require.Equal(t, 200, report.Cost.OutputTokens)
|
|
require.Equal(t, 100, report.Cost.CacheReadTokens)
|
|
require.True(t, report.Cost.Estimated)
|
|
require.Greater(t, report.Cost.USD, 0.0)
|
|
|
|
// The LLM finding carries an attributed token share.
|
|
var sawLLM bool
|
|
for _, f := range report.Findings {
|
|
if f.Source == "llm" {
|
|
sawLLM = true
|
|
require.Greater(t, f.GenTokens, 0, "an LLM finding must carry attributed gen tokens")
|
|
}
|
|
}
|
|
require.True(t, sawLLM, "the LLM finding must survive into the report")
|
|
}
|
|
|
|
// TestRunWithUsage_NilSeamZeroCost asserts a nil seam still produces a
|
|
// report with a zero, Estimated:false cost block (deterministic-only).
|
|
func TestRunWithUsage_NilSeamZeroCost(t *testing.T) {
|
|
report, err := RunWithUsage(context.Background(), nil, nil, llm.ProviderPricing{}, Options{
|
|
RepoRoot: "/tmp/repo",
|
|
Diff: sampleDiff,
|
|
Rules: testResolver(t),
|
|
UseLLM: true,
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, report.Cost)
|
|
require.False(t, report.Cost.Estimated)
|
|
require.Equal(t, 0.0, report.Cost.USD)
|
|
}
|