thread-master/apps/backend/internal/module/usage/usecase/analytics_test.go

74 lines
3.3 KiB
Go

package usecase_test
import (
"context"
"testing"
"time"
"apps/backend/internal/module/usage/domain"
"apps/backend/internal/module/usage/repository"
"apps/backend/internal/module/usage/usecase"
"github.com/stretchr/testify/require"
)
func analyticsNano(t *testing.T, value string) int64 {
t.Helper()
parsed, err := time.Parse(time.RFC3339, value)
require.NoError(t, err)
return parsed.UnixNano()
}
func TestParseAnalyticsQueryValidationAndCaps(t *testing.T) {
_, err := usecase.ParseAnalyticsQuery("week", "2026-01-01", "2026-01-02")
require.ErrorIs(t, err, domain.ErrInvalidRange)
_, err = usecase.ParseAnalyticsQuery("day", "2026-01-03", "2026-01-02")
require.ErrorIs(t, err, domain.ErrInvalidRange)
_, err = usecase.ParseAnalyticsQuery("day", "2026-01-01", "2026-04-04")
require.ErrorIs(t, err, domain.ErrInvalidRange)
_, err = usecase.ParseAnalyticsQuery("month", "2023-01-01", "2025-12-31")
require.NoError(t, err)
}
func TestTenantAnalyticsUsesInclusiveUTCBoundsAndSeparateModes(t *testing.T) {
repo := repository.NewMemory()
for _, event := range []*domain.Event{
{ID: "before", UID: 1, Meter: domain.MeterAICopy, Credits: 9, KeyMode: domain.KeyModePlatform, CreatedAt: analyticsNano(t, "2026-01-31T23:59:59Z")},
{ID: "first", UID: 1, Meter: domain.MeterAICopy, Credits: 2, KeyMode: domain.KeyModePlatform, CreatedAt: analyticsNano(t, "2026-02-01T00:00:00Z")},
{ID: "byok", UID: 1, Meter: domain.MeterWebSearch, Credits: 99, KeyMode: domain.KeyModeByok, CreatedAt: analyticsNano(t, "2026-02-02T12:00:00Z")},
{ID: "last", UID: 1, Meter: domain.MeterWebSearch, Credits: 1, KeyMode: domain.KeyModePlatform, CreatedAt: analyticsNano(t, "2026-02-02T23:59:59.999999999Z")},
{ID: "after", UID: 1, Meter: domain.MeterAICopy, Credits: 9, KeyMode: domain.KeyModePlatform, CreatedAt: analyticsNano(t, "2026-02-03T00:00:00Z")},
} {
require.NoError(t, repo.InsertEvent(context.Background(), event))
}
query, err := usecase.ParseAnalyticsQuery("day", "2026-02-01", "2026-02-02")
require.NoError(t, err)
result, err := usecase.New(repo, nil).TenantAnalytics(context.Background(), query, []domain.AnalyticsMember{{UID: 1, Email: "one@example.test", DisplayName: "One"}})
require.NoError(t, err)
require.Equal(t, 3, result.ConsumedCredits)
require.Equal(t, 1, result.AICalls)
require.Equal(t, 1, result.SearchCalls)
require.Equal(t, 1, result.Byok.CallCount)
require.Equal(t, 0, result.Byok.ByMeter[domain.MeterWebSearch].Credits)
require.Equal(t, 1, result.Byok.ByMeter[domain.MeterWebSearch].Count)
require.Len(t, result.Series, 2)
require.Equal(t, 2, result.Series[0].ConsumedCredits)
require.Equal(t, 1, result.Series[1].ConsumedCredits)
require.Equal(t, 1, result.Series[1].ByokCallCount)
require.Equal(t, 3, result.Members[0].TotalCredits)
require.Equal(t, 1, result.Members[0].ByokCallCount)
}
func TestMemoryListEventsRangeUsesExclusiveUpperBound(t *testing.T) {
repo := repository.NewMemory()
for i, nano := range []int64{99, 100, 199, 200} {
require.NoError(t, repo.InsertEvent(context.Background(), &domain.Event{ID: string(rune('a' + i)), CreatedAt: nano}))
}
list, err := repo.ListEventsRange(context.Background(), 100, 200)
require.NoError(t, err)
require.Len(t, list, 2)
require.Equal(t, int64(100), list[0].CreatedAt)
require.Equal(t, int64(199), list[1].CreatedAt)
}