From 3dccd540c77198ed7f1ebd453514fc57cddcd253 Mon Sep 17 00:00:00 2001 From: jiwangyihao Date: Sat, 9 May 2026 02:44:37 +0800 Subject: [PATCH] =?UTF-8?q?fix(frontend):=20=E4=BF=AE=E6=AD=A3=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E5=91=BD=E4=B8=AD=E7=8E=87=E5=88=86=E6=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/charts/TokenUsageTrend.vue | 4 +- .../charts/__tests__/TokenUsageTrend.spec.ts | 60 +++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 frontend/src/components/charts/__tests__/TokenUsageTrend.spec.ts diff --git a/frontend/src/components/charts/TokenUsageTrend.vue b/frontend/src/components/charts/TokenUsageTrend.vue index 4cd126b9314..871989953ca 100644 --- a/frontend/src/components/charts/TokenUsageTrend.vue +++ b/frontend/src/components/charts/TokenUsageTrend.vue @@ -109,8 +109,8 @@ const chartData = computed(() => { { label: 'Cache Hit Rate', data: props.trendData.map((d) => { - const total = d.cache_read_tokens + d.cache_creation_tokens - return total > 0 ? (d.cache_read_tokens / total) * 100 : 0 + const totalPromptTokens = d.input_tokens + d.cache_read_tokens + d.cache_creation_tokens + return totalPromptTokens > 0 ? (d.cache_read_tokens / totalPromptTokens) * 100 : 0 }), borderColor: chartColors.value.cacheHitRate, backgroundColor: `${chartColors.value.cacheHitRate}20`, diff --git a/frontend/src/components/charts/__tests__/TokenUsageTrend.spec.ts b/frontend/src/components/charts/__tests__/TokenUsageTrend.spec.ts new file mode 100644 index 00000000000..8b90e0a65ba --- /dev/null +++ b/frontend/src/components/charts/__tests__/TokenUsageTrend.spec.ts @@ -0,0 +1,60 @@ +import { describe, expect, it, vi } from 'vitest' +import { mount } from '@vue/test-utils' + +import TokenUsageTrend from '../TokenUsageTrend.vue' + +const messages: Record = { + 'admin.dashboard.tokenUsageTrend': 'Token Usage Trend', + 'admin.dashboard.noDataAvailable': 'No data available', +} + +vi.mock('vue-i18n', async () => { + const actual = await vi.importActual('vue-i18n') + return { + ...actual, + useI18n: () => ({ + t: (key: string) => messages[key] ?? key, + }), + } +}) + +vi.mock('vue-chartjs', () => ({ + Line: { + props: ['data', 'options'], + template: '
{{ JSON.stringify(data) }}
', + }, +})) + +describe('TokenUsageTrend', () => { + it('calculates cache hit rate against all prompt tokens', () => { + const wrapper = mount(TokenUsageTrend, { + props: { + trendData: [ + { + date: '2026-05-08', + requests: 1, + input_tokens: 1000, + output_tokens: 200, + cache_creation_tokens: 0, + cache_read_tokens: 250, + total_tokens: 1450, + cost: 0.01, + actual_cost: 0.01, + }, + ], + }, + global: { + stubs: { + LoadingSpinner: true, + }, + }, + }) + + const chartData = JSON.parse(wrapper.find('.chart-data').text()) + const hitRateDataset = chartData.datasets.find( + (dataset: { label: string }) => dataset.label === 'Cache Hit Rate' + ) + + expect(hitRateDataset.data).toEqual([20]) + }) +})