Skip to content

Commit dc14a29

Browse files
wait for wf more robustly in test
1 parent 8da5990 commit dc14a29

1 file changed

Lines changed: 48 additions & 18 deletions

File tree

tests/integration/test_comprehensive_e2e.py

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -195,36 +195,66 @@ def test_02_start_workers(self):
195195
self.__class__.workers_started = True
196196
self.assertTrue(self.workers_started)
197197

198-
def test_03_execute_workflow(self):
199-
"""Execute workflow and verify completion."""
200-
print("\n" + "="*80 + "\nTEST 3: Execute Workflow\n" + "="*80)
201-
202-
self.assertTrue(self.workers_started, "Workers must be started first")
203-
204-
workflow_client = OrkesWorkflowClient(self.config)
198+
EXPECTED_TASK_COUNT = 5
199+
200+
def _run_workflow_to_terminal(self, workflow_client, timeout_s=90):
201+
"""Start the e2e workflow and wait until it is genuinely terminal with
202+
all expected tasks present. Returns (wf_id, workflow_or_None). A None
203+
workflow (or a non-terminal / short task list) signals a timed-out run
204+
the caller can retry, rather than asserting against a half-scheduled
205+
workflow (which is what produced the flaky "4 != 5 tasks" failure).
206+
"""
205207
req = StartWorkflowRequest()
206208
req.name = 'e2e_comprehensive_test'
207209
req.version = 1
208210
req.input = {}
209-
211+
210212
wf_id = workflow_client.start_workflow(start_workflow_request=req)
211213
print(f"✓ Started workflow: {wf_id}")
212-
213-
# Wait for completion
214-
for i in range(30):
214+
215+
deadline = time.time() + timeout_s
216+
wf = None
217+
while time.time() < deadline:
215218
wf = workflow_client.get_workflow(wf_id, include_tasks=True)
216-
print(f" Status: {wf.status} - ({i*2}s)")
217-
if wf.status in ['COMPLETED', 'FAILED']:
218-
break
219-
for task in wf.tasks:
219+
print(f" Status: {wf.status} - tasks={len(wf.tasks or [])}")
220+
if wf.status in ('COMPLETED', 'FAILED') \
221+
and len(wf.tasks or []) == self.EXPECTED_TASK_COUNT:
222+
return wf_id, wf
223+
for task in (wf.tasks or []):
220224
print(f'task {task.task_def_name} : {task.status}')
221225
time.sleep(1)
226+
227+
return wf_id, wf
228+
229+
def test_03_execute_workflow(self):
230+
"""Execute workflow and verify completion."""
231+
print("\n" + "="*80 + "\nTEST 3: Execute Workflow\n" + "="*80)
222232

223-
final_wf = workflow_client.get_workflow(wf_id, include_tasks=True)
233+
self.assertTrue(self.workers_started, "Workers must be started first")
224234

235+
workflow_client = OrkesWorkflowClient(self.config)
236+
237+
# Each attempt starts a fresh workflow, so retrying a pathologically
238+
# slow run (cold workers / slow CI) is safe and self-contained. This
239+
# keeps a one-off timeout from forcing a manual CI job re-run.
240+
final_wf = None
241+
for attempt in range(3):
242+
wf_id, wf = self._run_workflow_to_terminal(workflow_client, timeout_s=90)
243+
if wf is not None and wf.status in ('COMPLETED', 'FAILED') \
244+
and len(wf.tasks or []) == self.EXPECTED_TASK_COUNT:
245+
final_wf = wf
246+
break
247+
print(f"attempt {attempt + 1}: wf={wf_id} "
248+
f"status={getattr(wf, 'status', None)} "
249+
f"tasks={len(getattr(wf, 'tasks', []) or [])}; retrying")
250+
225251
# Assertions
226-
self.assertIsNotNone(final_wf)
227-
self.assertEqual(len(final_wf.tasks), 5, "Should have 5 tasks")
252+
self.assertIsNotNone(
253+
final_wf,
254+
"workflow never reached a terminal state with all "
255+
f"{self.EXPECTED_TASK_COUNT} tasks after retries")
256+
self.assertEqual(len(final_wf.tasks), self.EXPECTED_TASK_COUNT,
257+
f"Should have {self.EXPECTED_TASK_COUNT} tasks")
228258

229259
# Verify each task
230260
tasks_by_ref = {t.reference_task_name: t for t in final_wf.tasks}

0 commit comments

Comments
 (0)