import { test } from 'node:test' import assert from 'node:assert/strict' import { runPageE2ECorePath, type PageE2EDeps } from '../src/shared/page-e2e-core-path.ts' const terminal = (status: string) => status === 'SUCCESS' || status === 'FAILED' function makeDeps(over: Partial = {}): PageE2EDeps & { calls: string[] } { 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_task_093_collect_asin_e2e_normal_default_path', async () => { const deps = makeDeps() const result = await runPageE2ECorePath(deps) assert.equal(result.taskId, 101) assert.equal(result.status, 'SUCCESS') assert.deepEqual(result.completedSteps, ['parse', 'create-task', 'poll', 'refresh-history']) assert.equal(result.attempts, 1) assert.deepEqual(deps.calls, ['parse', 'create:101', 'poll:101', 'refresh']) }) test('test_task_093_collect_asin_e2e_normal_multiple_items', 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]))) assert.equal(results.length, 3) assert.deepEqual( results.map((r) => r.taskId), [200, 201, 202], ) for (let i = 0; i < 3; i++) { assert.equal(results[i].status, 'SUCCESS') 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_task_093_collect_asin_e2e_normal_repeated_operation_is_idempotent', async () => { const deps = makeDeps() const first = await runPageE2ECorePath(deps) const second = await runPageE2ECorePath(deps) assert.equal(first.taskId, second.taskId) assert.deepEqual(first.completedSteps, second.completedSteps) // 重复执行不产生重复状态:调用计数线性增长,无残留 assert.equal(deps.calls.filter((c) => c === 'parse').length, 2) assert.equal(deps.calls.filter((c) => c === 'refresh').length, 2) assert.equal(deps.calls.length, 8) }) test('test_task_093_collect_asin_e2e_boundary_empty_input', async () => { // parse 无可处理数据:安全跳过建任务/轮询/刷新,不创建无效资源 const deps = makeDeps({ parse: async () => 0 }) const result = await runPageE2ECorePath(deps) assert.equal(result.taskId, 0) assert.deepEqual(result.completedSteps, ['parse']) assert.deepEqual(deps.calls, [], 'parse 返回 0 时不再触发任何调用') // 空步骤序列等价处理:不执行任何步骤 const empty = makeDeps({ parse: async () => 0 }) assert.deepEqual((await runPageE2ECorePath(empty)).completedSteps, ['parse']) }) test('test_task_093_collect_asin_e2e_boundary_single_item', async () => { // 单任务:一次轮询即终态,不依赖批量路径 const deps = makeDeps() const result = await runPageE2ECorePath(deps, { maxPollAttempts: 3 }) assert.equal(result.taskId, 101) assert.equal(result.attempts, 1) assert.equal(result.status, 'SUCCESS') assert.equal(deps.calls.filter((c) => c.startsWith('poll')).length, 1) }) test('test_task_093_collect_asin_e2e_boundary_limit_and_overflow', async () => { // 轮询次数有界:超过 maxPollAttempts 降级返回当前状态,不发生无界轮询 let pollCount = 0 const deps = makeDeps({ poll: async () => { pollCount += 1 return 'RUNNING' }, }) const result = await runPageE2ECorePath(deps, { maxPollAttempts: 4 }) assert.equal(result.status, 'RUNNING', '超过上限降级返回,不抛错') assert.equal(result.attempts, 4) assert.equal(pollCount, 4) assert.deepEqual(result.completedSteps, ['parse', 'create-task', 'poll', 'poll', 'poll', 'poll']) assert.ok(!result.completedSteps.includes('refresh-history'), '终态未达成不刷新历史') }) test('test_task_093_collect_asin_e2e_invalid_input_rejected', async () => { await assert.rejects(() => runPageE2ECorePath(null as never), /deps 必须是对象/) await assert.rejects(() => runPageE2ECorePath({} as never), /parse 必须是函数/) await assert.rejects( () => runPageE2ECorePath({ parse: async () => 1 } as never), /createTask 必须是函数/, ) const missingPoll = makeDeps() delete (missingPoll as Partial).poll await assert.rejects(() => runPageE2ECorePath(missingPoll as never), /poll 必须是函数/) const deps = makeDeps() await assert.rejects(() => runPageE2ECorePath(deps, { maxPollAttempts: 0 }), /maxPollAttempts 必须为正数/) await assert.rejects(() => runPageE2ECorePath(deps, { maxPollAttempts: -1 }), /maxPollAttempts 必须为正数/) // parse 返回非法 taskId const badTask = makeDeps({ parse: async () => -5 }) await assert.rejects(() => runPageE2ECorePath(badTask), /parse 必须返回正整数 taskId/) }) test('test_task_093_collect_asin_e2e_dependency_failure_releases_resources', async () => { // 断网:poll 抛错 → 执行终止、cleanup 释放资源;恢复后同一 deps 重跑成功 let broken = true let cleaned = 0 const deps = makeDeps({ poll: async (taskId: number) => { if (broken) throw new Error('Network Error') return 'SUCCESS' }, cleanup: async () => { cleaned += 1 }, }) await assert.rejects(() => runPageE2ECorePath(deps), /Network Error/) assert.equal(cleaned, 1, '失败后 cleanup 释放资源') broken = false const result = await runPageE2ECorePath(deps) assert.equal(result.status, 'SUCCESS') assert.equal(cleaned, 2, '成功路径同样清理') assert.equal(result.taskId, 101) // 中途失败(createTask 抛错)→ 剩余步骤跳过 + cleanup 执行 let createFailed = true const deps2 = makeDeps({ createTask: async (taskId: number) => { if (createFailed) throw new Error('create down') deps2.calls.push(`create:${taskId}`) }, cleanup: async () => { cleaned += 1 }, }) await assert.rejects(() => runPageE2ECorePath(deps2), /create down/) assert.equal(cleaned, 3) assert.deepEqual(deps2.calls, ['parse'], 'create 失败后不再 poll/refresh') createFailed = false const retry = await runPageE2ECorePath(deps2) assert.equal(retry.status, 'SUCCESS') assert.equal(cleaned, 4) })