mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-08 07:17:39 +00:00
fix: reconcile stale queued messages (#1063)
Recover missed messages-consumed events from authoritative Hub state after session SSE reconnects.
This commit is contained in:
@@ -35,6 +35,14 @@ function createMockApi(sendMessage: (...args: unknown[]) => Promise<void> = asyn
|
||||
return { sendMessage } as unknown as ApiClient
|
||||
}
|
||||
|
||||
function deferred<T>() {
|
||||
let resolve!: (value: T | PromiseLike<T>) => void
|
||||
const promise = new Promise<T>((res) => {
|
||||
resolve = res
|
||||
})
|
||||
return { promise, resolve }
|
||||
}
|
||||
|
||||
describe('useSendMessage', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
@@ -58,6 +66,43 @@ describe('useSendMessage', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps a thinking-session send in flight until the POST confirms it is queued', async () => {
|
||||
const request = deferred<void>()
|
||||
const api = createMockApi(() => request.promise)
|
||||
const { appendOptimisticMessage, updateMessageStatus } = await import('@/lib/message-window-store')
|
||||
const appendMock = vi.mocked(appendOptimisticMessage)
|
||||
const updateMock = vi.mocked(updateMessageStatus)
|
||||
|
||||
const { result } = renderHook(
|
||||
() => useSendMessage(api, 'session-A', { isSessionThinking: true }),
|
||||
{ wrapper: createWrapper() },
|
||||
)
|
||||
|
||||
act(() => {
|
||||
void result.current.sendMessage('queue this')
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(appendMock).toHaveBeenCalledWith(
|
||||
'session-A',
|
||||
expect.objectContaining({
|
||||
localId: 'local-id-1',
|
||||
status: 'sending',
|
||||
}),
|
||||
)
|
||||
})
|
||||
expect(updateMock).not.toHaveBeenCalledWith('session-A', 'local-id-1', 'queued')
|
||||
|
||||
await act(async () => {
|
||||
request.resolve()
|
||||
await request.promise
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(updateMock).toHaveBeenCalledWith('session-A', 'local-id-1', 'queued')
|
||||
})
|
||||
})
|
||||
|
||||
it('calls onSuccess with resolved session ID, not the original', async () => {
|
||||
const onSuccess = vi.fn()
|
||||
const api = createMockApi()
|
||||
|
||||
@@ -161,15 +161,15 @@ export function useSendMessage(
|
||||
await api.sendMessage(input.sessionId, input.text, input.localId, input.attachments, input.scheduledAt)
|
||||
},
|
||||
onMutate: async (input) => {
|
||||
const status = isSessionThinkingRef.current ? 'queued' as const : 'sending' as const
|
||||
appendOptimisticMessage(input.sessionId, createOptimisticMessage(input, status))
|
||||
return { status }
|
||||
const successStatus = isSessionThinkingRef.current ? 'queued' as const : 'sent' as const
|
||||
appendOptimisticMessage(input.sessionId, createOptimisticMessage(input, 'sending'))
|
||||
return { successStatus }
|
||||
},
|
||||
onSuccess: (_, input, context) => {
|
||||
updateMessageStatus(
|
||||
input.sessionId,
|
||||
input.localId,
|
||||
context?.status === 'queued' ? 'queued' : 'sent'
|
||||
context?.successStatus ?? 'sent'
|
||||
)
|
||||
haptic.notification('success')
|
||||
options?.onSuccess?.(input.sessionId)
|
||||
|
||||
Reference in New Issue
Block a user