@@ -527,10 +527,10 @@ async def test_schedule_activity_on_first_replay(self, mock_client: AsyncMock) -
527527 async def test_workflow_task_ambiguous_completion_error_preserves_commands (
528528 self , mock_client : AsyncMock
529529 ) -> None :
530- mock_client .complete_workflow_task .side_effect = TimeoutError ( "completion timed out" )
530+ mock_client .complete_workflow_task .side_effect = ServerError ( 409 , { "reason" : "task_not_leased" } )
531531 worker = Worker (mock_client , task_queue = "q1" , workflows = [TestWorkflow ], activities = [])
532532 task = {
533- "task_id" : "t-complete-timeout " ,
533+ "task_id" : "t-complete-not-leased " ,
534534 "workflow_type" : "test-wf" ,
535535 "workflow_task_attempt" : 2 ,
536536 "history_events" : [],
@@ -545,6 +545,31 @@ async def test_workflow_task_ambiguous_completion_error_preserves_commands(
545545 mock_client .complete_workflow_task .assert_awaited_once ()
546546 mock_client .fail_workflow_task .assert_not_called ()
547547
548+ @pytest .mark .asyncio
549+ async def test_workflow_task_retries_transient_completion_error (
550+ self , mock_client : AsyncMock
551+ ) -> None :
552+ mock_client .complete_workflow_task .side_effect = [
553+ TimeoutError ("completion timed out" ),
554+ {"outcome" : "completed" },
555+ ]
556+ worker = Worker (mock_client , task_queue = "q1" , workflows = [TestWorkflow ], activities = [])
557+ task = {
558+ "task_id" : "t-complete-retry" ,
559+ "workflow_type" : "test-wf" ,
560+ "workflow_task_attempt" : 2 ,
561+ "history_events" : [],
562+ "arguments" : '["hello"]' ,
563+ "payload_codec" : "json" ,
564+ }
565+
566+ result = await worker ._run_workflow_task (task )
567+
568+ assert result is not None
569+ assert result [0 ]["type" ] == "schedule_activity"
570+ assert mock_client .complete_workflow_task .await_count == 2
571+ mock_client .fail_workflow_task .assert_not_called ()
572+
548573 @pytest .mark .asyncio
549574 async def test_workflow_task_definite_completion_rejection_fails_task (
550575 self , mock_client : AsyncMock
@@ -807,10 +832,10 @@ async def test_update_backed_workflow_task_completes_update_command(
807832 async def test_update_task_ambiguous_completion_error_preserves_command (
808833 self , mock_client : AsyncMock
809834 ) -> None :
810- mock_client .complete_workflow_task .side_effect = TimeoutError ( "update completion timed out" )
835+ mock_client .complete_workflow_task .side_effect = ServerError ( 409 , { "reason" : "task_not_leased" } )
811836 worker = Worker (mock_client , task_queue = "q1" , workflows = [UpdateWorkflow ], activities = [])
812837 task = {
813- "task_id" : "t-update-timeout " ,
838+ "task_id" : "t-update-not-leased " ,
814839 "workflow_type" : "update-wf" ,
815840 "workflow_task_attempt" : 3 ,
816841 "workflow_update_id" : "upd-worker-1" ,
@@ -837,6 +862,43 @@ async def test_update_task_ambiguous_completion_error_preserves_command(
837862 mock_client .complete_workflow_task .assert_awaited_once ()
838863 mock_client .fail_workflow_task .assert_not_called ()
839864
865+ @pytest .mark .asyncio
866+ async def test_update_task_retries_transient_completion_error (
867+ self , mock_client : AsyncMock
868+ ) -> None :
869+ mock_client .complete_workflow_task .side_effect = [
870+ ServerError (503 , {"reason" : "server_busy" }),
871+ {"outcome" : "completed" },
872+ ]
873+ worker = Worker (mock_client , task_queue = "q1" , workflows = [UpdateWorkflow ], activities = [])
874+ task = {
875+ "task_id" : "t-update-retry" ,
876+ "workflow_type" : "update-wf" ,
877+ "workflow_task_attempt" : 3 ,
878+ "workflow_update_id" : "upd-worker-1" ,
879+ "workflow_wait_kind" : "update" ,
880+ "history_events" : [
881+ {
882+ "event_type" : "UpdateAccepted" ,
883+ "payload" : {
884+ "update_id" : "upd-worker-1" ,
885+ "update_name" : "increment" ,
886+ "arguments" : serializer .encode ([6 ], codec = "json" ),
887+ "payload_codec" : "json" ,
888+ },
889+ },
890+ ],
891+ "arguments" : "[]" ,
892+ "payload_codec" : "json" ,
893+ }
894+
895+ result = await worker ._run_workflow_task (task )
896+
897+ assert result is not None
898+ assert result [0 ]["type" ] == "complete_update"
899+ assert mock_client .complete_workflow_task .await_count == 2
900+ mock_client .fail_workflow_task .assert_not_called ()
901+
840902 @pytest .mark .asyncio
841903 async def test_query_task_executes_registered_query (self , mock_client : AsyncMock ) -> None :
842904 worker = Worker (mock_client , task_queue = "q1" , workflows = [QueryWorkflow ], activities = [])
0 commit comments