def test_simple_two_task_flow_with_final_task_set_to_fail( monkeypatch, executor): flow_run_id = str(uuid.uuid4()) task_run_id_1 = str(uuid.uuid4()) task_run_id_2 = str(uuid.uuid4()) with prefect.Flow(name="test") as flow: t1 = prefect.Task() t2 = prefect.Task() t2.set_upstream(t1) client = MockedCloudClient( flow_runs=[FlowRun(id=flow_run_id)], task_runs=[ TaskRun(id=task_run_id_1, task_id=t1.id, flow_run_id=flow_run_id), TaskRun(id=task_run_id_2, task_id=t2.id, flow_run_id=flow_run_id, state=Failed()), ], monkeypatch=monkeypatch, ) with prefect.context(flow_run_id=flow_run_id): state = CloudFlowRunner(flow=flow).run(return_tasks=flow.tasks, executor=executor) assert state.is_failed() assert client.flow_runs[flow_run_id].state.is_failed() assert client.task_runs[task_run_id_1].state.is_successful() assert client.task_runs[task_run_id_1].version == 2 assert client.task_runs[task_run_id_2].state.is_failed() assert client.task_runs[task_run_id_2].version == 0
def test_slug_mismatch_raises_informative_error(monkeypatch): flow_run_id = str(uuid.uuid4()) task_run_id_1 = str(uuid.uuid4()) task_run_id_2 = str(uuid.uuid4()) with prefect.Flow(name="test") as flow: t1 = prefect.Task() t2 = prefect.Task() t2.set_upstream(t1) client = MockedCloudClient( flow_runs=[FlowRun(id=flow_run_id)], task_runs=[ TaskRun(id=task_run_id_1, task_slug=flow.slugs[t1], flow_run_id=flow_run_id), TaskRun(id=task_run_id_2, task_slug="bad-slug", flow_run_id=flow_run_id), ], monkeypatch=monkeypatch, ) with prefect.context(flow_run_id=flow_run_id): state = CloudFlowRunner(flow=flow).run(return_tasks=flow.tasks) assert state.is_failed() ## assert informative message; can't use `match` because the real exception is one layer depeer than the ENDRUN assert "KeyError" in repr(state.result) assert "not found" in repr(state.result) assert "changing the Flow" in repr(state.result)
def test_deep_map_with_a_failure(monkeypatch, executor): flow_run_id = str(uuid.uuid4()) task_run_id_1 = str(uuid.uuid4()) task_run_id_2 = str(uuid.uuid4()) task_run_id_3 = str(uuid.uuid4()) with prefect.Flow(name="test", result_handler=JSONResultHandler()) as flow: t1 = plus_one.map([-1, 0, 1]) t2 = invert_fail_once.map(t1) t3 = plus_one.map(t2) client = MockedCloudClient( flow_runs=[FlowRun(id=flow_run_id)], task_runs=[ TaskRun(id=task_run_id_1, task_slug=flow.slugs[t1], flow_run_id=flow_run_id), TaskRun(id=task_run_id_2, task_slug=flow.slugs[t2], flow_run_id=flow_run_id), TaskRun(id=task_run_id_3, task_slug=flow.slugs[t3], flow_run_id=flow_run_id), ] + [ TaskRun(id=str(uuid.uuid4()), task_slug=flow.slugs[t], flow_run_id=flow_run_id) for t in flow.tasks if t not in [t1, t2, t3] ], monkeypatch=monkeypatch, ) with prefect.context(flow_run_id=flow_run_id): state = CloudFlowRunner(flow=flow).run(return_tasks=flow.tasks) assert state.is_failed() assert client.flow_runs[flow_run_id].state.is_failed() assert client.task_runs[task_run_id_1].state.is_mapped() assert client.task_runs[task_run_id_2].state.is_mapped() assert client.task_runs[task_run_id_3].state.is_mapped() # there should be a total of 4 task runs corresponding to each mapped task for t in [t1, t2, t3]: assert (len([ tr for tr in client.task_runs.values() if tr.task_slug == flow.slugs[t] ]) == 4) # t2's first child task should have failed t2_0 = next(tr for tr in client.task_runs.values() if tr.task_slug == flow.slugs[t2] and tr.map_index == 0) assert t2_0.state.is_failed() # t3's first child task should have failed t3_0 = next(tr for tr in client.task_runs.values() if tr.task_slug == flow.slugs[t3] and tr.map_index == 0) assert t3_0.state.is_failed()
def test_flow_runner_raises_endrun_if_client_cant_retrieve_state(monkeypatch): flow = prefect.Flow(name="test") get_flow_run_info = MagicMock(side_effect=SyntaxError) set_flow_run_state = MagicMock() client = MagicMock(get_flow_run_info=get_flow_run_info, set_flow_run_state=set_flow_run_state) monkeypatch.setattr("prefect.engine.cloud.flow_runner.Client", MagicMock(return_value=client)) ## if ENDRUN is raised, res will be last state seen res = CloudFlowRunner(flow=flow).run() assert get_flow_run_info.called assert res.is_failed() assert isinstance(res.result, SyntaxError)
def test_simple_three_task_flow_with_one_failing_task(monkeypatch, executor): @prefect.task def error(): 1 / 0 flow_run_id = str(uuid.uuid4()) task_run_id_1 = str(uuid.uuid4()) task_run_id_2 = str(uuid.uuid4()) task_run_id_3 = str(uuid.uuid4()) with prefect.Flow(name="test") as flow: t1 = prefect.Task() t2 = prefect.Task() t3 = error() t2.set_upstream(t1) t3.set_upstream(t2) client = MockedCloudClient( flow_runs=[FlowRun(id=flow_run_id)], task_runs=[ TaskRun( id=task_run_id_1, task_slug=flow.slugs[t1], flow_run_id=flow_run_id ), TaskRun( id=task_run_id_2, task_slug=flow.slugs[t2], flow_run_id=flow_run_id ), TaskRun( id=task_run_id_3, task_slug=flow.slugs[t3], flow_run_id=flow_run_id ), ], monkeypatch=monkeypatch, ) with prefect.context(flow_run_id=flow_run_id): state = CloudFlowRunner(flow=flow).run( return_tasks=flow.tasks, executor=executor ) assert state.is_failed() assert client.flow_runs[flow_run_id].state.is_failed() assert client.task_runs[task_run_id_1].state.is_successful() assert client.task_runs[task_run_id_1].version == 2 assert client.task_runs[task_run_id_2].state.is_successful() assert client.task_runs[task_run_id_2].version == 2 assert client.task_runs[task_run_id_3].state.is_failed() assert client.task_runs[task_run_id_2].version == 2
def test_state_handler_failures_are_handled_appropriately(client): def bad(*args, **kwargs): raise SyntaxError("Syntax Errors are nice because they're so unique") @prefect.task def do_nothing(): raise ValueError("This task failed somehow") f = prefect.Flow(name="test", tasks=[do_nothing], on_failure=bad) res = CloudFlowRunner(flow=f).run() assert res.is_failed() assert "SyntaxError" in res.message assert isinstance(res.result, SyntaxError) assert client.set_flow_run_state.call_count == 2 states = [call[1]["state"] for call in client.set_flow_run_state.call_args_list] assert states[0].is_running() assert states[1].is_failed() assert isinstance(states[1].result, SyntaxError)