fix: 完善自动导入异常中断与恢复
- 自动导入基础设施异常触发批次熔断,保留完整日志并输出安全错误信息 - 增加恢复令牌与租约围栏、无文档失败项重建及消息退避机制 - 前端展示中断状态并在状态请求失败后自动恢复轮询 - 补充批次中断迁移、配置与并发异常路径测试
This commit is contained in:
@@ -153,4 +153,95 @@ describe('documentImportBatchStatus', () => {
|
||||
expect(wrapper.find('.batch-status__continue').exists()).toBe(false);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('中断批次展示异常原因、错误标识和继续入口', async () => {
|
||||
apiMocks.get.mockResolvedValue({
|
||||
data: {
|
||||
...createBatch('RUNNING', 0),
|
||||
failedCount: 2,
|
||||
interruptCode: 'redis_unavailable',
|
||||
interruptedAt: '2026-08-07T09:25:28+08:00',
|
||||
interruptMessage: '缓存与消息服务异常,自动导入已中断',
|
||||
pendingCount: 0,
|
||||
status: 'INTERRUPTED',
|
||||
},
|
||||
errorCode: 0,
|
||||
});
|
||||
|
||||
const wrapper = mount(DocumentImportBatchStatus, {
|
||||
props: {
|
||||
knowledgeId: 'knowledge-1',
|
||||
manageable: true,
|
||||
},
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
const alert = wrapper.find('.batch-status__alert');
|
||||
expect(alert.exists()).toBe(true);
|
||||
expect(alert.text()).toContain('缓存与消息服务异常,自动导入已中断');
|
||||
expect(alert.text()).toContain('redis_unavailable');
|
||||
expect(wrapper.find('.batch-status__count').text()).toContain('2 / 2');
|
||||
expect(wrapper.find('.batch-status__continue').exists()).toBe(true);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('状态接口暂时失败时展示错误并自动退避恢复轮询', async () => {
|
||||
vi.useFakeTimers();
|
||||
apiMocks.get
|
||||
.mockRejectedValueOnce(new Error('network unavailable'))
|
||||
.mockResolvedValueOnce({
|
||||
data: createBatch('RUNNING', 1),
|
||||
errorCode: 0,
|
||||
});
|
||||
|
||||
const wrapper = mount(DocumentImportBatchStatus, {
|
||||
props: { knowledgeId: 'knowledge-1' },
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
expect(wrapper.find('.batch-status--error').exists()).toBe(true);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(3000);
|
||||
await flushPromises();
|
||||
|
||||
expect(apiMocks.get).toHaveBeenCalledTimes(2);
|
||||
expect(wrapper.find('.batch-status--error').exists()).toBe(false);
|
||||
expect(wrapper.find('.batch-status').exists()).toBe(true);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('运行批次轮询失败时保留旧状态、展示错误并自动恢复', async () => {
|
||||
vi.useFakeTimers();
|
||||
apiMocks.get
|
||||
.mockResolvedValueOnce({
|
||||
data: createBatch('RUNNING', 1),
|
||||
errorCode: 0,
|
||||
})
|
||||
.mockRejectedValueOnce(new Error('network unavailable'))
|
||||
.mockResolvedValueOnce({
|
||||
data: createBatch('COMPLETED', 2),
|
||||
errorCode: 0,
|
||||
});
|
||||
|
||||
const wrapper = mount(DocumentImportBatchStatus, {
|
||||
props: { knowledgeId: 'knowledge-1' },
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
await vi.advanceTimersByTimeAsync(3000);
|
||||
await flushPromises();
|
||||
|
||||
expect(wrapper.find('.batch-status__summary').exists()).toBe(true);
|
||||
expect(wrapper.find('.batch-status__load-error').exists()).toBe(true);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(3000);
|
||||
await flushPromises();
|
||||
|
||||
expect(apiMocks.get).toHaveBeenCalledTimes(3);
|
||||
expect(wrapper.find('.batch-status__load-error').exists()).toBe(false);
|
||||
expect(wrapper.text()).toContain(
|
||||
'documentCollection.importDoc.batchCompleted',
|
||||
);
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user