dex-python-sdk 0.0.1__py3-none-any.whl

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.
Files changed (93) hide show
  1. dex/__init__.py +14 -0
  2. dex/client.py +568 -0
  3. dex/client_options.py +34 -0
  4. dex/command_request.py +130 -0
  5. dex/command_results.py +116 -0
  6. dex/communication.py +141 -0
  7. dex/communication_schema.py +68 -0
  8. dex/data_attributes.py +75 -0
  9. dex/dexpb/__init__.py +1 -0
  10. dex/dexpb/dex_pb2.py +305 -0
  11. dex/dexpb/dex_pb2.pyi +1273 -0
  12. dex/dexpb/dex_pb2_grpc.py +1126 -0
  13. dex/errors.py +132 -0
  14. dex/object_encoder.py +818 -0
  15. dex/persistence.py +94 -0
  16. dex/persistence_options.py +24 -0
  17. dex/persistence_schema.py +58 -0
  18. dex/py.typed +1 -0
  19. dex/registry.py +209 -0
  20. dex/reset_workflow_type_and_options.py +77 -0
  21. dex/rpc.py +100 -0
  22. dex/search_attributes.py +189 -0
  23. dex/state_decision.py +161 -0
  24. dex/state_execution_locals.py +71 -0
  25. dex/state_movement.py +123 -0
  26. dex/state_schema.py +54 -0
  27. dex/stop_workflow_options.py +23 -0
  28. dex/tests/__init__.py +84 -0
  29. dex/tests/dex-service-env/.env +7 -0
  30. dex/tests/dex-service-env/docker-compose-init.sh +44 -0
  31. dex/tests/dex-service-env/docker-compose.yml +87 -0
  32. dex/tests/dex-service-env/dynamicconfig/README.md +39 -0
  33. dex/tests/dex-service-env/dynamicconfig/development-sql.yaml +6 -0
  34. dex/tests/dex-service-env/dynamicconfig/docker.yaml +0 -0
  35. dex/tests/test_abnormal_exit_workflow.py +48 -0
  36. dex/tests/test_basic_workflow.py +75 -0
  37. dex/tests/test_conditional_complete.py +56 -0
  38. dex/tests/test_describe_workflow.py +45 -0
  39. dex/tests/test_empty_data_decodes_properly.py +79 -0
  40. dex/tests/test_internal_channel.py +33 -0
  41. dex/tests/test_internal_channel_with_no_prefix_channel.py +46 -0
  42. dex/tests/test_persistence_data_attributes.py +67 -0
  43. dex/tests/test_persistence_search_attributes.py +132 -0
  44. dex/tests/test_persistence_state_execution_locals.py +43 -0
  45. dex/tests/test_rpc.py +69 -0
  46. dex/tests/test_rpc_with_memo.py +200 -0
  47. dex/tests/test_rpc_with_memo_duplicate_java_tests.py +122 -0
  48. dex/tests/test_signal.py +56 -0
  49. dex/tests/test_skip_wait_until.py +85 -0
  50. dex/tests/test_state_failure_recovery.py +33 -0
  51. dex/tests/test_timer.py +40 -0
  52. dex/tests/test_wait_for_state_execution_completion.py +58 -0
  53. dex/tests/test_workflow_errors.py +92 -0
  54. dex/tests/test_workflow_state_options.py +123 -0
  55. dex/tests/test_workflow_state_options_override.py +49 -0
  56. dex/tests/worker_server.py +74 -0
  57. dex/tests/workflows/abnormal_exit_workflow.py +48 -0
  58. dex/tests/workflows/basic_workflow.py +69 -0
  59. dex/tests/workflows/conditional_complete_workflow.py +101 -0
  60. dex/tests/workflows/describe_workflow.py +52 -0
  61. dex/tests/workflows/empty_data_workflow.py +51 -0
  62. dex/tests/workflows/internal_channel_workflow.py +137 -0
  63. dex/tests/workflows/internal_channel_workflow_with_no_prefix_channel.py +108 -0
  64. dex/tests/workflows/java_duplicate_rpc_memo_workflow.py +285 -0
  65. dex/tests/workflows/persistence_data_attributes_workflow.py +104 -0
  66. dex/tests/workflows/persistence_search_attributes_workflow.py +167 -0
  67. dex/tests/workflows/persistence_state_execution_local_workflow.py +69 -0
  68. dex/tests/workflows/recovery_workflow.py +90 -0
  69. dex/tests/workflows/rpc_memo_workflow.py +238 -0
  70. dex/tests/workflows/rpc_workflow.py +125 -0
  71. dex/tests/workflows/state_options_override_workflow.py +100 -0
  72. dex/tests/workflows/state_options_workflow.py +92 -0
  73. dex/tests/workflows/timer_workflow.py +52 -0
  74. dex/tests/workflows/wait_for_state_with_state_execution_id_workflow.py +77 -0
  75. dex/tests/workflows/wait_for_state_with_wait_for_key_workflow.py +78 -0
  76. dex/tests/workflows/wait_internal_channel_workflow.py +53 -0
  77. dex/tests/workflows/wait_signal_workflow.py +154 -0
  78. dex/type_store.py +105 -0
  79. dex/unregistered_client.py +595 -0
  80. dex/utils/__init__.py +14 -0
  81. dex/utils/dex_typing.py +31 -0
  82. dex/utils/persistence_utils.py +37 -0
  83. dex/worker_service.py +441 -0
  84. dex/workflow.py +86 -0
  85. dex/workflow_context.py +50 -0
  86. dex/workflow_info.py +21 -0
  87. dex/workflow_options.py +79 -0
  88. dex/workflow_state.py +134 -0
  89. dex/workflow_state_options.py +160 -0
  90. dex_python_sdk-0.0.1.dist-info/METADATA +140 -0
  91. dex_python_sdk-0.0.1.dist-info/RECORD +93 -0
  92. dex_python_sdk-0.0.1.dist-info/WHEEL +4 -0
  93. dex_python_sdk-0.0.1.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,67 @@
1
+ # Copyright (c) 2022-2026 Super Durable, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ import time
17
+ import unittest
18
+
19
+ from dex.client import Client
20
+ from dex.tests.worker_server import registry
21
+ from dex.tests.workflows.persistence_data_attributes_workflow import (
22
+ PersistenceDataAttributesWorkflow,
23
+ initial_da_1,
24
+ initial_da_2,
25
+ initial_da_value_2,
26
+ initial_da_value_1,
27
+ test_da_set_key,
28
+ test_da_set_value,
29
+ expected_final_das,
30
+ )
31
+ from dex.workflow_options import WorkflowOptions
32
+
33
+
34
+ class TestPersistenceDataAttributes(unittest.TestCase):
35
+ @classmethod
36
+ def setUpClass(cls):
37
+ cls.client = Client(registry)
38
+
39
+ def test_persistence_data_attributes_workflow(self):
40
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
41
+
42
+ start_options = WorkflowOptions(
43
+ initial_data_attributes={
44
+ initial_da_1: initial_da_value_1,
45
+ initial_da_2: initial_da_value_2,
46
+ },
47
+ )
48
+
49
+ wf_run_id = self.client.start_workflow(
50
+ PersistenceDataAttributesWorkflow, wf_id, 100, None, start_options
51
+ )
52
+ self.client.set_workflow_data_attributes(
53
+ PersistenceDataAttributesWorkflow,
54
+ wf_id,
55
+ wf_run_id,
56
+ {
57
+ test_da_set_key: test_da_set_value,
58
+ },
59
+ )
60
+ self.client.wait_for_workflow_completion(wf_id, None)
61
+
62
+ all_data_attributes = self.client.get_all_workflow_data_attributes(
63
+ PersistenceDataAttributesWorkflow, wf_id, wf_run_id
64
+ )
65
+ for k, v in expected_final_das.items():
66
+ assert k in all_data_attributes
67
+ assert all_data_attributes[k] == v
@@ -0,0 +1,132 @@
1
+ # Copyright (c) 2022-2026 Super Durable, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ import time
17
+ import unittest
18
+
19
+ from dex.client import Client
20
+ from dex.tests.worker_server import registry
21
+ from dex.tests.workflows.persistence_search_attributes_workflow import (
22
+ PersistenceSearchAttributesWorkflow,
23
+ sa_keyword_key,
24
+ sa_double_key,
25
+ sa_int_key,
26
+ sa_bool_key,
27
+ sa_datetime_key,
28
+ sa_keyword_array_key,
29
+ initial_sa_keyword,
30
+ initial_sa_double,
31
+ initial_sa_int,
32
+ initial_sa_bool,
33
+ initial_sa_datetime,
34
+ initial_sa_keyword_array,
35
+ SearchAttributeState1,
36
+ sa_keyword,
37
+ sa_double,
38
+ sa_bool,
39
+ sa_keyword_array,
40
+ sa_int,
41
+ final_sa_keyword,
42
+ final_sa_bool,
43
+ final_sa_int,
44
+ final_sa_keyword_array,
45
+ )
46
+ from dex.workflow_options import WorkflowOptions
47
+
48
+
49
+ class TestPersistenceSearchAttributes(unittest.TestCase):
50
+ @classmethod
51
+ def setUpClass(cls):
52
+ cls.client = Client(registry)
53
+
54
+ def test_persistence_search_attributes_workflow(self):
55
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
56
+
57
+ wf_opts = WorkflowOptions(
58
+ initial_search_attributes={
59
+ sa_keyword_key: initial_sa_keyword,
60
+ sa_double_key: initial_sa_double,
61
+ sa_int_key: initial_sa_int,
62
+ sa_bool_key: initial_sa_bool,
63
+ sa_datetime_key: initial_sa_datetime,
64
+ sa_keyword_array_key: initial_sa_keyword_array,
65
+ }
66
+ )
67
+ wf_opts.add_wait_for_completion_state_ids(SearchAttributeState1)
68
+ self.client.start_workflow(
69
+ PersistenceSearchAttributesWorkflow, wf_id, 100, None, wf_opts
70
+ )
71
+
72
+ initial_returned_search_attributes = self.client.get_all_search_attributes(
73
+ PersistenceSearchAttributesWorkflow,
74
+ wf_id,
75
+ )
76
+
77
+ initial_expected_search_attributes = dict()
78
+ initial_expected_search_attributes[sa_keyword_key] = initial_sa_keyword
79
+ initial_expected_search_attributes[sa_double_key] = initial_sa_double
80
+ initial_expected_search_attributes[sa_bool_key] = initial_sa_bool
81
+ initial_expected_search_attributes[sa_keyword_array_key] = (
82
+ initial_sa_keyword_array
83
+ )
84
+ initial_expected_search_attributes[sa_int_key] = initial_sa_int
85
+ initial_expected_search_attributes[sa_datetime_key] = (
86
+ "2024-11-10T00:00:01.731455544Z" # This is a bug. The dex-server always returns utc time. See https://github.com/superdurable/dex/issues/261
87
+ # "2024-11-09T18:00:01.731455544-06:00"
88
+ )
89
+
90
+ assert initial_expected_search_attributes == initial_returned_search_attributes
91
+
92
+ self.client.wait_for_state_execution_completion_with_state_execution_id(
93
+ SearchAttributeState1, wf_id
94
+ )
95
+
96
+ returned_search_attributes = self.client.get_all_search_attributes(
97
+ PersistenceSearchAttributesWorkflow,
98
+ wf_id,
99
+ )
100
+
101
+ expected_search_attributes = dict()
102
+ expected_search_attributes[sa_keyword_key] = sa_keyword
103
+ expected_search_attributes[sa_double_key] = sa_double
104
+ expected_search_attributes[sa_bool_key] = sa_bool
105
+ expected_search_attributes[sa_keyword_array_key] = sa_keyword_array
106
+ expected_search_attributes[sa_int_key] = sa_int
107
+ expected_search_attributes[sa_datetime_key] = (
108
+ "2024-11-13T00:00:01.731455544Z" # This is a bug. The dex-server always returns utc time. See https://github.com/superdurable/dex/issues/261
109
+ # "2024-11-12T18:00:01.731455544-06:00"
110
+ )
111
+
112
+ assert expected_search_attributes == returned_search_attributes
113
+
114
+ self.client.wait_for_workflow_completion(wf_id)
115
+
116
+ final_expected_search_attributes = dict()
117
+ final_expected_search_attributes[sa_keyword_key] = final_sa_keyword
118
+ final_expected_search_attributes[sa_double_key] = sa_double
119
+ final_expected_search_attributes[sa_int_key] = final_sa_int
120
+ final_expected_search_attributes[sa_bool_key] = final_sa_bool
121
+ final_expected_search_attributes[sa_keyword_array_key] = final_sa_keyword_array
122
+ final_expected_search_attributes[sa_datetime_key] = (
123
+ "2024-12-14T00:00:01.731455544Z" # This is a bug. The dex-server always returns utc time. See https://github.com/superdurable/dex/issues/261
124
+ # "2024-12-13T18:00:01.731455544-06:00"
125
+ )
126
+
127
+ final_returned_search_attributes = self.client.get_all_search_attributes(
128
+ PersistenceSearchAttributesWorkflow,
129
+ wf_id,
130
+ )
131
+
132
+ assert final_expected_search_attributes == final_returned_search_attributes
@@ -0,0 +1,43 @@
1
+ # Copyright (c) 2022-2026 Super Durable, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ import time
17
+ import unittest
18
+
19
+ from dex.client import Client
20
+ from dex.tests.worker_server import registry
21
+ from dex.tests.workflows.persistence_state_execution_local_workflow import (
22
+ PersistenceStateExecutionLocalWorkflow,
23
+ PERSISTENCE_LOCAL_VALUE,
24
+ )
25
+ from dex.workflow_options import WorkflowOptions
26
+
27
+
28
+ class TestPersistenceExecutionLocalRead(unittest.TestCase):
29
+ @classmethod
30
+ def setUpClass(cls):
31
+ cls.client = Client(registry)
32
+
33
+ def test_persistence_execution_local_workflow(self):
34
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
35
+ start_options = WorkflowOptions()
36
+ self.client.start_workflow(
37
+ PersistenceStateExecutionLocalWorkflow, wf_id, 200, None, start_options
38
+ )
39
+ self.client.wait_for_workflow_completion(wf_id, None)
40
+ res = self.client.invoke_rpc(
41
+ wf_id, PersistenceStateExecutionLocalWorkflow.test_persistence_read
42
+ )
43
+ assert res == PERSISTENCE_LOCAL_VALUE
dex/tests/test_rpc.py ADDED
@@ -0,0 +1,69 @@
1
+ # Copyright (c) 2022-2026 Super Durable, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ import time
17
+ import unittest
18
+
19
+ from dex.client import Client
20
+ from dex.tests.worker_server import registry
21
+ from dex.tests.workflows.rpc_workflow import RPCWorkflow, Mydata
22
+
23
+
24
+ class TestRPCs(unittest.TestCase):
25
+ @classmethod
26
+ def setUpClass(cls):
27
+ cls.client = Client(registry)
28
+
29
+ def test_simple_rpc(self):
30
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
31
+ self.client.start_workflow(RPCWorkflow, wf_id, 10)
32
+
33
+ input = Mydata("test", 100)
34
+ output = self.client.invoke_rpc(
35
+ wf_id, RPCWorkflow.test_rpc_input_type, input, Mydata
36
+ )
37
+ assert output == input
38
+
39
+ output = self.client.invoke_rpc(wf_id, RPCWorkflow.test_simple_rpc)
40
+ assert output == 123
41
+ wf = RPCWorkflow()
42
+ output = self.client.invoke_rpc(wf_id, wf.test_simple_rpc)
43
+ assert output == 123
44
+
45
+ def test_complicated_rpc(self):
46
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
47
+ self.client.start_workflow(RPCWorkflow, wf_id, 10)
48
+ self.client.invoke_rpc(wf_id, RPCWorkflow.test_rpc_persistence_write, 100)
49
+ res = self.client.invoke_rpc(wf_id, RPCWorkflow.test_rpc_persistence_read)
50
+ assert res == 100
51
+ self.client.invoke_rpc(wf_id, RPCWorkflow.test_rpc_trigger_state, 200)
52
+ res = self.client.invoke_rpc(wf_id, RPCWorkflow.test_rpc_persistence_read)
53
+ assert res == 200
54
+
55
+ res1 = self.client.invoke_rpc(
56
+ wf_id, RPCWorkflow.test_rpc_publish_to_idle_channel, "input-1"
57
+ )
58
+ # Channel Size should be 1
59
+ assert res1 == 1
60
+
61
+ res2 = self.client.invoke_rpc(
62
+ wf_id, RPCWorkflow.test_rpc_publish_to_idle_channel, "input-2"
63
+ )
64
+ # Channel Size should be 2
65
+ assert res2 == 2
66
+
67
+ self.client.invoke_rpc(wf_id, RPCWorkflow.test_rpc_publish_channel)
68
+ result = self.client.wait_for_workflow_completion(wf_id, str)
69
+ assert result == "done"
@@ -0,0 +1,200 @@
1
+ # Copyright (c) 2022-2026 Super Durable, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ import time
17
+ import unittest
18
+
19
+ from dex.client import Client
20
+ from dex.dex_api.models.workflow_stop_type import WorkflowStopType
21
+ from dex.stop_workflow_options import StopWorkflowOptions
22
+ from dex.tests import registry
23
+ from dex.tests.workflows.rpc_memo_workflow import (
24
+ RpcMemoWorkflow,
25
+ TEST_DATA_OBJECT_KEY,
26
+ TEST_SEARCH_ATTRIBUTE_INT,
27
+ TEST_SEARCH_ATTRIBUTE_KEY,
28
+ RPC_INPUT,
29
+ RPC_OUTPUT,
30
+ TEST_STR,
31
+ TEST_DELAY,
32
+ )
33
+
34
+
35
+ class TestRpcWithMemo(unittest.TestCase):
36
+ @classmethod
37
+ def setUpClass(cls):
38
+ cls.client = Client(registry)
39
+
40
+ def test_rpc_memo_workflow_func1(self):
41
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
42
+ run_id = self.client.start_workflow(RpcMemoWorkflow, wf_id, 30, 999)
43
+
44
+ self.client.invoke_rpc(
45
+ wf_id, RpcMemoWorkflow.test_rpc_set_data_attribute, TEST_STR
46
+ )
47
+ value = self.client.invoke_rpc(
48
+ wf_id, RpcMemoWorkflow.test_rpc_get_data_attribute_strong_consistency
49
+ )
50
+ assert value == TEST_STR
51
+ time.sleep(TEST_DELAY)
52
+ value = self.client.invoke_rpc(
53
+ wf_id, RpcMemoWorkflow.test_rpc_get_data_attribute, None, str
54
+ )
55
+
56
+ self.client.invoke_rpc(wf_id, RpcMemoWorkflow.test_rpc_set_data_attribute, None)
57
+ value = self.client.invoke_rpc(
58
+ wf_id, RpcMemoWorkflow.test_rpc_get_data_attribute_strong_consistency
59
+ )
60
+ assert value is None
61
+ time.sleep(TEST_DELAY)
62
+ value = self.client.invoke_rpc(
63
+ wf_id, RpcMemoWorkflow.test_rpc_get_data_attribute
64
+ )
65
+ assert value is None
66
+
67
+ self.client.invoke_rpc(wf_id, RpcMemoWorkflow.test_rpc_set_keyword, TEST_STR)
68
+ value = self.client.invoke_rpc(
69
+ wf_id, RpcMemoWorkflow.test_rpc_get_keyword_strong_consistency
70
+ )
71
+ assert value == TEST_STR
72
+ time.sleep(TEST_DELAY)
73
+ value = self.client.invoke_rpc(wf_id, RpcMemoWorkflow.test_rpc_get_keyword)
74
+ assert value == TEST_STR
75
+
76
+ self.client.invoke_rpc(wf_id, RpcMemoWorkflow.test_rpc_set_keyword, None)
77
+ value = self.client.invoke_rpc(
78
+ wf_id, RpcMemoWorkflow.test_rpc_get_keyword_strong_consistency
79
+ )
80
+ assert value is None
81
+ time.sleep(TEST_DELAY)
82
+ value = self.client.invoke_rpc(wf_id, RpcMemoWorkflow.test_rpc_get_keyword)
83
+ assert value is None
84
+
85
+ rpc_output = self.client.invoke_rpc(
86
+ wf_id, RpcMemoWorkflow.test_rpc_func1, RPC_INPUT
87
+ )
88
+ assert RPC_OUTPUT == rpc_output
89
+
90
+ self.client.wait_for_workflow_completion(wf_id)
91
+
92
+ data_attributes = self.client.get_workflow_data_attributes(
93
+ RpcMemoWorkflow, wf_id, run_id, [TEST_DATA_OBJECT_KEY]
94
+ )
95
+ assert TEST_DATA_OBJECT_KEY in data_attributes
96
+ assert data_attributes[TEST_DATA_OBJECT_KEY] == RPC_INPUT
97
+
98
+ search_attributes = self.client.get_workflow_search_attributes(
99
+ RpcMemoWorkflow,
100
+ wf_id,
101
+ [TEST_SEARCH_ATTRIBUTE_KEY, TEST_SEARCH_ATTRIBUTE_INT],
102
+ run_id,
103
+ )
104
+ assert TEST_SEARCH_ATTRIBUTE_INT in search_attributes
105
+ assert TEST_SEARCH_ATTRIBUTE_KEY in search_attributes
106
+ assert search_attributes[TEST_SEARCH_ATTRIBUTE_INT] == RPC_OUTPUT
107
+ assert search_attributes[TEST_SEARCH_ATTRIBUTE_KEY] == RPC_INPUT
108
+
109
+ def test_rpc_memo_workflow_func0(self):
110
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
111
+ run_id = self.client.start_workflow(RpcMemoWorkflow, wf_id, 30, 999)
112
+
113
+ rpc_output = self.client.invoke_rpc(
114
+ wf_id, RpcMemoWorkflow.test_rpc_func0, TEST_STR
115
+ )
116
+ assert RPC_OUTPUT == rpc_output
117
+
118
+ self.client.wait_for_workflow_completion(wf_id)
119
+
120
+ data_attributes = self.client.get_workflow_data_attributes(
121
+ RpcMemoWorkflow, wf_id, run_id, [TEST_DATA_OBJECT_KEY]
122
+ )
123
+ assert TEST_DATA_OBJECT_KEY in data_attributes
124
+ assert data_attributes[TEST_DATA_OBJECT_KEY] == TEST_STR
125
+
126
+ search_attributes = self.client.get_workflow_search_attributes(
127
+ RpcMemoWorkflow,
128
+ wf_id,
129
+ [TEST_SEARCH_ATTRIBUTE_KEY, TEST_SEARCH_ATTRIBUTE_INT],
130
+ run_id,
131
+ )
132
+ assert TEST_SEARCH_ATTRIBUTE_INT in search_attributes
133
+ assert TEST_SEARCH_ATTRIBUTE_KEY in search_attributes
134
+ assert search_attributes[TEST_SEARCH_ATTRIBUTE_INT] == RPC_OUTPUT
135
+ assert search_attributes[TEST_SEARCH_ATTRIBUTE_KEY] == TEST_STR
136
+
137
+ def test_rpc_memo_workflow_proc1(self):
138
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
139
+ run_id = self.client.start_workflow(RpcMemoWorkflow, wf_id, 30, 999)
140
+
141
+ self.client.invoke_rpc(wf_id, RpcMemoWorkflow.test_rpc_proc1, RPC_INPUT)
142
+ self.client.wait_for_workflow_completion(wf_id)
143
+
144
+ data_attributes = self.client.get_workflow_data_attributes(
145
+ RpcMemoWorkflow, wf_id, run_id, [TEST_DATA_OBJECT_KEY]
146
+ )
147
+ assert TEST_DATA_OBJECT_KEY in data_attributes
148
+ assert data_attributes[TEST_DATA_OBJECT_KEY] == RPC_INPUT
149
+
150
+ search_attributes = self.client.get_workflow_search_attributes(
151
+ RpcMemoWorkflow,
152
+ wf_id,
153
+ [TEST_SEARCH_ATTRIBUTE_KEY, TEST_SEARCH_ATTRIBUTE_INT],
154
+ run_id,
155
+ )
156
+ assert TEST_SEARCH_ATTRIBUTE_INT in search_attributes
157
+ assert TEST_SEARCH_ATTRIBUTE_KEY in search_attributes
158
+ assert search_attributes[TEST_SEARCH_ATTRIBUTE_INT] == RPC_OUTPUT
159
+ assert search_attributes[TEST_SEARCH_ATTRIBUTE_KEY] == RPC_INPUT
160
+
161
+ def test_rpc_memo_workflow_proc0(self):
162
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
163
+ run_id = self.client.start_workflow(RpcMemoWorkflow, wf_id, 30, 999)
164
+
165
+ self.client.invoke_rpc(wf_id, RpcMemoWorkflow.test_rpc_proc0)
166
+ self.client.wait_for_workflow_completion(wf_id)
167
+
168
+ data_attributes = self.client.get_workflow_data_attributes(
169
+ RpcMemoWorkflow, wf_id, run_id, [TEST_DATA_OBJECT_KEY]
170
+ )
171
+ assert TEST_DATA_OBJECT_KEY in data_attributes
172
+ assert data_attributes[TEST_DATA_OBJECT_KEY] == TEST_STR
173
+
174
+ search_attributes = self.client.get_workflow_search_attributes(
175
+ RpcMemoWorkflow,
176
+ wf_id,
177
+ [TEST_SEARCH_ATTRIBUTE_KEY, TEST_SEARCH_ATTRIBUTE_INT],
178
+ run_id,
179
+ )
180
+ assert TEST_SEARCH_ATTRIBUTE_INT in search_attributes
181
+ assert TEST_SEARCH_ATTRIBUTE_KEY in search_attributes
182
+ assert search_attributes[TEST_SEARCH_ATTRIBUTE_INT] == RPC_OUTPUT
183
+ assert search_attributes[TEST_SEARCH_ATTRIBUTE_KEY] == TEST_STR
184
+
185
+ def test_rpc_memo_workflow_func1_readonly(self):
186
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
187
+ self.client.start_workflow(RpcMemoWorkflow, wf_id, 30, 999)
188
+
189
+ rpc_output = self.client.invoke_rpc(
190
+ wf_id, RpcMemoWorkflow.test_rpc_func1_readonly, RPC_INPUT
191
+ )
192
+ assert RPC_OUTPUT == rpc_output
193
+
194
+ self.client.stop_workflow(
195
+ wf_id,
196
+ StopWorkflowOptions(
197
+ workflow_stop_type=WorkflowStopType.FAIL,
198
+ reason=TEST_STR,
199
+ ),
200
+ )
@@ -0,0 +1,122 @@
1
+ # Copyright (c) 2022-2026 Super Durable, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ import time
17
+ import unittest
18
+
19
+ from dex.client import Client
20
+ from dex.tests.worker_server import registry
21
+ from dex.tests.workflows.java_duplicate_rpc_memo_workflow import (
22
+ JavaDuplicateRpcMemoWorkflow,
23
+ RpcMemoWorkflowState2,
24
+ TEST_DATA_OBJECT_KEY,
25
+ TEST_SEARCH_ATTRIBUTE_INT,
26
+ TEST_SEARCH_ATTRIBUTE_KEY,
27
+ RPC_INPUT,
28
+ RPC_OUTPUT,
29
+ TEST_STR,
30
+ TEST_DELAY,
31
+ )
32
+
33
+
34
+ class TestRpcWithMemo(unittest.TestCase):
35
+ @classmethod
36
+ def setUpClass(cls):
37
+ cls.client = Client(registry)
38
+
39
+ def test_rpc_memo_workflow_func1(self):
40
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
41
+ run_id = self.client.start_workflow(
42
+ JavaDuplicateRpcMemoWorkflow, wf_id, 30, 999
43
+ )
44
+
45
+ self.client.invoke_rpc(
46
+ wf_id, JavaDuplicateRpcMemoWorkflow.test_rpc_set_data_attribute, TEST_STR
47
+ )
48
+ value = self.client.invoke_rpc(
49
+ wf_id,
50
+ JavaDuplicateRpcMemoWorkflow.test_rpc_get_data_attribute_strong_consistency,
51
+ )
52
+ assert value == TEST_STR
53
+ time.sleep(TEST_DELAY)
54
+ value = self.client.invoke_rpc(
55
+ wf_id, JavaDuplicateRpcMemoWorkflow.test_rpc_get_data_attribute, None, str
56
+ )
57
+
58
+ self.client.invoke_rpc(
59
+ wf_id, JavaDuplicateRpcMemoWorkflow.test_rpc_set_data_attribute, None
60
+ )
61
+ value = self.client.invoke_rpc(
62
+ wf_id,
63
+ JavaDuplicateRpcMemoWorkflow.test_rpc_get_data_attribute_strong_consistency,
64
+ )
65
+ assert value is None
66
+ time.sleep(TEST_DELAY)
67
+ value = self.client.invoke_rpc(
68
+ wf_id, JavaDuplicateRpcMemoWorkflow.test_rpc_get_data_attribute
69
+ )
70
+ assert value is None
71
+
72
+ self.client.invoke_rpc(
73
+ wf_id, JavaDuplicateRpcMemoWorkflow.test_rpc_set_keyword, TEST_STR
74
+ )
75
+ value = self.client.invoke_rpc(
76
+ wf_id, JavaDuplicateRpcMemoWorkflow.test_rpc_get_keyword_strong_consistency
77
+ )
78
+ assert value == TEST_STR
79
+ time.sleep(TEST_DELAY)
80
+ value = self.client.invoke_rpc(
81
+ wf_id, JavaDuplicateRpcMemoWorkflow.test_rpc_get_keyword
82
+ )
83
+ assert value == TEST_STR
84
+
85
+ self.client.invoke_rpc(
86
+ wf_id, JavaDuplicateRpcMemoWorkflow.test_rpc_set_keyword, None
87
+ )
88
+ value = self.client.invoke_rpc(
89
+ wf_id, JavaDuplicateRpcMemoWorkflow.test_rpc_get_keyword_strong_consistency
90
+ )
91
+ assert value is None
92
+ time.sleep(TEST_DELAY)
93
+ value = self.client.invoke_rpc(
94
+ wf_id, JavaDuplicateRpcMemoWorkflow.test_rpc_get_keyword
95
+ )
96
+ assert value is None
97
+
98
+ rpc_output = self.client.invoke_rpc(
99
+ wf_id, JavaDuplicateRpcMemoWorkflow.test_rpc_func1, RPC_INPUT
100
+ )
101
+ assert RPC_OUTPUT == rpc_output
102
+
103
+ output = self.client.wait_for_workflow_completion(wf_id, int)
104
+ RpcMemoWorkflowState2.reset_counter()
105
+ assert 2 == output
106
+
107
+ data_attributes = self.client.get_workflow_data_attributes(
108
+ JavaDuplicateRpcMemoWorkflow, wf_id, run_id, [TEST_DATA_OBJECT_KEY]
109
+ )
110
+ assert TEST_DATA_OBJECT_KEY in data_attributes
111
+ assert data_attributes[TEST_DATA_OBJECT_KEY] == RPC_INPUT
112
+
113
+ search_attributes = self.client.get_workflow_search_attributes(
114
+ JavaDuplicateRpcMemoWorkflow,
115
+ wf_id,
116
+ [TEST_SEARCH_ATTRIBUTE_KEY, TEST_SEARCH_ATTRIBUTE_INT],
117
+ run_id,
118
+ )
119
+ assert TEST_SEARCH_ATTRIBUTE_INT in search_attributes
120
+ assert TEST_SEARCH_ATTRIBUTE_KEY in search_attributes
121
+ assert search_attributes[TEST_SEARCH_ATTRIBUTE_INT] == RPC_OUTPUT
122
+ assert search_attributes[TEST_SEARCH_ATTRIBUTE_KEY] == RPC_INPUT
@@ -0,0 +1,56 @@
1
+ # Copyright (c) 2022-2026 Super Durable, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ import time
17
+ import unittest
18
+
19
+ from dex.client import Client
20
+ from dex.tests.worker_server import registry
21
+ from dex.tests.workflows.wait_signal_workflow import (
22
+ WaitSignalWorkflow,
23
+ test_channel_int,
24
+ test_channel_str,
25
+ test_channel_none,
26
+ test_channel1,
27
+ test_idle_channel_none,
28
+ )
29
+
30
+
31
+ class TestSignal(unittest.TestCase):
32
+ @classmethod
33
+ def setUpClass(cls):
34
+ cls.client = Client(registry)
35
+
36
+ def test_signal(self):
37
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
38
+ self.client.start_workflow(WaitSignalWorkflow, wf_id, 10)
39
+ self.client.signal_workflow(wf_id, test_channel_int, 123)
40
+ self.client.signal_workflow(wf_id, test_channel_str, "abc")
41
+ self.client.signal_workflow(wf_id, test_channel_none)
42
+
43
+ self.client.signal_workflow(wf_id, test_channel1)
44
+ res = self.client.wait_for_workflow_completion(wf_id, str)
45
+ assert res == "abc"
46
+
47
+ def test_signal_channel_size(self):
48
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
49
+ self.client.start_workflow(WaitSignalWorkflow, wf_id, 1)
50
+ self.client.signal_workflow(wf_id, test_idle_channel_none)
51
+ self.client.signal_workflow(wf_id, test_idle_channel_none)
52
+ self.client.signal_workflow(wf_id, test_idle_channel_none)
53
+ res = self.client.invoke_rpc(
54
+ wf_id, WaitSignalWorkflow.get_idle_signal_channel_size
55
+ )
56
+ assert res == 3