mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-04-09 01:24:46 +08:00
355 lines
8.5 KiB
TypeScript
355 lines
8.5 KiB
TypeScript
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
||
import { flushPromises, mount } from '@vue/test-utils'
|
||
import AccountUsageCell from '../AccountUsageCell.vue'
|
||
|
||
const { getUsage } = vi.hoisted(() => ({
|
||
getUsage: vi.fn()
|
||
}))
|
||
|
||
vi.mock('@/api/admin', () => ({
|
||
adminAPI: {
|
||
accounts: {
|
||
getUsage
|
||
}
|
||
}
|
||
}))
|
||
|
||
vi.mock('vue-i18n', async () => {
|
||
const actual = await vi.importActual<typeof import('vue-i18n')>('vue-i18n')
|
||
return {
|
||
...actual,
|
||
useI18n: () => ({
|
||
t: (key: string) => key
|
||
})
|
||
}
|
||
})
|
||
|
||
describe('AccountUsageCell', () => {
|
||
beforeEach(() => {
|
||
getUsage.mockReset()
|
||
})
|
||
|
||
it('Antigravity 图片用量会聚合新旧 image 模型', async () => {
|
||
getUsage.mockResolvedValue({
|
||
antigravity_quota: {
|
||
'gemini-3.1-flash-image': {
|
||
utilization: 20,
|
||
reset_time: '2026-03-01T10:00:00Z'
|
||
},
|
||
'gemini-3-pro-image': {
|
||
utilization: 70,
|
||
reset_time: '2026-03-01T09:00:00Z'
|
||
}
|
||
}
|
||
})
|
||
|
||
const wrapper = mount(AccountUsageCell, {
|
||
props: {
|
||
account: {
|
||
id: 1001,
|
||
platform: 'antigravity',
|
||
type: 'oauth',
|
||
extra: {}
|
||
} as any
|
||
},
|
||
global: {
|
||
stubs: {
|
||
UsageProgressBar: {
|
||
props: ['label', 'utilization', 'resetsAt', 'color'],
|
||
template: '<div class="usage-bar">{{ label }}|{{ utilization }}|{{ resetsAt }}</div>'
|
||
},
|
||
AccountQuotaInfo: true
|
||
}
|
||
}
|
||
})
|
||
|
||
await flushPromises()
|
||
|
||
expect(wrapper.text()).toContain('admin.accounts.usageWindow.gemini3Image|70|2026-03-01T09:00:00Z')
|
||
})
|
||
|
||
|
||
it('OpenAI OAuth 快照已过期时首屏会重新请求 usage', async () => {
|
||
getUsage.mockResolvedValue({
|
||
five_hour: {
|
||
utilization: 15,
|
||
resets_at: '2026-03-08T12:00:00Z',
|
||
remaining_seconds: 3600,
|
||
window_stats: {
|
||
requests: 3,
|
||
tokens: 300,
|
||
cost: 0.03,
|
||
standard_cost: 0.03,
|
||
user_cost: 0.03
|
||
}
|
||
},
|
||
seven_day: {
|
||
utilization: 77,
|
||
resets_at: '2026-03-13T12:00:00Z',
|
||
remaining_seconds: 3600,
|
||
window_stats: {
|
||
requests: 3,
|
||
tokens: 300,
|
||
cost: 0.03,
|
||
standard_cost: 0.03,
|
||
user_cost: 0.03
|
||
}
|
||
}
|
||
})
|
||
|
||
const wrapper = mount(AccountUsageCell, {
|
||
props: {
|
||
account: {
|
||
id: 2000,
|
||
platform: 'openai',
|
||
type: 'oauth',
|
||
extra: {
|
||
codex_usage_updated_at: '2026-03-07T00:00:00Z',
|
||
codex_5h_used_percent: 12,
|
||
codex_5h_reset_at: '2026-03-08T12:00:00Z',
|
||
codex_7d_used_percent: 34,
|
||
codex_7d_reset_at: '2026-03-13T12:00:00Z'
|
||
}
|
||
} as any
|
||
},
|
||
global: {
|
||
stubs: {
|
||
UsageProgressBar: {
|
||
props: ['label', 'utilization', 'resetsAt', 'windowStats', 'color'],
|
||
template: '<div class="usage-bar">{{ label }}|{{ utilization }}|{{ windowStats?.tokens }}</div>'
|
||
},
|
||
AccountQuotaInfo: true
|
||
}
|
||
}
|
||
})
|
||
|
||
await flushPromises()
|
||
|
||
expect(getUsage).toHaveBeenCalledWith(2000)
|
||
expect(wrapper.text()).toContain('5h|15|300')
|
||
expect(wrapper.text()).toContain('7d|77|300')
|
||
})
|
||
|
||
it('OpenAI OAuth 有现成快照且未限额时不会首屏请求 usage', async () => {
|
||
const wrapper = mount(AccountUsageCell, {
|
||
props: {
|
||
account: {
|
||
id: 2001,
|
||
platform: 'openai',
|
||
type: 'oauth',
|
||
extra: {
|
||
codex_usage_updated_at: '2099-03-07T10:00:00Z',
|
||
codex_5h_used_percent: 12,
|
||
codex_5h_reset_at: '2099-03-07T12:00:00Z',
|
||
codex_7d_used_percent: 34,
|
||
codex_7d_reset_at: '2099-03-13T12:00:00Z'
|
||
}
|
||
} as any
|
||
},
|
||
global: {
|
||
stubs: {
|
||
UsageProgressBar: {
|
||
props: ['label', 'utilization', 'resetsAt', 'windowStats', 'color'],
|
||
template: '<div class="usage-bar">{{ label }}|{{ utilization }}</div>'
|
||
},
|
||
AccountQuotaInfo: true
|
||
}
|
||
}
|
||
})
|
||
|
||
await flushPromises()
|
||
|
||
expect(getUsage).not.toHaveBeenCalled()
|
||
expect(wrapper.text()).toContain('5h|12')
|
||
expect(wrapper.text()).toContain('7d|34')
|
||
})
|
||
|
||
it('OpenAI OAuth 在无 codex 快照时会回退显示 usage 接口窗口', async () => {
|
||
getUsage.mockResolvedValue({
|
||
five_hour: {
|
||
utilization: 0,
|
||
resets_at: null,
|
||
remaining_seconds: 0,
|
||
window_stats: {
|
||
requests: 2,
|
||
tokens: 27700,
|
||
cost: 0.06,
|
||
standard_cost: 0.06,
|
||
user_cost: 0.06
|
||
}
|
||
},
|
||
seven_day: {
|
||
utilization: 0,
|
||
resets_at: null,
|
||
remaining_seconds: 0,
|
||
window_stats: {
|
||
requests: 2,
|
||
tokens: 27700,
|
||
cost: 0.06,
|
||
standard_cost: 0.06,
|
||
user_cost: 0.06
|
||
}
|
||
}
|
||
})
|
||
|
||
const wrapper = mount(AccountUsageCell, {
|
||
props: {
|
||
account: {
|
||
id: 2002,
|
||
platform: 'openai',
|
||
type: 'oauth',
|
||
extra: {}
|
||
} as any
|
||
},
|
||
global: {
|
||
stubs: {
|
||
UsageProgressBar: {
|
||
props: ['label', 'utilization', 'resetsAt', 'windowStats', 'color'],
|
||
template: '<div class="usage-bar">{{ label }}|{{ utilization }}|{{ windowStats?.tokens }}</div>'
|
||
},
|
||
AccountQuotaInfo: true
|
||
}
|
||
}
|
||
})
|
||
|
||
await flushPromises()
|
||
|
||
expect(getUsage).toHaveBeenCalledWith(2002)
|
||
expect(wrapper.text()).toContain('5h|0|27700')
|
||
expect(wrapper.text()).toContain('7d|0|27700')
|
||
})
|
||
|
||
it('OpenAI OAuth 在行数据刷新但仍无 codex 快照时会重新拉取 usage', async () => {
|
||
getUsage
|
||
.mockResolvedValueOnce({
|
||
five_hour: {
|
||
utilization: 0,
|
||
resets_at: null,
|
||
remaining_seconds: 0,
|
||
window_stats: {
|
||
requests: 1,
|
||
tokens: 100,
|
||
cost: 0.01,
|
||
standard_cost: 0.01,
|
||
user_cost: 0.01
|
||
}
|
||
},
|
||
seven_day: null
|
||
})
|
||
.mockResolvedValueOnce({
|
||
five_hour: {
|
||
utilization: 0,
|
||
resets_at: null,
|
||
remaining_seconds: 0,
|
||
window_stats: {
|
||
requests: 2,
|
||
tokens: 200,
|
||
cost: 0.02,
|
||
standard_cost: 0.02,
|
||
user_cost: 0.02
|
||
}
|
||
},
|
||
seven_day: null
|
||
})
|
||
|
||
const wrapper = mount(AccountUsageCell, {
|
||
props: {
|
||
account: {
|
||
id: 2003,
|
||
platform: 'openai',
|
||
type: 'oauth',
|
||
updated_at: '2026-03-07T10:00:00Z',
|
||
extra: {}
|
||
} as any
|
||
},
|
||
global: {
|
||
stubs: {
|
||
UsageProgressBar: {
|
||
props: ['label', 'utilization', 'resetsAt', 'windowStats', 'color'],
|
||
template: '<div class="usage-bar">{{ label }}|{{ utilization }}|{{ windowStats?.tokens }}</div>'
|
||
},
|
||
AccountQuotaInfo: true
|
||
}
|
||
}
|
||
})
|
||
|
||
await flushPromises()
|
||
expect(wrapper.text()).toContain('5h|0|100')
|
||
expect(getUsage).toHaveBeenCalledTimes(1)
|
||
|
||
await wrapper.setProps({
|
||
account: {
|
||
id: 2003,
|
||
platform: 'openai',
|
||
type: 'oauth',
|
||
updated_at: '2026-03-07T10:01:00Z',
|
||
extra: {}
|
||
}
|
||
})
|
||
|
||
await flushPromises()
|
||
expect(getUsage).toHaveBeenCalledTimes(2)
|
||
expect(wrapper.text()).toContain('5h|0|200')
|
||
})
|
||
|
||
it('OpenAI OAuth 已限额时首屏优先展示重新查询后的 usage,而不是旧 codex 快照', async () => {
|
||
getUsage.mockResolvedValue({
|
||
five_hour: {
|
||
utilization: 100,
|
||
resets_at: '2026-03-07T12:00:00Z',
|
||
remaining_seconds: 3600,
|
||
window_stats: {
|
||
requests: 211,
|
||
tokens: 106540000,
|
||
cost: 38.13,
|
||
standard_cost: 38.13,
|
||
user_cost: 38.13
|
||
}
|
||
},
|
||
seven_day: {
|
||
utilization: 100,
|
||
resets_at: '2026-03-13T12:00:00Z',
|
||
remaining_seconds: 3600,
|
||
window_stats: {
|
||
requests: 211,
|
||
tokens: 106540000,
|
||
cost: 38.13,
|
||
standard_cost: 38.13,
|
||
user_cost: 38.13
|
||
}
|
||
}
|
||
})
|
||
|
||
const wrapper = mount(AccountUsageCell, {
|
||
props: {
|
||
account: {
|
||
id: 2004,
|
||
platform: 'openai',
|
||
type: 'oauth',
|
||
rate_limit_reset_at: '2099-03-07T12:00:00Z',
|
||
extra: {
|
||
codex_5h_used_percent: 0,
|
||
codex_7d_used_percent: 0
|
||
}
|
||
} as any
|
||
},
|
||
global: {
|
||
stubs: {
|
||
UsageProgressBar: {
|
||
props: ['label', 'utilization', 'resetsAt', 'windowStats', 'color'],
|
||
template: '<div class="usage-bar">{{ label }}|{{ utilization }}|{{ windowStats?.tokens }}</div>'
|
||
},
|
||
AccountQuotaInfo: true
|
||
}
|
||
}
|
||
})
|
||
|
||
await flushPromises()
|
||
|
||
expect(getUsage).toHaveBeenCalledWith(2004)
|
||
expect(wrapper.text()).toContain('5h|100|106540000')
|
||
expect(wrapper.text()).toContain('7d|100|106540000')
|
||
expect(wrapper.text()).not.toContain('5h|0|')
|
||
})
|
||
})
|