112 lines
3.4 KiB
Python
112 lines
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any, cast
|
|
|
|
import pytest
|
|
|
|
from examples.authoring_concurrent_foreach import (
|
|
ConcurrentForeachOutput,
|
|
ConcurrentForeachState,
|
|
ItemsInput,
|
|
build_concurrent_foreach_workflow,
|
|
record_item,
|
|
run_async_ordered_example,
|
|
run_collected_errors_example,
|
|
run_replace_conflict_example,
|
|
)
|
|
from wf_authoring import WorkflowBuilder, state_path
|
|
from wf_core import ForeachConcurrentPolicy, RunStatus
|
|
|
|
|
|
def test_authoring_concurrent_foreach_collects_item_errors() -> None:
|
|
run = run_collected_errors_example()
|
|
|
|
assert run.status == RunStatus.COMPLETED
|
|
assert run.output["seen"] == ["a", "c"]
|
|
assert len(run.output["errors"]) == 1
|
|
error = run.output["errors"][0]
|
|
assert error["index"] == 1
|
|
assert error["node_id"] == "record"
|
|
assert error["error_type"] == "ValueError"
|
|
assert error["message"] == "bad item"
|
|
assert error["item"] == "bad"
|
|
|
|
|
|
async def test_authoring_async_concurrent_foreach_commits_in_item_order() -> None:
|
|
run = await run_async_ordered_example()
|
|
|
|
assert run.status == RunStatus.COMPLETED
|
|
assert run.output["seen"] == ["a", "b", "c"]
|
|
|
|
|
|
def test_authoring_foreach_accepts_item_error_mapping_with_authoring_path() -> None:
|
|
builder = build_concurrent_foreach_workflow(
|
|
record_item,
|
|
item_error={"action": "collect", "collect_to": state_path("errors")},
|
|
)
|
|
|
|
foreach = builder.compile().nodes[0]
|
|
|
|
assert foreach.model_dump(mode="json")["item_error"]["collect_to"] == "state.errors"
|
|
|
|
|
|
def test_authoring_foreach_accepts_item_error_action_string() -> None:
|
|
builder = build_concurrent_foreach_workflow(record_item, item_error="skip")
|
|
|
|
foreach = builder.compile().nodes[0]
|
|
|
|
assert foreach.model_dump(mode="json")["item_error"]["action"] == "skip"
|
|
|
|
|
|
def test_authoring_foreach_accepts_concurrent_policy_object() -> None:
|
|
builder = build_concurrent_foreach_workflow(
|
|
record_item,
|
|
concurrent=ForeachConcurrentPolicy(max_active=1, max_outstanding=3),
|
|
)
|
|
|
|
foreach = builder.compile().nodes[0]
|
|
|
|
assert foreach.model_dump(mode="json")["concurrent"]["max_active"] == 1
|
|
assert foreach.model_dump(mode="json")["concurrent"]["max_outstanding"] == 3
|
|
|
|
|
|
def test_authoring_concurrent_foreach_example_documents_replace_conflict() -> None:
|
|
run_replace_conflict_example()
|
|
|
|
|
|
def test_authoring_foreach_deprecated_on_item_error_warns() -> None:
|
|
builder = WorkflowBuilder(
|
|
name="deprecated_item_error",
|
|
input_schema=ItemsInput,
|
|
state_schema=ConcurrentForeachState,
|
|
output_schema=ConcurrentForeachOutput,
|
|
)
|
|
|
|
with pytest.warns(DeprecationWarning, match="on_item_error"):
|
|
foreach = builder.foreach(
|
|
id="each",
|
|
over=state_path("items"),
|
|
as_="item",
|
|
on_item_error="skip",
|
|
)
|
|
|
|
assert foreach.item_error.action == "skip"
|
|
|
|
|
|
def test_authoring_foreach_rejects_mixed_item_error_styles() -> None:
|
|
builder = WorkflowBuilder(
|
|
name="mixed_item_error",
|
|
input_schema=ItemsInput,
|
|
state_schema=ConcurrentForeachState,
|
|
output_schema=ConcurrentForeachOutput,
|
|
)
|
|
|
|
with pytest.raises(TypeError, match="cannot mix item_error"):
|
|
cast(Any, builder.foreach)(
|
|
id="each",
|
|
over=state_path("items"),
|
|
as_="item",
|
|
item_error="skip",
|
|
on_item_error="collect",
|
|
)
|