package chat import ( "context" "testing" "time" "github.com/google/uuid" "github.com/stretchr/testify/require" ) // usageRepo is a tiny stub that records what was called and returns canned values. // It satisfies only the UsageService-relevant subset of Repository; unused methods // inherit panic stubs from fakeRepo so we don't need to declare them here. type usageRepo struct { *fakeRepo insertedUsage []UsageRecord byUserCalls []timeRange byModelCalls []timeRange byDayCalls []timeRange dailyCalls []userTimeRange usersOut []UserUsageRow modelsOut []ModelUsageRow daysOut []DayUsageRow } type timeRange struct{ from, to time.Time } type userTimeRange struct { user uuid.UUID from time.Time to time.Time } func (r *usageRepo) InsertUsage(_ context.Context, rec UsageRecord) error { r.insertedUsage = append(r.insertedUsage, rec) return nil } func (r *usageRepo) SummarizeUsageByUser(_ context.Context, from, to time.Time) ([]UserUsageRow, error) { r.byUserCalls = append(r.byUserCalls, timeRange{from, to}) return r.usersOut, nil } func (r *usageRepo) SummarizeUsageByModel(_ context.Context, from, to time.Time) ([]ModelUsageRow, error) { r.byModelCalls = append(r.byModelCalls, timeRange{from, to}) return r.modelsOut, nil } func (r *usageRepo) SummarizeUsageByDay(_ context.Context, from, to time.Time) ([]DayUsageRow, error) { r.byDayCalls = append(r.byDayCalls, timeRange{from, to}) return r.daysOut, nil } func (r *usageRepo) SummarizeUserDaily(_ context.Context, uid uuid.UUID, from, to time.Time) ([]DayUsageRow, error) { r.dailyCalls = append(r.dailyCalls, userTimeRange{uid, from, to}) return r.daysOut, nil } func TestUsageService_DelegatesToRepo(t *testing.T) { repo := &usageRepo{fakeRepo: newFakeRepo()} svc := NewUsageService(repo) from := time.Date(2026, 5, 1, 0, 0, 0, 0, time.UTC) to := from.Add(24 * time.Hour) uid := uuid.Must(uuid.NewV7()) rec := UsageRecord{ ConversationID: uuid.Must(uuid.NewV7()), MessageID: 42, UserID: uid, PromptTokens: 100, CompletionTokens: 50, ThinkingTokens: 0, CostUSD: 0.001, } require.NoError(t, svc.Insert(context.Background(), rec)) require.Len(t, repo.insertedUsage, 1) require.Equal(t, rec, repo.insertedUsage[0]) repo.usersOut = []UserUsageRow{{UserID: uid, PromptTokens: 100}} users, err := svc.SummarizeByUser(context.Background(), from, to) require.NoError(t, err) require.Equal(t, repo.usersOut, users) require.Equal(t, []timeRange{{from, to}}, repo.byUserCalls) repo.modelsOut = []ModelUsageRow{{ModelID: uuid.Must(uuid.NewV7()), CostUSD: 1.5}} models, err := svc.SummarizeByModel(context.Background(), from, to) require.NoError(t, err) require.Equal(t, repo.modelsOut, models) require.Equal(t, []timeRange{{from, to}}, repo.byModelCalls) repo.daysOut = []DayUsageRow{{Day: from, PromptTokens: 200}} days, err := svc.SummarizeByDay(context.Background(), from, to) require.NoError(t, err) require.Equal(t, repo.daysOut, days) require.Equal(t, []timeRange{{from, to}}, repo.byDayCalls) dailyOut, err := svc.UserDaily(context.Background(), uid, from, to) require.NoError(t, err) require.Equal(t, repo.daysOut, dailyOut) require.Equal(t, []userTimeRange{{uid, from, to}}, repo.dailyCalls) }