mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-20 00:18:55 +00:00
Merge pull request #7237 from ouchihao/codex/bug-platform-quota-negative
fix(admin): reject negative platform quota limits before saving
This commit is contained in:
@@ -231,14 +231,12 @@ function onClearAll() {
|
||||
|
||||
async function onSave() {
|
||||
if (!props.user) return
|
||||
// 校验所有 input:v-model.number 在用户输入"0."等中间状态时会写回 NaN,
|
||||
// 之前的 normalizeLimit(NaN) 静默返回 null("无限制"),把"有限额"配置悄悄改成"无限制"。
|
||||
// 这里在 save 前显式检测 NaN,提示用户修正后再提交。
|
||||
// 拒绝非法数值,避免 normalizeLimit 将其静默转换为 null(无限额)。
|
||||
const invalid: string[] = []
|
||||
for (const row of quotas.value) {
|
||||
for (const win of ['daily', 'weekly', 'monthly'] as const) {
|
||||
const v = row[`${win}_limit_usd` as const]
|
||||
if (typeof v === 'number' && Number.isNaN(v)) {
|
||||
if (typeof v === 'number' && (!Number.isFinite(v) || v < 0)) {
|
||||
invalid.push(`${row.platform}.${win}`)
|
||||
}
|
||||
}
|
||||
@@ -268,7 +266,7 @@ async function onSave() {
|
||||
}
|
||||
|
||||
// 仅在合法输入下返回数字:null/undefined/NaN/±Inf/负数 → null(视为"无限额")。
|
||||
// 调用方负责在 NaN 路径上做单独的用户提示(见 onSave)。
|
||||
// 调用方在保存前拦截非法数值(见 onSave)。
|
||||
function normalizeLimit(v: number | null | undefined): number | null {
|
||||
if (v === null || v === undefined) return null
|
||||
if (typeof v === 'number' && Number.isFinite(v) && v >= 0) return v
|
||||
|
||||
@@ -3,6 +3,7 @@ import { mount, flushPromises } from '@vue/test-utils'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
|
||||
const apiMocks = vi.hoisted(() => ({
|
||||
showError: vi.fn(),
|
||||
getPlatformQuotas: vi.fn(),
|
||||
updatePlatformQuotas: vi.fn(),
|
||||
resetPlatformQuotaWindow: vi.fn(),
|
||||
@@ -20,7 +21,7 @@ vi.mock('@/api/admin', () => ({
|
||||
|
||||
vi.mock('@/stores/app', () => ({
|
||||
useAppStore: () => ({
|
||||
showError: vi.fn(),
|
||||
showError: apiMocks.showError,
|
||||
showSuccess: vi.fn(),
|
||||
}),
|
||||
}))
|
||||
@@ -74,6 +75,27 @@ beforeEach(() => {
|
||||
})
|
||||
|
||||
describe('UserPlatformQuotaModal', () => {
|
||||
it.each([0, 4, 14])('does not turn a negative limit in input %s into unlimited', async (index) => {
|
||||
const w = await mountAndOpen()
|
||||
await w.findAll('input[type=number]')[index].setValue('-1')
|
||||
await w.findAll('button').find(b => b.text() === 'admin.users.platformQuota.save')!.trigger('click')
|
||||
await flushPromises()
|
||||
expect(apiMocks.updatePlatformQuotas).not.toHaveBeenCalled()
|
||||
expect(apiMocks.showError).toHaveBeenCalledWith('admin.users.platformQuota.invalidNumber')
|
||||
expect(w.emitted('success')).toBeUndefined()
|
||||
w.unmount()
|
||||
})
|
||||
|
||||
it.each([['0', 0], ['', null]])('preserves the meaning of a limit entered as %j', async (input, expected) => {
|
||||
const w = await mountAndOpen()
|
||||
await w.findAll('input[type=number]')[0].setValue(input)
|
||||
await w.findAll('button').find(b => b.text() === 'admin.users.platformQuota.save')!.trigger('click')
|
||||
await flushPromises()
|
||||
expect(apiMocks.updatePlatformQuotas).toHaveBeenCalledTimes(1)
|
||||
expect(apiMocks.updatePlatformQuotas.mock.calls[0][1][0].daily_limit_usd).toBe(expected)
|
||||
w.unmount()
|
||||
})
|
||||
|
||||
it('挂载并 show=true 时调用 getPlatformQuotas', async () => {
|
||||
await mountAndOpen()
|
||||
expect(apiMocks.getPlatformQuotas).toHaveBeenCalledWith(99)
|
||||
|
||||
Reference in New Issue
Block a user