import { test } from 'node:test' import assert from 'node:assert/strict' import { createTaskPollingCoordinator } from '../src/shared/task-polling-coordinator.ts' import { useTaskProgressLoop } from '../src/shared/composables/useTaskProgressLoop.ts' test('test_terminal_called_once', () => { const called: number[] = [] const coordinator = createTaskPollingCoordinator({ onTerminal: (id) => called.push(id) }) coordinator.add(5) coordinator.markTerminal(5) coordinator.markTerminal(5) coordinator.markTerminal(5) assert.deepEqual(called, [5], '同一任务终态只回调一次') assert.equal(coordinator.stats().terminalCount, 1) }) test('test_terminal_removed_from_poll', () => { const coordinator = createTaskPollingCoordinator({}) coordinator.add(7) coordinator.add(8) coordinator.markTerminal(7) assert.deepEqual(coordinator.ids(), [8], '终态任务从轮询集合移除') }) test('test_terminal_repeat_response', async () => { const storage = new Map() ;(globalThis as Record).window = { localStorage: { getItem: (k: string) => storage.get(k) ?? null, setItem: (k: string, v: string) => { storage.set(k, v) }, removeItem: (k: string) => { storage.delete(k) }, }, setTimeout: (fn: () => void, ms: number) => globalThis.setTimeout(fn, ms), clearTimeout: (id: unknown) => globalThis.clearTimeout(id as number), setInterval: (fn: () => void, ms: number) => globalThis.setInterval(fn, ms), clearInterval: (id: unknown) => globalThis.clearInterval(id as number), } const terminalCalls: number[] = [] const coordinator = createTaskPollingCoordinator({ onTerminal: (id) => terminalCalls.push(id) }) const loop = useTaskProgressLoop<{ taskId?: number; status?: string }>({ scope: 'repeat-terminal', fetchProgress: async () => ({ items: [{ taskId: 9, status: 'SUCCESS' }] }), extractTaskId: (d) => d?.taskId, extractStatus: (d) => d?.status, getIntervalMs: () => 60000, onTerminal: (id) => { terminalCalls.push(id) }, coordinator, }) loop.reset([9]) await new Promise((r) => globalThis.setTimeout(r, 0)) await loop.refreshOnce() const once = terminalCalls.filter((id) => id === 9).length assert.equal(once, 1, '重复收到同任务终态不重复回调') assert.equal(coordinator.has(9), false, '终态任务已从协调器移除') loop.dispose() }) test('test_terminal_after_readd', () => { const called: number[] = [] const coordinator = createTaskPollingCoordinator({ onTerminal: (id) => called.push(id) }) coordinator.add(3) coordinator.markTerminal(3) coordinator.add(3) assert.ok(coordinator.has(3), '重新 add 后任务回归集合') coordinator.markTerminal(3) assert.deepEqual(called, [3, 3], '重新 add 后可再次触发') }) test('test_terminal_multiple_tasks', () => { const called: number[] = [] const coordinator = createTaskPollingCoordinator({ onTerminal: (id) => called.push(id) }) coordinator.add(1) coordinator.add(2) coordinator.add(3) coordinator.markTerminal(2) coordinator.markTerminal(1) coordinator.markTerminal(3) assert.deepEqual(called, [2, 1, 3], '多任务各自回调一次') assert.equal(coordinator.stats().terminalCount, 3) assert.deepEqual(coordinator.ids(), []) }) test('test_terminal_coordinator_order', () => { const order: string[] = [] const coordinator = createTaskPollingCoordinator({ onTerminal: (id) => order.push(`coordinator:${id}`), }) coordinator.add(4) coordinator.markTerminal(4) assert.deepEqual(order, ['coordinator:4'], 'markTerminal 先于移除(回调时任务已不在集合)') assert.equal(coordinator.has(4), false) }) test('test_terminal_storage_cleared', async () => { const storage = new Map() storage.set('term:storage', JSON.stringify([11])) ;(globalThis as Record).window = { localStorage: { getItem: (k: string) => storage.get(k) ?? null, setItem: (k: string, v: string) => { storage.set(k, v) }, removeItem: (k: string) => { storage.delete(k) }, }, setTimeout: (fn: () => void, ms: number) => globalThis.setTimeout(fn, ms), clearTimeout: (id: unknown) => globalThis.clearTimeout(id as number), setInterval: (fn: () => void, ms: number) => globalThis.setInterval(fn, ms), clearInterval: (id: unknown) => globalThis.clearInterval(id as number), } const coordinator = createTaskPollingCoordinator({}) const loop = useTaskProgressLoop<{ taskId?: number; status?: string }>({ scope: 'storage-clear', storageKey: 'term:storage', fetchProgress: async () => ({ items: [{ taskId: 11, status: 'SUCCESS' }] }), extractTaskId: (d) => d?.taskId, extractStatus: (d) => d?.status, getIntervalMs: () => 60000, onTerminal: () => {}, coordinator, }) await new Promise((r) => globalThis.setTimeout(r, 0)) await loop.refreshOnce() assert.equal(storage.has('term:storage'), false, '终态后 localStorage 同步清理') loop.dispose() }) test('test_terminal_dispose_no_call', () => { const called: number[] = [] const coordinator = createTaskPollingCoordinator({ onTerminal: (id) => called.push(id) }) coordinator.add(2) // dispose 等价于停止轮询:此后不应再有任何终态回调(没有 markTerminal) assert.equal(called.length, 0) assert.ok(coordinator.has(2), '任务仍在集合,仅停止轮询不清理') })