Skip to content

Commit eae7cae

Browse files
Assert Python workflow start against polyglot parity fixture
Assert Python workflow start parity fixture
1 parent b39509c commit eae7cae

2 files changed

Lines changed: 153 additions & 0 deletions

File tree

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
{
2+
"schema": "durable-workflow.polyglot.control-plane-request-fixture",
3+
"version": 1,
4+
"operation": "workflow.start",
5+
"request": {
6+
"method": "POST",
7+
"path": "/workflows"
8+
},
9+
"semantic_body": {
10+
"workflow_type": "orders.process",
11+
"workflow_id": "wf-polyglot-231",
12+
"task_queue": "orders",
13+
"input": [
14+
{
15+
"order_id": 42,
16+
"priority": "gold"
17+
}
18+
],
19+
"memo": {
20+
"source": "polyglot-fixture"
21+
},
22+
"search_attributes": {
23+
"CustomerId": "cust-42",
24+
"Tier": "gold"
25+
},
26+
"execution_timeout_seconds": 300,
27+
"run_timeout_seconds": 120,
28+
"duplicate_policy": "use-existing"
29+
},
30+
"cli": {
31+
"argv": {
32+
"--type": "orders.process",
33+
"--workflow-id": "wf-polyglot-231",
34+
"--task-queue": "orders",
35+
"--input": "[{\"order_id\":42,\"priority\":\"gold\"}]",
36+
"--memo": "{\"source\":\"polyglot-fixture\"}",
37+
"--search-attr": [
38+
"CustomerId=cust-42",
39+
"Tier=gold"
40+
],
41+
"--execution-timeout": "300",
42+
"--run-timeout": "120",
43+
"--duplicate-policy": "use-existing"
44+
},
45+
"expected_body": {
46+
"workflow_type": "orders.process",
47+
"workflow_id": "wf-polyglot-231",
48+
"task_queue": "orders",
49+
"duplicate_policy": "use-existing",
50+
"input": [
51+
{
52+
"order_id": 42,
53+
"priority": "gold"
54+
}
55+
],
56+
"memo": {
57+
"source": "polyglot-fixture"
58+
},
59+
"execution_timeout_seconds": 300,
60+
"run_timeout_seconds": 120,
61+
"search_attributes": {
62+
"CustomerId": "cust-42",
63+
"Tier": "gold"
64+
}
65+
}
66+
},
67+
"sdk_python": {
68+
"kwargs": {
69+
"workflow_type": "orders.process",
70+
"workflow_id": "wf-polyglot-231",
71+
"task_queue": "orders",
72+
"input": [
73+
{
74+
"order_id": 42,
75+
"priority": "gold"
76+
}
77+
],
78+
"memo": {
79+
"source": "polyglot-fixture"
80+
},
81+
"search_attributes": {
82+
"CustomerId": "cust-42",
83+
"Tier": "gold"
84+
},
85+
"execution_timeout_seconds": 300,
86+
"run_timeout_seconds": 120,
87+
"duplicate_policy": "use-existing"
88+
},
89+
"expected_body": {
90+
"workflow_type": "orders.process",
91+
"workflow_id": "wf-polyglot-231",
92+
"task_queue": "orders",
93+
"duplicate_policy": "use-existing",
94+
"memo": {
95+
"source": "polyglot-fixture"
96+
},
97+
"search_attributes": {
98+
"CustomerId": "cust-42",
99+
"Tier": "gold"
100+
},
101+
"execution_timeout_seconds": 300,
102+
"run_timeout_seconds": 120
103+
},
104+
"payload_envelope": {
105+
"field": "input",
106+
"codec": "avro",
107+
"decoded": [
108+
{
109+
"order_id": 42,
110+
"priority": "gold"
111+
}
112+
]
113+
}
114+
}
115+
}

tests/test_client.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import json
44
import logging
5+
from pathlib import Path
56
from unittest.mock import AsyncMock, patch
67

78
import httpx
@@ -102,6 +103,43 @@ async def test_success(self, client: Client) -> None:
102103
assert body["input"]["codec"] == "avro"
103104
assert serializer.decode(body["input"]["blob"], codec="avro") == ["hello"]
104105

106+
@pytest.mark.asyncio
107+
async def test_start_request_matches_polyglot_fixture(self, client: Client) -> None:
108+
fixture_path = Path(__file__).parent / "fixtures" / "control-plane" / "workflow-start-parity.json"
109+
fixture = json.loads(fixture_path.read_text())
110+
sdk = fixture["sdk_python"]
111+
expected = sdk["expected_body"]
112+
envelope_contract = sdk["payload_envelope"]
113+
114+
resp = _mock_response(201, {
115+
"workflow_id": fixture["semantic_body"]["workflow_id"],
116+
"run_id": "run-polyglot-231",
117+
"workflow_type": fixture["semantic_body"]["workflow_type"],
118+
"namespace": "ns1",
119+
"status": "running",
120+
})
121+
122+
with patch.object(client._http, "request", new_callable=AsyncMock, return_value=resp) as mock:
123+
handle = await client.start_workflow(**sdk["kwargs"])
124+
125+
assert handle.workflow_id == fixture["semantic_body"]["workflow_id"]
126+
127+
call_args = mock.call_args
128+
assert call_args.args[0] == fixture["request"]["method"]
129+
assert call_args.args[1] == f"/api{fixture['request']['path']}"
130+
body = call_args.kwargs.get("json") or call_args[1].get("json")
131+
132+
for field, value in expected.items():
133+
assert body[field] == value
134+
135+
envelope = body[envelope_contract["field"]]
136+
assert envelope["codec"] == envelope_contract["codec"]
137+
assert serializer.decode(envelope["blob"], codec=envelope["codec"]) == envelope_contract["decoded"]
138+
139+
semantic = fixture["semantic_body"]
140+
for field in ["workflow_type", "workflow_id", "task_queue", "memo", "search_attributes", "duplicate_policy"]:
141+
assert body[field] == semantic[field]
142+
105143
@pytest.mark.asyncio
106144
async def test_warns_when_start_input_approaches_payload_limit(
107145
self, caplog: pytest.LogCaptureFixture

0 commit comments

Comments
 (0)