Skip to content

Commit d9ed5bb

Browse files
committed
respond to comments
Signed-off-by: Tim Li <[email protected]>
1 parent 216a5e8 commit d9ed5bb

File tree

2 files changed

+66
-31
lines changed

2 files changed

+66
-31
lines changed

cadence/client.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from cadence.api.v1.service_workflow_pb2 import StartWorkflowExecutionRequest, StartWorkflowExecutionResponse
1919
from cadence.api.v1.common_pb2 import WorkflowType, WorkflowExecution
2020
from cadence.api.v1.tasklist_pb2 import TaskList
21-
from cadence.api.v1.workflow_pb2 import WorkflowIdReusePolicy
2221
from cadence.data_converter import DataConverter, DefaultDataConverter
2322
from cadence.metrics import MetricsEmitter, NoOpMetricsEmitter
2423

@@ -27,13 +26,19 @@
2726
@dataclass
2827
class StartWorkflowOptions:
2928
"""Options for starting a workflow execution."""
30-
workflow_id: Optional[str] = None
31-
task_list: str = ""
29+
task_list: str
3230
execution_start_to_close_timeout: Optional[timedelta] = None
3331
task_start_to_close_timeout: Optional[timedelta] = None
34-
workflow_id_reuse_policy: int = WorkflowIdReusePolicy.WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE
32+
workflow_id: Optional[str] = None
3533
cron_schedule: Optional[str] = None
3634

35+
def __post_init__(self):
36+
"""Validate required fields after initialization."""
37+
if not self.task_list:
38+
raise ValueError("task_list is required")
39+
if not self.execution_start_to_close_timeout and not self.task_start_to_close_timeout:
40+
raise ValueError("either execution_start_to_close_timeout or task_start_to_close_timeout is required")
41+
3742

3843
class ClientOptions(TypedDict, total=False):
3944
domain: str
@@ -118,10 +123,6 @@ async def _build_start_workflow_request(
118123
# Generate workflow ID if not provided
119124
workflow_id = options.workflow_id or str(uuid.uuid4())
120125

121-
# Validate required fields
122-
if not options.task_list:
123-
raise ValueError("task_list is required")
124-
125126
# Determine workflow type name
126127
if isinstance(workflow, str):
127128
workflow_type_name = workflow
@@ -158,9 +159,6 @@ async def _build_start_workflow_request(
158159
request_id=str(uuid.uuid4())
159160
)
160161

161-
# Set workflow_id_reuse_policy separately to avoid type issues
162-
request.workflow_id_reuse_policy = options.workflow_id_reuse_policy # type: ignore[assignment]
163-
164162
# Set optional fields
165163
if input_payload:
166164
request.input.CopyFrom(input_payload)

tests/cadence/test_client_workflow.py

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
from cadence.api.v1.common_pb2 import WorkflowExecution
77
from cadence.api.v1.service_workflow_pb2 import StartWorkflowExecutionRequest, StartWorkflowExecutionResponse
8-
from cadence.api.v1.workflow_pb2 import WorkflowIdReusePolicy
98
from cadence.client import Client, StartWorkflowOptions
109
from cadence.data_converter import DefaultDataConverter
1110

@@ -31,12 +30,15 @@ class TestStartWorkflowOptions:
3130

3231
def test_default_values(self):
3332
"""Test default values for StartWorkflowOptions."""
34-
options = StartWorkflowOptions()
33+
options = StartWorkflowOptions(
34+
task_list="test-task-list",
35+
execution_start_to_close_timeout=timedelta(minutes=10),
36+
task_start_to_close_timeout=timedelta(seconds=30)
37+
)
3538
assert options.workflow_id is None
36-
assert options.task_list == ""
37-
assert options.execution_start_to_close_timeout is None
38-
assert options.task_start_to_close_timeout is None
39-
assert options.workflow_id_reuse_policy == WorkflowIdReusePolicy.WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE
39+
assert options.task_list == "test-task-list"
40+
assert options.execution_start_to_close_timeout == timedelta(minutes=10)
41+
assert options.task_start_to_close_timeout == timedelta(seconds=30)
4042
assert options.cron_schedule is None
4143

4244
def test_custom_values(self):
@@ -46,15 +48,13 @@ def test_custom_values(self):
4648
task_list="test-task-list",
4749
execution_start_to_close_timeout=timedelta(minutes=30),
4850
task_start_to_close_timeout=timedelta(seconds=10),
49-
workflow_id_reuse_policy=WorkflowIdReusePolicy.WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE,
5051
cron_schedule="0 * * * *"
5152
)
5253

5354
assert options.workflow_id == "custom-id"
5455
assert options.task_list == "test-task-list"
5556
assert options.execution_start_to_close_timeout == timedelta(minutes=30)
5657
assert options.task_start_to_close_timeout == timedelta(seconds=10)
57-
assert options.workflow_id_reuse_policy == WorkflowIdReusePolicy.WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE
5858
assert options.cron_schedule == "0 * * * *"
5959

6060

@@ -84,7 +84,7 @@ async def test_build_request_with_string_workflow(self, mock_client):
8484
assert request.workflow_type.name == "TestWorkflow"
8585
assert request.task_list.name == "test-task-list"
8686
assert request.identity == client.identity
87-
assert request.workflow_id_reuse_policy == WorkflowIdReusePolicy.WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE
87+
assert request.workflow_id_reuse_policy == 0 # Default protobuf value when not set
8888
assert request.request_id != "" # Should be a UUID
8989

9090
# Verify UUID format
@@ -99,7 +99,9 @@ def test_workflow():
9999
client = Client(domain="test-domain", target="localhost:7933")
100100

101101
options = StartWorkflowOptions(
102-
task_list="test-task-list"
102+
task_list="test-task-list",
103+
execution_start_to_close_timeout=timedelta(minutes=10),
104+
task_start_to_close_timeout=timedelta(seconds=30)
103105
)
104106

105107
request = await client._build_start_workflow_request(test_workflow, (), options)
@@ -111,7 +113,11 @@ async def test_build_request_generates_workflow_id(self, mock_client):
111113
"""Test that workflow_id is generated when not provided."""
112114
client = Client(domain="test-domain", target="localhost:7933")
113115

114-
options = StartWorkflowOptions(task_list="test-task-list")
116+
options = StartWorkflowOptions(
117+
task_list="test-task-list",
118+
execution_start_to_close_timeout=timedelta(minutes=10),
119+
task_start_to_close_timeout=timedelta(seconds=30)
120+
)
115121

116122
request = await client._build_start_workflow_request("TestWorkflow", (), options)
117123

@@ -122,19 +128,42 @@ async def test_build_request_generates_workflow_id(self, mock_client):
122128
@pytest.mark.asyncio
123129
async def test_build_request_missing_task_list(self, mock_client):
124130
"""Test that missing task_list raises ValueError."""
125-
client = Client(domain="test-domain", target="localhost:7933")
131+
with pytest.raises(TypeError): # task_list is now required positional argument
132+
StartWorkflowOptions() # No task_list
126133

127-
options = StartWorkflowOptions() # No task_list
134+
def test_missing_timeout_raises_error(self):
135+
"""Test that missing both timeouts raises ValueError."""
136+
with pytest.raises(ValueError, match="either execution_start_to_close_timeout or task_start_to_close_timeout is required"):
137+
StartWorkflowOptions(task_list="test-task-list")
128138

129-
with pytest.raises(ValueError, match="task_list is required"):
130-
await client._build_start_workflow_request("TestWorkflow", (), options)
139+
def test_only_execution_timeout(self):
140+
"""Test that only execution_start_to_close_timeout is valid."""
141+
options = StartWorkflowOptions(
142+
task_list="test-task-list",
143+
execution_start_to_close_timeout=timedelta(minutes=10)
144+
)
145+
assert options.execution_start_to_close_timeout == timedelta(minutes=10)
146+
assert options.task_start_to_close_timeout is None
147+
148+
def test_only_task_timeout(self):
149+
"""Test that only task_start_to_close_timeout is valid."""
150+
options = StartWorkflowOptions(
151+
task_list="test-task-list",
152+
task_start_to_close_timeout=timedelta(seconds=30)
153+
)
154+
assert options.execution_start_to_close_timeout is None
155+
assert options.task_start_to_close_timeout == timedelta(seconds=30)
131156

132157
@pytest.mark.asyncio
133158
async def test_build_request_with_input_args(self, mock_client):
134159
"""Test building request with input arguments."""
135160
client = Client(domain="test-domain", target="localhost:7933")
136161

137-
options = StartWorkflowOptions(task_list="test-task-list")
162+
options = StartWorkflowOptions(
163+
task_list="test-task-list",
164+
execution_start_to_close_timeout=timedelta(minutes=10),
165+
task_start_to_close_timeout=timedelta(seconds=30)
166+
)
138167

139168
request = await client._build_start_workflow_request("TestWorkflow", ("arg1", 42, {"key": "value"}), options)
140169

@@ -169,6 +198,8 @@ async def test_build_request_with_cron_schedule(self, mock_client):
169198

170199
options = StartWorkflowOptions(
171200
task_list="test-task-list",
201+
execution_start_to_close_timeout=timedelta(minutes=10),
202+
task_start_to_close_timeout=timedelta(seconds=30),
172203
cron_schedule="0 * * * *"
173204
)
174205

@@ -206,7 +237,9 @@ async def mock_build_request(workflow, args, options):
206237
"TestWorkflow",
207238
"arg1", "arg2",
208239
task_list="test-task-list",
209-
workflow_id="test-workflow-id"
240+
workflow_id="test-workflow-id",
241+
execution_start_to_close_timeout=timedelta(minutes=10),
242+
task_start_to_close_timeout=timedelta(seconds=30)
210243
)
211244

212245
assert isinstance(execution, WorkflowExecution)
@@ -231,7 +264,9 @@ async def test_start_workflow_grpc_error(self, mock_client):
231264
with pytest.raises(Exception, match="Failed to start workflow: gRPC error"):
232265
await client.start_workflow(
233266
"TestWorkflow",
234-
task_list="test-task-list"
267+
task_list="test-task-list",
268+
execution_start_to_close_timeout=timedelta(minutes=10),
269+
task_start_to_close_timeout=timedelta(seconds=30)
235270
)
236271

237272
@pytest.mark.asyncio
@@ -261,7 +296,8 @@ async def mock_build_request(workflow, args, options):
261296
"arg1",
262297
task_list="test-task-list",
263298
workflow_id="custom-id",
264-
execution_start_to_close_timeout=timedelta(minutes=30)
299+
execution_start_to_close_timeout=timedelta(minutes=30),
300+
task_start_to_close_timeout=timedelta(seconds=30)
265301
)
266302

267303
# Verify options were properly constructed
@@ -292,7 +328,8 @@ async def test_integration_workflow_invocation():
292328
{"data": "value"},
293329
task_list="integration-task-list",
294330
workflow_id="integration-workflow-id",
295-
execution_start_to_close_timeout=timedelta(minutes=10)
331+
execution_start_to_close_timeout=timedelta(minutes=10),
332+
task_start_to_close_timeout=timedelta(seconds=30)
296333
)
297334

298335
# Verify result

0 commit comments

Comments
 (0)