Files
X-Financial/web/tests/approval-task-queue-composable.test.mjs

202 lines
6.0 KiB
JavaScript
Raw Permalink Normal View History

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])
})