package admin import ( "context" "net/http" "net/http/httptest" "sync/atomic" "testing" "time" "github.com/Wei-Shaw/sub2api/internal/pkg/usagestats" "github.com/Wei-Shaw/sub2api/internal/service" "github.com/gin-gonic/gin" "github.com/stretchr/testify/require" ) type dashboardUsageRepoCacheProbe struct { service.UsageLogRepository trendCalls atomic.Int32 usersTrendCalls atomic.Int32 } func (r *dashboardUsageRepoCacheProbe) GetUsageTrendWithFilters( ctx context.Context, startTime, endTime time.Time, granularity string, userID, apiKeyID, accountID, groupID int64, model string, requestType *int16, stream *bool, billingType *int8, ) ([]usagestats.TrendDataPoint, error) { r.trendCalls.Add(1) return []usagestats.TrendDataPoint{{ Date: "2026-03-11", Requests: 1, TotalTokens: 2, Cost: 3, ActualCost: 4, }}, nil } func (r *dashboardUsageRepoCacheProbe) GetUserUsageTrend( ctx context.Context, startTime, endTime time.Time, granularity string, limit int, ) ([]usagestats.UserUsageTrendPoint, error) { r.usersTrendCalls.Add(1) return []usagestats.UserUsageTrendPoint{{ Date: "2026-03-11", UserID: 1, Email: "cache@test.dev", Requests: 2, Tokens: 20, Cost: 2, ActualCost: 1, }}, nil } func resetDashboardReadCachesForTest() { dashboardTrendCache = newSnapshotCache(30 * time.Second) dashboardUsersTrendCache = newSnapshotCache(30 * time.Second) dashboardAPIKeysTrendCache = newSnapshotCache(30 * time.Second) dashboardModelStatsCache = newSnapshotCache(30 * time.Second) dashboardGroupStatsCache = newSnapshotCache(30 * time.Second) dashboardSnapshotV2Cache = newSnapshotCache(30 * time.Second) } func TestDashboardHandler_GetUsageTrend_UsesCache(t *testing.T) { t.Cleanup(resetDashboardReadCachesForTest) resetDashboardReadCachesForTest() gin.SetMode(gin.TestMode) repo := &dashboardUsageRepoCacheProbe{} dashboardSvc := service.NewDashboardService(repo, nil, nil, nil) handler := NewDashboardHandler(dashboardSvc, nil) router := gin.New() router.GET("/admin/dashboard/trend", handler.GetUsageTrend) req1 := httptest.NewRequest(http.MethodGet, "/admin/dashboard/trend?start_date=2026-03-01&end_date=2026-03-07&granularity=day", nil) rec1 := httptest.NewRecorder() router.ServeHTTP(rec1, req1) require.Equal(t, http.StatusOK, rec1.Code) require.Equal(t, "miss", rec1.Header().Get("X-Snapshot-Cache")) req2 := httptest.NewRequest(http.MethodGet, "/admin/dashboard/trend?start_date=2026-03-01&end_date=2026-03-07&granularity=day", nil) rec2 := httptest.NewRecorder() router.ServeHTTP(rec2, req2) require.Equal(t, http.StatusOK, rec2.Code) require.Equal(t, "hit", rec2.Header().Get("X-Snapshot-Cache")) require.Equal(t, int32(1), repo.trendCalls.Load()) } func TestDashboardHandler_GetUserUsageTrend_UsesCache(t *testing.T) { t.Cleanup(resetDashboardReadCachesForTest) resetDashboardReadCachesForTest() gin.SetMode(gin.TestMode) repo := &dashboardUsageRepoCacheProbe{} dashboardSvc := service.NewDashboardService(repo, nil, nil, nil) handler := NewDashboardHandler(dashboardSvc, nil) router := gin.New() router.GET("/admin/dashboard/users-trend", handler.GetUserUsageTrend) req1 := httptest.NewRequest(http.MethodGet, "/admin/dashboard/users-trend?start_date=2026-03-01&end_date=2026-03-07&granularity=day&limit=8", nil) rec1 := httptest.NewRecorder() router.ServeHTTP(rec1, req1) require.Equal(t, http.StatusOK, rec1.Code) require.Equal(t, "miss", rec1.Header().Get("X-Snapshot-Cache")) req2 := httptest.NewRequest(http.MethodGet, "/admin/dashboard/users-trend?start_date=2026-03-01&end_date=2026-03-07&granularity=day&limit=8", nil) rec2 := httptest.NewRecorder() router.ServeHTTP(rec2, req2) require.Equal(t, http.StatusOK, rec2.Code) require.Equal(t, "hit", rec2.Header().Get("X-Snapshot-Cache")) require.Equal(t, int32(1), repo.usersTrendCalls.Load()) }