@@ -220,6 +220,61 @@ def test_schedule_activity_server_command_includes_retry_policy_and_timeouts(sel
220220 assert server_cmd ["schedule_to_close_timeout" ] == 300
221221 assert server_cmd ["heartbeat_timeout" ] == 15
222222
223+ @pytest .mark .parametrize (
224+ ("field" , "value" , "message" ),
225+ [
226+ ("start_to_close_timeout" , 0 , "start_to_close_timeout must be >= 1 second" ),
227+ ("schedule_to_start_timeout" , 0 , "schedule_to_start_timeout must be >= 1 second" ),
228+ ("schedule_to_close_timeout" , 0 , "schedule_to_close_timeout must be >= 1 second" ),
229+ ("heartbeat_timeout" , 0 , "heartbeat_timeout must be >= 1 second" ),
230+ ],
231+ )
232+ def test_schedule_activity_rejects_non_positive_timeout_budgets (
233+ self ,
234+ field : str ,
235+ value : int ,
236+ message : str ,
237+ ) -> None :
238+ cmd = ScheduleActivity (
239+ activity_type = "charge-card" ,
240+ arguments = [],
241+ ** {field : value },
242+ )
243+
244+ with pytest .raises (ValueError , match = message ):
245+ cmd .to_server_command ("default-queue" )
246+
247+ @pytest .mark .parametrize (
248+ ("kwargs" , "message" ),
249+ [
250+ (
251+ {"start_to_close_timeout" : 10 , "heartbeat_timeout" : 11 },
252+ "heartbeat_timeout must be <= start_to_close_timeout" ,
253+ ),
254+ (
255+ {"start_to_close_timeout" : 301 , "schedule_to_close_timeout" : 300 },
256+ "start_to_close_timeout must be <= schedule_to_close_timeout" ,
257+ ),
258+ (
259+ {"schedule_to_start_timeout" : 301 , "schedule_to_close_timeout" : 300 },
260+ "schedule_to_start_timeout must be <= schedule_to_close_timeout" ,
261+ ),
262+ ],
263+ )
264+ def test_schedule_activity_rejects_incoherent_timeout_envelopes (
265+ self ,
266+ kwargs : dict [str , int ],
267+ message : str ,
268+ ) -> None :
269+ cmd = ScheduleActivity (
270+ activity_type = "charge-card" ,
271+ arguments = [],
272+ ** kwargs ,
273+ )
274+
275+ with pytest .raises (ValueError , match = message ):
276+ cmd .to_server_command ("default-queue" )
277+
223278
224279class TestTwoActivities :
225280 def test_first_schedules (self ) -> None :
@@ -728,6 +783,39 @@ def test_server_command_shape(self) -> None:
728783 assert sc ["execution_timeout_seconds" ] == 600
729784 assert sc ["run_timeout_seconds" ] == 120
730785
786+ @pytest .mark .parametrize (
787+ ("field" , "value" , "message" ),
788+ [
789+ ("execution_timeout_seconds" , 0 , "execution_timeout_seconds must be >= 1 second" ),
790+ ("run_timeout_seconds" , 0 , "run_timeout_seconds must be >= 1 second" ),
791+ ],
792+ )
793+ def test_server_command_rejects_non_positive_child_timeout_budgets (
794+ self ,
795+ field : str ,
796+ value : int ,
797+ message : str ,
798+ ) -> None :
799+ cmd = StartChildWorkflow (
800+ workflow_type = "sub" ,
801+ arguments = [],
802+ ** {field : value },
803+ )
804+
805+ with pytest .raises (ValueError , match = message ):
806+ cmd .to_server_command ("default-q" )
807+
808+ def test_server_command_rejects_child_run_timeout_larger_than_execution_timeout (self ) -> None :
809+ cmd = StartChildWorkflow (
810+ workflow_type = "sub" ,
811+ arguments = [],
812+ execution_timeout_seconds = 120 ,
813+ run_timeout_seconds = 121 ,
814+ )
815+
816+ with pytest .raises (ValueError , match = "run_timeout_seconds must be <= execution_timeout_seconds" ):
817+ cmd .to_server_command ("default-q" )
818+
731819 def test_server_command_defaults (self ) -> None :
732820 cmd = StartChildWorkflow (workflow_type = "sub" , arguments = [])
733821 sc = cmd .to_server_command ("default-q" )
0 commit comments