@@ -250,9 +250,16 @@ public async Task StartAll_StartsAConnectionPerConnectableServer()
250250 [ Fact ]
251251 public async Task First_marker_poll_is_a_silent_baseline ( )
252252 {
253- // Arrange: the connection always returns an empty marker list.
254- // After two full poll cycles the first (silent baseline) plus one repeated poll should
255- // produce no MapMarkersChangedEvent because there is no diff to report.
253+ // Contract: the FIRST poll after connect must not publish any event even when markers are
254+ // present on that first poll (the baseline records them silently). Only LATER changes alert.
255+ //
256+ // Script:
257+ // Poll 1 → [CargoShip] (baseline — must NOT fire an event)
258+ // Poll 2 → [CargoShip] (no change — still no event)
259+ // Poll 3 → [CargoShip, PatrolHelicopter] (heli added — ONE event, Added=[heli])
260+ //
261+ // Waiting for the definite heli-event signal proves both that baseline suppression held AND
262+ // that the diff works, without any fixed-sleep assertion.
256263 var source = new FakeRustSocketSource ( ) ;
257264 source . EnqueueConnect ( SocketConnectOutcome . Connected ) ;
258265 source . EnqueueHeartbeat ( HeartbeatResult . Ok ( 1 ) ) ;
@@ -269,16 +276,25 @@ public async Task First_marker_poll_is_a_silent_baseline()
269276 }
270277 } , CancellationToken . None ) ;
271278
272- await h . Supervisor . EnsureConnectionAsync ( 10UL , serverId , cts . Token ) ;
279+ // Script the three polls before EnsureConnectionAsync so the script is in place before the
280+ // poll loop starts — no race between test setup and the supervisor's background poll task.
281+ var cargo = new MapMarkerSnapshot ( 1UL , MarkerKind . CargoShip , 1f , 1f , null ) ;
282+ var heli = new MapMarkerSnapshot ( 2UL , MarkerKind . PatrolHelicopter , 2f , 2f , null ) ;
283+ source . EnqueueMarkers ( [ cargo ] ) ; // poll 1: baseline (silent)
284+ source . EnqueueMarkers ( [ cargo ] ) ; // poll 2: no change
285+ source . EnqueueMarkers ( [ cargo , heli ] ) ; // poll 3: heli added → one event
273286
274- // FakeConnection defaults to empty MarkersResult — baseline poll sees nothing.
275- await WaitUntilAsync ( ( ) => h . Supervisor . HasLiveSocket ( 10UL , serverId ) , cts . Token ) ;
287+ await h . Supervisor . EnsureConnectionAsync ( 10UL , serverId , cts . Token ) ;
276288
277- // Wait two full poll cycles so the baseline and one follow-up are definitely consumed.
278- await Task . Delay ( TimeSpan . FromMilliseconds ( 100 ) , cts . Token ) ;
289+ // Wait for the heli-added event — its arrival is the definite signal that at least three
290+ // poll cycles have completed and proves the baseline CargoShip never triggered an event.
291+ await WaitUntilAsync ( ( ) => ! captured . IsEmpty , cts . Token ) ;
279292
280- // The first poll establishes a silent baseline; repeated empty polls produce no event.
281- Assert . Empty ( captured ) ;
293+ Assert . Single ( captured ) ;
294+ Assert . True ( captured . TryPeek ( out var evt ) ) ;
295+ Assert . Single ( evt ! . Added ) ;
296+ Assert . Equal ( MarkerKind . PatrolHelicopter , evt . Added [ 0 ] . Kind ) ;
297+ Assert . Empty ( evt . Removed ) ;
282298
283299 await h . Supervisor . StopAllAsync ( ) ;
284300 await cts . CancelAsync ( ) ;
@@ -292,7 +308,12 @@ public async Task First_marker_poll_is_a_silent_baseline()
292308 [ Fact ]
293309 public async Task Marker_added_on_a_later_poll_publishes_changed_event ( )
294310 {
295- // Arrange: first poll sees nothing (baseline); second poll sees a CargoShip → one event.
311+ // Contract: first poll is a silent baseline; a new marker on a subsequent poll fires exactly
312+ // one MapMarkersChangedEvent with the correct Added entry and the connect-time dimensions.
313+ //
314+ // Script:
315+ // Poll 1 → [] (baseline — no event)
316+ // Poll 2 → [CargoShip] (added → one event)
296317 var source = new FakeRustSocketSource ( ) ;
297318 source . EnqueueConnect ( SocketConnectOutcome . Connected ) ;
298319 source . EnqueueHeartbeat ( HeartbeatResult . Ok ( 1 ) ) ;
@@ -309,33 +330,26 @@ public async Task Marker_added_on_a_later_poll_publishes_changed_event()
309330 }
310331 } , CancellationToken . None ) ;
311332
312- await h . Supervisor . EnsureConnectionAsync ( 10UL , serverId , cts . Token ) ;
333+ // FakeConnection default DimensionsResult is new(4000u, 4000u, 500); assert those exact values.
334+ var expectedDims = new MapDimensions ( 4000u , 4000u , 500 ) ;
313335
314- // Wait for the fake connection to exist; start with empty markers (baseline poll sees nothing).
315- await WaitUntilAsync ( ( ) => source . LastConnection is not null , cts . Token ) ;
316- source . LastConnection ! . MarkersResult = [ ] ;
317- await WaitUntilAsync ( ( ) => h . Supervisor . HasLiveSocket ( 10UL , serverId ) , cts . Token ) ;
336+ // Script polls before EnsureConnectionAsync so the marker script is in the connection before
337+ // the poll loop can start — eliminates any setup race.
338+ source . EnqueueMarkers ( [ ] ) ; // poll 1: baseline
339+ source . EnqueueMarkers ( [ new MapMarkerSnapshot ( 2UL , MarkerKind . CargoShip , 1f , 1f , "Cargo A" ) ] ) ; // poll 2
318340
319- // Wait one poll cycle for the baseline to be established, then add a CargoShip.
320- await Task . Delay ( TimeSpan . FromMilliseconds ( 60 ) , cts . Token ) ;
321- source . LastConnection ! . MarkersResult =
322- [ new MapMarkerSnapshot ( 2UL , MarkerKind . CargoShip , 1f , 1f , "Cargo A" ) ] ;
341+ await h . Supervisor . EnsureConnectionAsync ( 10UL , serverId , cts . Token ) ;
323342
324- // Wait until the event arrives (up to 30s deadline).
325- var deadline = DateTimeOffset . UtcNow . AddSeconds ( 30 ) ;
326- while ( captured . IsEmpty && DateTimeOffset . UtcNow < deadline )
327- {
328- await Task . Delay ( 15 , cts . Token ) ;
329- }
343+ // Wait for the definite signal: the CargoShip-added event.
344+ await WaitUntilAsync ( ( ) => ! captured . IsEmpty , cts . Token ) ;
330345
331346 Assert . Single ( captured ) ;
332- var evt = captured . TryPeek ( out var e ) ? e : null ;
347+ Assert . True ( captured . TryPeek ( out var evt ) ) ;
333348 Assert . NotNull ( evt ) ;
334349 Assert . Single ( evt ! . Added ) ;
335350 Assert . Equal ( MarkerKind . CargoShip , evt . Added [ 0 ] . Kind ) ;
336351 Assert . Empty ( evt . Removed ) ;
337- // Dimensions should ride along from the connect-time fetch.
338- Assert . NotNull ( evt . Dimensions ) ;
352+ Assert . Equal ( expectedDims , evt . Dimensions ) ;
339353
340354 await h . Supervisor . StopAllAsync ( ) ;
341355 await cts . CancelAsync ( ) ;
@@ -349,8 +363,13 @@ public async Task Marker_added_on_a_later_poll_publishes_changed_event()
349363 [ Fact ]
350364 public async Task Failed_marker_poll_retains_previous_snapshot ( )
351365 {
352- // Arrange: baseline is empty. The second poll adds a CargoShip (event published).
353- // The third poll throws (no event). The fourth returns the same CargoShip (no spurious diff).
366+ // Contract: a thrown poll does not corrupt the previous snapshot.
367+ //
368+ // Script:
369+ // Poll 1 → [] (baseline — no event)
370+ // Poll 2 → [CargoShip] (added → exactly one event; queue empties, hold-last = CargoShip)
371+ // Poll 3 → throws (MarkersThrow = true; no event, snapshot retained)
372+ // Poll 4 → [CargoShip] (same as held-last → no spurious diff, still exactly one event total)
354373 var source = new FakeRustSocketSource ( ) ;
355374 source . EnqueueConnect ( SocketConnectOutcome . Connected ) ;
356375 source . EnqueueHeartbeat ( HeartbeatResult . Ok ( 1 ) ) ;
@@ -367,35 +386,24 @@ public async Task Failed_marker_poll_retains_previous_snapshot()
367386 }
368387 } , CancellationToken . None ) ;
369388
370- await h . Supervisor . EnsureConnectionAsync ( 10UL , serverId , cts . Token ) ;
371-
372- await WaitUntilAsync ( ( ) => source . LastConnection is not null , cts . Token ) ;
373- source . LastConnection ! . MarkersResult = [ ] ;
374- await WaitUntilAsync ( ( ) => h . Supervisor . HasLiveSocket ( 10UL , serverId ) , cts . Token ) ;
375-
376- // Let baseline settle.
377- await Task . Delay ( TimeSpan . FromMilliseconds ( 60 ) , cts . Token ) ;
378-
379- // Poll 2: add CargoShip → expect exactly one event.
380389 var cargo = new MapMarkerSnapshot ( 3UL , MarkerKind . CargoShip , 2f , 2f , null ) ;
381- source . LastConnection ! . MarkersResult = [ cargo ] ;
390+ // Script polls before EnsureConnectionAsync to eliminate the setup race.
391+ source . EnqueueMarkers ( [ ] ) ; // poll 1: baseline
392+ source . EnqueueMarkers ( [ cargo ] ) ; // poll 2: CargoShip added; queue empties → hold-last = [cargo]
382393
383- var deadline = DateTimeOffset . UtcNow . AddSeconds ( 30 ) ;
384- while ( captured . IsEmpty && DateTimeOffset . UtcNow < deadline )
385- {
386- await Task . Delay ( 15 , cts . Token ) ;
387- }
394+ await h . Supervisor . EnsureConnectionAsync ( 10UL , serverId , cts . Token ) ;
388395
396+ // Wait for the one cargo-added event — definite signal that poll 2 completed.
397+ await WaitUntilAsync ( ( ) => ! captured . IsEmpty , cts . Token ) ;
389398 Assert . Single ( captured ) ;
390399
391- // Poll 3: make the poll throw.
400+ // Poll 3: make the next poll throw; the poll loop catches the exception and retains the snapshot .
392401 source . LastConnection ! . MarkersThrow = true ;
393- // Give it time to attempt the failing poll.
402+ // Give enough time for at least one failing poll to be attempted .
394403 await Task . Delay ( TimeSpan . FromMilliseconds ( 60 ) , cts . Token ) ;
395404
396- // Poll 4: recover; same CargoShip → snapshot unchanged, no new event.
405+ // Poll 4: recover — hold-last still returns [cargo], so snapshot is unchanged, no new event.
397406 source . LastConnection ! . MarkersThrow = false ;
398- source . LastConnection ! . MarkersResult = [ cargo ] ;
399407 await Task . Delay ( TimeSpan . FromMilliseconds ( 100 ) , cts . Token ) ;
400408
401409 // Still only one event total — the failed poll did not corrupt the snapshot.
0 commit comments