Files
wehub-resource-sync db620d33df
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:39:25 +08:00

124 lines
4.9 KiB
Python

# Copyright (c) Microsoft. All rights reserved.
"""Unit tests for execute_workflow_activity (shared non-agent executor activity body).
These tests exercise the host-agnostic activity execution shared by the Azure
Functions and standalone durabletask workflow hosts. In particular they protect
the state snapshot/diff semantics: the snapshot must be a *deep* copy so that
in-place mutations to nested objects (dicts, lists) are correctly detected as
updates (regression guard for the shallow-copy bug, #4500).
"""
import json
from typing import Any
from unittest.mock import AsyncMock, Mock
from agent_framework_durabletask import execute_workflow_activity
from agent_framework_durabletask._workflows.orchestrator import SOURCE_ORCHESTRATOR
def _make_executor(executor_id: str, mutate: Any) -> Mock:
"""Build a mock non-agent executor whose execute() mutates shared state."""
executor = Mock()
executor.id = executor_id
executor.execute = AsyncMock(side_effect=mutate)
return executor
def _run(executor: Mock, snapshot: dict[str, Any]) -> dict[str, Any]:
"""Invoke execute_workflow_activity and return the parsed result dict."""
input_data = json.dumps({
"message": "test",
"shared_state_snapshot": snapshot,
"source_executor_ids": [SOURCE_ORCHESTRATOR],
})
return json.loads(execute_workflow_activity(executor, input_data))
class TestExecuteWorkflowActivityStateDiff:
"""State snapshot/diff behavior of the shared workflow activity body."""
def test_nested_dict_mutation_detected(self) -> None:
"""In-place mutation of a nested dict is reported as an update."""
async def mutate(message: Any, source_executor_ids: Any, state: Any, runner_context: Any) -> None:
config = state.get("Local.config")
config["code"] = "SOMECODEXXX"
config["enabled"] = True
state.commit()
executor = _make_executor("test-exec", mutate)
result = _run(executor, {"Local.config": {"code": "", "enabled": False}, "simple_key": "simple_value"})
updates = result["shared_state_updates"]
assert "Local.config" in updates, "nested mutation not detected — snapshot may be a shallow copy"
assert updates["Local.config"]["code"] == "SOMECODEXXX"
assert updates["Local.config"]["enabled"] is True
def test_new_key_in_nested_dict_detected(self) -> None:
"""Adding a key to a nested dict is reported as an update."""
async def mutate(message: Any, source_executor_ids: Any, state: Any, runner_context: Any) -> None:
state.get("Local.data")["code"] = "NEW_CODE"
state.commit()
executor = _make_executor("test-exec", mutate)
result = _run(executor, {"Local.data": {"existing": "value"}})
assert result["shared_state_updates"]["Local.data"]["code"] == "NEW_CODE"
def test_nested_list_mutation_detected(self) -> None:
"""Appending to a nested list is reported as an update."""
async def mutate(message: Any, source_executor_ids: Any, state: Any, runner_context: Any) -> None:
state.get("Local.items").append(4)
state.commit()
executor = _make_executor("test-exec", mutate)
result = _run(executor, {"Local.items": [1, 2, 3]})
assert result["shared_state_updates"]["Local.items"] == [1, 2, 3, 4]
def test_new_top_level_key_detected(self) -> None:
"""Setting a new top-level key is reported as an update."""
async def mutate(message: Any, source_executor_ids: Any, state: Any, runner_context: Any) -> None:
state.set("Local.code", "SOMECODEXXX")
state.commit()
executor = _make_executor("test-exec", mutate)
result = _run(executor, {"existing": "value"})
assert result["shared_state_updates"]["Local.code"] == "SOMECODEXXX"
def test_unchanged_state_produces_empty_diff(self) -> None:
"""Unmodified state produces no updates."""
async def mutate(message: Any, source_executor_ids: Any, state: Any, runner_context: Any) -> None:
# No mutations performed.
state.commit()
executor = _make_executor("test-exec", mutate)
result = _run(executor, {"Local.config": {"code": "existing", "enabled": True}, "simple_key": "v"})
assert result["shared_state_updates"] == {}
def test_deleted_key_reported(self) -> None:
"""A key removed during execution is reported as a delete."""
async def mutate(message: Any, source_executor_ids: Any, state: Any, runner_context: Any) -> None:
state.delete("to_remove")
state.commit()
executor = _make_executor("test-exec", mutate)
result = _run(executor, {"to_remove": "value", "keep": "value"})
assert "to_remove" in result["shared_state_deletes"]
assert "keep" not in result["shared_state_deletes"]
if __name__ == "__main__":
import pytest
pytest.main([__file__, "-v", "--tb=short"])