import assert from 'node:assert/strict' import test from 'node:test' import { useApprovalTaskQueue } from '../src/composables/useApprovalTaskQueue.js' function queueItem(id, overrides = {}) { return { task: { id, claimId: `claim-${id}`, status: 'pending', version: 1, canAct: true, availableActions: ['approve'], batchEligible: true, claimStatusSnapshot: 'submitted', claimStageSnapshot: '直属领导审批', ...overrides }, claim: { id: `claim-${id}`, claim_no: `BX-${id}` } } } function listResult(items, page = 1) { return { items, total: items.length, page, pageSize: 20, totalPages: items.length ? 1 : 0, generatedAt: '2026-07-16T10:00:00Z' } } function deferred() { let resolve let reject const promise = new Promise((resolvePromise, rejectPromise) => { resolve = resolvePromise reject = rejectPromise }) return { promise, reject, resolve } } test('列表请求代次阻止慢请求覆盖新页结果', async () => { const requests = [] const queue = useApprovalTaskQueue({ services: { fetchApprovalTasks(params) { const request = deferred() requests.push({ params, request }) return request.promise } } }) const firstLoad = queue.loadQueue() await queue.setPage(2, { reload: false }) const secondLoad = queue.loadQueue() requests[1].request.resolve(listResult([queueItem('task-new')], 2)) await secondLoad requests[0].request.resolve(listResult([queueItem('task-stale')], 1)) const staleResult = await firstLoad assert.equal(requests[0].params.page, 1) assert.equal(requests[1].params.page, 2) assert.equal(staleResult.ignored, true) assert.equal(queue.items.value[0].task.id, 'task-new') assert.equal(queue.page.value, 2) }) test('筛选变化清空选择并回到第一页', async () => { const queue = useApprovalTaskQueue({ services: { async fetchApprovalTasks() { return listResult([queueItem('task-001')]) } } }) await queue.loadQueue() queue.toggleTask(queue.items.value[0], true) await queue.setPage(3, { reload: false }) assert.deepEqual(queue.selectedTaskIds.value, ['task-001']) await queue.setFilters({ riskLevel: 'high' }, { reload: false }) assert.equal(queue.page.value, 1) assert.equal(queue.filters.value.riskLevel, 'high') assert.deepEqual(queue.selectedTaskIds.value, []) }) test('批量部分成功保留失败选择并使用逐项任务快照', async () => { const batchCalls = [] let listCallCount = 0 const rows = [queueItem('task-001'), queueItem('task-002')] const queue = useApprovalTaskQueue({ services: { async fetchApprovalTasks() { listCallCount += 1 return listResult(rows) }, async batchApproveApprovalTasks(payload) { batchCalls.push(payload) return { batchRequestId: payload.batchRequestId, status: 'partial', succeededCount: 1, replayedCount: 0, conflictCount: 0, blockedCount: 1, forbiddenCount: 0, failedCount: 0, items: [ { taskId: 'task-001', status: 'succeeded' }, { taskId: 'task-002', status: 'blocked', message: '存在开放高风险' } ] } } } }) await queue.loadQueue() queue.toggleCurrentPage(true) const result = await queue.batchApprove({ opinion: '批量核对完成' }) assert.equal(listCallCount, 2) assert.equal(batchCalls.length, 1) assert.equal(batchCalls[0].items[0].task.id, 'task-001') assert.equal(batchCalls[0].items[0].opinion, '批量核对完成') assert.equal(result.status, 'partial') assert.deepEqual(queue.selectedTaskIds.value, ['task-002']) assert.equal(queue.lastBatchResult.value.items[1].status, 'blocked') assert.equal(queue.pendingBatchRequestId.value, '') }) test('单项刷新采用服务端最新权限并移除失效选择', async () => { const queue = useApprovalTaskQueue({ services: { async fetchApprovalTasks() { return listResult([queueItem('task-001')]) }, async fetchApprovalTaskDetail() { return queueItem('task-001', { version: 2, canAct: false, availableActions: [], batchEligible: false, readOnlyReason: '任务已转交给其他审批人' }) } } }) await queue.loadQueue() queue.toggleTask(queue.items.value[0], true) await queue.refreshTask('task-001') assert.equal(queue.items.value[0].task.version, 2) assert.equal(queue.items.value[0].task.readOnlyReason, '任务已转交给其他审批人') assert.deepEqual(queue.selectedTaskIds.value, []) }) test('批量选择在前端基础层限制为服务端允许的 20 项', async () => { const rows = Array.from({ length: 21 }, (_, index) => queueItem(`task-${index + 1}`)) const queue = useApprovalTaskQueue({ services: { async fetchApprovalTasks() { return listResult(rows) } } }) await queue.loadQueue() queue.toggleCurrentPage(true) assert.equal(queue.selectedTaskIds.value.length, 20) assert.match(queue.errorMessage.value, /最多选择 20 项/) }) test('批量失败按相同负载复用幂等键,编辑意见后生成新键', async () => { const requestIds = [] const queue = useApprovalTaskQueue({ services: { async fetchApprovalTasks() { return listResult([queueItem('task-retry')]) }, async batchApproveApprovalTasks(payload) { requestIds.push(payload.batchRequestId) throw new Error('模拟网络中断') } } }) await queue.loadQueue() queue.toggleCurrentPage(true) await assert.rejects(queue.batchApprove({ opinion: '核对通过' }), /模拟网络中断/) await assert.rejects(queue.batchApprove({ opinion: '核对通过' }), /模拟网络中断/) await assert.rejects(queue.batchApprove({ opinion: '补充核对后通过' }), /模拟网络中断/) assert.equal(requestIds[1], requestIds[0]) assert.notEqual(requestIds[2], requestIds[0]) })