test(web): 补充流式断连的对抗性测试(stream_end 幂等性 + 多周期循环)

对抗性审查 commit 30b7d90 的修复健壮性,新增两个边缘场景测试:

- 用例 18: 验证断连后 stream_end 到达是幂等的(不崩溃、不改变
  已 flush 内容),且后续 assistant_response 能正常按 id 替换
- 用例 19: 验证多次断连-重连循环下 finishStreaming 幂等
  (连续调用不崩溃,消息状态稳定)
This commit is contained in:
oudecheng 2026-08-07 06:54:51 +08:00
parent c749df4e0a
commit 934c7aa804

View File

@ -523,4 +523,56 @@ describe('useChat - handleServerMessage characterization', () => {
const s1 = result.current.messages.find((m) => m.id === 's1');
expect(s1?.content).toBe('part1part2');
});
it('18. stream_end arriving after onDisconnect is idempotent (no crash, no side effect)', () => {
const { result } = renderUseChat();
act(() => {
result.current.handleServerMessage({ type: 'stream_delta', id: 'e1', delta: 'partial' });
});
expect(result.current.messages[0].content).toBe('partial');
// 断连finishStreaming 重置 streamingRef
act(() => result.current.finishStreaming());
// 重连后 stream_end 到达(可能在断连前已发出,重连后才收到)
expect(() => {
act(() => result.current.handleServerMessage({ type: 'stream_end', id: 'e1' }));
}).not.toThrow();
// stream_end 不应改变已 flush 的内容
const e1 = result.current.messages.find((m) => m.id === 'e1');
expect(e1?.content).toBe('partial');
// 紧接着 assistant_response 到达,应正常按 id 替换(断连后的正常恢复路径)
act(() =>
result.current.handleServerMessage({
type: 'assistant_response',
id: 'e1',
content: 'final',
role: 'assistant',
}),
);
expect(result.current.messages.find((m) => m.id === 'e1')?.content).toBe('final');
});
it('19. multiple disconnect-reconnect cycles: finishStreaming is idempotent across cycles', () => {
const { result } = renderUseChat();
// 周期 1
act(() => result.current.handleServerMessage({ type: 'stream_delta', id: 'c1', delta: 'a' }));
act(() => result.current.finishStreaming());
expect(result.current.messages.find((m) => m.id === 'c1')?.content).toBe('a');
// 周期 2新流式消息
act(() => result.current.handleServerMessage({ type: 'stream_delta', id: 'c2', delta: 'b' }));
act(() => result.current.finishStreaming());
expect(result.current.messages.find((m) => m.id === 'c2')?.content).toBe('b');
// c1 不受影响
expect(result.current.messages.find((m) => m.id === 'c1')?.content).toBe('a');
// 周期 3连续两次 finishStreaming无 delta 间隔),验证幂等
act(() => result.current.finishStreaming());
act(() => result.current.finishStreaming());
// 不崩溃,消息状态稳定
expect(result.current.messages).toHaveLength(2);
});
});