@@ -135,7 +135,9 @@ def test_pipeline_runs_help_exposes_run_commands_not_local_pipeline_commands(cap
135135 assert "diagram" not in output
136136
137137 run_app (app , ["sdk" , "pipeline-runs" , "submit" , "--help" ])
138- assert "--log-type" in capsys .readouterr ().out
138+ submit_help = capsys .readouterr ().out
139+ assert "--log-type" in submit_help
140+ assert "--submit-recovery-attempts" in submit_help
139141
140142
141143def test_pipeline_runs_submit_builds_create_payload (monkeypatch , tmp_path : Path , capsys ):
@@ -161,12 +163,62 @@ def test_pipeline_runs_submit_builds_create_payload(monkeypatch, tmp_path: Path,
161163
162164 result = json .loads (capsys .readouterr ().out )
163165 assert result == {"id" : "run-1" , "root_execution_id" : "exec-1" }
164- assert fake_client .created [0 ]["annotations" ] == {"team" : "oss" }
166+ assert fake_client .created [0 ]["annotations" ]["team" ] == "oss"
167+ assert fake_client .created [0 ]["annotations" ]["tangle-cli/submission-id" ]
165168 root_task = fake_client .created [0 ]["root_task" ]
166169 assert root_task ["componentRef" ]["spec" ]["name" ] == "Demo Pipeline"
167170 assert root_task ["arguments" ] == {"query" : "default" , "required" : "value" }
168171
169172
173+ def test_pipeline_runs_submit_uses_default_submit_recovery_attempts (monkeypatch , tmp_path : Path , capsys ):
174+ pipeline_path = _write_pipeline (tmp_path / "pipeline.yaml" )
175+ captured : dict [str , Any ] = {}
176+
177+ def fake_run_pipeline (self , pipeline_path , ** kwargs ):
178+ del self , pipeline_path
179+ captured .update (kwargs )
180+ return {"response" : {"id" : "run-1" }}
181+
182+ monkeypatch .setattr (PipelineRunManager , "run_pipeline" , fake_run_pipeline )
183+ monkeypatch .setattr (pipeline_runs_cli , "LazyTangleApiClient" , lambda ** kwargs : FakeClient ())
184+ app = cli .build_app ()
185+
186+ run_app (app , ["sdk" , "pipeline-runs" , "submit" , str (pipeline_path ), "--no-hydrate" ])
187+
188+ assert json .loads (capsys .readouterr ().out ) == {"id" : "run-1" }
189+ assert captured ["submit_recovery_attempts" ] == pipeline_run_manager ._DEFAULT_SUBMIT_RECOVERY_ATTEMPTS
190+
191+
192+ def test_pipeline_runs_submit_accepts_submit_recovery_attempts (monkeypatch , tmp_path : Path , capsys ):
193+ pipeline_path = _write_pipeline (tmp_path / "pipeline.yaml" )
194+ captured : dict [str , Any ] = {}
195+
196+ def fake_run_pipeline (self , pipeline_path , ** kwargs ):
197+ del self , pipeline_path
198+ captured .update (kwargs )
199+ return {"response" : {"id" : "run-1" }}
200+
201+ monkeypatch .setattr (PipelineRunManager , "run_pipeline" , fake_run_pipeline )
202+ monkeypatch .setattr (pipeline_runs_cli , "LazyTangleApiClient" , lambda ** kwargs : FakeClient ())
203+ app = cli .build_app ()
204+
205+ run_app (
206+ app ,
207+ [
208+ "sdk" ,
209+ "pipeline-runs" ,
210+ "submit" ,
211+ str (pipeline_path ),
212+ "--no-hydrate" ,
213+ "--submit-recovery-attempts" ,
214+ "6" ,
215+ ],
216+ )
217+
218+ assert json .loads (capsys .readouterr ().out ) == {"id" : "run-1" }
219+ assert captured ["submit_recovery_attempts" ] == 6
220+
221+
170222def test_pipeline_runs_submit_accepts_export_config_args_and_hydrate (monkeypatch , tmp_path : Path ):
171223 pipeline_path = _write_pipeline (tmp_path / "pipeline.yaml" )
172224 config = tmp_path / "pipeline.config.yaml"
@@ -1752,7 +1804,7 @@ def pipeline_runs_create(self, body: Any = None) -> dict[str, Any]:
17521804
17531805 def pipeline_runs_list (self , ** kwargs : Any ) -> dict [str , Any ]:
17541806 self .list_calls .append (kwargs )
1755- if len (self .list_calls ) <= 2 :
1807+ if len (self .list_calls ) < len ( pipeline_run_manager . _SUBMIT_RECOVERY_BACKOFF_SECONDS ) :
17561808 return {"pipeline_runs" : [], "next_page_token" : None }
17571809 return {
17581810 "pipeline_runs" : [{"id" : "run-created" , "root_execution_id" : "exec-created" }],
@@ -1763,13 +1815,13 @@ def pipeline_runs_list(self, **kwargs: Any) -> dict[str, Any]:
17631815 manager = PipelineRunManager (client = client )
17641816 body = {"root_task" : {"componentRef" : {"spec" : {"name" : "delayed-recovery" }}}}
17651817
1766- result = manager .run_prepared_body (body )
1818+ result = manager .run_prepared_body (body , submit_recovery_attempts = 6 )
17671819
17681820 assert result ["response" ]["id" ] == "run-created"
17691821 assert result ["context" ].metadata ["recovered_after_submit_error" ] is True
17701822 assert len (client .created ) == 1
1771- assert len (client .list_calls ) == 3
1772- assert sleeps == [ 0.5 , 1.0 , 2.0 ]
1823+ assert len (client .list_calls ) == len ( pipeline_run_manager . _SUBMIT_RECOVERY_BACKOFF_SECONDS )
1824+ assert sleeps == list ( pipeline_run_manager . _SUBMIT_RECOVERY_BACKOFF_SECONDS )
17731825
17741826
17751827def test_pipeline_runs_submit_failure_recovery_refuses_ambiguous_matches (monkeypatch ) -> None :
@@ -1869,8 +1921,8 @@ def pipeline_runs_list(self, **kwargs: Any) -> dict[str, Any]:
18691921 assert client .created [0 ]["annotations" ]["tangle-cli/submission-id" ] == client .created [1 ]["annotations" ][
18701922 "tangle-cli/submission-id"
18711923 ]
1872- assert len ( client . list_calls ) == 2 * len (pipeline_run_manager ._SUBMIT_RECOVERY_BACKOFF_SECONDS )
1873- expected_sleeps = list ( pipeline_run_manager . _SUBMIT_RECOVERY_BACKOFF_SECONDS )
1924+ expected_sleeps = list (pipeline_run_manager ._SUBMIT_RECOVERY_BACKOFF_SECONDS [: 2 ] )
1925+ assert len ( client . list_calls ) == 2 * len ( expected_sleeps )
18741926 assert sleeps == expected_sleeps + expected_sleeps
18751927
18761928
@@ -1927,15 +1979,15 @@ def pipeline_runs_list(self, **kwargs: Any) -> dict[str, Any]:
19271979 client = RecoverOnRetryClient ()
19281980 manager = PipelineRunner (client = client , hooks = hooks )
19291981
1930- result = manager .run_pipeline (pipeline_path , hydrate = False , max_attempts = 2 )
1982+ result = manager .run_pipeline (pipeline_path , hydrate = False , max_attempts = 2 , submit_recovery_attempts = 6 )
19311983
19321984 assert result ["response" ]["id" ] == "run-created"
19331985 assert result ["context" ].attempt == 2
19341986 assert result ["context" ].metadata ["recovered_after_submit_error" ] is True
19351987 assert hooks .prepare_run_arguments_calls == 1
19361988 assert len (client .created ) == 1
19371989 assert len (client .list_calls ) == len (pipeline_run_manager ._SUBMIT_RECOVERY_BACKOFF_SECONDS ) + 1
1938- assert sleeps == [0.5 , 1.0 , 2.0 , 0.5 ]
1990+ assert sleeps == [* pipeline_run_manager . _SUBMIT_RECOVERY_BACKOFF_SECONDS , 0.5 ]
19391991 assert events == [("before_retry" , 2 , None ), ("after_retry_submit" , 2 , "run-created" )]
19401992
19411993
0 commit comments