dex-python-sdk 0.0.2__cp311-abi3-win_amd64.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 (121) hide show
  1. dex/__init__.py +146 -0
  2. dex/_grpc_errors.py +73 -0
  3. dex/_invocation_context.py +269 -0
  4. dex/_native.pyd +0 -0
  5. dex/_native.pyi +20 -0
  6. dex/_utils.py +18 -0
  7. dex/_value_hydrator.py +223 -0
  8. dex/_value_mapper.py +174 -0
  9. dex/_worker_dispatcher.py +451 -0
  10. dex/_worker_service.py +60 -0
  11. dex/attribute.py +87 -0
  12. dex/blob_cache.py +73 -0
  13. dex/channel.py +165 -0
  14. dex/client.py +715 -0
  15. dex/client_options.py +19 -0
  16. dex/codec.py +286 -0
  17. dex/command_request.py +120 -0
  18. dex/command_results.py +107 -0
  19. dex/communication.py +136 -0
  20. dex/communication_schema.py +54 -0
  21. dex/condition.py +74 -0
  22. dex/context.py +86 -0
  23. dex/data_attributes.py +70 -0
  24. dex/dexpb/__init__.py +1 -0
  25. dex/dexpb/dex_pb2.py +381 -0
  26. dex/dexpb/dex_pb2.pyi +1734 -0
  27. dex/dexpb/dex_pb2_grpc.py +1298 -0
  28. dex/errors.py +109 -0
  29. dex/flow.py +456 -0
  30. dex/flow_config.py +29 -0
  31. dex/flow_info.py +51 -0
  32. dex/flow_options.py +122 -0
  33. dex/object_encoder.py +799 -0
  34. dex/persistence.py +89 -0
  35. dex/persistence_options.py +12 -0
  36. dex/persistence_schema.py +51 -0
  37. dex/py.typed +1 -0
  38. dex/registry.py +204 -0
  39. dex/reset_workflow_type_and_options.py +67 -0
  40. dex/rpc.py +93 -0
  41. dex/runtime_errors.py +81 -0
  42. dex/search_attributes.py +184 -0
  43. dex/state_decision.py +153 -0
  44. dex/state_execution_locals.py +66 -0
  45. dex/state_movement.py +115 -0
  46. dex/state_schema.py +48 -0
  47. dex/step.py +194 -0
  48. dex/step_execution.py +42 -0
  49. dex/stop_workflow_options.py +18 -0
  50. dex/tests/__init__.py +80 -0
  51. dex/tests/dex-service-env/.env +7 -0
  52. dex/tests/dex-service-env/docker-compose-init.sh +44 -0
  53. dex/tests/dex-service-env/docker-compose.yml +97 -0
  54. dex/tests/dex-service-env/dynamicconfig/README.md +39 -0
  55. dex/tests/dex-service-env/dynamicconfig/development-sql.yaml +9 -0
  56. dex/tests/dex-service-env/dynamicconfig/docker.yaml +2 -0
  57. dex/tests/test_abnormal_exit_workflow.py +43 -0
  58. dex/tests/test_basic_workflow.py +70 -0
  59. dex/tests/test_conditional_complete.py +50 -0
  60. dex/tests/test_describe_workflow.py +40 -0
  61. dex/tests/test_empty_data_decodes_properly.py +74 -0
  62. dex/tests/test_internal_channel.py +28 -0
  63. dex/tests/test_internal_channel_with_no_prefix_channel.py +41 -0
  64. dex/tests/test_persistence_data_attributes.py +62 -0
  65. dex/tests/test_persistence_search_attributes.py +127 -0
  66. dex/tests/test_persistence_state_execution_locals.py +38 -0
  67. dex/tests/test_rpc.py +64 -0
  68. dex/tests/test_rpc_with_memo.py +195 -0
  69. dex/tests/test_rpc_with_memo_duplicate_java_tests.py +117 -0
  70. dex/tests/test_signal.py +51 -0
  71. dex/tests/test_skip_wait_until.py +76 -0
  72. dex/tests/test_state_failure_recovery.py +28 -0
  73. dex/tests/test_timer.py +35 -0
  74. dex/tests/test_wait_for_state_execution_completion.py +53 -0
  75. dex/tests/test_workflow_errors.py +87 -0
  76. dex/tests/test_workflow_state_options.py +118 -0
  77. dex/tests/test_workflow_state_options_override.py +44 -0
  78. dex/tests/worker_server.py +64 -0
  79. dex/tests/workflows/abnormal_exit_workflow.py +42 -0
  80. dex/tests/workflows/basic_workflow.py +62 -0
  81. dex/tests/workflows/conditional_complete_workflow.py +95 -0
  82. dex/tests/workflows/describe_workflow.py +46 -0
  83. dex/tests/workflows/empty_data_workflow.py +45 -0
  84. dex/tests/workflows/internal_channel_workflow.py +129 -0
  85. dex/tests/workflows/internal_channel_workflow_with_no_prefix_channel.py +100 -0
  86. dex/tests/workflows/java_duplicate_rpc_memo_workflow.py +276 -0
  87. dex/tests/workflows/persistence_data_attributes_workflow.py +98 -0
  88. dex/tests/workflows/persistence_search_attributes_workflow.py +159 -0
  89. dex/tests/workflows/persistence_state_execution_local_workflow.py +63 -0
  90. dex/tests/workflows/recovery_workflow.py +82 -0
  91. dex/tests/workflows/rpc_memo_workflow.py +231 -0
  92. dex/tests/workflows/rpc_workflow.py +117 -0
  93. dex/tests/workflows/state_options_override_workflow.py +93 -0
  94. dex/tests/workflows/state_options_workflow.py +84 -0
  95. dex/tests/workflows/timer_workflow.py +46 -0
  96. dex/tests/workflows/wait_for_state_with_state_execution_id_workflow.py +70 -0
  97. dex/tests/workflows/wait_for_state_with_wait_for_key_workflow.py +71 -0
  98. dex/tests/workflows/wait_internal_channel_workflow.py +47 -0
  99. dex/tests/workflows/wait_signal_workflow.py +147 -0
  100. dex/timer.py +21 -0
  101. dex/type_store.py +99 -0
  102. dex/unregistered_client.py +585 -0
  103. dex/utils/__init__.py +3 -0
  104. dex/utils/dex_typing.py +25 -0
  105. dex/utils/persistence_utils.py +32 -0
  106. dex/wait.py +49 -0
  107. dex/worker.py +121 -0
  108. dex/worker_options.py +22 -0
  109. dex/worker_service.py +432 -0
  110. dex/workflow.py +79 -0
  111. dex/workflow_context.py +44 -0
  112. dex/workflow_info.py +16 -0
  113. dex/workflow_options.py +74 -0
  114. dex/workflow_state.py +123 -0
  115. dex/workflow_state_options.py +154 -0
  116. dex_python_sdk-0.0.2.dist-info/METADATA +202 -0
  117. dex_python_sdk-0.0.2.dist-info/RECORD +121 -0
  118. dex_python_sdk-0.0.2.dist-info/WHEEL +4 -0
  119. dex_python_sdk-0.0.2.dist-info/licenses/LEGACY_NOTICES.md +61 -0
  120. dex_python_sdk-0.0.2.dist-info/licenses/LICENSE +192 -0
  121. dex_python_sdk-0.0.2.dist-info/sboms/dex-blob-cache-python.cyclonedx.json +2406 -0
@@ -0,0 +1,50 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ import inspect
12
+ import time
13
+ import unittest
14
+
15
+ from dex.client import Client
16
+ from dex.tests.worker_server import registry
17
+ from dex.tests.workflows.conditional_complete_workflow import (
18
+ ConditionalCompleteWorkflow,
19
+ test_signal_channel,
20
+ )
21
+
22
+ class TestConditionalComplete(unittest.TestCase):
23
+ @classmethod
24
+ def setUpClass(cls):
25
+ cls.client = Client(registry)
26
+
27
+ def test_internal_channel_conditional_complete(self):
28
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
29
+ do_test_conditional_workflow(self.client, wf_id, False)
30
+
31
+ def test_signal_channel_conditional_complete(self):
32
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
33
+ do_test_conditional_workflow(self.client, wf_id, True)
34
+
35
+ def do_test_conditional_workflow(client: Client, wf_id: str, use_signal: bool):
36
+ client.start_workflow(ConditionalCompleteWorkflow, wf_id, 10, use_signal)
37
+
38
+ for x in range(3):
39
+ if use_signal:
40
+ client.signal_workflow(wf_id, test_signal_channel, 123)
41
+ else:
42
+ client.invoke_rpc(
43
+ wf_id, ConditionalCompleteWorkflow.test_rpc_publish_channel
44
+ )
45
+ if x == 0:
46
+ # wait for a second so that the workflow is in execute state
47
+ time.sleep(1)
48
+
49
+ res = client.wait_for_workflow_completion(wf_id)
50
+ assert res == 3
@@ -0,0 +1,40 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ import inspect
12
+ import time
13
+ import unittest
14
+
15
+ from dex.client import Client
16
+ from dex.errors import WorkflowNotExistsError
17
+ from dex.dex_api.models import WorkflowStatus
18
+ from dex.tests.workflows.describe_workflow import DescribeWorkflow
19
+ from dex.tests.worker_server import registry
20
+
21
+ class TestDescribeWorkflow(unittest.TestCase):
22
+ @classmethod
23
+ def setUpClass(cls):
24
+ cls.client = Client(registry)
25
+
26
+ def test_describe_workflow(self):
27
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
28
+
29
+ self.client.start_workflow(DescribeWorkflow, wf_id, 100)
30
+ workflow_info = self.client.describe_workflow(wf_id)
31
+ assert workflow_info.workflow_status == WorkflowStatus.RUNNING
32
+
33
+ # Stop the workflow
34
+ self.client.stop_workflow(wf_id)
35
+
36
+ def test_describe_workflow_when_workflow_not_exists(self):
37
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
38
+
39
+ with self.assertRaises(WorkflowNotExistsError):
40
+ self.client.describe_workflow(wf_id)
@@ -0,0 +1,74 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ import inspect
12
+ import time
13
+ import unittest
14
+
15
+ import httpx
16
+
17
+ from dex.client import Client
18
+ from dex.tests.worker_server import registry
19
+ from dex.worker_service import WorkerService
20
+
21
+ class TestBinaryNullDecodesCorrectly(unittest.TestCase):
22
+ @classmethod
23
+ def setUpClass(cls):
24
+ cls.client = Client(registry)
25
+
26
+ def test_binary_null_input_decodes_correctly(self):
27
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
28
+
29
+ response = httpx.post(
30
+ f"http://0.0.0.0:8802/{WorkerService.api_path_workflow_state_execute}",
31
+ json={
32
+ "DataObjects": [
33
+ {"key": "test-da", "value": {"encoding": "binary/null"}}
34
+ ],
35
+ "commandResults": {
36
+ "interStateChannelResults": [],
37
+ "stateStartApiSucceeded": True,
38
+ },
39
+ "context": {
40
+ "attempt": 1,
41
+ "firstAttemptTimestamp": 1747935829,
42
+ "stateExecutionId": "State1-1",
43
+ "workflowId": wf_id,
44
+ "workflowRunId": "0196f734-d037-7432-bd63-e1136cd34dbd",
45
+ "workflowStartedTimestamp": 1747904155,
46
+ },
47
+ "stateInput": {"encoding": "binary/null"},
48
+ "stateLocals": [],
49
+ "workflowStateId": "State1",
50
+ "workflowType": "EmptyDataWorkflow",
51
+ },
52
+ )
53
+ assert response.is_success
54
+ response_json = response.json()
55
+ self.assertEqual(
56
+ response_json,
57
+ {
58
+ "publishToInterStateChannel": [],
59
+ "recordEvents": [],
60
+ "stateDecision": {
61
+ "nextStates": [
62
+ {
63
+ "stateId": "_SYS_GRACEFUL_COMPLETING_WORKFLOW",
64
+ "stateInput": {
65
+ "data": '"success"',
66
+ "encoding": "json/plain",
67
+ },
68
+ }
69
+ ]
70
+ },
71
+ "upsertDataObjects": [],
72
+ "upsertStateLocals": [],
73
+ },
74
+ )
@@ -0,0 +1,28 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ import inspect
12
+ import time
13
+ import unittest
14
+
15
+ from dex.client import Client
16
+ from dex.tests.worker_server import registry
17
+ from dex.tests.workflows.internal_channel_workflow import InternalChannelWorkflow
18
+
19
+ class TestConditionalComplete(unittest.TestCase):
20
+ @classmethod
21
+ def setUpClass(cls):
22
+ cls.client = Client(registry)
23
+
24
+ def test_internal_channel_workflow(self):
25
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
26
+
27
+ self.client.start_workflow(InternalChannelWorkflow, wf_id, 100, None)
28
+ self.client.wait_for_workflow_completion(wf_id, None)
@@ -0,0 +1,41 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ import inspect
12
+ import time
13
+ import unittest
14
+
15
+ from dex.client import Client
16
+ from dex.tests.worker_server import registry
17
+ from dex.tests.workflows.internal_channel_workflow_with_no_prefix_channel import (
18
+ InternalChannelWorkflowWithNoPrefixChannel,
19
+ test_non_prefix_channel_name_with_suffix,
20
+ )
21
+
22
+ class TestInternalChannelWithNoPrefix(unittest.TestCase):
23
+ @classmethod
24
+ def setUpClass(cls):
25
+ cls.client = Client(registry)
26
+
27
+ def test_internal_channel_workflow_with_no_prefix_channel(self):
28
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
29
+
30
+ self.client.start_workflow(
31
+ InternalChannelWorkflowWithNoPrefixChannel, wf_id, 5, None
32
+ )
33
+
34
+ with self.assertRaises(Exception) as context:
35
+ self.client.wait_for_workflow_completion(wf_id, None)
36
+
37
+ self.assertIn("FAILED", context.exception.workflow_status)
38
+ self.assertIn(
39
+ f"WorkerExecutionError: InternalChannel channel_name is not defined {test_non_prefix_channel_name_with_suffix}",
40
+ context.exception.error_message,
41
+ )
@@ -0,0 +1,62 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ import inspect
12
+ import time
13
+ import unittest
14
+
15
+ from dex.client import Client
16
+ from dex.tests.worker_server import registry
17
+ from dex.tests.workflows.persistence_data_attributes_workflow import (
18
+ PersistenceDataAttributesWorkflow,
19
+ initial_da_1,
20
+ initial_da_2,
21
+ initial_da_value_2,
22
+ initial_da_value_1,
23
+ test_da_set_key,
24
+ test_da_set_value,
25
+ expected_final_das,
26
+ )
27
+ from dex.workflow_options import WorkflowOptions
28
+
29
+ class TestPersistenceDataAttributes(unittest.TestCase):
30
+ @classmethod
31
+ def setUpClass(cls):
32
+ cls.client = Client(registry)
33
+
34
+ def test_persistence_data_attributes_workflow(self):
35
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
36
+
37
+ start_options = WorkflowOptions(
38
+ initial_data_attributes={
39
+ initial_da_1: initial_da_value_1,
40
+ initial_da_2: initial_da_value_2,
41
+ },
42
+ )
43
+
44
+ wf_run_id = self.client.start_workflow(
45
+ PersistenceDataAttributesWorkflow, wf_id, 100, None, start_options
46
+ )
47
+ self.client.set_workflow_data_attributes(
48
+ PersistenceDataAttributesWorkflow,
49
+ wf_id,
50
+ wf_run_id,
51
+ {
52
+ test_da_set_key: test_da_set_value,
53
+ },
54
+ )
55
+ self.client.wait_for_workflow_completion(wf_id, None)
56
+
57
+ all_data_attributes = self.client.get_all_workflow_data_attributes(
58
+ PersistenceDataAttributesWorkflow, wf_id, wf_run_id
59
+ )
60
+ for k, v in expected_final_das.items():
61
+ assert k in all_data_attributes
62
+ assert all_data_attributes[k] == v
@@ -0,0 +1,127 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ import inspect
12
+ import time
13
+ import unittest
14
+
15
+ from dex.client import Client
16
+ from dex.tests.worker_server import registry
17
+ from dex.tests.workflows.persistence_search_attributes_workflow import (
18
+ PersistenceSearchAttributesWorkflow,
19
+ sa_keyword_key,
20
+ sa_double_key,
21
+ sa_int_key,
22
+ sa_bool_key,
23
+ sa_datetime_key,
24
+ sa_keyword_array_key,
25
+ initial_sa_keyword,
26
+ initial_sa_double,
27
+ initial_sa_int,
28
+ initial_sa_bool,
29
+ initial_sa_datetime,
30
+ initial_sa_keyword_array,
31
+ SearchAttributeState1,
32
+ sa_keyword,
33
+ sa_double,
34
+ sa_bool,
35
+ sa_keyword_array,
36
+ sa_int,
37
+ final_sa_keyword,
38
+ final_sa_bool,
39
+ final_sa_int,
40
+ final_sa_keyword_array,
41
+ )
42
+ from dex.workflow_options import WorkflowOptions
43
+
44
+ class TestPersistenceSearchAttributes(unittest.TestCase):
45
+ @classmethod
46
+ def setUpClass(cls):
47
+ cls.client = Client(registry)
48
+
49
+ def test_persistence_search_attributes_workflow(self):
50
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
51
+
52
+ wf_opts = WorkflowOptions(
53
+ initial_search_attributes={
54
+ sa_keyword_key: initial_sa_keyword,
55
+ sa_double_key: initial_sa_double,
56
+ sa_int_key: initial_sa_int,
57
+ sa_bool_key: initial_sa_bool,
58
+ sa_datetime_key: initial_sa_datetime,
59
+ sa_keyword_array_key: initial_sa_keyword_array,
60
+ }
61
+ )
62
+ wf_opts.add_wait_for_completion_state_ids(SearchAttributeState1)
63
+ self.client.start_workflow(
64
+ PersistenceSearchAttributesWorkflow, wf_id, 100, None, wf_opts
65
+ )
66
+
67
+ initial_returned_search_attributes = self.client.get_all_search_attributes(
68
+ PersistenceSearchAttributesWorkflow,
69
+ wf_id,
70
+ )
71
+
72
+ initial_expected_search_attributes = dict()
73
+ initial_expected_search_attributes[sa_keyword_key] = initial_sa_keyword
74
+ initial_expected_search_attributes[sa_double_key] = initial_sa_double
75
+ initial_expected_search_attributes[sa_bool_key] = initial_sa_bool
76
+ initial_expected_search_attributes[sa_keyword_array_key] = (
77
+ initial_sa_keyword_array
78
+ )
79
+ initial_expected_search_attributes[sa_int_key] = initial_sa_int
80
+ initial_expected_search_attributes[sa_datetime_key] = (
81
+ "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
82
+ # "2024-11-09T18:00:01.731455544-06:00"
83
+ )
84
+
85
+ assert initial_expected_search_attributes == initial_returned_search_attributes
86
+
87
+ self.client.wait_for_state_execution_completion_with_state_execution_id(
88
+ SearchAttributeState1, wf_id
89
+ )
90
+
91
+ returned_search_attributes = self.client.get_all_search_attributes(
92
+ PersistenceSearchAttributesWorkflow,
93
+ wf_id,
94
+ )
95
+
96
+ expected_search_attributes = dict()
97
+ expected_search_attributes[sa_keyword_key] = sa_keyword
98
+ expected_search_attributes[sa_double_key] = sa_double
99
+ expected_search_attributes[sa_bool_key] = sa_bool
100
+ expected_search_attributes[sa_keyword_array_key] = sa_keyword_array
101
+ expected_search_attributes[sa_int_key] = sa_int
102
+ expected_search_attributes[sa_datetime_key] = (
103
+ "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
104
+ # "2024-11-12T18:00:01.731455544-06:00"
105
+ )
106
+
107
+ assert expected_search_attributes == returned_search_attributes
108
+
109
+ self.client.wait_for_workflow_completion(wf_id)
110
+
111
+ final_expected_search_attributes = dict()
112
+ final_expected_search_attributes[sa_keyword_key] = final_sa_keyword
113
+ final_expected_search_attributes[sa_double_key] = sa_double
114
+ final_expected_search_attributes[sa_int_key] = final_sa_int
115
+ final_expected_search_attributes[sa_bool_key] = final_sa_bool
116
+ final_expected_search_attributes[sa_keyword_array_key] = final_sa_keyword_array
117
+ final_expected_search_attributes[sa_datetime_key] = (
118
+ "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
119
+ # "2024-12-13T18:00:01.731455544-06:00"
120
+ )
121
+
122
+ final_returned_search_attributes = self.client.get_all_search_attributes(
123
+ PersistenceSearchAttributesWorkflow,
124
+ wf_id,
125
+ )
126
+
127
+ assert final_expected_search_attributes == final_returned_search_attributes
@@ -0,0 +1,38 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ import inspect
12
+ import time
13
+ import unittest
14
+
15
+ from dex.client import Client
16
+ from dex.tests.worker_server import registry
17
+ from dex.tests.workflows.persistence_state_execution_local_workflow import (
18
+ PersistenceStateExecutionLocalWorkflow,
19
+ PERSISTENCE_LOCAL_VALUE,
20
+ )
21
+ from dex.workflow_options import WorkflowOptions
22
+
23
+ class TestPersistenceExecutionLocalRead(unittest.TestCase):
24
+ @classmethod
25
+ def setUpClass(cls):
26
+ cls.client = Client(registry)
27
+
28
+ def test_persistence_execution_local_workflow(self):
29
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
30
+ start_options = WorkflowOptions()
31
+ self.client.start_workflow(
32
+ PersistenceStateExecutionLocalWorkflow, wf_id, 200, None, start_options
33
+ )
34
+ self.client.wait_for_workflow_completion(wf_id, None)
35
+ res = self.client.invoke_rpc(
36
+ wf_id, PersistenceStateExecutionLocalWorkflow.test_persistence_read
37
+ )
38
+ assert res == PERSISTENCE_LOCAL_VALUE
dex/tests/test_rpc.py ADDED
@@ -0,0 +1,64 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ import inspect
12
+ import time
13
+ import unittest
14
+
15
+ from dex.client import Client
16
+ from dex.tests.worker_server import registry
17
+ from dex.tests.workflows.rpc_workflow import RPCWorkflow, Mydata
18
+
19
+ class TestRPCs(unittest.TestCase):
20
+ @classmethod
21
+ def setUpClass(cls):
22
+ cls.client = Client(registry)
23
+
24
+ def test_simple_rpc(self):
25
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
26
+ self.client.start_workflow(RPCWorkflow, wf_id, 10)
27
+
28
+ input = Mydata("test", 100)
29
+ output = self.client.invoke_rpc(
30
+ wf_id, RPCWorkflow.test_rpc_input_type, input, Mydata
31
+ )
32
+ assert output == input
33
+
34
+ output = self.client.invoke_rpc(wf_id, RPCWorkflow.test_simple_rpc)
35
+ assert output == 123
36
+ wf = RPCWorkflow()
37
+ output = self.client.invoke_rpc(wf_id, wf.test_simple_rpc)
38
+ assert output == 123
39
+
40
+ def test_complicated_rpc(self):
41
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
42
+ self.client.start_workflow(RPCWorkflow, wf_id, 10)
43
+ self.client.invoke_rpc(wf_id, RPCWorkflow.test_rpc_persistence_write, 100)
44
+ res = self.client.invoke_rpc(wf_id, RPCWorkflow.test_rpc_persistence_read)
45
+ assert res == 100
46
+ self.client.invoke_rpc(wf_id, RPCWorkflow.test_rpc_trigger_state, 200)
47
+ res = self.client.invoke_rpc(wf_id, RPCWorkflow.test_rpc_persistence_read)
48
+ assert res == 200
49
+
50
+ res1 = self.client.invoke_rpc(
51
+ wf_id, RPCWorkflow.test_rpc_publish_to_idle_channel, "input-1"
52
+ )
53
+ # Channel Size should be 1
54
+ assert res1 == 1
55
+
56
+ res2 = self.client.invoke_rpc(
57
+ wf_id, RPCWorkflow.test_rpc_publish_to_idle_channel, "input-2"
58
+ )
59
+ # Channel Size should be 2
60
+ assert res2 == 2
61
+
62
+ self.client.invoke_rpc(wf_id, RPCWorkflow.test_rpc_publish_channel)
63
+ result = self.client.wait_for_workflow_completion(wf_id, str)
64
+ assert result == "done"