@@ -177,3 +177,131 @@ def test_remote_events_list_empty(mock_client, conversation_id):
177177
178178 with pytest .raises (IndexError ):
179179 _ = events_list [0 ]
180+
181+
182+ def test_remote_events_list_maintains_timestamp_order (mock_client , conversation_id ):
183+ """Test that events are inserted in sorted order by timestamp.
184+
185+ This tests the fix for the race condition where WebSocket might deliver
186+ events out of order (e.g., ActionEvent arriving before MessageEvent).
187+ """
188+ mock_response = create_mock_api_response ([])
189+ mock_client .request .return_value = mock_response
190+
191+ events_list = RemoteEventsList (mock_client , conversation_id )
192+
193+ # Create events with specific timestamps (out of order)
194+ event1 = MessageEvent (
195+ id = "event-1" ,
196+ timestamp = "2024-01-01T10:00:00" , # First chronologically
197+ source = "user" ,
198+ llm_message = Message (role = "user" , content = [TextContent (text = "Hello" )]),
199+ )
200+ event2 = MessageEvent (
201+ id = "event-2" ,
202+ timestamp = "2024-01-01T10:00:02" , # Third chronologically
203+ source = "agent" ,
204+ llm_message = Message (role = "assistant" , content = [TextContent (text = "Response" )]),
205+ )
206+ event3 = MessageEvent (
207+ id = "event-3" ,
208+ timestamp = "2024-01-01T10:00:01" , # Second chronologically
209+ source = "agent" ,
210+ llm_message = Message (role = "assistant" , content = [TextContent (text = "Action" )]),
211+ )
212+
213+ # Add events in wrong order (simulating WebSocket out-of-order delivery)
214+ events_list .add_event (event2 ) # Add third event first
215+ events_list .add_event (event1 ) # Add first event second
216+ events_list .add_event (event3 ) # Add second event last
217+
218+ # Events should be sorted by timestamp regardless of insertion order
219+ assert len (events_list ) == 3
220+ assert events_list [0 ].id == "event-1" # 10:00:00
221+ assert events_list [1 ].id == "event-3" # 10:00:01
222+ assert events_list [2 ].id == "event-2" # 10:00:02
223+
224+
225+ def test_remote_events_list_timestamp_order_with_existing_events (
226+ mock_client , conversation_id
227+ ):
228+ """Test that new events are inserted in correct position among existing events."""
229+ # Start with some events already loaded
230+ initial_events : list [Event ] = [
231+ MessageEvent (
232+ id = "initial-1" ,
233+ timestamp = "2024-01-01T10:00:00" ,
234+ source = "user" ,
235+ llm_message = Message (role = "user" , content = [TextContent (text = "First" )]),
236+ ),
237+ MessageEvent (
238+ id = "initial-2" ,
239+ timestamp = "2024-01-01T10:00:02" ,
240+ source = "agent" ,
241+ llm_message = Message (role = "assistant" , content = [TextContent (text = "Third" )]),
242+ ),
243+ ]
244+
245+ mock_response = create_mock_api_response (initial_events )
246+ mock_client .request .return_value = mock_response
247+
248+ events_list = RemoteEventsList (mock_client , conversation_id )
249+ assert len (events_list ) == 2
250+
251+ # Add an event that should be inserted in the middle
252+ middle_event = MessageEvent (
253+ id = "middle" ,
254+ timestamp = "2024-01-01T10:00:01" , # Between initial-1 and initial-2
255+ source = "agent" ,
256+ llm_message = Message (role = "assistant" , content = [TextContent (text = "Middle" )]),
257+ )
258+ events_list .add_event (middle_event )
259+
260+ assert len (events_list ) == 3
261+ assert events_list [0 ].id == "initial-1"
262+ assert events_list [1 ].id == "middle"
263+ assert events_list [2 ].id == "initial-2"
264+
265+
266+ def test_remote_events_list_identical_timestamps_stable_order (
267+ mock_client , conversation_id
268+ ):
269+ """Test that events with identical timestamps maintain insertion order."""
270+ mock_response = create_mock_api_response ([])
271+ mock_client .request .return_value = mock_response
272+
273+ events_list = RemoteEventsList (mock_client , conversation_id )
274+
275+ # Create events with identical timestamps
276+ same_timestamp = "2024-01-01T10:00:00"
277+ event1 = MessageEvent (
278+ id = "event-1" ,
279+ timestamp = same_timestamp ,
280+ source = "user" ,
281+ llm_message = Message (role = "user" , content = [TextContent (text = "First" )]),
282+ )
283+ event2 = MessageEvent (
284+ id = "event-2" ,
285+ timestamp = same_timestamp ,
286+ source = "agent" ,
287+ llm_message = Message (role = "assistant" , content = [TextContent (text = "Second" )]),
288+ )
289+ event3 = MessageEvent (
290+ id = "event-3" ,
291+ timestamp = same_timestamp ,
292+ source = "agent" ,
293+ llm_message = Message (role = "assistant" , content = [TextContent (text = "Third" )]),
294+ )
295+
296+ # Add events in order
297+ events_list .add_event (event1 )
298+ events_list .add_event (event2 )
299+ events_list .add_event (event3 )
300+
301+ # Events with identical timestamps should maintain insertion order.
302+ # bisect_right ensures new events are inserted after existing ones
303+ # with the same timestamp.
304+ assert len (events_list ) == 3
305+ assert events_list [0 ].id == "event-1"
306+ assert events_list [1 ].id == "event-2"
307+ assert events_list [2 ].id == "event-3"
0 commit comments