su2a / frontend /src /views /__tests__ /KeyUsageView.spec.ts
cjovs's picture
PG local + bucket backup/restore, DB: su2adb
126ef31 verified
Raw
History Blame Contribute Delete
6.33 kB
import { describe, expect, it, beforeEach, afterEach, vi } from 'vitest'
import { flushPromises, mount } from '@vue/test-utils'
import { nextTick } from 'vue'
import KeyUsageView from '../KeyUsageView.vue'
const { showInfo, showSuccess, showError, fetchPublicSettings } = vi.hoisted(() => ({
showInfo: vi.fn(),
showSuccess: vi.fn(),
showError: vi.fn(),
fetchPublicSettings: vi.fn(),
}))
const messages: Record<string, string> = {
'keyUsage.title': 'API Key Usage',
'keyUsage.subtitle': 'Usage status',
'keyUsage.placeholder': 'sk-test',
'keyUsage.query': 'Query',
'keyUsage.querying': 'Querying...',
'keyUsage.privacyNote': 'Privacy note',
'keyUsage.dateRange': 'Date Range:',
'keyUsage.dateRangeToday': 'Today',
'keyUsage.dateRange7d': '7 Days',
'keyUsage.dateRange30d': '30 Days',
'keyUsage.dateRange90d': '90 Days',
'keyUsage.dateRangeCustom': 'Custom',
'keyUsage.apply': 'Apply',
'keyUsage.used': 'Used',
'keyUsage.detailInfo': 'Detail Information',
'keyUsage.tokenStats': 'Token Statistics',
'keyUsage.dailyDetail': 'Daily Detail',
'keyUsage.date': 'Date',
'keyUsage.requests': 'Requests',
'keyUsage.inputTokens': 'Input Tokens',
'keyUsage.outputTokens': 'Output Tokens',
'keyUsage.cacheReadTokens': 'Cache Read',
'keyUsage.cacheWriteTokens': 'Cache Write',
'keyUsage.cost': 'Cost',
'keyUsage.quotaMode': 'Key Quota Mode',
'keyUsage.walletBalance': 'Wallet Balance',
'keyUsage.totalQuota': 'Total Quota',
'keyUsage.limit5h': '5-Hour Limit',
'keyUsage.limitDaily': 'Daily Limit',
'keyUsage.limit7d': '7-Day Limit',
'keyUsage.limitWeekly': 'Weekly Limit',
'keyUsage.limitMonthly': 'Monthly Limit',
'keyUsage.remainingQuota': 'Remaining Quota',
'keyUsage.usedQuota': 'Used Quota',
'keyUsage.subscriptionType': 'Subscription Type',
'keyUsage.todayRequests': 'Today Requests',
'keyUsage.todayInputTokens': 'Today Input',
'keyUsage.todayOutputTokens': 'Today Output',
'keyUsage.todayTokens': 'Today Tokens',
'keyUsage.todayCacheCreation': 'Today Cache Creation',
'keyUsage.todayCacheRead': 'Today Cache Read',
'keyUsage.todayCost': 'Today Cost',
'keyUsage.rpmTpm': 'RPM / TPM',
'keyUsage.totalRequests': 'Total Requests',
'keyUsage.totalInputTokens': 'Total Input',
'keyUsage.totalOutputTokens': 'Total Output',
'keyUsage.totalTokensLabel': 'Total Tokens',
'keyUsage.totalCacheCreation': 'Total Cache Creation',
'keyUsage.totalCacheRead': 'Total Cache Read',
'keyUsage.totalCost': 'Total Cost',
'keyUsage.avgDuration': 'Avg Duration',
'keyUsage.querySuccess': 'Query successful',
'keyUsage.queryFailed': 'Query failed',
'keyUsage.queryFailedRetry': 'Query failed, please try again later',
'home.viewDocs': 'Docs',
'home.switchToLight': 'Light',
'home.switchToDark': 'Dark',
'home.footer.allRightsReserved': 'All rights reserved.',
}
vi.mock('vue-i18n', async () => {
const actual = await vi.importActual<typeof import('vue-i18n')>('vue-i18n')
return {
...actual,
useI18n: () => ({
t: (key: string) => messages[key] ?? key,
locale: { value: 'en' },
}),
}
})
vi.mock('@/stores', () => ({
useAppStore: () => ({
cachedPublicSettings: null,
siteName: 'Sub2API',
siteLogo: '',
docUrl: '',
publicSettingsLoaded: true,
fetchPublicSettings,
showInfo,
showSuccess,
showError,
}),
}))
describe('KeyUsageView daily detail', () => {
beforeEach(() => {
showInfo.mockReset()
showSuccess.mockReset()
showError.mockReset()
fetchPublicSettings.mockReset()
localStorage.clear()
Object.defineProperty(window, 'matchMedia', {
configurable: true,
value: vi.fn().mockReturnValue({ matches: false }),
})
vi.stubGlobal('requestAnimationFrame', (cb: FrameRequestCallback) => window.setTimeout(() => cb(0), 0))
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: async () => ({
mode: 'quota_limited',
isValid: true,
status: 'active',
quota: {
limit: 10,
used: 1,
remaining: 9,
unit: 'USD',
},
usage: {
today: {
requests: 1,
input_tokens: 10,
output_tokens: 20,
cache_creation_tokens: 0,
cache_read_tokens: 0,
total_tokens: 30,
actual_cost: 0.01,
},
total: {
requests: 12,
input_tokens: 100,
output_tokens: 200,
cache_creation_tokens: 10,
cache_read_tokens: 30,
total_tokens: 340,
actual_cost: 0.12,
},
rpm: 0,
tpm: 0,
},
daily_usage: [
{
date: '2026-05-19',
requests: 12,
input_tokens: 100,
output_tokens: 200,
cache_read_tokens: 30,
cache_write_tokens: 10,
total_tokens: 340,
cost: 0.15,
actual_cost: 0.12,
},
],
}),
}))
})
afterEach(() => {
vi.unstubAllGlobals()
})
it('renders daily usage detail rows after a successful query', async () => {
const wrapper = mount(KeyUsageView, {
global: {
stubs: {
RouterLink: { template: '<a><slot /></a>' },
LocaleSwitcher: true,
Icon: true,
},
},
})
await wrapper.find('input').setValue('sk-test-key')
await wrapper.find('input').trigger('keydown.enter')
await flushPromises()
await nextTick()
const fetchMock = vi.mocked(fetch)
expect(fetchMock).toHaveBeenCalledWith(
expect.stringContaining('/v1/usage?'),
expect.objectContaining({
headers: { Authorization: 'Bearer sk-test-key' },
})
)
expect(String(fetchMock.mock.calls[0][0])).toContain('days=30')
const text = wrapper.text()
expect(text).toContain('Daily Detail')
expect(text).toContain('Date')
expect(text).toContain('Cache Read')
expect(text).toContain('Cache Write')
expect(text).toContain('2026-05-19')
expect(text).toContain('12')
expect(text).toContain('100')
expect(text).toContain('200')
expect(text).toContain('30')
expect(text).toContain('10')
expect(text).toContain('$0.12')
wrapper.unmount()
})
})