Files
kandev/apps/backend/internal/agent/usage/service_test.go
T
2026-05-16 20:07:57 +01:00

166 lines
4.3 KiB
Go

package usage_test
import (
"context"
"testing"
"time"
"github.com/kandev/kandev/internal/agent/usage"
)
// mockClient implements ProviderUsageClient for testing.
type mockClient struct {
usage *usage.ProviderUsage
err error
calls int
}
func (m *mockClient) FetchUsage(_ context.Context) (*usage.ProviderUsage, error) {
m.calls++
return m.usage, m.err
}
func TestUsageService_GetUsage_NoClient(t *testing.T) {
svc := usage.NewUsageService()
got, err := svc.GetUsage(context.Background(), "unknown-profile")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != nil {
t.Fatalf("expected nil for unregistered profile, got %+v", got)
}
}
func TestUsageService_GetUsage_CachesResult(t *testing.T) {
svc := usage.NewUsageService()
now := time.Now()
mock := &mockClient{
usage: &usage.ProviderUsage{
Provider: "anthropic",
Windows: []usage.UtilizationWindow{
{Label: "5-hour", UtilizationPct: 50, ResetAt: now.Add(5 * time.Hour)},
},
FetchedAt: now,
},
}
key := usage.CacheKey("anthropic", "/path/to/creds")
svc.Register("profile-1", mock, key)
// First call: fetches from client.
got1, err := svc.GetUsage(context.Background(), "profile-1")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got1 == nil {
t.Fatal("expected non-nil usage")
}
if mock.calls != 1 {
t.Fatalf("expected 1 fetch call, got %d", mock.calls)
}
// Second call: should use cache.
got2, err := svc.GetUsage(context.Background(), "profile-1")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got2 != got1 {
t.Fatal("expected cached value to be identical pointer")
}
if mock.calls != 1 {
t.Fatalf("expected still 1 fetch call after cache hit, got %d", mock.calls)
}
}
func TestUsageService_IsPotentiallyRateLimited_NotLimited(t *testing.T) {
svc := usage.NewUsageService()
now := time.Now()
mock := &mockClient{
usage: &usage.ProviderUsage{
Provider: "anthropic",
Windows: []usage.UtilizationWindow{
{Label: "5-hour", UtilizationPct: 50, ResetAt: now.Add(5 * time.Hour)},
{Label: "7-day", UtilizationPct: 30, ResetAt: now.Add(7 * 24 * time.Hour)},
},
FetchedAt: now,
},
}
svc.Register("profile-2", mock, usage.CacheKey("anthropic", "/creds"))
limited, resetAt, err := svc.IsPotentiallyRateLimited(context.Background(), "profile-2")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if limited {
t.Fatal("expected not limited")
}
if !resetAt.IsZero() {
t.Fatalf("expected zero resetAt, got %v", resetAt)
}
}
func TestUsageService_IsPotentiallyRateLimited_Limited(t *testing.T) {
svc := usage.NewUsageService()
now := time.Now()
resetTime := now.Add(3 * time.Hour)
mock := &mockClient{
usage: &usage.ProviderUsage{
Provider: "anthropic",
Windows: []usage.UtilizationWindow{
{Label: "5-hour", UtilizationPct: 95, ResetAt: resetTime},
{Label: "7-day", UtilizationPct: 40, ResetAt: now.Add(7 * 24 * time.Hour)},
},
FetchedAt: now,
},
}
svc.Register("profile-3", mock, usage.CacheKey("anthropic", "/creds2"))
limited, resetAt, err := svc.IsPotentiallyRateLimited(context.Background(), "profile-3")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !limited {
t.Fatal("expected limited")
}
if !resetAt.Equal(resetTime) {
t.Fatalf("expected resetAt %v, got %v", resetTime, resetAt)
}
}
func TestUsageService_IsPotentiallyRateLimited_NoClient(t *testing.T) {
svc := usage.NewUsageService()
limited, resetAt, err := svc.IsPotentiallyRateLimited(context.Background(), "unknown")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if limited {
t.Fatal("expected not limited for unregistered profile")
}
if !resetAt.IsZero() {
t.Fatalf("expected zero resetAt, got %v", resetAt)
}
}
func TestUsageService_IsPotentiallyRateLimited_ExactThreshold(t *testing.T) {
svc := usage.NewUsageService()
now := time.Now()
resetTime := now.Add(time.Hour)
mock := &mockClient{
usage: &usage.ProviderUsage{
Provider: "anthropic",
Windows: []usage.UtilizationWindow{
{Label: "5-hour", UtilizationPct: 90.0, ResetAt: resetTime},
},
FetchedAt: now,
},
}
svc.Register("profile-4", mock, usage.CacheKey("anthropic", "/creds3"))
limited, _, err := svc.IsPotentiallyRateLimited(context.Background(), "profile-4")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !limited {
t.Fatal("expected limited at exactly 90%")
}
}