import { test } from 'node:test' import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import { fileURLToPath } from 'node:url' import { dirname, join } from 'node:path' /** * 「行数据按需拉取」接线守卫(审查 F5 后续): * 5 个品牌工具页用轻量端点的结果行版本跳过重型 batch;这里守住两件最容易写错的事 * ——①接线没被误删;②跳过分支的返回值必须符合调用方契约(曾经把 shopDataCrawl 写成裸 return, * 而调用方 waitForTerminal 把 falsy 当「任务已消失 → FAILED」,会误杀正在跑的任务)。 */ const PAGES_DIR = join(dirname(fileURLToPath(import.meta.url)), '../src/pages/brand/components') const WIRED_PAGES = [ 'BrandQueryAsinTab.vue', 'BrandWithdrawTab.vue', 'BrandPatrolDeleteTab.vue', 'BrandShopDataCrawlTab.vue', 'BrandAppearancePatentTab.vue', ] function sourceOf(page: string) { return readFileSync(join(PAGES_DIR, page), 'utf8') } test('已接线的品牌工具页仍使用结果行版本跳过重型 batch', () => { for (const page of WIRED_PAGES) { const source = sourceOf(page) assert.ok(source.includes('createRowsVersionTracker'), `${page} 缺少版本跟踪器声明`) assert.ok(source.includes('selectTasksNeedingRows'), `${page} 缺少按需拉取判断`) assert.ok(source.includes('getModuleProgressLight'), `${page} 缺少轻量端点调用`) } }) test('轻量请求失败必须回退为全量拉取(不得因此少拉任务)', () => { // 多任务页:catch 里把 heavyIds 还原成全部任务 for (const page of ['BrandQueryAsinTab.vue', 'BrandWithdrawTab.vue', 'BrandPatrolDeleteTab.vue']) { assert.match(sourceOf(page), /catch\s*\{[\s\S]{0,200}?heavyIds = ids/, `${page} 缺少轻量失败回退`) } assert.match(sourceOf('BrandAppearancePatentTab.vue'), /catch\s*\{[\s\S]{0,200}?heavyIds = taskIds/, 'BrandAppearancePatentTab 缺少轻量失败回退') // 单任务页:catch 里不能有 return(直接落到批量拉取) const shopDataCrawl = sourceOf('BrandShopDataCrawlTab.vue') const catchBlock = shopDataCrawl.match(/catch\s*\{([^}]*)\}/)?.[1] ?? '' assert.ok(catchBlock.length > 0, 'BrandShopDataCrawlTab 未找到 catch 分支') assert.ok(!catchBlock.includes('return'), 'BrandShopDataCrawlTab 轻量失败时不得提前返回,必须照旧拉批量') }) test('跳过分支的返回值符合调用方契约', () => { const shopDataCrawl = sourceOf('BrandShopDataCrawlTab.vue') // waitForTerminal: `if (!exists) return 'FAILED'` —— 任务仍在时必须返回 true assert.match(shopDataCrawl, /selectTasksNeedingRows\([\s\S]{0,200}?return true/, 'BrandShopDataCrawlTab 跳过分支必须 return true(false/undefined 会被当成任务已消失)') const patrolDelete = sourceOf('BrandPatrolDeleteTab.vue') // waitForTaskTerminal: `missingTaskIds.includes(taskId)` —— 未消失时返回空数组 assert.match(patrolDelete, /selectTasksNeedingRows\([\s\S]{0,300}?return \[\]/, 'BrandPatrolDeleteTab 跳过分支必须返回空 missingTaskIds 数组') })