Files
wehub-resource-sync f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

128 lines
3.6 KiB
Go

package service
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.kenn.io/agentsview/internal/db"
"go.kenn.io/agentsview/internal/export"
"go.kenn.io/agentsview/internal/parser"
"go.kenn.io/agentsview/internal/parsertest"
)
func TestUsageSummaryResultEmitsEmptyProjectsMap(t *testing.T) {
b, err := json.Marshal(UsageSummaryResult{
SchemaVersion: export.UsageDailySchemaVersion,
Projects: map[string]export.ProjectMapEntry{},
})
require.NoError(t, err)
assert.Contains(t, string(b), `"projects":{}`)
}
func TestComputeCacheStats_SavingsPassThrough(t *testing.T) {
t.Parallel()
// SavingsVsUncached is computed per-model in the DB layer;
// computeCacheStats just forwards totals.CacheSavings. Verify the
// pass-through at the positive, negative, and zero boundaries so a
// future refactor that drops the field trips a test.
cases := []struct {
name string
in float64
}{
{"positive", 4.65},
{"negative", -0.75},
{"zero", 0},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
cs := computeCacheStats(db.UsageTotals{CacheSavings: tc.in})
assert.InDelta(t, tc.in, cs.SavingsVsUncached, 1e-9)
})
}
}
func TestComputeCacheStats_ZeroTotalsIsZero(t *testing.T) {
cs := computeCacheStats(db.UsageTotals{})
assert.Zero(t, cs.SavingsVsUncached)
assert.Zero(t, cs.HitRate)
}
func TestComputeCacheStats_HitRate(t *testing.T) {
// 800 cache reads, 200 uncached inputs -> 0.80 hit rate. The
// HitRate denominator is cacheRead + input where input is already
// the uncached portion.
cs := computeCacheStats(db.UsageTotals{
InputTokens: 200,
CacheReadTokens: 800,
})
assert.InDelta(t, 0.80, cs.HitRate, 1e-9)
}
func TestComputeCacheStats_UncachedPassesInputThrough(t *testing.T) {
// Anthropic's input_tokens field is the NON-cached portion of the
// input; cache_read and cache_creation are tracked separately.
// UncachedInputTokens must equal InputTokens directly, not input
// minus the cache buckets (which would double-subtract).
cs := computeCacheStats(db.UsageTotals{
InputTokens: 100,
CacheReadTokens: 200,
CacheCreationTokens: 50,
})
assert.Equal(t, 100, cs.UncachedInputTokens)
assert.Equal(t, 200, cs.CacheReadTokens)
assert.Equal(t, 50, cs.CacheCreationTokens)
}
// TestUnsupportedUsageKindForAgentFilter pins Copilot branding to Copilot
// identity: an agent that merely shares Copilot's capabilities (no token
// data, AI-credits denominated) must degrade to the generic kind, not be
// described as Copilot. No t.Parallel: it stubs the parser registry.
func TestUnsupportedUsageKindForAgentFilter(t *testing.T) {
parsertest.StubAgentDefs(t, parser.AgentDef{
Type: parser.AgentType("credit-note-agent"),
DisplayName: "Credit Note Agent",
Usage: parser.UsageCapabilities{
NoPerMessageTokenData: true,
AICreditsDenominated: true,
},
})
cases := []struct {
name string
filter string
want string
}{
{
name: "all-copilot filter",
filter: "copilot,vscode-copilot",
want: UnsupportedUsageKindCopilotNoTokenData,
},
{
name: "non-copilot agent with copilot capabilities",
filter: "credit-note-agent",
want: UnsupportedUsageKindNoTokenData,
},
{
name: "copilot mixed with non-copilot",
filter: "copilot,credit-note-agent",
want: UnsupportedUsageKindNoTokenData,
},
{
name: "empty filter",
filter: "",
want: UnsupportedUsageKindNoTokenData,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.want,
UnsupportedUsageKindForAgentFilter(tc.filter))
})
}
}