durable-workflow 2.5.9__tar.gz → 2.5.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {durable_workflow-2.5.9/src/durable_workflow.egg-info → durable_workflow-2.5.10}/PKG-INFO +1 -1
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/pyproject.toml +3 -3
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/workflow.py +9 -13
- {durable_workflow-2.5.9 → durable_workflow-2.5.10/src/durable_workflow.egg-info}/PKG-INFO +1 -1
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_patch_history.py +74 -1
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_replay.py +18 -6
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_replay_regression_corpus.py +7 -1
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/LICENSE +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/README.md +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/setup.cfg +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/__init__.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/_activity_process.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/_avro.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/_cancellation_scope.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/_cancellation_scope_history.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/_cooperative_cancellation.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/_prepared_local_activity.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/_sticky_workflow_cache.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/activity.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/auth_composition.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/cancellation.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/client.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/errors.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/external_storage.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/external_task_input.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/external_task_result.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/history_bundle_verify.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/interceptors.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/invocable.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/metrics.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/nexus.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/py.typed +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/python_conformance.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/replay_conformance.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/replay_verify.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/retry_policy.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/serializer.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/sync.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/testing.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/worker.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/worker_session.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/workflow_updates_conformance.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow.egg-info/SOURCES.txt +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow.egg-info/dependency_links.txt +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow.egg-info/entry_points.txt +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow.egg-info/requires.txt +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow.egg-info/top_level.txt +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_activity_cancellation_policies.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_activity_context.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_activity_process.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_auth_composition.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_avro_value_benchmark.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_avro_value_protocol.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cancellation_context.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cancellation_remaining_time.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cancellation_scope_admission.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cancellation_scope_authoring.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cancellation_scope_boundary.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cancellation_scope_opening.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_child_workflow_policies.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_ci_checkout.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_ci_integration_endpoint.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_client.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_client_timeouts.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_codec_regression_corpus.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_committed_cancellation_scope_descendants.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_committed_cancellation_scope_groups.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_committed_cancellation_scope_history.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_committed_cancellation_scope_replay.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_condition_replay_drift.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_control_plane_parity_fixtures.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cooperative_cancellation.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cooperative_cancellation_client.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cooperative_cancellation_worker.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cooperative_remote_worker.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_docstring_cross_references.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_durable_selection.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_errors.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_external_storage.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_external_task_input.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_external_task_result.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_golden_history_replay.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_history_bundle_verify.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_history_event_contract.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_invocable.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_message_streams.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_metrics.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_nexus_namespace.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_nexus_wire.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_order_processing_example.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_parallel_completion_order_replay.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_parallel_saga_authoring.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_pending_operation_replay.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_prepared_local_activity_client.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_prepared_local_activity_groups.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_prepared_local_activity_policies.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_prepared_local_activity_worker.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_public_boundary_scanner.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_pypi_project_surface.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_python_conformance.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_queries.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_readme_quickstart.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_redrive_replay.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_registration_ambiguity.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_release_metadata.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_replay_conformance.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_replay_verify.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_retry_policy.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_run_cancellation_scope_replay.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_runtime_external_payload_transport.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_schedules.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_scoped_run_cancellation_context.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_serializer.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_signals.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_sleep.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_standalone_activity_client.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_sticky_execution.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_storage_admission.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_sync.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_testing_harness.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_update_signal_condition_replay.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_updates.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_wait_condition.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_worker.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_worker_session.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_workflow_entrypoint_validation.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_workflow_result_timeout.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_workflow_streams.py +0 -0
- {durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_workflow_updates_conformance.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "durable-workflow"
|
|
7
|
-
version = "2.5.
|
|
7
|
+
version = "2.5.10"
|
|
8
8
|
description = "Python client and worker SDK for Durable Workflow Cloud and self-hosted Server"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -71,8 +71,8 @@ durable-workflow-replay-conformance = "durable_workflow.replay_conformance:main"
|
|
|
71
71
|
durable-workflow-workflow-updates-conformance = "durable_workflow.workflow_updates_conformance:main"
|
|
72
72
|
|
|
73
73
|
[tool.durable-workflow]
|
|
74
|
-
product-train = "2.5.
|
|
75
|
-
registry-version = "2.5.
|
|
74
|
+
product-train = "2.5.10"
|
|
75
|
+
registry-version = "2.5.10"
|
|
76
76
|
supported-server-versions = "2.5.16"
|
|
77
77
|
worker-protocol-version = "1.20"
|
|
78
78
|
control-plane-version = "2"
|
|
@@ -5238,7 +5238,7 @@ def _replay_state(
|
|
|
5238
5238
|
pending: list[Command] = []
|
|
5239
5239
|
advanced_cmd: Any = None
|
|
5240
5240
|
terminal_condition_reopen_cmd: WaitCondition | None = None
|
|
5241
|
-
version_decisions: dict[str,
|
|
5241
|
+
version_decisions: dict[str, int] = {}
|
|
5242
5242
|
|
|
5243
5243
|
def _version_error(detail: str, step: _RecordedStep | None = None) -> NonDeterministicReplayError:
|
|
5244
5244
|
return NonDeterministicReplayError(
|
|
@@ -5256,13 +5256,12 @@ def _replay_state(
|
|
|
5256
5256
|
if type(version) is not int:
|
|
5257
5257
|
raise _version_error("Version marker must contain an integer version.", step)
|
|
5258
5258
|
change_id = payload.get("change_id")
|
|
5259
|
-
if
|
|
5259
|
+
if not isinstance(change_id, str) or not change_id.strip():
|
|
5260
5260
|
raise _version_error("Version marker must retain a non-empty change ID.", step)
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
raise _version_error("Recorded version marker has an invalid supported range.", step)
|
|
5261
|
+
minimum, maximum = payload.get("min_supported"), payload.get("max_supported")
|
|
5262
|
+
if (type(minimum) is not int or type(maximum) is not int
|
|
5263
|
+
or not minimum <= version <= maximum):
|
|
5264
|
+
raise _version_error("Recorded version marker has an invalid supported range.", step)
|
|
5266
5265
|
return version
|
|
5267
5266
|
|
|
5268
5267
|
def _consume_duplicate_version_markers() -> None:
|
|
@@ -5277,7 +5276,7 @@ def _replay_state(
|
|
|
5277
5276
|
if _next_unconsumed_recorded_step() is not step:
|
|
5278
5277
|
return
|
|
5279
5278
|
version = _recorded_version(step)
|
|
5280
|
-
if version != version_decisions[change_id]
|
|
5279
|
+
if version != version_decisions[change_id]:
|
|
5281
5280
|
raise _version_error(f"Conflicting recorded decisions for change ID {change_id!r}.", step)
|
|
5282
5281
|
# Earlier SDKs emitted a marker for every invocation. Consume these
|
|
5283
5282
|
# aliases while retaining their physical positions for cancellation
|
|
@@ -5293,13 +5292,10 @@ def _replay_state(
|
|
|
5293
5292
|
if (type(command.min_supported) is not int or type(command.max_supported) is not int
|
|
5294
5293
|
or command.min_supported > command.max_supported):
|
|
5295
5294
|
raise _version_error(f"Invalid supported range for change ID {change_id!r}.")
|
|
5296
|
-
family = "version" if command.result_kind == "version" else "patch"
|
|
5297
5295
|
decision = version_decisions.get(change_id)
|
|
5298
5296
|
step = _next_unconsumed_recorded_step()
|
|
5299
5297
|
if decision is not None:
|
|
5300
|
-
version
|
|
5301
|
-
if original_family != family:
|
|
5302
|
-
raise _version_error(f"Change ID {change_id!r} cannot switch between version and patch helpers.", step)
|
|
5298
|
+
version = decision
|
|
5303
5299
|
elif step is not None and step.shape == "version marker":
|
|
5304
5300
|
_assert_step_matches(command, step)
|
|
5305
5301
|
version = _recorded_version(step)
|
|
@@ -5326,7 +5322,7 @@ def _replay_state(
|
|
|
5326
5322
|
f"Recorded version {version} for change ID {change_id!r} is outside "
|
|
5327
5323
|
f"the supported range {command.min_supported}..{command.max_supported}.", step,
|
|
5328
5324
|
)
|
|
5329
|
-
version_decisions.setdefault(change_id,
|
|
5325
|
+
version_decisions.setdefault(change_id, version)
|
|
5330
5326
|
return _version_marker_result(command, version)
|
|
5331
5327
|
|
|
5332
5328
|
def _parallel_leaf_kind(command: Any) -> str:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from itertools import permutations
|
|
3
4
|
from typing import Any
|
|
4
5
|
|
|
5
6
|
import pytest
|
|
@@ -74,6 +75,43 @@ class PatchCancellationWorkflow:
|
|
|
74
75
|
return [decisions, "cancelled"]
|
|
75
76
|
|
|
76
77
|
|
|
78
|
+
@workflow.defn(name="tests.patch.helpers")
|
|
79
|
+
class MixedPatchHelpersWorkflow:
|
|
80
|
+
def run(self, ctx, helpers): # type: ignore[no-untyped-def]
|
|
81
|
+
decisions = []
|
|
82
|
+
for helper in helpers:
|
|
83
|
+
command = ctx.get_version("same", -1, 1) if helper == "get_version" else getattr(ctx, helper)("same")
|
|
84
|
+
decisions.append((yield command))
|
|
85
|
+
result = yield ctx.schedule_activity("old", [])
|
|
86
|
+
return [decisions, result]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@pytest.mark.parametrize("helpers", list(permutations(["get_version", "patched", "deprecate_patch"])))
|
|
90
|
+
@pytest.mark.parametrize("marker_count", [0, 1, 2])
|
|
91
|
+
@pytest.mark.parametrize("completed", [False, True])
|
|
92
|
+
def test_helpers_share_one_decision_on_old_history(
|
|
93
|
+
helpers: tuple[str, ...], marker_count: int, completed: bool,
|
|
94
|
+
) -> None:
|
|
95
|
+
history = [version_marker(sequence) for sequence in range(1, marker_count + 1)]
|
|
96
|
+
history += old_activity(marker_count + 1, completed)
|
|
97
|
+
commands = replay(MixedPatchHelpersWorkflow, history, [helpers]).commands
|
|
98
|
+
if not completed:
|
|
99
|
+
assert commands == []
|
|
100
|
+
else:
|
|
101
|
+
assert len(commands) == 1
|
|
102
|
+
assert isinstance(commands[0], CompleteWorkflow)
|
|
103
|
+
version = 1 if marker_count else -1
|
|
104
|
+
values = {"get_version": version, "patched": version == 1, "deprecate_patch": None}
|
|
105
|
+
assert commands[0].result == [[values[helper] for helper in helpers], "recorded"]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@pytest.mark.parametrize("helpers", list(permutations(["get_version", "patched", "deprecate_patch"])))
|
|
109
|
+
def test_helpers_record_one_decision_on_fresh_history(helpers: tuple[str, ...]) -> None:
|
|
110
|
+
commands = replay(MixedPatchHelpersWorkflow, [], [helpers]).commands
|
|
111
|
+
assert [type(command) for command in commands] == [RecordVersionMarker, ScheduleActivity]
|
|
112
|
+
assert commands[0].version == 1
|
|
113
|
+
|
|
114
|
+
|
|
77
115
|
@pytest.mark.parametrize("completed", [False, True])
|
|
78
116
|
def test_inserted_patch_preserves_unmarked_activity(completed: bool) -> None:
|
|
79
117
|
outcome = replay(PatchInsertionWorkflow, old_activity(1, completed), [])
|
|
@@ -122,7 +160,7 @@ def test_repeated_version_reuses_first_decision() -> None:
|
|
|
122
160
|
|
|
123
161
|
|
|
124
162
|
@pytest.mark.parametrize("arguments", [[3], [1, True]])
|
|
125
|
-
def
|
|
163
|
+
def test_repeated_version_rejects_incompatible_range(arguments: list[Any]) -> None:
|
|
126
164
|
with pytest.raises(NonDeterministicReplayError):
|
|
127
165
|
replay(VersionRangeWorkflow, [], arguments)
|
|
128
166
|
|
|
@@ -134,6 +172,41 @@ def test_conflicting_duplicate_marker_is_rejected(version: Any) -> None:
|
|
|
134
172
|
replay(RepeatedPatchWorkflow, history, [])
|
|
135
173
|
|
|
136
174
|
|
|
175
|
+
@pytest.mark.parametrize("sequence", [1, 2])
|
|
176
|
+
@pytest.mark.parametrize("bounds", [
|
|
177
|
+
{}, {"min_supported": -1}, {"max_supported": 1},
|
|
178
|
+
{"min_supported": True, "max_supported": 1},
|
|
179
|
+
{"min_supported": -1, "max_supported": "1"},
|
|
180
|
+
{"min_supported": 2, "max_supported": 1},
|
|
181
|
+
{"min_supported": -1, "max_supported": 0},
|
|
182
|
+
])
|
|
183
|
+
def test_canonical_markers_require_valid_recorded_ranges(sequence: int, bounds: dict[str, Any]) -> None:
|
|
184
|
+
markers = [version_marker(1), version_marker(2)]
|
|
185
|
+
markers[sequence - 1]["payload"].pop("min_supported")
|
|
186
|
+
markers[sequence - 1]["payload"].pop("max_supported")
|
|
187
|
+
markers[sequence - 1]["payload"].update(bounds)
|
|
188
|
+
with pytest.raises(NonDeterministicReplayError, match="invalid supported range"):
|
|
189
|
+
replay(RepeatedPatchWorkflow, markers + old_activity(3), [])
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@pytest.mark.parametrize("identity", [{}, {"change_id": None}, {"change_id": ""}, {"change_id": True}])
|
|
193
|
+
def test_canonical_marker_requires_a_change_id(identity: dict[str, Any]) -> None:
|
|
194
|
+
marker = version_marker(1)
|
|
195
|
+
marker["payload"].pop("change_id")
|
|
196
|
+
marker["payload"].update(identity)
|
|
197
|
+
with pytest.raises(NonDeterministicReplayError, match="non-empty change ID"):
|
|
198
|
+
replay(RepeatedPatchWorkflow, [marker] + old_activity(2), [])
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def test_duplicate_ranges_may_differ_around_the_same_version() -> None:
|
|
202
|
+
marker = version_marker(2)
|
|
203
|
+
marker["payload"].update(min_supported=0, max_supported=3)
|
|
204
|
+
commands = replay(RepeatedPatchWorkflow, [version_marker(1), marker] + old_activity(3), []).commands
|
|
205
|
+
assert len(commands) == 1
|
|
206
|
+
assert isinstance(commands[0], CompleteWorkflow)
|
|
207
|
+
assert commands[0].result == [True, True, "recorded"]
|
|
208
|
+
|
|
209
|
+
|
|
137
210
|
@pytest.mark.parametrize("marker_count", [0, 1, 2])
|
|
138
211
|
def test_patch_decisions_preserve_committed_cancellation_sequence(marker_count: int) -> None:
|
|
139
212
|
history = [version_marker(sequence) for sequence in range(1, marker_count + 1)]
|
|
@@ -1949,7 +1949,9 @@ class TestVersionMarker:
|
|
|
1949
1949
|
|
|
1950
1950
|
def test_version_from_history(self) -> None:
|
|
1951
1951
|
history = [
|
|
1952
|
-
{"event_type": "VersionMarkerRecorded", "payload": {
|
|
1952
|
+
{"event_type": "VersionMarkerRecorded", "payload": {
|
|
1953
|
+
"sequence": 1, "change_id": "change-1", "version": 2, "min_supported": 1, "max_supported": 2,
|
|
1954
|
+
}},
|
|
1953
1955
|
]
|
|
1954
1956
|
outcome = replay(VersionWorkflow, history, [])
|
|
1955
1957
|
assert len(outcome.commands) == 1
|
|
@@ -1959,7 +1961,9 @@ class TestVersionMarker:
|
|
|
1959
1961
|
|
|
1960
1962
|
def test_old_version_from_history(self) -> None:
|
|
1961
1963
|
history = [
|
|
1962
|
-
{"event_type": "VersionMarkerRecorded", "payload": {
|
|
1964
|
+
{"event_type": "VersionMarkerRecorded", "payload": {
|
|
1965
|
+
"sequence": 1, "change_id": "change-1", "version": 1, "min_supported": 1, "max_supported": 2,
|
|
1966
|
+
}},
|
|
1963
1967
|
]
|
|
1964
1968
|
outcome = replay(VersionWorkflow, history, [])
|
|
1965
1969
|
assert len(outcome.commands) == 1
|
|
@@ -1969,7 +1973,9 @@ class TestVersionMarker:
|
|
|
1969
1973
|
|
|
1970
1974
|
def test_full_replay(self) -> None:
|
|
1971
1975
|
history = [
|
|
1972
|
-
{"event_type": "VersionMarkerRecorded", "payload": {
|
|
1976
|
+
{"event_type": "VersionMarkerRecorded", "payload": {
|
|
1977
|
+
"sequence": 1, "change_id": "change-1", "version": 2, "min_supported": 1, "max_supported": 2,
|
|
1978
|
+
}},
|
|
1973
1979
|
{"event_type": "ActivityCompleted", "payload": {"result": _avro("done")}},
|
|
1974
1980
|
]
|
|
1975
1981
|
outcome = replay(VersionWorkflow, history, [])
|
|
@@ -2006,7 +2012,9 @@ class TestVersionMarker:
|
|
|
2006
2012
|
|
|
2007
2013
|
def test_patched_uses_recorded_marker_for_existing_patched_runs(self) -> None:
|
|
2008
2014
|
history = [
|
|
2009
|
-
{"event_type": "VersionMarkerRecorded", "payload": {
|
|
2015
|
+
{"event_type": "VersionMarkerRecorded", "payload": {
|
|
2016
|
+
"sequence": 1, "change_id": "patch-1", "version": 1, "min_supported": -1, "max_supported": 1,
|
|
2017
|
+
}},
|
|
2010
2018
|
]
|
|
2011
2019
|
|
|
2012
2020
|
outcome = replay(PatchedWorkflow, history, [])
|
|
@@ -2017,7 +2025,9 @@ class TestVersionMarker:
|
|
|
2017
2025
|
|
|
2018
2026
|
def test_patched_uses_legacy_default_for_existing_unpatched_runs(self) -> None:
|
|
2019
2027
|
history = [
|
|
2020
|
-
{"event_type": "VersionMarkerRecorded", "payload": {
|
|
2028
|
+
{"event_type": "VersionMarkerRecorded", "payload": {
|
|
2029
|
+
"sequence": 1, "change_id": "patch-1", "version": -1, "min_supported": -1, "max_supported": 1,
|
|
2030
|
+
}},
|
|
2021
2031
|
]
|
|
2022
2032
|
|
|
2023
2033
|
outcome = replay(PatchedWorkflow, history, [])
|
|
@@ -2040,7 +2050,9 @@ class TestVersionMarker:
|
|
|
2040
2050
|
|
|
2041
2051
|
replayed = replay(
|
|
2042
2052
|
DeprecatedPatchWorkflow,
|
|
2043
|
-
[{"event_type": "VersionMarkerRecorded", "payload": {
|
|
2053
|
+
[{"event_type": "VersionMarkerRecorded", "payload": {
|
|
2054
|
+
"sequence": 1, "change_id": "patch-1", "version": 1, "min_supported": -1, "max_supported": 1,
|
|
2055
|
+
}}],
|
|
2044
2056
|
[],
|
|
2045
2057
|
)
|
|
2046
2058
|
assert len(replayed.commands) == 1
|
|
@@ -25,7 +25,12 @@ from tests.test_golden_history_replay import (
|
|
|
25
25
|
GoldenTimeoutWaitWorkflow,
|
|
26
26
|
GoldenVersionMarkerWorkflow,
|
|
27
27
|
)
|
|
28
|
-
from tests.test_patch_history import
|
|
28
|
+
from tests.test_patch_history import (
|
|
29
|
+
MixedPatchHelpersWorkflow,
|
|
30
|
+
PatchCancellationWorkflow,
|
|
31
|
+
PatchInsertionWorkflow,
|
|
32
|
+
RepeatedPatchWorkflow,
|
|
33
|
+
)
|
|
29
34
|
from tests.test_pending_operation_replay import PendingOperation
|
|
30
35
|
from tests.test_update_signal_condition_replay import (
|
|
31
36
|
PostConditionReceiversWorkflow,
|
|
@@ -290,6 +295,7 @@ WORKFLOWS = [
|
|
|
290
295
|
ParallelResultBindingWorkflow,
|
|
291
296
|
PatchCancellationWorkflow,
|
|
292
297
|
PatchInsertionWorkflow,
|
|
298
|
+
MixedPatchHelpersWorkflow,
|
|
293
299
|
RepeatedPatchWorkflow,
|
|
294
300
|
PendingOperation,
|
|
295
301
|
PagedRecordedSideEffectsWorkflow,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/_activity_process.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/_cancellation_scope.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/_cooperative_cancellation.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/_prepared_local_activity.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/_sticky_workflow_cache.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/external_task_input.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/external_task_result.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/history_bundle_verify.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/python_conformance.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow/replay_conformance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow.egg-info/requires.txt
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/src/durable_workflow.egg-info/top_level.txt
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_activity_cancellation_policies.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cancellation_remaining_time.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cancellation_scope_admission.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cancellation_scope_authoring.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cancellation_scope_boundary.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_committed_cancellation_scope_groups.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_committed_cancellation_scope_replay.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_control_plane_parity_fixtures.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cooperative_cancellation_client.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_cooperative_cancellation_worker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_parallel_completion_order_replay.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_prepared_local_activity_client.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_prepared_local_activity_groups.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_prepared_local_activity_policies.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_prepared_local_activity_worker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_run_cancellation_scope_replay.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_runtime_external_payload_transport.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_scoped_run_cancellation_context.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_update_signal_condition_replay.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_workflow_entrypoint_validation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.9 → durable_workflow-2.5.10}/tests/test_workflow_updates_conformance.py
RENAMED
|
File without changes
|