Refactor: Fix Event Buffer Deadlock with State Machine (#1633)

* feat: implement state machine for chat synchronization and buffer management

* test: Add Jest configuration and tests for connection deadlock handling

- Created a Jest configuration file to set up testing environment.
- Implemented utility functions for creating isolated authentication sessions and mocking WebSocket connections.
- Added a test case to ensure that the connection does not deadlock when history sync is disabled, verifying the correct handling of message events.

* feat: add GitHub Actions workflow for running tests

* chore: sort import lint

* fix: implement timeout handling for AwaitingInitialSync state in chat socket, maybe fix memory leak

* feat: enhance chat synchronization by refining AwaitingInitialSync handling and adding history sync checks
This commit is contained in:
João Lucas de Oliveira Lopes
2025-08-06 19:15:41 -03:00
committed by GitHub
parent 1a721bb242
commit 812e53e4eb
13 changed files with 1137 additions and 717 deletions
@@ -0,0 +1,54 @@
import { jest } from '@jest/globals'
import { proto } from '../..'
import { DEFAULT_CONNECTION_CONFIG } from '../../Defaults'
import makeWASocket from '../../Socket'
import { makeSession, mockWebSocket } from '../TestUtils/session'
mockWebSocket()
describe('Connection Deadlock Test', () => {
it('should not deadlock when history sync is disabled', async () => {
const { state, clear } = await makeSession()
state.creds.me = { id: '1234567890:1@s.whatsapp.net', name: 'Test User' }
const sock = makeWASocket({
...DEFAULT_CONNECTION_CONFIG,
auth: state,
shouldSyncHistoryMessage: () => false
})
const regularMessageListener = jest.fn()
sock.ev.on('messages.upsert', regularMessageListener)
// 1. Simulate receiving pending notifications. This activates the buffer.
sock.ev.emit('connection.update', { receivedPendingNotifications: true })
// 2. Now, emit a regular message. Because the previous step should have
// flushed the buffer, this message should be processed immediately.
const regularMessage = proto.WebMessageInfo.fromObject({
key: { remoteJid: '1234567890@s.whatsapp.net', fromMe: false, id: 'REGULAR_MSG_1' },
messageTimestamp: Date.now() / 1000,
message: { conversation: 'Hello, world!' }
})
sock.ev.emit('messages.upsert', { messages: [regularMessage], type: 'notify' })
// Wait for the event loop to process any final events.
await new Promise(resolve => setTimeout(resolve, 50))
// 3. Check if the regular message listener was called.
expect(regularMessageListener).toHaveBeenCalledTimes(1)
expect(regularMessageListener).toHaveBeenCalledWith(
expect.objectContaining({
messages: expect.arrayContaining([
expect.objectContaining({
key: regularMessage.key
})
]),
type: 'notify'
})
)
sock.end(new Error('Test completed'))
await clear()
})
})