@@ -469,6 +469,28 @@ def test_deadline_exhausted_raises_last_error(self):
469469 with self .assertRaises (grpc .RpcError ):
470470 mcp_client .connect ('weather' )
471471
472+ def test_budget_exhausted_after_schedule_succeeds (self ):
473+ """If retries burn the budget but schedule eventually succeeds, raise
474+ without calling wait_for_workflow_completion (timeout=0 means
475+ 'wait forever' in the underlying client)."""
476+ mock_wf = MagicMock ()
477+ mock_wf .schedule_new_workflow .side_effect = [
478+ _StubRpcError (grpc .StatusCode .CANCELLED ),
479+ 'inst-1' ,
480+ ]
481+
482+ mcp_client = DaprMCPClient (timeout_in_seconds = 1 , wf_client = mock_wf )
483+ # monotonic: 0.0 → deadline = 1.0; 0.4 → sleep_for = 0.5 (still in budget);
484+ # 2.0 → post-loop remaining = -1.0 → raise.
485+ with patch ('dapr.ext.workflow.mcp.time.sleep' ), patch (
486+ 'dapr.ext.workflow.mcp.time.monotonic' ,
487+ side_effect = [0.0 , 0.4 , 2.0 ],
488+ ):
489+ with self .assertRaises (RuntimeError ) as ctx :
490+ mcp_client .connect ('weather' )
491+ self .assertIn ('timed out' , str (ctx .exception ))
492+ mock_wf .wait_for_workflow_completion .assert_not_called ()
493+
472494
473495class TestAioDaprMCPClientConnectRetry (unittest .IsolatedAsyncioTestCase ):
474496 """Async counterpart of TestDaprMCPClientConnectRetry."""
@@ -504,6 +526,24 @@ async def test_deadline_exhausted_raises(self):
504526 with self .assertRaises (grpc .RpcError ):
505527 await mcp_client .connect ('weather' )
506528
529+ async def test_budget_exhausted_after_schedule_succeeds (self ):
530+ """Async mirror of the fail-fast-after-schedule-success guard."""
531+ mock_wf = AsyncMock ()
532+ mock_wf .schedule_new_workflow .side_effect = [
533+ _StubRpcError (grpc .StatusCode .CANCELLED ),
534+ 'inst-1' ,
535+ ]
536+
537+ mcp_client = AioDaprMCPClient (timeout_in_seconds = 1 , wf_client = mock_wf )
538+ with patch ('dapr.ext.workflow.aio.mcp.asyncio.sleep' , new = AsyncMock ()), patch (
539+ 'dapr.ext.workflow.aio.mcp.time.monotonic' ,
540+ side_effect = [0.0 , 0.4 , 2.0 ],
541+ ):
542+ with self .assertRaises (RuntimeError ) as ctx :
543+ await mcp_client .connect ('weather' )
544+ self .assertIn ('timed out' , str (ctx .exception ))
545+ mock_wf .wait_for_workflow_completion .assert_not_awaited ()
546+
507547
508548class TestMCPWorkflowPrefix (unittest .TestCase ):
509549 """Tests for the workflow naming constant."""
0 commit comments