From dca320b47dd6f994f9eb45849f19657192f0c15d Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Thu, 27 Aug 2026 18:27:56 +0530 Subject: [PATCH] objects/uts: pin the sync-wait ordering with process_pending_events() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The five sync-wait pseudocode blocks (RTO23c1 x3, RTO20e1 x2) start an operation, assert its future IS NOT complete, then inject a channel-state change. On async SDKs the negative assert is vacuous without a drain — the operation's dispatch races the injection, so the test can observe the RTO23e/RTL33 pre-wait outcome (90001 / re-attach) instead of the parked- waiter 92008 the block asserts. This caused a real CI failure in ably/ably-java#1228 (expected 92008, got 90001); ably-js's synchronous mocks never exercise the gap, so the reference implementation gave no corrective. Deploy the corpus's existing process_pending_events() convention (uts/README.md; writing-derived-tests.md "prove a negative") between the operation call and the negative assert in all five blocks, with pointer comments — the same per-site style realtime_client.md and channel_detach.md already use. The two FAILED blocks additionally explain the 90001-vs-92008 mechanism. Not related to #518: that PR governs timer-driven work vs ADVANCE_TIME; this is dispatch-queue ordering with no timers involved. --- uts/objects/unit/realtime_object.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/uts/objects/unit/realtime_object.md b/uts/objects/unit/realtime_object.md index c00e07968..ed2bcd512 100644 --- a/uts/objects/unit/realtime_object.md +++ b/uts/objects/unit/realtime_object.md @@ -217,6 +217,9 @@ mock_ws.send_to_client(ProtocolMessage( get_future = channel.object.get() +# Let get() park in the RTO23c sync wait before the channel-state change (see process_pending_events in uts/README.md) +process_pending_events() + # While still SYNCING the get() cannot complete — it parks in the RTO23c wait for SYNCED ASSERT get_future IS NOT complete @@ -262,6 +265,9 @@ mock_ws.send_to_client(ProtocolMessage( )) get_future = channel.object.get() + +# Let get() park in the RTO23c sync wait before the channel-state change (see process_pending_events in uts/README.md) +process_pending_events() ASSERT get_future IS NOT complete # The mock cannot drive SUSPENDED; drive the channel-state handler directly (as RTO27 does) @@ -304,6 +310,9 @@ mock_ws.send_to_client(ProtocolMessage( )) get_future = channel.object.get() + +# Let get() park in the RTO23c sync wait before the channel-state change (see process_pending_events in uts/README.md) +process_pending_events() ASSERT get_future IS NOT complete # A channel ERROR moves the channel to FAILED and sets its errorReason @@ -677,6 +686,9 @@ inc_future = root.get("score").increment(10) # The publish and its ACK complete against the mock; publishAndApply parks in the # RTO20e wait for SYNCED + +# Let it settle in the RTO20e wait before the channel-state change (see process_pending_events in uts/README.md) +process_pending_events() ASSERT inc_future IS NOT complete # A client-side detach then moves the channel to DETACHED @@ -720,6 +732,9 @@ inc_future = root.get("score").increment(10) # The publish and its ACK complete against the mock; publishAndApply parks in the # RTO20e wait for SYNCED + +# Let it settle in the RTO20e wait before the channel-state change (see process_pending_events in uts/README.md) +process_pending_events() ASSERT inc_future IS NOT complete # Then the channel ERROR moves the channel to FAILED