diff --git a/frontend/src/views/admin/DashboardView.vue b/frontend/src/views/admin/DashboardView.vue index 8b7ff632..bc8b5aa2 100644 --- a/frontend/src/views/admin/DashboardView.vue +++ b/frontend/src/views/admin/DashboardView.vue @@ -348,12 +348,20 @@ const formatLocalDate = (date: Date): string => { return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}` } -const getTodayLocalDate = () => formatLocalDate(new Date()) +const getLast24HoursRangeDates = (): { start: string; end: string } => { + const end = new Date() + const start = new Date(end.getTime() - 24 * 60 * 60 * 1000) + return { + start: formatLocalDate(start), + end: formatLocalDate(end) + } +} // Date range const granularity = ref<'day' | 'hour'>('hour') -const startDate = ref(getTodayLocalDate()) -const endDate = ref(getTodayLocalDate()) +const defaultRange = getLast24HoursRangeDates() +const startDate = ref(defaultRange.start) +const endDate = ref(defaultRange.end) // Granularity options for Select component const granularityOptions = computed(() => [ diff --git a/frontend/src/views/admin/__tests__/DashboardView.spec.ts b/frontend/src/views/admin/__tests__/DashboardView.spec.ts new file mode 100644 index 00000000..7cb51232 --- /dev/null +++ b/frontend/src/views/admin/__tests__/DashboardView.spec.ts @@ -0,0 +1,143 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { flushPromises, mount } from '@vue/test-utils' + +import type { DashboardStats } from '@/types' +import DashboardView from '../DashboardView.vue' + +const { getSnapshotV2, getUserUsageTrend, getUserSpendingRanking } = vi.hoisted(() => ({ + getSnapshotV2: vi.fn(), + getUserUsageTrend: vi.fn(), + getUserSpendingRanking: vi.fn() +})) + +vi.mock('@/api/admin', () => ({ + adminAPI: { + dashboard: { + getSnapshotV2, + getUserUsageTrend, + getUserSpendingRanking + } + } +})) + +vi.mock('@/stores/app', () => ({ + useAppStore: () => ({ + showError: vi.fn() + }) +})) + +vi.mock('vue-router', () => ({ + useRouter: () => ({ + push: vi.fn() + }) +})) + +vi.mock('vue-i18n', async () => { + const actual = await vi.importActual('vue-i18n') + return { + ...actual, + useI18n: () => ({ + t: (key: string) => key + }) + } +}) + +const formatLocalDate = (date: Date): string => { + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + return `${year}-${month}-${day}` +} + +const createDashboardStats = (): DashboardStats => ({ + total_users: 0, + today_new_users: 0, + active_users: 0, + hourly_active_users: 0, + stats_updated_at: '', + stats_stale: false, + total_api_keys: 0, + active_api_keys: 0, + total_accounts: 0, + normal_accounts: 0, + error_accounts: 0, + ratelimit_accounts: 0, + overload_accounts: 0, + total_requests: 0, + total_input_tokens: 0, + total_output_tokens: 0, + total_cache_creation_tokens: 0, + total_cache_read_tokens: 0, + total_tokens: 0, + total_cost: 0, + total_actual_cost: 0, + today_requests: 0, + today_input_tokens: 0, + today_output_tokens: 0, + today_cache_creation_tokens: 0, + today_cache_read_tokens: 0, + today_tokens: 0, + today_cost: 0, + today_actual_cost: 0, + average_duration_ms: 0, + uptime: 0, + rpm: 0, + tpm: 0 +}) + +describe('admin DashboardView', () => { + beforeEach(() => { + getSnapshotV2.mockReset() + getUserUsageTrend.mockReset() + getUserSpendingRanking.mockReset() + + getSnapshotV2.mockResolvedValue({ + stats: createDashboardStats(), + trend: [], + models: [] + }) + getUserUsageTrend.mockResolvedValue({ + trend: [], + start_date: '', + end_date: '', + granularity: 'hour' + }) + getUserSpendingRanking.mockResolvedValue({ + ranking: [], + total_actual_cost: 0, + total_requests: 0, + total_tokens: 0, + start_date: '', + end_date: '' + }) + }) + + it('uses last 24 hours as default dashboard range', async () => { + mount(DashboardView, { + global: { + stubs: { + AppLayout: { template: '
' }, + LoadingSpinner: true, + Icon: true, + DateRangePicker: true, + Select: true, + ModelDistributionChart: true, + TokenUsageTrend: true, + Line: true + } + } + }) + + await flushPromises() + + const now = new Date() + const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000) + + expect(getSnapshotV2).toHaveBeenCalledTimes(1) + expect(getSnapshotV2).toHaveBeenCalledWith(expect.objectContaining({ + start_date: formatLocalDate(yesterday), + end_date: formatLocalDate(now), + granularity: 'hour' + })) + }) +})