|
26 | 26 | from conductor.client.workflow.conductor_workflow import ConductorWorkflow |
27 | 27 | from conductor.client.workflow.executor.workflow_executor import WorkflowExecutor |
28 | 28 | from conductor.client.workflow.task.simple_task import SimpleTask |
29 | | -from tests.integration.retry_helpers import retry_scenario |
| 29 | +from tests.integration.retry_helpers import retry_scenario, TERMINAL_WORKFLOW_STATES |
30 | 30 |
|
31 | 31 | SUFFIX = str(uuid()) |
32 | 32 | WORKFLOW_NAME = 'IntegrationTestOrkesClientsWf_' + SUFFIX |
@@ -394,24 +394,28 @@ def __test_unit_test_workflow(self): |
394 | 394 | execution = self.workflow_client.test_workflow(testRequest) |
395 | 395 | assert execution != None |
396 | 396 |
|
397 | | - print( |
398 | | - f"[test_workflow] workflow_id={getattr(execution, 'workflow_id', None)} " |
399 | | - f"status={execution.status} " |
400 | | - f"task_count={len(execution.tasks or [])}" |
401 | | - ) |
402 | | - for t in (execution.tasks or []): |
403 | | - print( |
404 | | - f"[test_workflow] task ref={t.reference_task_name} " |
405 | | - f"type={t.task_type} status={t.status} " |
406 | | - f"retried={getattr(t, 'retry_count', None)}" |
407 | | - ) |
| 397 | + # There appears to be no guarantee about it actually coming back |
| 398 | + # complete, because it happens (often) that it does not. So we accept a |
| 399 | + # COMPLETED result immediately, but if it isn't COMPLETED yet we don't |
| 400 | + # fail: we poll the workflow and wait for it to reach a terminal state. |
408 | 401 | if execution.status != "COMPLETED": |
| 402 | + #need to fix the print statement below to have the wf id and the status |
409 | 403 | print( |
410 | | - f"[test_workflow] non-terminal output={getattr(execution, 'output', None)}" |
| 404 | + f"[test_workflow] workflow_id={getattr(execution, 'workflow_id', None)} status={execution.status} (was expecting COMPLETED - but will poll for that now)" |
411 | 405 | ) |
| 406 | + workflow_id = getattr(execution, "workflow_id", None) |
| 407 | + polled = ( |
| 408 | + self.__poll_workflow_until_complete(workflow_id) |
| 409 | + if workflow_id else None |
| 410 | + ) |
| 411 | + if polled is not None: |
| 412 | + execution = polled |
412 | 413 |
|
413 | 414 | # Ensure workflow is completed successfully |
414 | | - assert execution.status == "COMPLETED" |
| 415 | + assert execution.status == "COMPLETED", ( |
| 416 | + f"workflow expected to be COMPLETED, but received {execution.status}, " |
| 417 | + f"workflow_id: {getattr(execution, 'workflow_id', None)}" |
| 418 | + ) |
415 | 419 |
|
416 | 420 | # Ensure the inputs were captured correctly |
417 | 421 | assert execution.input["loanAmount"] == testRequest.input["loanAmount"] |
@@ -463,6 +467,35 @@ def __test_unit_test_workflow(self): |
463 | 467 | # Workflow output takes the latest iteration output of a loopOver task. |
464 | 468 | assert execution.output["phoneNumberValid"] |
465 | 469 |
|
| 470 | + def __poll_workflow_until_complete(self, workflow_id, timeout_seconds=60, |
| 471 | + poll_interval=2): |
| 472 | + """Poll ``workflow_id`` until it reaches a terminal state or the timeout |
| 473 | + passes, returning the last observed Workflow (or None if it could never |
| 474 | + be fetched). Transient poll errors are logged and retried within the |
| 475 | + timeout so a slow-but-eventually-complete run isn't reported as a bare |
| 476 | + failure. |
| 477 | + """ |
| 478 | + deadline = time.monotonic() + timeout_seconds |
| 479 | + workflow = None |
| 480 | + while True: |
| 481 | + try: |
| 482 | + workflow = self.workflow_client.get_workflow( |
| 483 | + workflow_id, include_tasks=True) |
| 484 | + except Exception as e: # transient blip against the shared server |
| 485 | + print(f"[test_workflow] error polling {workflow_id}: {e}") |
| 486 | + status = getattr(workflow, "status", None) |
| 487 | + if status in TERMINAL_WORKFLOW_STATES: |
| 488 | + print(f"[test_workflow] {workflow_id} reached {status}") |
| 489 | + return workflow |
| 490 | + if time.monotonic() >= deadline: |
| 491 | + print( |
| 492 | + f"[test_workflow] {workflow_id} still {status} after " |
| 493 | + f"{timeout_seconds}s; giving up wait" |
| 494 | + ) |
| 495 | + return workflow |
| 496 | + print(f"[test_workflow] {workflow_id} still {status}; waiting") |
| 497 | + time.sleep(poll_interval) |
| 498 | + |
466 | 499 | def __test_unregister_workflow_definition(self): |
467 | 500 | self.metadata_client.unregister_workflow_def(WORKFLOW_NAME, 1) |
468 | 501 |
|
|
0 commit comments