37eabcbb2e
task-progress-config.ts 新增 getTaskPollBackoffMaxMs()(默认 10000, 由 5000 提升)与 getTaskPollBackoffFactor()(默认 2,<1 钳制为 1); configureTaskPolling 支持 backoffFactor;getTaskPollBackoffMs 改用 base × factor^attempt 计算。 既有 task-086 边界测试中封顶 5000 的快照随新默认更新为 10000。 新增 8 个测试:max/factor 默认、可覆盖、非正钳制、factor 下限、 配置隔离、默认值快照。
150 lines
6.4 KiB
TypeScript
150 lines
6.4 KiB
TypeScript
import { test } from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import {
|
|
TASK_POLL_VISIBLE_INTERVAL_MS,
|
|
TASK_POLL_HIDDEN_INTERVAL_MS,
|
|
TASK_POLL_BACKOFF_BASE_MS,
|
|
TASK_POLL_BACKOFF_MAX_MS,
|
|
getTaskPollIntervalMs,
|
|
getTaskProgressCacheTtlMs,
|
|
getTaskForegroundRefreshEnabled,
|
|
getTaskForegroundRefreshDelayMs,
|
|
getTaskPollBackoffMs,
|
|
configureTaskPolling,
|
|
resetTaskPollingConfig,
|
|
} from '../src/shared/task-progress-config.ts'
|
|
|
|
function withVisibility(visible: boolean, fn: () => void) {
|
|
const original = (globalThis as Record<string, unknown>).document
|
|
const fakeDocument = { visibilityState: visible ? 'visible' : 'hidden', hidden: !visible }
|
|
Object.defineProperty(globalThis, 'document', {
|
|
value: fakeDocument,
|
|
configurable: true,
|
|
writable: true,
|
|
})
|
|
try {
|
|
fn()
|
|
} finally {
|
|
Object.defineProperty(globalThis, 'document', {
|
|
value: original,
|
|
configurable: true,
|
|
writable: true,
|
|
})
|
|
}
|
|
}
|
|
|
|
test('test_task_086_polling_normal_default_path', () => {
|
|
assert.equal(getTaskPollBackoffMs(0), TASK_POLL_BACKOFF_BASE_MS)
|
|
assert.equal(getTaskPollBackoffMs(1), 1000)
|
|
assert.equal(getTaskForegroundRefreshEnabled(), true)
|
|
assert.equal(getTaskForegroundRefreshDelayMs(), 0)
|
|
assert.equal(getTaskPollIntervalMs(), TASK_POLL_VISIBLE_INTERVAL_MS)
|
|
assert.equal(getTaskProgressCacheTtlMs(), 5000)
|
|
// hidden 分支:可见性变化切换间隔
|
|
withVisibility(false, () => {
|
|
assert.equal(getTaskPollIntervalMs(), TASK_POLL_HIDDEN_INTERVAL_MS)
|
|
assert.equal(getTaskProgressCacheTtlMs(), 30000)
|
|
})
|
|
withVisibility(true, () => {
|
|
assert.equal(getTaskPollIntervalMs(), TASK_POLL_VISIBLE_INTERVAL_MS)
|
|
assert.equal(getTaskProgressCacheTtlMs(), 5000)
|
|
})
|
|
})
|
|
|
|
test('test_task_086_polling_normal_multiple_items', () => {
|
|
resetTaskPollingConfig()
|
|
configureTaskPolling({ backoffBaseMs: 1000, backoffMaxMs: 10_000 })
|
|
assert.deepEqual([0, 1, 2, 3].map((n) => getTaskPollBackoffMs(n)), [1000, 2000, 4000, 8000])
|
|
configureTaskPolling({ backoffBaseMs: 200, backoffMaxMs: 1600 })
|
|
assert.deepEqual([0, 1, 2, 3, 4].map((n) => getTaskPollBackoffMs(n)), [200, 400, 800, 1600, 1600])
|
|
configureTaskPolling({ foregroundRefreshEnabled: false })
|
|
assert.equal(getTaskForegroundRefreshEnabled(), false)
|
|
configureTaskPolling({ foregroundRefreshDelayMs: 250 })
|
|
assert.equal(getTaskForegroundRefreshDelayMs(), 250)
|
|
assert.equal(getTaskForegroundRefreshEnabled(), false, '未覆盖的字段保持原值')
|
|
assert.equal(getTaskPollBackoffMs(0), 200, '未覆盖的字段保持原值')
|
|
resetTaskPollingConfig()
|
|
})
|
|
|
|
test('test_task_086_polling_normal_repeated_operation_is_idempotent', () => {
|
|
resetTaskPollingConfig()
|
|
configureTaskPolling({ backoffBaseMs: 300, backoffMaxMs: 2400 })
|
|
configureTaskPolling({ backoffBaseMs: 300, backoffMaxMs: 2400 })
|
|
configureTaskPolling({ backoffBaseMs: 300, backoffMaxMs: 2400 })
|
|
assert.deepEqual([0, 1, 2, 3].map((n) => getTaskPollBackoffMs(n)), [300, 600, 1200, 2400])
|
|
configureTaskPolling({ foregroundRefreshEnabled: true })
|
|
configureTaskPolling({ foregroundRefreshEnabled: true })
|
|
assert.equal(getTaskForegroundRefreshEnabled(), true)
|
|
resetTaskPollingConfig()
|
|
})
|
|
|
|
test('test_task_086_polling_boundary_empty_input', () => {
|
|
resetTaskPollingConfig()
|
|
configureTaskPolling({})
|
|
assert.equal(getTaskPollBackoffMs(0), TASK_POLL_BACKOFF_BASE_MS)
|
|
assert.equal(getTaskForegroundRefreshEnabled(), true)
|
|
assert.equal(getTaskForegroundRefreshDelayMs(), 0)
|
|
resetTaskPollingConfig()
|
|
})
|
|
|
|
test('test_task_086_polling_boundary_single_item', () => {
|
|
resetTaskPollingConfig()
|
|
configureTaskPolling({ backoffMaxMs: 999 })
|
|
assert.equal(getTaskPollBackoffMs(0), TASK_POLL_BACKOFF_BASE_MS, '未配置字段不受影响')
|
|
assert.equal(getTaskPollBackoffMs(5), 999, '超过上限封顶')
|
|
configureTaskPolling({ foregroundRefreshEnabled: false })
|
|
assert.equal(getTaskForegroundRefreshEnabled(), false)
|
|
assert.equal(getTaskPollBackoffMs(5), 999, '配置之间互不影响')
|
|
resetTaskPollingConfig()
|
|
})
|
|
|
|
test('test_task_086_polling_boundary_limit_and_overflow', () => {
|
|
resetTaskPollingConfig()
|
|
// attempt 超过最大重试次数后封顶,不发生无界增长
|
|
assert.equal(getTaskPollBackoffMs(0), 500)
|
|
assert.equal(getTaskPollBackoffMs(1), 1000)
|
|
assert.equal(getTaskPollBackoffMs(2), 2000)
|
|
assert.equal(getTaskPollBackoffMs(3), 4000)
|
|
assert.equal(getTaskPollBackoffMs(4), 8000)
|
|
assert.equal(getTaskPollBackoffMs(5), 10000)
|
|
assert.equal(getTaskPollBackoffMs(10), 10000)
|
|
assert.equal(getTaskPollBackoffMs(100), 10000)
|
|
// attempt 为 0 或负数按首次退避处理
|
|
assert.equal(getTaskPollBackoffMs(-1), 500)
|
|
assert.equal(getTaskPollBackoffMs(NaN), 500)
|
|
assert.equal(getTaskPollBackoffMs(0.5), 500)
|
|
resetTaskPollingConfig()
|
|
})
|
|
|
|
test('test_task_086_polling_invalid_input_rejected', () => {
|
|
resetTaskPollingConfig()
|
|
assert.throws(() => configureTaskPolling({ backoffBaseMs: 0 }), /退避基数必须为正数/)
|
|
assert.throws(() => configureTaskPolling({ backoffBaseMs: -1 }), /退避基数必须为正数/)
|
|
assert.throws(() => configureTaskPolling({ backoffMaxMs: 0 }), /退避上限必须为正数/)
|
|
assert.throws(() => configureTaskPolling({ backoffMaxMs: -100 }), /退避上限必须为正数/)
|
|
assert.throws(() => configureTaskPolling({ foregroundRefreshDelayMs: -1 }), /恢复延迟不能为负数/)
|
|
// 拒绝后配置保持原值(零状态变更)
|
|
assert.equal(getTaskPollBackoffMs(0), TASK_POLL_BACKOFF_BASE_MS)
|
|
assert.equal(getTaskForegroundRefreshEnabled(), true)
|
|
resetTaskPollingConfig()
|
|
})
|
|
|
|
test('test_task_086_polling_dependency_failure_releases_resources', () => {
|
|
resetTaskPollingConfig()
|
|
// document 不可用(Node 环境/SSR):按可见间隔降级,不抛错
|
|
const original = (globalThis as Record<string, unknown>).document
|
|
Object.defineProperty(globalThis, 'document', { value: undefined, configurable: true, writable: true })
|
|
try {
|
|
assert.equal(getTaskPollIntervalMs(), TASK_POLL_VISIBLE_INTERVAL_MS)
|
|
assert.equal(getTaskProgressCacheTtlMs(), 5000)
|
|
} finally {
|
|
Object.defineProperty(globalThis, 'document', { value: original, configurable: true, writable: true })
|
|
}
|
|
// 配置校验失败后错误可恢复:修正后同一配置系统继续工作
|
|
assert.throws(() => configureTaskPolling({ backoffBaseMs: -5 }), /退避基数必须为正数/)
|
|
configureTaskPolling({ backoffBaseMs: 700, backoffMaxMs: 2800 })
|
|
assert.equal(getTaskPollBackoffMs(0), 700)
|
|
assert.equal(getTaskPollBackoffMs(2), 2800)
|
|
resetTaskPollingConfig()
|
|
})
|