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
dex/tests/__init__.py ADDED
@@ -0,0 +1,84 @@
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
+ from dex.registry import Registry
16
+ from dex.tests.workflows.abnormal_exit_workflow import AbnormalExitWorkflow
17
+ from dex.tests.workflows.basic_workflow import BasicWorkflow
18
+ from dex.tests.workflows.conditional_complete_workflow import (
19
+ ConditionalCompleteWorkflow,
20
+ )
21
+ from dex.tests.workflows.describe_workflow import DescribeWorkflow
22
+ from dex.tests.workflows.empty_data_workflow import EmptyDataWorkflow
23
+ from dex.tests.workflows.internal_channel_workflow import InternalChannelWorkflow
24
+ from dex.tests.workflows.internal_channel_workflow_with_no_prefix_channel import (
25
+ InternalChannelWorkflowWithNoPrefixChannel,
26
+ )
27
+ from dex.tests.workflows.java_duplicate_rpc_memo_workflow import (
28
+ JavaDuplicateRpcMemoWorkflow,
29
+ )
30
+ from dex.tests.workflows.persistence_data_attributes_workflow import (
31
+ PersistenceDataAttributesWorkflow,
32
+ )
33
+ from dex.tests.workflows.persistence_search_attributes_workflow import (
34
+ PersistenceSearchAttributesWorkflow,
35
+ )
36
+ from dex.tests.workflows.persistence_state_execution_local_workflow import (
37
+ PersistenceStateExecutionLocalWorkflow,
38
+ )
39
+ from dex.tests.workflows.recovery_workflow import RecoveryWorkflow
40
+ from dex.tests.workflows.rpc_memo_workflow import RpcMemoWorkflow
41
+ from dex.tests.workflows.rpc_workflow import RPCWorkflow
42
+ from dex.tests.workflows.state_options_override_workflow import (
43
+ StateOptionsOverrideWorkflow,
44
+ )
45
+ from dex.tests.workflows.state_options_workflow import (
46
+ StateOptionsWorkflow1,
47
+ StateOptionsWorkflow2,
48
+ )
49
+ from dex.tests.workflows.timer_workflow import TimerWorkflow
50
+ from dex.tests.workflows.wait_for_state_with_state_execution_id_workflow import (
51
+ WaitForStateWithStateExecutionIdWorkflow,
52
+ )
53
+ from dex.tests.workflows.wait_for_state_with_wait_for_key_workflow import (
54
+ WaitForStateWithWaitForKeyWorkflow,
55
+ )
56
+ from dex.tests.workflows.wait_internal_channel_workflow import (
57
+ WaitInternalChannelWorkflow,
58
+ )
59
+ from dex.tests.workflows.wait_signal_workflow import WaitSignalWorkflow
60
+
61
+ registry = Registry()
62
+
63
+ registry.add_workflow(AbnormalExitWorkflow())
64
+ registry.add_workflow(BasicWorkflow())
65
+ registry.add_workflow(ConditionalCompleteWorkflow())
66
+ registry.add_workflow(DescribeWorkflow())
67
+ registry.add_workflow(EmptyDataWorkflow())
68
+ registry.add_workflow(InternalChannelWorkflow())
69
+ registry.add_workflow(InternalChannelWorkflowWithNoPrefixChannel())
70
+ registry.add_workflow(JavaDuplicateRpcMemoWorkflow())
71
+ registry.add_workflow(PersistenceDataAttributesWorkflow())
72
+ registry.add_workflow(PersistenceSearchAttributesWorkflow())
73
+ registry.add_workflow(PersistenceStateExecutionLocalWorkflow())
74
+ registry.add_workflow(RecoveryWorkflow())
75
+ registry.add_workflow(RpcMemoWorkflow())
76
+ registry.add_workflow(RPCWorkflow())
77
+ registry.add_workflow(StateOptionsOverrideWorkflow())
78
+ registry.add_workflow(StateOptionsWorkflow1())
79
+ registry.add_workflow(StateOptionsWorkflow2())
80
+ registry.add_workflow(TimerWorkflow())
81
+ registry.add_workflow(WaitForStateWithStateExecutionIdWorkflow())
82
+ registry.add_workflow(WaitForStateWithWaitForKeyWorkflow())
83
+ registry.add_workflow(WaitInternalChannelWorkflow())
84
+ registry.add_workflow(WaitSignalWorkflow())
@@ -0,0 +1,7 @@
1
+ CASSANDRA_VERSION=3.11.9
2
+ ELASTICSEARCH_VERSION=7.17.27
3
+ MYSQL_VERSION=8
4
+ POSTGRESQL_VERSION=13
5
+ TEMPORAL_VERSION=1.25
6
+ TEMPORAL_UI_VERSION=2.31.2
7
+ TEMPORAL_ADMIN_TOOLS_VERSION=1.25.2-tctl-1.18.1-cli-1.1.1
@@ -0,0 +1,44 @@
1
+ #!/bin/bash
2
+
3
+ checkExists () {
4
+ # see https://github.com/temporalio/temporal/issues/4160
5
+ if temporal operator search-attribute list | grep -q "$1"; then
6
+ return 0
7
+ else
8
+ return 1
9
+ fi
10
+ }
11
+
12
+ echo "now trying to register Dex system search attributes..."
13
+ for run in {1..120}; do
14
+ sleep 1
15
+ temporal operator search-attribute create --name DexWorkflowType --type Keyword
16
+ sleep 0.1
17
+ temporal operator search-attribute create --name DexGlobalWorkflowVersion --type Int
18
+ sleep 0.1
19
+ temporal operator search-attribute create --name DexExecutingStateIds --type KeywordList
20
+ sleep 0.1
21
+ temporal operator search-attribute create --name CustomKeywordField --type Keyword
22
+ sleep 0.1
23
+ temporal operator search-attribute create --name CustomIntField --type Int
24
+ sleep 0.1
25
+ temporal operator search-attribute create --name CustomBoolField --type Bool
26
+ sleep 0.1
27
+ temporal operator search-attribute create --name CustomDoubleField --type Double
28
+ sleep 0.1
29
+ temporal operator search-attribute create --name CustomDatetimeField --type Datetime
30
+ sleep 0.1
31
+ temporal operator search-attribute create --name CustomStringField --type Text
32
+ sleep 0.1
33
+ temporal operator search-attribute create --name CustomKeywordArrayField --type KeywordList
34
+ sleep 0.1
35
+
36
+ if checkExists "DexWorkflowType" && checkExists "DexGlobalWorkflowVersion" && checkExists "DexExecutingStateIds" && checkExists "CustomKeywordField" && checkExists "CustomIntField" && checkExists "CustomBoolField" && checkExists "CustomDoubleField" && checkExists "CustomDatetimeField" && checkExists "CustomStringField" && checkExists "CustomKeywordArrayField"; then
37
+ echo "All search attributes are registered"
38
+ break
39
+ fi
40
+
41
+ done
42
+
43
+
44
+ tail -f /dev/null
@@ -0,0 +1,87 @@
1
+ version: '3.9'
2
+
3
+ # see .env file for the default value of the environment variables
4
+ services:
5
+ elasticsearch:
6
+ container_name: temporal-elasticsearch-dex
7
+ environment:
8
+ - cluster.routing.allocation.disk.threshold_enabled=true
9
+ - cluster.routing.allocation.disk.watermark.low=512mb
10
+ - cluster.routing.allocation.disk.watermark.high=256mb
11
+ - cluster.routing.allocation.disk.watermark.flood_stage=128mb
12
+ - discovery.type=single-node
13
+ - ES_JAVA_OPTS=-Xms512m -Xmx512m
14
+ - xpack.security.enabled=false
15
+ image: elasticsearch:${ELASTICSEARCH_VERSION}
16
+ networks:
17
+ - temporal-network
18
+ expose:
19
+ - 9200
20
+ postgresql:
21
+ container_name: temporal-postgresql-dex
22
+ environment:
23
+ POSTGRES_PASSWORD: temporal
24
+ POSTGRES_USER: temporal
25
+ image: postgres:${POSTGRESQL_VERSION}
26
+ networks:
27
+ - temporal-network
28
+ expose:
29
+ - 5432
30
+ temporal:
31
+ container_name: temporal-dex
32
+ depends_on:
33
+ - postgresql
34
+ - elasticsearch
35
+ environment:
36
+ - DB=postgres12
37
+ - DB_PORT=5432
38
+ - POSTGRES_USER=temporal
39
+ - POSTGRES_PWD=temporal
40
+ - POSTGRES_SEEDS=postgresql
41
+ - DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development-sql.yaml
42
+ - ENABLE_ES=true
43
+ - ES_SEEDS=elasticsearch
44
+ - ES_VERSION=v7
45
+ image: temporalio/auto-setup:${TEMPORAL_VERSION}
46
+ networks:
47
+ - temporal-network
48
+ ports:
49
+ - 7233:7233
50
+ labels:
51
+ kompose.volume.type: configMap
52
+ volumes:
53
+ - ./dynamicconfig:/etc/temporal/config/dynamicconfig
54
+ temporal-admin-tools:
55
+ container_name: temporal-admin-tools-dex
56
+ depends_on:
57
+ - temporal
58
+ environment:
59
+ - TEMPORAL_ADDRESS=temporal:7233
60
+ - TEMPORAL_CLI_ADDRESS=temporal:7233
61
+ image: temporalio/admin-tools:${TEMPORAL_ADMIN_TOOLS_VERSION}
62
+ networks:
63
+ - temporal-network
64
+ stdin_open: true
65
+ tty: true
66
+ volumes:
67
+ - ./docker-compose-init.sh:/etc/temporal/init.sh
68
+ entrypoint: sh -c "/etc/temporal/init.sh"
69
+ dex-server:
70
+ container_name: dex-server
71
+ image: ${DEX_SERVER_IMAGE:-superdurable/dex-server:latest}
72
+ environment:
73
+ # Replace localhost/127.0.0.1 worker URLs so the container can reach the host worker.
74
+ # host.docker.internal works on Docker Desktop and on Linux with extra_hosts below.
75
+ - AUTO_FIX_WORKER_URL=host.docker.internal
76
+ networks:
77
+ - temporal-network
78
+ ports:
79
+ - 8801:8801
80
+ depends_on:
81
+ - temporal
82
+ extra_hosts:
83
+ - host.docker.internal:host-gateway
84
+ networks:
85
+ temporal-network:
86
+ driver: bridge
87
+ name: temporal-network
@@ -0,0 +1,39 @@
1
+ Use `docker.yaml` file to override the default dynamic config value (they are specified
2
+ when creating the service config).
3
+
4
+ Each key can have zero or more values and each value can have zero or more
5
+ constraints. There are only three types of constraint:
6
+ 1. `namespace`: `string`
7
+ 2. `taskQueueName`: `string`
8
+ 3. `taskType`: `int` (`1`:`Workflow`, `2`:`Activity`)
9
+ A value will be selected and returned if all its has exactly the same constraints
10
+ as the ones specified in query filters (including the number of constraints).
11
+
12
+ Please use the following format:
13
+ ```
14
+ testGetBoolPropertyKey:
15
+ - value: false
16
+ - value: true
17
+ constraints:
18
+ namespace: "global-samples-namespace"
19
+ - value: false
20
+ constraints:
21
+ namespace: "samples-namespace"
22
+ testGetDurationPropertyKey:
23
+ - value: "1m"
24
+ constraints:
25
+ namespace: "samples-namespace"
26
+ taskQueueName: "longIdleTimeTaskqueue"
27
+ testGetFloat64PropertyKey:
28
+ - value: 12.0
29
+ constraints:
30
+ namespace: "samples-namespace"
31
+ testGetMapPropertyKey:
32
+ - value:
33
+ key1: 1
34
+ key2: "value 2"
35
+ key3:
36
+ - false
37
+ - key4: true
38
+ key5: 2.0
39
+ ```
@@ -0,0 +1,6 @@
1
+ limit.maxIDLength:
2
+ - value: 255
3
+ constraints: {}
4
+ system.forceSearchAttributesCacheRefreshOnRead:
5
+ - value: true # Dev setup only. Please don't turn this on in production.
6
+ constraints: {}
File without changes
@@ -0,0 +1,48 @@
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.errors import WorkflowFailed
21
+ from dex.dex_api.models.id_reuse_policy import IDReusePolicy
22
+ from dex.tests.worker_server import registry
23
+ from dex.tests.workflows.abnormal_exit_workflow import AbnormalExitWorkflow
24
+ from dex.tests.workflows.basic_workflow import BasicWorkflow
25
+ from dex.workflow_options import WorkflowOptions
26
+
27
+
28
+ class TestAbnormalWorkflow(unittest.TestCase):
29
+ @classmethod
30
+ def setUpClass(cls):
31
+ cls.client = Client(registry)
32
+
33
+ def test_abnormal_exit_workflow(self):
34
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
35
+ start_options = WorkflowOptions(
36
+ workflow_id_reuse_policy=IDReusePolicy.ALLOW_IF_PREVIOUS_EXITS_ABNORMALLY
37
+ )
38
+
39
+ self.client.start_workflow(
40
+ AbnormalExitWorkflow, wf_id, 100, "input", start_options
41
+ )
42
+ with self.assertRaises(WorkflowFailed):
43
+ self.client.wait_for_workflow_completion(wf_id, str)
44
+
45
+ # Starting a workflow with the same ID should be allowed since the previous failed abnormally
46
+ self.client.start_workflow(BasicWorkflow, wf_id, 100, "input", start_options)
47
+ res = self.client.wait_for_workflow_completion(wf_id, str)
48
+ assert res == "done"
@@ -0,0 +1,75 @@
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.errors import WorkflowAlreadyStartedError
21
+ from dex.dex_api.models import WorkflowAlreadyStartedOptions
22
+ from dex.tests.worker_server import registry
23
+ from dex.tests.workflows.basic_workflow import BasicWorkflow
24
+ from dex.workflow_options import WorkflowOptions
25
+
26
+
27
+ class TestWorkflowErrors(unittest.TestCase):
28
+ @classmethod
29
+ def setUpClass(cls):
30
+ cls.client = Client(registry)
31
+
32
+ def test_basic_workflow(self):
33
+ original_request_id = "1"
34
+ later_request_id = "2"
35
+
36
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
37
+
38
+ workflow_already_started_options_1 = WorkflowAlreadyStartedOptions(
39
+ ignore_already_started_error=True
40
+ )
41
+ workflow_already_started_options_1.request_id = original_request_id
42
+
43
+ start_options_1 = WorkflowOptions()
44
+ start_options_1.workflow_already_started_options = (
45
+ workflow_already_started_options_1
46
+ )
47
+
48
+ wf_run_id = self.client.start_workflow(
49
+ BasicWorkflow, wf_id, 100, "input", start_options_1
50
+ )
51
+ assert wf_run_id
52
+
53
+ wf_run_id = self.client.start_workflow(
54
+ BasicWorkflow, wf_id, 100, "input", start_options_1
55
+ )
56
+ assert wf_run_id
57
+
58
+ workflow_already_started_options_2 = WorkflowAlreadyStartedOptions(
59
+ ignore_already_started_error=True
60
+ )
61
+ workflow_already_started_options_2.request_id = later_request_id
62
+
63
+ start_options_2 = WorkflowOptions()
64
+ start_options_2.workflow_already_started_option = (
65
+ workflow_already_started_options_2
66
+ )
67
+
68
+ with self.assertRaises(WorkflowAlreadyStartedError):
69
+ wf_run_id = self.client.start_workflow(
70
+ BasicWorkflow, wf_id, 100, "input", start_options_2
71
+ )
72
+ assert wf_run_id
73
+
74
+ res = self.client.wait_for_workflow_completion(wf_id, str)
75
+ assert res == "done"
@@ -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.conditional_complete_workflow import (
22
+ ConditionalCompleteWorkflow,
23
+ test_signal_channel,
24
+ )
25
+
26
+
27
+ class TestConditionalComplete(unittest.TestCase):
28
+ @classmethod
29
+ def setUpClass(cls):
30
+ cls.client = Client(registry)
31
+
32
+ def test_internal_channel_conditional_complete(self):
33
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
34
+ do_test_conditional_workflow(self.client, wf_id, False)
35
+
36
+ def test_signal_channel_conditional_complete(self):
37
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
38
+ do_test_conditional_workflow(self.client, wf_id, True)
39
+
40
+
41
+ def do_test_conditional_workflow(client: Client, wf_id: str, use_signal: bool):
42
+ client.start_workflow(ConditionalCompleteWorkflow, wf_id, 10, use_signal)
43
+
44
+ for x in range(3):
45
+ if use_signal:
46
+ client.signal_workflow(wf_id, test_signal_channel, 123)
47
+ else:
48
+ client.invoke_rpc(
49
+ wf_id, ConditionalCompleteWorkflow.test_rpc_publish_channel
50
+ )
51
+ if x == 0:
52
+ # wait for a second so that the workflow is in execute state
53
+ time.sleep(1)
54
+
55
+ res = client.wait_for_workflow_completion(wf_id)
56
+ assert res == 3
@@ -0,0 +1,45 @@
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.errors import WorkflowNotExistsError
21
+ from dex.dex_api.models import WorkflowStatus
22
+ from dex.tests.workflows.describe_workflow import DescribeWorkflow
23
+ from dex.tests.worker_server import registry
24
+
25
+
26
+ class TestDescribeWorkflow(unittest.TestCase):
27
+ @classmethod
28
+ def setUpClass(cls):
29
+ cls.client = Client(registry)
30
+
31
+ def test_describe_workflow(self):
32
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
33
+
34
+ self.client.start_workflow(DescribeWorkflow, wf_id, 100)
35
+ workflow_info = self.client.describe_workflow(wf_id)
36
+ assert workflow_info.workflow_status == WorkflowStatus.RUNNING
37
+
38
+ # Stop the workflow
39
+ self.client.stop_workflow(wf_id)
40
+
41
+ def test_describe_workflow_when_workflow_not_exists(self):
42
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
43
+
44
+ with self.assertRaises(WorkflowNotExistsError):
45
+ self.client.describe_workflow(wf_id)
@@ -0,0 +1,79 @@
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
+ import httpx
20
+
21
+ from dex.client import Client
22
+ from dex.tests.worker_server import registry
23
+ from dex.worker_service import WorkerService
24
+
25
+
26
+ class TestBinaryNullDecodesCorrectly(unittest.TestCase):
27
+ @classmethod
28
+ def setUpClass(cls):
29
+ cls.client = Client(registry)
30
+
31
+ def test_binary_null_input_decodes_correctly(self):
32
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
33
+
34
+ response = httpx.post(
35
+ f"http://0.0.0.0:8802/{WorkerService.api_path_workflow_state_execute}",
36
+ json={
37
+ "DataObjects": [
38
+ {"key": "test-da", "value": {"encoding": "binary/null"}}
39
+ ],
40
+ "commandResults": {
41
+ "interStateChannelResults": [],
42
+ "stateStartApiSucceeded": True,
43
+ },
44
+ "context": {
45
+ "attempt": 1,
46
+ "firstAttemptTimestamp": 1747935829,
47
+ "stateExecutionId": "State1-1",
48
+ "workflowId": wf_id,
49
+ "workflowRunId": "0196f734-d037-7432-bd63-e1136cd34dbd",
50
+ "workflowStartedTimestamp": 1747904155,
51
+ },
52
+ "stateInput": {"encoding": "binary/null"},
53
+ "stateLocals": [],
54
+ "workflowStateId": "State1",
55
+ "workflowType": "EmptyDataWorkflow",
56
+ },
57
+ )
58
+ assert response.is_success
59
+ response_json = response.json()
60
+ self.assertEqual(
61
+ response_json,
62
+ {
63
+ "publishToInterStateChannel": [],
64
+ "recordEvents": [],
65
+ "stateDecision": {
66
+ "nextStates": [
67
+ {
68
+ "stateId": "_SYS_GRACEFUL_COMPLETING_WORKFLOW",
69
+ "stateInput": {
70
+ "data": '"success"',
71
+ "encoding": "json/plain",
72
+ },
73
+ }
74
+ ]
75
+ },
76
+ "upsertDataObjects": [],
77
+ "upsertStateLocals": [],
78
+ },
79
+ )
@@ -0,0 +1,33 @@
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.internal_channel_workflow import InternalChannelWorkflow
22
+
23
+
24
+ class TestConditionalComplete(unittest.TestCase):
25
+ @classmethod
26
+ def setUpClass(cls):
27
+ cls.client = Client(registry)
28
+
29
+ def test_internal_channel_workflow(self):
30
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
31
+
32
+ self.client.start_workflow(InternalChannelWorkflow, wf_id, 100, None)
33
+ self.client.wait_for_workflow_completion(wf_id, None)
@@ -0,0 +1,46 @@
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.internal_channel_workflow_with_no_prefix_channel import (
22
+ InternalChannelWorkflowWithNoPrefixChannel,
23
+ test_non_prefix_channel_name_with_suffix,
24
+ )
25
+
26
+
27
+ class TestInternalChannelWithNoPrefix(unittest.TestCase):
28
+ @classmethod
29
+ def setUpClass(cls):
30
+ cls.client = Client(registry)
31
+
32
+ def test_internal_channel_workflow_with_no_prefix_channel(self):
33
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
34
+
35
+ self.client.start_workflow(
36
+ InternalChannelWorkflowWithNoPrefixChannel, wf_id, 5, None
37
+ )
38
+
39
+ with self.assertRaises(Exception) as context:
40
+ self.client.wait_for_workflow_completion(wf_id, None)
41
+
42
+ self.assertIn("FAILED", context.exception.workflow_status)
43
+ self.assertIn(
44
+ f"WorkerExecutionError: InternalChannel channel_name is not defined {test_non_prefix_channel_name_with_suffix}",
45
+ context.exception.error_message,
46
+ )