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 = {}): 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).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({ 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({ 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')) })