3e45aa2a91
8 个测试固化退避封顶语义:达到 maxMs 后保持恒定(含低 maxMs、 高 factor 提前封顶)、成功后从基数重新起算、恰等于 maxMs 不溢出、 循环内失败计数映射退避间隔的集成、非法 maxMs 配置拒绝。 行为由任务 29/30 的 factor 与 maxMs 配置已实现,本次为验证型测试。
111 lines
4.1 KiB
TypeScript
111 lines
4.1 KiB
TypeScript
import { test } from 'node:test'
|
||
import assert from 'node:assert/strict'
|
||
import {
|
||
getTaskPollBackoffMs,
|
||
configureTaskPolling,
|
||
resetTaskPollingConfig,
|
||
} from '../src/shared/task-progress-config.ts'
|
||
import { useTaskProgressLoop } from '../src/shared/composables/useTaskProgressLoop.ts'
|
||
|
||
test('test_backoff_cap_reached', () => {
|
||
resetTaskPollingConfig()
|
||
assert.equal(getTaskPollBackoffMs(4), 8000)
|
||
assert.equal(getTaskPollBackoffMs(5), 10000, 'attempt 5 达到封顶')
|
||
})
|
||
|
||
test('test_backoff_stays_capped', () => {
|
||
resetTaskPollingConfig()
|
||
for (const attempt of [5, 6, 7, 10, 50]) {
|
||
assert.equal(getTaskPollBackoffMs(attempt), 10000, `attempt ${attempt} 保持 maxMs`)
|
||
}
|
||
})
|
||
|
||
test('test_cap_with_low_max', () => {
|
||
resetTaskPollingConfig()
|
||
configureTaskPolling({ backoffMaxMs: 1500 })
|
||
assert.equal(getTaskPollBackoffMs(0), 500)
|
||
assert.equal(getTaskPollBackoffMs(1), 1000)
|
||
assert.equal(getTaskPollBackoffMs(2), 1500, '低 maxMs 提前封顶')
|
||
assert.equal(getTaskPollBackoffMs(3), 1500)
|
||
resetTaskPollingConfig()
|
||
})
|
||
|
||
test('test_cap_with_high_factor', () => {
|
||
resetTaskPollingConfig()
|
||
configureTaskPolling({ backoffFactor: 5 })
|
||
assert.equal(getTaskPollBackoffMs(0), 500)
|
||
assert.equal(getTaskPollBackoffMs(1), 2500)
|
||
assert.equal(getTaskPollBackoffMs(2), 10000, '高 factor 快速封顶')
|
||
assert.equal(getTaskPollBackoffMs(3), 10000)
|
||
resetTaskPollingConfig()
|
||
})
|
||
|
||
test('test_cap_resets_after_success', () => {
|
||
resetTaskPollingConfig()
|
||
// 封顶只由 attempt 驱动:attempt 归 0 即从基数重新开始
|
||
assert.equal(getTaskPollBackoffMs(0), 500)
|
||
assert.equal(getTaskPollBackoffMs(50), 10000)
|
||
assert.equal(getTaskPollBackoffMs(0), 500, '成功后从 500 起')
|
||
assert.equal(getTaskPollBackoffMs(1), 1000)
|
||
})
|
||
|
||
test('test_cap_attempt_boundary', () => {
|
||
resetTaskPollingConfig()
|
||
configureTaskPolling({ backoffMaxMs: 2000 })
|
||
// 恰好等于封顶那一档:base × factor^1 = 1000 < 2000;^2 = 2000 = 封顶
|
||
assert.equal(getTaskPollBackoffMs(1), 1000)
|
||
assert.equal(getTaskPollBackoffMs(2), 2000, '恰等于 maxMs 不溢出')
|
||
assert.equal(getTaskPollBackoffMs(3), 2000)
|
||
resetTaskPollingConfig()
|
||
})
|
||
|
||
test('test_cap_integration_loop', async () => {
|
||
resetTaskPollingConfig()
|
||
const storage = new Map<string, string>()
|
||
;(globalThis as Record<string, unknown>).window = {
|
||
localStorage: {
|
||
getItem: (k: string) => storage.get(k) ?? null,
|
||
setItem: (k: string, v: string) => { storage.set(k, v) },
|
||
removeItem: (k: string) => { storage.delete(k) },
|
||
},
|
||
setTimeout: (fn: () => void, ms: number) => globalThis.setTimeout(fn, ms),
|
||
clearTimeout: (id: unknown) => globalThis.clearTimeout(id as number),
|
||
setInterval: (fn: () => void, ms: number) => globalThis.setInterval(fn, ms),
|
||
clearInterval: (id: unknown) => globalThis.clearInterval(id as number),
|
||
}
|
||
const attempts: number[] = []
|
||
const loop = useTaskProgressLoop<{ taskId?: number; status?: string }>({
|
||
scope: 'cap-integration',
|
||
fetchProgress: async () => { throw new Error('net') },
|
||
extractTaskId: (d) => d?.taskId,
|
||
extractStatus: (d) => d?.status,
|
||
getIntervalMs: () => 60000,
|
||
onError: (_e, attempt) => attempts.push(attempt ?? -1),
|
||
})
|
||
loop.reset([1])
|
||
await new Promise<void>((r) => globalThis.setTimeout(r, 0))
|
||
for (let i = 0; i < 12; i++) {
|
||
await loop.refreshOnce()
|
||
}
|
||
const intervals = attempts.map((a) => getTaskPollBackoffMs(a))
|
||
assert.deepEqual(
|
||
intervals.slice(0, 7),
|
||
[500, 1000, 2000, 4000, 8000, 10000, 10000],
|
||
'失败计数映射到退避间隔,达到 maxMs 后保持',
|
||
)
|
||
const tail = intervals.slice(7)
|
||
assert.ok(tail.every((v) => v === 10000), '封顶后间隔恒等于 maxMs')
|
||
loop.dispose()
|
||
resetTaskPollingConfig()
|
||
})
|
||
|
||
test('test_cap_config_negative', () => {
|
||
resetTaskPollingConfig()
|
||
assert.throws(() => configureTaskPolling({ backoffMaxMs: 0 }), /退避上限必须为正数/)
|
||
assert.throws(() => configureTaskPolling({ backoffMaxMs: -5 }), /退避上限必须为正数/)
|
||
// 拒绝后退避仍按默认值工作,不产生非法间隔
|
||
assert.equal(getTaskPollBackoffMs(0), 500)
|
||
assert.equal(getTaskPollBackoffMs(50), 10000)
|
||
resetTaskPollingConfig()
|
||
})
|