mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-09 10:57:03 +08:00
fix(codex): restore recovered quota status and stabilize lifecycle tests
This commit is contained in:
@@ -1395,6 +1395,8 @@ async function loadOverview(options: { cacheTtlMs?: number, silent?: boolean } =
|
||||
|
||||
async function handleSchedulingSaved(updatedProvider: ProviderWithEndpointsSummary) {
|
||||
if (!selectedProviderId.value || updatedProvider.id !== selectedProviderId.value) return
|
||||
// 保存前发出的详情读取不得覆盖这次保存返回的新配置。
|
||||
providerDataRequestId += 1
|
||||
// 优先回写保存接口返回值,避免弹窗立即重开时读到旧配置。
|
||||
if (selectedProviderData.value) {
|
||||
Object.assign(selectedProviderData.value, updatedProvider)
|
||||
@@ -1403,7 +1405,7 @@ async function handleSchedulingSaved(updatedProvider: ProviderWithEndpointsSumma
|
||||
}
|
||||
showSchedulingDialog.value = false
|
||||
showAdvancedDialog.value = false
|
||||
await loadOverview({ silent: true })
|
||||
await Promise.all([loadKeys({ silent: true }), loadOverview({ silent: true })])
|
||||
}
|
||||
|
||||
// --- Provider Selection ---
|
||||
|
||||
@@ -340,12 +340,24 @@ vi.mock('@/components/ui/refresh-button.vue', async () => {
|
||||
})
|
||||
|
||||
vi.mock('@/features/pool/components/PoolSchedulingDialog.vue', async () => {
|
||||
const { defineComponent } = await import('vue')
|
||||
const { defineComponent, h } = await import('vue')
|
||||
return {
|
||||
default: defineComponent({
|
||||
name: 'PoolSchedulingDialogStub',
|
||||
setup() {
|
||||
return () => null
|
||||
props: {
|
||||
providerId: { type: String, required: true },
|
||||
currentConfig: { type: Object, default: null },
|
||||
},
|
||||
emits: ['saved'],
|
||||
setup(props, { emit }) {
|
||||
return () => h('button', {
|
||||
'data-testid': 'save-pool-scheduling',
|
||||
'data-reserve': String(props.currentConfig?.reserve_minimum_quota ?? false),
|
||||
onClick: () => emit('saved', {
|
||||
id: props.providerId,
|
||||
pool_advanced: { ...props.currentConfig, reserve_minimum_quota: false },
|
||||
}),
|
||||
}, '保存调度测试')
|
||||
},
|
||||
}),
|
||||
}
|
||||
@@ -1278,4 +1290,80 @@ describe('PoolManagement Codex cycle stats mode', () => {
|
||||
|
||||
expect(disabledRoot.querySelector('[data-testid="pool-demand-metrics-button"]')).toBeNull()
|
||||
})
|
||||
|
||||
it('reloads account scheduling state immediately after disabling minimum quota reserve', async () => {
|
||||
const key = createPoolKey('codex', {
|
||||
scheduling_status: 'blocked',
|
||||
scheduling_reason: 'account_quota_exhausted',
|
||||
scheduling_label: '额度耗尽',
|
||||
})
|
||||
endpointMocks.getPoolOverview.mockResolvedValue({ items: [createOverview('codex')] })
|
||||
endpointMocks.listPoolKeys.mockResolvedValue(createKeyPage(key))
|
||||
endpointMocks.getProvider.mockResolvedValue(createProvider('codex', {
|
||||
pool_advanced: { reserve_minimum_quota: true },
|
||||
}))
|
||||
const root = mountPoolManagement()
|
||||
await settle()
|
||||
expect(root.querySelector('tbody')?.textContent).toContain('额度耗尽')
|
||||
endpointMocks.listPoolKeys.mockClear()
|
||||
endpointMocks.listPoolKeys.mockResolvedValue(createKeyPage({
|
||||
...key,
|
||||
scheduling_status: 'available',
|
||||
scheduling_reason: 'available',
|
||||
scheduling_label: '可用',
|
||||
}))
|
||||
|
||||
root.querySelector<HTMLButtonElement>('[data-testid="save-pool-scheduling"]')?.click()
|
||||
await settle()
|
||||
|
||||
expect(endpointMocks.listPoolKeys).toHaveBeenCalledWith(
|
||||
'codex-provider', expect.anything(), { cacheTtlMs: 0 },
|
||||
)
|
||||
expect(root.querySelector('tbody')?.textContent).not.toContain('额度耗尽')
|
||||
expect(root.querySelector('tbody')?.textContent).toContain('可用')
|
||||
})
|
||||
|
||||
it('shows 17% remaining without marking a schedulable account exhausted when reserve is enabled', async () => {
|
||||
endpointMocks.getPoolOverview.mockResolvedValue({ items: [createOverview('codex')] })
|
||||
endpointMocks.getProvider.mockResolvedValue(createProvider('codex', {
|
||||
pool_advanced: { reserve_minimum_quota: true },
|
||||
}))
|
||||
endpointMocks.listPoolKeys.mockResolvedValue(createKeyPage(createPoolKey('codex', {
|
||||
scheduling_status: 'available',
|
||||
scheduling_reason: 'available',
|
||||
scheduling_label: '可用',
|
||||
status_snapshot: {
|
||||
account: { code: 'ok', blocked: false },
|
||||
quota: {
|
||||
code: 'ok',
|
||||
exhausted: false,
|
||||
provider_type: 'codex',
|
||||
windows: [{ code: '5h', used_ratio: 0.83, remaining_ratio: 0.17 }],
|
||||
},
|
||||
oauth: { code: 'none' },
|
||||
},
|
||||
})))
|
||||
const root = mountPoolManagement()
|
||||
await settle()
|
||||
|
||||
expect(root.querySelector('[data-testid="pool-quota-meter-text"]')?.textContent).toBe('17.0%')
|
||||
expect(root.querySelector('tbody')?.textContent).toContain('可用')
|
||||
expect(root.querySelector('tbody')?.textContent).not.toContain('额度耗尽')
|
||||
})
|
||||
|
||||
it('does not replace saved scheduling settings with an older provider detail response', async () => {
|
||||
let resolveOldProvider!: (provider: ReturnType<typeof createProvider>) => void
|
||||
endpointMocks.getProvider.mockReturnValue(new Promise((resolve) => { resolveOldProvider = resolve }))
|
||||
endpointMocks.getPoolOverview.mockResolvedValue({ items: [createOverview('codex')] })
|
||||
endpointMocks.listPoolKeys.mockResolvedValue(createKeyPage(createPoolKey()))
|
||||
const root = mountPoolManagement()
|
||||
await settle()
|
||||
|
||||
root.querySelector<HTMLButtonElement>('[data-testid="save-pool-scheduling"]')?.click()
|
||||
await settle()
|
||||
resolveOldProvider(createProvider('codex', { pool_advanced: { reserve_minimum_quota: true } }))
|
||||
await settle()
|
||||
|
||||
expect(root.querySelector('[data-testid="save-pool-scheduling"]')?.getAttribute('data-reserve')).toBe('false')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user