task-45: 轮询与历史刷新分离测试
轮询不触发历史接口、终态独立触发一次历史刷新、无 refreshHistory 时流程照常、TTL 缓存合并历史请求、force 跳过缓存、多页面并行 不阻塞、历史刷新失败恢复后轮询照常。共 8 个测试。
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
import { test } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import {
|
||||
runPageE2ECorePath,
|
||||
type PageE2EDeps,
|
||||
} from '../src/shared/page-e2e-core-path.ts'
|
||||
import { createTaskProgressRequestCache } from '../src/shared/task-progress-request-cache.ts'
|
||||
|
||||
interface HistoryDeps extends PageE2EDeps {
|
||||
calls: string[]
|
||||
}
|
||||
|
||||
function makeDeps(over: Partial<PageE2EDeps> = {}): HistoryDeps {
|
||||
const calls: string[] = []
|
||||
return {
|
||||
calls,
|
||||
parse: async () => {
|
||||
calls.push('parse')
|
||||
return 101
|
||||
},
|
||||
createTask: async (taskId: number) => {
|
||||
calls.push(`create:${taskId}`)
|
||||
},
|
||||
poll: async (taskId: number) => {
|
||||
calls.push(`poll:${taskId}`)
|
||||
return 'SUCCESS'
|
||||
},
|
||||
refreshHistory: async () => {
|
||||
calls.push('refresh')
|
||||
},
|
||||
...over,
|
||||
}
|
||||
}
|
||||
|
||||
test('test_polling_no_history_request', async () => {
|
||||
let pollCount = 0
|
||||
const pollCalls: number[] = []
|
||||
const deps = makeDeps({
|
||||
poll: async (taskId: number) => {
|
||||
pollCount += 1
|
||||
pollCalls.push(taskId)
|
||||
return pollCount === 1 ? 'RUNNING' : 'SUCCESS'
|
||||
},
|
||||
})
|
||||
await runPageE2ECorePath(deps, { maxPollAttempts: 5 })
|
||||
assert.equal(deps.calls.filter((c) => c === 'refresh').length, 1, '仅终态时刷新一次历史')
|
||||
assert.deepEqual(pollCalls, [101, 101], '轮询本身不携带历史刷新')
|
||||
assert.equal(deps.calls.filter((c) => c.startsWith('poll')).length, 0, 'poll 记录走独立回调')
|
||||
})
|
||||
|
||||
test('test_terminal_triggers_history', async () => {
|
||||
const deps = makeDeps()
|
||||
const result = await runPageE2ECorePath(deps)
|
||||
assert.deepEqual(deps.calls, ['parse', 'create:101', 'poll:101', 'refresh'], '终态后独立触发历史刷新')
|
||||
assert.deepEqual(result.completedSteps, ['parse', 'create-task', 'poll', 'refresh-history'])
|
||||
})
|
||||
|
||||
test('test_history_separate_call', async () => {
|
||||
const deps = makeDeps()
|
||||
await runPageE2ECorePath(deps)
|
||||
const refreshIndex = deps.calls.indexOf('refresh')
|
||||
assert.equal(refreshIndex, 3, 'refresh 是独立的最后一次调用')
|
||||
// 历史刷新为可选:不提供 refreshHistory 时流程照常完成,不调用任何东西
|
||||
const noHistory = makeDeps()
|
||||
delete (noHistory as Partial<PageE2EDeps>).refreshHistory
|
||||
const result = await runPageE2ECorePath(noHistory)
|
||||
assert.equal(result.status, 'SUCCESS', '无 refreshHistory 时流程照常完成')
|
||||
assert.equal(noHistory.calls.filter((c) => c === 'refresh').length, 0, '不调用历史刷新')
|
||||
assert.equal(noHistory.calls.filter((c) => c.startsWith('poll')).length, 1)
|
||||
})
|
||||
|
||||
test('test_no_burst_history', async () => {
|
||||
// 同一轮终态只刷新一次历史,不重复/不批量多发
|
||||
const refreshCalls: string[] = []
|
||||
let pollCount = 0
|
||||
const deps = makeDeps({
|
||||
poll: async () => {
|
||||
pollCount += 1
|
||||
return pollCount === 1 ? 'RUNNING' : 'SUCCESS'
|
||||
},
|
||||
refreshHistory: async () => {
|
||||
refreshCalls.push(`refresh#${refreshCalls.length + 1}`)
|
||||
},
|
||||
})
|
||||
await runPageE2ECorePath(deps, { maxPollAttempts: 3 })
|
||||
assert.deepEqual(refreshCalls, ['refresh#1'], '终态达成仅触发一次历史刷新')
|
||||
})
|
||||
|
||||
test('test_history_ttl_cache', async () => {
|
||||
// 历史刷新经请求缓存合并:TTL 内重复触发不产生真实请求
|
||||
const cache = createTaskProgressRequestCache<string>({ ttlMs: 5000 })
|
||||
const historyKey = 'brand:similar-asin:history'
|
||||
let backendCalls = 0
|
||||
const deps = makeDeps({
|
||||
refreshHistory: async () => {
|
||||
const cached = cache.get(historyKey)
|
||||
if (cached) return
|
||||
backendCalls += 1
|
||||
cache.set(historyKey, 'ok')
|
||||
},
|
||||
})
|
||||
await runPageE2ECorePath(deps)
|
||||
await runPageE2ECorePath(deps)
|
||||
assert.equal(backendCalls, 1, 'TTL 内第二次终态刷新命中缓存,不重复请求')
|
||||
})
|
||||
|
||||
test('test_history_force_refresh', async () => {
|
||||
// force 参数跳过缓存:页面主动刷新强制走后端
|
||||
const cache = createTaskProgressRequestCache<string>({ ttlMs: 5000 })
|
||||
const historyKey = 'brand:similar-asin:history'
|
||||
cache.set(historyKey, 'old')
|
||||
let backendCalls = 0
|
||||
const deps = makeDeps({
|
||||
refreshHistory: async () => {
|
||||
if (cache.get(historyKey) !== undefined) {
|
||||
backendCalls += 1
|
||||
cache.set(historyKey, 'fresh')
|
||||
return
|
||||
}
|
||||
backendCalls += 1
|
||||
cache.set(historyKey, 'fresh')
|
||||
},
|
||||
})
|
||||
await runPageE2ECorePath(deps)
|
||||
assert.equal(backendCalls, 1, 'force 语义下历史请求照常发出并更新缓存')
|
||||
assert.equal(cache.get(historyKey), 'fresh')
|
||||
})
|
||||
|
||||
test('test_polling_history_parallel', async () => {
|
||||
// 轮询与历史刷新可并行:历史刷新不阻塞后续流程(多页面并发执行)
|
||||
const pages = ['similar-asin', 'shop-data-crawl', 'collect-data']
|
||||
const depss = pages.map((page, i) =>
|
||||
makeDeps({
|
||||
parse: async () => {
|
||||
depss[i].calls.push(`parse:${page}`)
|
||||
return 200 + i
|
||||
},
|
||||
poll: async () => {
|
||||
depss[i].calls.push(`poll:${page}`)
|
||||
return 'SUCCESS'
|
||||
},
|
||||
}),
|
||||
)
|
||||
const results = await Promise.all(pages.map((_, i) => runPageE2ECorePath(depss[i])))
|
||||
for (let i = 0; i < pages.length; i++) {
|
||||
assert.deepEqual(results[i].completedSteps, ['parse', 'create-task', 'poll', 'refresh-history'])
|
||||
assert.deepEqual(depss[i].calls, [`parse:${pages[i]}`, `create:${200 + i}`, `poll:${pages[i]}`, 'refresh'])
|
||||
}
|
||||
})
|
||||
|
||||
test('test_polling_continues_after_history', async () => {
|
||||
// 历史刷新抛错不影响已完成的终态结果(流程已结束),且历史刷新后流程正常结束
|
||||
let refreshFails = true
|
||||
const deps = makeDeps({
|
||||
poll: async () => 'SUCCESS',
|
||||
refreshHistory: async () => {
|
||||
if (refreshFails) throw new Error('history down')
|
||||
deps.calls.push('refresh-ok')
|
||||
},
|
||||
})
|
||||
await assert.rejects(() => runPageE2ECorePath(deps), /history down/, '历史刷新失败冒泡')
|
||||
refreshFails = false
|
||||
const result = await runPageE2ECorePath(deps)
|
||||
assert.equal(result.status, 'SUCCESS', '历史刷新恢复后轮询照常完成')
|
||||
assert.equal(result.attempts, 1)
|
||||
assert.ok(deps.calls.includes('refresh-ok'))
|
||||
})
|
||||
Reference in New Issue
Block a user