@temporalio/core-bridge 1.15.0 → 1.16.1

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 (209) hide show
  1. package/Cargo.lock +172 -70
  2. package/lib/native.d.ts +1 -1
  3. package/package.json +2 -2
  4. package/releases/aarch64-apple-darwin/index.node +0 -0
  5. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  6. package/releases/x86_64-apple-darwin/index.node +0 -0
  7. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  8. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  9. package/sdk-core/.github/workflows/per-pr.yml +6 -6
  10. package/sdk-core/AGENTS.md +41 -30
  11. package/sdk-core/Cargo.toml +3 -0
  12. package/sdk-core/README.md +15 -9
  13. package/sdk-core/crates/client/Cargo.toml +4 -0
  14. package/sdk-core/crates/client/README.md +139 -0
  15. package/sdk-core/crates/client/src/async_activity_handle.rs +297 -0
  16. package/sdk-core/crates/client/src/callback_based.rs +7 -0
  17. package/sdk-core/crates/client/src/errors.rs +294 -0
  18. package/sdk-core/crates/client/src/{raw.rs → grpc.rs} +280 -159
  19. package/sdk-core/crates/client/src/lib.rs +920 -1326
  20. package/sdk-core/crates/client/src/metrics.rs +24 -33
  21. package/sdk-core/crates/client/src/options_structs.rs +457 -0
  22. package/sdk-core/crates/client/src/replaceable.rs +5 -4
  23. package/sdk-core/crates/client/src/request_extensions.rs +8 -9
  24. package/sdk-core/crates/client/src/retry.rs +99 -54
  25. package/sdk-core/crates/client/src/{worker/mod.rs → worker.rs} +1 -1
  26. package/sdk-core/crates/client/src/workflow_handle.rs +826 -0
  27. package/sdk-core/crates/common/Cargo.toml +61 -2
  28. package/sdk-core/crates/common/build.rs +742 -12
  29. package/sdk-core/crates/common/protos/api_upstream/.github/workflows/ci.yml +2 -0
  30. package/sdk-core/crates/common/protos/api_upstream/Makefile +2 -1
  31. package/sdk-core/crates/common/protos/api_upstream/buf.yaml +0 -3
  32. package/sdk-core/crates/common/protos/api_upstream/cmd/check-path-conflicts/main.go +137 -0
  33. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv2.json +1166 -770
  34. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv3.yaml +1243 -750
  35. package/sdk-core/crates/common/protos/api_upstream/temporal/api/deployment/v1/message.proto +2 -2
  36. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/workflow.proto +4 -3
  37. package/sdk-core/crates/common/protos/api_upstream/temporal/api/failure/v1/message.proto +1 -0
  38. package/sdk-core/crates/common/protos/api_upstream/temporal/api/history/v1/message.proto +4 -0
  39. package/sdk-core/crates/common/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
  40. package/sdk-core/crates/common/protos/api_upstream/temporal/api/nexus/v1/message.proto +16 -1
  41. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +64 -6
  42. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +88 -33
  43. package/sdk-core/crates/common/protos/local/temporal/sdk/core/nexus/nexus.proto +4 -2
  44. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -0
  45. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +5 -5
  46. package/sdk-core/crates/common/src/activity_definition.rs +20 -0
  47. package/sdk-core/crates/common/src/data_converters.rs +770 -0
  48. package/sdk-core/crates/common/src/envconfig.rs +5 -0
  49. package/sdk-core/crates/common/src/lib.rs +15 -211
  50. package/sdk-core/crates/common/src/payload_visitor.rs +648 -0
  51. package/sdk-core/crates/common/src/priority.rs +110 -0
  52. package/sdk-core/crates/common/src/protos/canned_histories.rs +3 -0
  53. package/sdk-core/crates/common/src/protos/history_builder.rs +45 -0
  54. package/sdk-core/crates/common/src/protos/history_info.rs +2 -0
  55. package/sdk-core/crates/common/src/protos/mod.rs +122 -27
  56. package/sdk-core/crates/common/src/protos/task_token.rs +3 -3
  57. package/sdk-core/crates/common/src/protos/utilities.rs +11 -0
  58. package/sdk-core/crates/{sdk-core → common}/src/telemetry/log_export.rs +5 -7
  59. package/sdk-core/crates/common/src/telemetry/metrics/core.rs +125 -0
  60. package/sdk-core/crates/common/src/telemetry/metrics.rs +268 -223
  61. package/sdk-core/crates/{sdk-core → common}/src/telemetry/otel.rs +8 -13
  62. package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_meter.rs +49 -50
  63. package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_server.rs +2 -3
  64. package/sdk-core/crates/common/src/telemetry.rs +264 -4
  65. package/sdk-core/crates/common/src/worker.rs +68 -603
  66. package/sdk-core/crates/common/src/workflow_definition.rs +60 -0
  67. package/sdk-core/crates/macros/Cargo.toml +5 -1
  68. package/sdk-core/crates/macros/src/activities_definitions.rs +585 -0
  69. package/sdk-core/crates/macros/src/fsm_impl.rs +507 -0
  70. package/sdk-core/crates/macros/src/lib.rs +138 -512
  71. package/sdk-core/crates/macros/src/macro_utils.rs +106 -0
  72. package/sdk-core/crates/macros/src/workflow_definitions.rs +1224 -0
  73. package/sdk-core/crates/sdk/Cargo.toml +19 -6
  74. package/sdk-core/crates/sdk/README.md +415 -0
  75. package/sdk-core/crates/sdk/src/activities.rs +417 -0
  76. package/sdk-core/crates/sdk/src/interceptors.rs +1 -1
  77. package/sdk-core/crates/sdk/src/lib.rs +757 -442
  78. package/sdk-core/crates/sdk/src/workflow_context/options.rs +45 -35
  79. package/sdk-core/crates/sdk/src/workflow_context.rs +1033 -289
  80. package/sdk-core/crates/sdk/src/workflow_future.rs +277 -213
  81. package/sdk-core/crates/sdk/src/workflows.rs +711 -0
  82. package/sdk-core/crates/sdk-core/Cargo.toml +57 -64
  83. package/sdk-core/crates/sdk-core/benches/workflow_replay_bench.rs +41 -35
  84. package/sdk-core/crates/sdk-core/machine_coverage/ActivityMachine_Coverage.puml +1 -1
  85. package/sdk-core/crates/sdk-core/src/abstractions.rs +6 -10
  86. package/sdk-core/crates/sdk-core/src/core_tests/activity_tasks.rs +6 -5
  87. package/sdk-core/crates/sdk-core/src/core_tests/mod.rs +13 -15
  88. package/sdk-core/crates/sdk-core/src/core_tests/queries.rs +21 -25
  89. package/sdk-core/crates/sdk-core/src/core_tests/replay_flag.rs +7 -10
  90. package/sdk-core/crates/sdk-core/src/core_tests/updates.rs +14 -17
  91. package/sdk-core/crates/sdk-core/src/core_tests/workers.rs +493 -26
  92. package/sdk-core/crates/sdk-core/src/core_tests/workflow_tasks.rs +4 -8
  93. package/sdk-core/crates/sdk-core/src/ephemeral_server/mod.rs +7 -7
  94. package/sdk-core/crates/sdk-core/src/histfetch.rs +20 -10
  95. package/sdk-core/crates/sdk-core/src/lib.rs +41 -111
  96. package/sdk-core/crates/sdk-core/src/pollers/mod.rs +4 -9
  97. package/sdk-core/crates/sdk-core/src/pollers/poll_buffer.rs +118 -19
  98. package/sdk-core/crates/sdk-core/src/protosext/mod.rs +2 -2
  99. package/sdk-core/crates/sdk-core/src/replay/mod.rs +14 -5
  100. package/sdk-core/crates/sdk-core/src/telemetry/metrics.rs +179 -196
  101. package/sdk-core/crates/sdk-core/src/telemetry/mod.rs +3 -280
  102. package/sdk-core/crates/sdk-core/src/test_help/integ_helpers.rs +6 -9
  103. package/sdk-core/crates/sdk-core/src/test_help/unit_helpers.rs +3 -6
  104. package/sdk-core/crates/sdk-core/src/worker/activities/local_activities.rs +11 -14
  105. package/sdk-core/crates/sdk-core/src/worker/activities.rs +16 -19
  106. package/sdk-core/crates/sdk-core/src/worker/client/mocks.rs +9 -5
  107. package/sdk-core/crates/sdk-core/src/worker/client.rs +103 -81
  108. package/sdk-core/crates/sdk-core/src/worker/heartbeat.rs +7 -11
  109. package/sdk-core/crates/sdk-core/src/worker/mod.rs +1124 -229
  110. package/sdk-core/crates/sdk-core/src/worker/nexus.rs +145 -23
  111. package/sdk-core/crates/sdk-core/src/worker/slot_provider.rs +2 -2
  112. package/sdk-core/crates/sdk-core/src/worker/tuner/fixed_size.rs +2 -2
  113. package/sdk-core/crates/sdk-core/src/worker/tuner/resource_based.rs +13 -13
  114. package/sdk-core/crates/sdk-core/src/worker/tuner.rs +28 -8
  115. package/sdk-core/crates/sdk-core/src/worker/workflow/driven_workflow.rs +9 -3
  116. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +21 -22
  117. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/workflow_machines.rs +19 -4
  118. package/sdk-core/crates/sdk-core/src/worker/workflow/managed_run.rs +14 -18
  119. package/sdk-core/crates/sdk-core/src/worker/workflow/mod.rs +4 -6
  120. package/sdk-core/crates/sdk-core/src/worker/workflow/run_cache.rs +4 -7
  121. package/sdk-core/crates/sdk-core/src/worker/workflow/wft_extraction.rs +2 -4
  122. package/sdk-core/crates/sdk-core/src/worker/workflow/wft_poller.rs +8 -9
  123. package/sdk-core/crates/sdk-core/src/worker/workflow/workflow_stream.rs +1 -3
  124. package/sdk-core/crates/sdk-core/tests/activities_procmacro.rs +6 -0
  125. package/sdk-core/crates/sdk-core/tests/activities_trybuild/basic_pass.rs +54 -0
  126. package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.rs +18 -0
  127. package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.stderr +5 -0
  128. package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.rs +14 -0
  129. package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.stderr +5 -0
  130. package/sdk-core/crates/sdk-core/tests/activities_trybuild/multi_arg_pass.rs +48 -0
  131. package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_input_pass.rs +14 -0
  132. package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_return_type_pass.rs +19 -0
  133. package/sdk-core/crates/sdk-core/tests/cloud_tests.rs +14 -5
  134. package/sdk-core/crates/sdk-core/tests/common/activity_functions.rs +55 -0
  135. package/sdk-core/crates/sdk-core/tests/common/mod.rs +241 -196
  136. package/sdk-core/crates/sdk-core/tests/common/workflows.rs +41 -28
  137. package/sdk-core/crates/sdk-core/tests/global_metric_tests.rs +3 -5
  138. package/sdk-core/crates/sdk-core/tests/heavy_tests/fuzzy_workflow.rs +73 -64
  139. package/sdk-core/crates/sdk-core/tests/heavy_tests.rs +298 -252
  140. package/sdk-core/crates/sdk-core/tests/integ_tests/async_activity_client_tests.rs +230 -0
  141. package/sdk-core/crates/sdk-core/tests/integ_tests/client_tests.rs +94 -57
  142. package/sdk-core/crates/sdk-core/tests/integ_tests/data_converter_tests.rs +381 -0
  143. package/sdk-core/crates/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +16 -12
  144. package/sdk-core/crates/sdk-core/tests/integ_tests/heartbeat_tests.rs +48 -40
  145. package/sdk-core/crates/sdk-core/tests/integ_tests/metrics_tests.rs +327 -255
  146. package/sdk-core/crates/sdk-core/tests/integ_tests/pagination_tests.rs +50 -45
  147. package/sdk-core/crates/sdk-core/tests/integ_tests/polling_tests.rs +147 -126
  148. package/sdk-core/crates/sdk-core/tests/integ_tests/queries_tests.rs +103 -89
  149. package/sdk-core/crates/sdk-core/tests/integ_tests/update_tests.rs +609 -453
  150. package/sdk-core/crates/sdk-core/tests/integ_tests/visibility_tests.rs +80 -62
  151. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_heartbeat_tests.rs +360 -231
  152. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_tests.rs +248 -185
  153. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_versioning_tests.rs +52 -43
  154. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_client_tests.rs +180 -0
  155. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/activities.rs +428 -315
  156. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +82 -56
  157. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +56 -28
  158. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +364 -243
  159. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/client_interactions.rs +552 -0
  160. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +101 -42
  161. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +243 -147
  162. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/eager.rs +98 -28
  163. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1475 -1036
  164. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +73 -41
  165. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +397 -238
  166. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/patches.rs +414 -189
  167. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/queries.rs +415 -0
  168. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/replay.rs +96 -36
  169. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/resets.rs +154 -137
  170. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/signals.rs +183 -105
  171. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +85 -38
  172. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/timers.rs +142 -40
  173. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +73 -54
  174. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests.rs +363 -226
  175. package/sdk-core/crates/sdk-core/tests/main.rs +17 -15
  176. package/sdk-core/crates/sdk-core/tests/manual_tests.rs +207 -152
  177. package/sdk-core/crates/sdk-core/tests/shared_tests/mod.rs +65 -34
  178. package/sdk-core/crates/sdk-core/tests/shared_tests/priority.rs +107 -84
  179. package/sdk-core/crates/sdk-core/tests/workflows_procmacro.rs +6 -0
  180. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.rs +26 -0
  181. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.stderr +5 -0
  182. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/basic_pass.rs +49 -0
  183. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/minimal_pass.rs +21 -0
  184. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.rs +26 -0
  185. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.stderr +5 -0
  186. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.rs +21 -0
  187. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.stderr +5 -0
  188. package/sdk-core/crates/sdk-core-c-bridge/Cargo.toml +7 -1
  189. package/sdk-core/crates/sdk-core-c-bridge/include/temporal-sdk-core-c-bridge.h +14 -14
  190. package/sdk-core/crates/sdk-core-c-bridge/src/client.rs +83 -74
  191. package/sdk-core/crates/sdk-core-c-bridge/src/metric.rs +9 -14
  192. package/sdk-core/crates/sdk-core-c-bridge/src/runtime.rs +1 -2
  193. package/sdk-core/crates/sdk-core-c-bridge/src/tests/context.rs +13 -13
  194. package/sdk-core/crates/sdk-core-c-bridge/src/tests/mod.rs +6 -6
  195. package/sdk-core/crates/sdk-core-c-bridge/src/tests/utils.rs +3 -4
  196. package/sdk-core/crates/sdk-core-c-bridge/src/worker.rs +62 -75
  197. package/sdk-core/rustfmt.toml +2 -1
  198. package/src/client.rs +205 -318
  199. package/src/metrics.rs +22 -30
  200. package/src/runtime.rs +4 -5
  201. package/src/worker.rs +16 -19
  202. package/ts/native.ts +1 -1
  203. package/sdk-core/crates/client/src/workflow_handle/mod.rs +0 -212
  204. package/sdk-core/crates/common/src/errors.rs +0 -85
  205. package/sdk-core/crates/common/tests/worker_task_types_test.rs +0 -129
  206. package/sdk-core/crates/sdk/src/activity_context.rs +0 -238
  207. package/sdk-core/crates/sdk/src/app_data.rs +0 -37
  208. package/sdk-core/crates/sdk-core/tests/integ_tests/activity_functions.rs +0 -5
  209. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +0 -61
@@ -0,0 +1,415 @@
1
+ //! Tests for SDK-level query handling
2
+
3
+ use crate::common::build_fake_sdk;
4
+ use serde::{Deserialize, Serialize};
5
+ use std::{collections::HashMap, future::poll_fn, task::Poll, time::Duration};
6
+ use temporalio_common::protos::{
7
+ DEFAULT_WORKFLOW_TYPE, TestHistoryBuilder,
8
+ coresdk::workflow_commands::query_result,
9
+ temporal::api::{
10
+ common::v1::WorkflowType,
11
+ enums::v1::{CommandType, EventType},
12
+ history::v1::WorkflowExecutionStartedEventAttributes,
13
+ query::v1::WorkflowQuery,
14
+ taskqueue::v1::TaskQueue,
15
+ },
16
+ };
17
+ use temporalio_macros::{workflow, workflow_methods};
18
+ use temporalio_sdk::{SyncWorkflowContext, WorkflowContext, WorkflowContextView, WorkflowResult};
19
+ use temporalio_sdk_core::test_help::{
20
+ MockPollCfg, ResponseType, hist_to_poll_resp, mock_worker_client,
21
+ };
22
+
23
+ /// A workflow that returns Pending on first poll and Ready on second poll.
24
+ /// Uses workflow state to track whether it has been polled before.
25
+ #[workflow]
26
+ #[derive(Default)]
27
+ struct CompleteOnSecondPollWf {
28
+ polled_once: bool,
29
+ value: u32,
30
+ }
31
+
32
+ #[workflow_methods]
33
+ impl CompleteOnSecondPollWf {
34
+ #[run(name = DEFAULT_WORKFLOW_TYPE)]
35
+ async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<u32> {
36
+ poll_fn(|_| {
37
+ if ctx.state(|s| s.polled_once) {
38
+ Poll::Ready(())
39
+ } else {
40
+ ctx.state_mut(|s| s.polled_once = true);
41
+ Poll::Pending
42
+ }
43
+ })
44
+ .await;
45
+ Ok(42)
46
+ }
47
+
48
+ #[query]
49
+ fn get_value(&self, _ctx: &WorkflowContextView) -> u32 {
50
+ self.value
51
+ }
52
+ }
53
+
54
+ /// This test demonstrates a bug where the SDK advances the workflow future even when
55
+ /// receiving a query-only activation. When the workflow would complete on the next poll,
56
+ /// this causes both a query response AND a workflow completion command to be sent
57
+ /// together, which is invalid.
58
+ ///
59
+ /// The error message from core when this happens is:
60
+ /// "Workflow completion had a legacy query response along with other commands.
61
+ /// This is not allowed and constitutes an error in the lang SDK."
62
+ #[tokio::test]
63
+ async fn query_only_activation_should_not_advance_workflow() {
64
+ let mut t = TestHistoryBuilder::default();
65
+ t.add_by_type(EventType::WorkflowExecutionStarted);
66
+ t.add_full_wf_task();
67
+
68
+ let wfid = "query_only_test";
69
+
70
+ let tasks = [
71
+ hist_to_poll_resp(&t, wfid.to_owned(), ResponseType::ToTaskNum(1)),
72
+ {
73
+ let mut pr = hist_to_poll_resp(&t, wfid.to_owned(), ResponseType::ToTaskNum(1));
74
+ pr.query = Some(WorkflowQuery {
75
+ query_type: "get_value".to_string(),
76
+ query_args: None,
77
+ header: None,
78
+ });
79
+ pr.history = Some(Default::default());
80
+ pr
81
+ },
82
+ ];
83
+
84
+ let mut mock_cfg = MockPollCfg::from_resp_batches(wfid, t, tasks, mock_worker_client());
85
+ mock_cfg.num_expected_legacy_query_resps = 1;
86
+
87
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
88
+ asserts
89
+ .then(|wft| {
90
+ let has_complete_cmd = wft
91
+ .commands
92
+ .iter()
93
+ .any(|c| c.command_type() == CommandType::CompleteWorkflowExecution);
94
+ assert!(
95
+ !has_complete_cmd,
96
+ "First activation should not complete workflow! Commands: {:?}",
97
+ wft.commands
98
+ );
99
+ })
100
+ .then(|wft| {
101
+ let has_complete_cmd = wft
102
+ .commands
103
+ .iter()
104
+ .any(|c| c.command_type() == CommandType::CompleteWorkflowExecution);
105
+ assert!(
106
+ !has_complete_cmd,
107
+ "Query-only activation should NOT cause workflow completion! Commands: {:?}",
108
+ wft.commands
109
+ );
110
+ });
111
+ });
112
+
113
+ let mut worker = build_fake_sdk(mock_cfg);
114
+ worker.register_workflow::<CompleteOnSecondPollWf>();
115
+ worker.run().await.unwrap();
116
+ }
117
+
118
+ /// Test that a query for a non-existent handler doesn't advance the workflow either.
119
+ #[tokio::test]
120
+ async fn nonexistent_query_should_not_advance_workflow() {
121
+ let mut t = TestHistoryBuilder::default();
122
+ t.add_by_type(EventType::WorkflowExecutionStarted);
123
+ t.add_full_wf_task();
124
+
125
+ let wfid = "nonexistent_query_test";
126
+
127
+ let tasks = [
128
+ hist_to_poll_resp(&t, wfid.to_owned(), ResponseType::ToTaskNum(1)),
129
+ {
130
+ let mut pr = hist_to_poll_resp(&t, wfid.to_owned(), ResponseType::ToTaskNum(1));
131
+ pr.query = Some(WorkflowQuery {
132
+ query_type: "__temporal_workflow_metadata".to_string(),
133
+ query_args: None,
134
+ header: None,
135
+ });
136
+ pr.history = Some(Default::default());
137
+ pr
138
+ },
139
+ ];
140
+
141
+ let mut mock_cfg = MockPollCfg::from_resp_batches(wfid, t, tasks, mock_worker_client());
142
+ mock_cfg.num_expected_legacy_query_resps = 1;
143
+
144
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
145
+ asserts
146
+ .then(|wft| {
147
+ let has_complete_cmd = wft
148
+ .commands
149
+ .iter()
150
+ .any(|c| c.command_type() == CommandType::CompleteWorkflowExecution);
151
+ assert!(
152
+ !has_complete_cmd,
153
+ "First activation should not complete workflow! Commands: {:?}",
154
+ wft.commands
155
+ );
156
+ })
157
+ .then(|wft| {
158
+ let has_complete_cmd = wft
159
+ .commands
160
+ .iter()
161
+ .any(|c| c.command_type() == CommandType::CompleteWorkflowExecution);
162
+ assert!(
163
+ !has_complete_cmd,
164
+ "Nonexistent query should NOT cause workflow completion! Commands: {:?}",
165
+ wft.commands
166
+ );
167
+ });
168
+ });
169
+
170
+ let mut worker = build_fake_sdk(mock_cfg);
171
+ worker.register_workflow::<CompleteOnSecondPollWf>();
172
+ worker.run().await.unwrap();
173
+ }
174
+
175
+ /// A workflow that increments a counter when started and when it receives a signal.
176
+ /// Used to test that non-legacy queries see state after the workflow has advanced.
177
+ #[workflow]
178
+ #[derive(Default)]
179
+ struct CounterWf {
180
+ counter: u32,
181
+ got_signal: bool,
182
+ }
183
+
184
+ #[workflow_methods]
185
+ impl CounterWf {
186
+ #[run(name = DEFAULT_WORKFLOW_TYPE)]
187
+ async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
188
+ ctx.state_mut(|s| s.counter += 1);
189
+ ctx.wait_condition(|s| s.got_signal).await;
190
+ ctx.state_mut(|s| s.counter += 1);
191
+ Ok(())
192
+ }
193
+
194
+ #[signal]
195
+ fn my_signal(&mut self, _ctx: &mut SyncWorkflowContext<Self>) {
196
+ self.got_signal = true;
197
+ }
198
+
199
+ #[query]
200
+ fn get_counter(&self, _ctx: &WorkflowContextView) -> u32 {
201
+ self.counter
202
+ }
203
+ }
204
+
205
+ /// Non-legacy queries (in the `queries` field) come bundled with new history.
206
+ /// Core sends these queries in their own activation after the workflow has processed
207
+ /// the history, so queries should observe state AFTER the workflow has advanced.
208
+ #[tokio::test]
209
+ async fn non_legacy_query_should_see_state_after_workflow_advances() {
210
+ let wfid = "non_legacy_query_state_test";
211
+
212
+ let mut t = TestHistoryBuilder::default();
213
+ t.add_by_type(EventType::WorkflowExecutionStarted);
214
+ t.add_full_wf_task();
215
+ t.add_we_signaled("my_signal", vec![]);
216
+ t.add_full_wf_task();
217
+ t.add_workflow_execution_completed();
218
+
219
+ let tasks = [
220
+ {
221
+ let mut pr = hist_to_poll_resp(&t, wfid.to_owned(), ResponseType::ToTaskNum(1));
222
+ pr.queries = HashMap::from([(
223
+ "q1".to_string(),
224
+ WorkflowQuery {
225
+ query_type: "get_counter".to_string(),
226
+ query_args: None,
227
+ header: None,
228
+ },
229
+ )]);
230
+ pr
231
+ },
232
+ {
233
+ let mut pr = hist_to_poll_resp(&t, wfid.to_owned(), ResponseType::ToTaskNum(2));
234
+ pr.queries = HashMap::from([(
235
+ "q2".to_string(),
236
+ WorkflowQuery {
237
+ query_type: "get_counter".to_string(),
238
+ query_args: None,
239
+ header: None,
240
+ },
241
+ )]);
242
+ pr
243
+ },
244
+ ];
245
+
246
+ let mut mock_cfg = MockPollCfg::from_resp_batches(wfid, t, tasks, mock_worker_client());
247
+
248
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
249
+ asserts
250
+ .then(|wft| {
251
+ assert_eq!(wft.query_responses.len(), 1);
252
+ let query_resp = &wft.query_responses[0];
253
+ assert_eq!(query_resp.query_id, "q1");
254
+
255
+ match &query_resp.variant {
256
+ Some(query_result::Variant::Succeeded(success)) => {
257
+ let payload = success
258
+ .response
259
+ .as_ref()
260
+ .expect("Expected response payload");
261
+ let value: u32 =
262
+ serde_json::from_slice(&payload.data).expect("Expected u32 payload");
263
+ assert_eq!(
264
+ value, 1,
265
+ "After start, counter should be 1 but got {}",
266
+ value
267
+ );
268
+ }
269
+ other => panic!("Expected successful query response, got {:?}", other),
270
+ }
271
+ })
272
+ .then(|wft| {
273
+ assert_eq!(wft.query_responses.len(), 1);
274
+ let query_resp = &wft.query_responses[0];
275
+ assert_eq!(query_resp.query_id, "q2");
276
+
277
+ match &query_resp.variant {
278
+ Some(query_result::Variant::Succeeded(success)) => {
279
+ let payload = success
280
+ .response
281
+ .as_ref()
282
+ .expect("Expected response payload");
283
+ let value: u32 =
284
+ serde_json::from_slice(&payload.data).expect("Expected u32 payload");
285
+ assert_eq!(
286
+ value, 2,
287
+ "After signal, counter should be 2 but got {}",
288
+ value
289
+ );
290
+ }
291
+ other => panic!("Expected successful query response, got {:?}", other),
292
+ }
293
+ });
294
+ });
295
+
296
+ let mut worker = build_fake_sdk(mock_cfg);
297
+ worker.register_workflow::<CounterWf>();
298
+ worker.run().await.unwrap();
299
+ }
300
+
301
+ /// Struct to capture WorkflowContextView information for testing.
302
+ #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
303
+ struct WorkflowInfo {
304
+ workflow_id: String,
305
+ workflow_type: String,
306
+ task_queue: String,
307
+ namespace: String,
308
+ attempt: u32,
309
+ first_execution_run_id: String,
310
+ }
311
+
312
+ impl<'a> From<&'a WorkflowContextView> for WorkflowInfo {
313
+ fn from(ctx: &'a WorkflowContextView) -> Self {
314
+ Self {
315
+ workflow_id: ctx.workflow_id.clone(),
316
+ workflow_type: ctx.workflow_type.clone(),
317
+ task_queue: ctx.task_queue.clone(),
318
+ namespace: ctx.namespace.clone(),
319
+ attempt: ctx.attempt,
320
+ first_execution_run_id: ctx.first_execution_run_id.clone(),
321
+ }
322
+ }
323
+ }
324
+
325
+ /// A workflow that captures context view information at initialization time.
326
+ #[workflow]
327
+ struct ContextViewWf;
328
+
329
+ #[workflow_methods]
330
+ impl ContextViewWf {
331
+ #[init]
332
+ fn new(_ctx: &WorkflowContextView) -> Self {
333
+ Self
334
+ }
335
+
336
+ #[run(name = "context_view_wf")]
337
+ async fn run(_ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
338
+ Ok(())
339
+ }
340
+
341
+ #[query]
342
+ fn get_info(&self, ctx: &WorkflowContextView) -> WorkflowInfo {
343
+ ctx.into()
344
+ }
345
+ }
346
+
347
+ /// Test that WorkflowContextView contains the correct workflow information.
348
+ #[tokio::test]
349
+ async fn query_returns_workflow_context_view_info() {
350
+ const WFID: &str = "context_view_test_wf";
351
+ const FIRST_RUN_ID: &str = "first-run-id-12345";
352
+
353
+ let mut t = TestHistoryBuilder::default();
354
+ t.add(WorkflowExecutionStartedEventAttributes {
355
+ workflow_type: Some(WorkflowType {
356
+ name: "context_view_wf".to_string(),
357
+ }),
358
+ task_queue: Some(TaskQueue {
359
+ name: "test-task-queue".to_string(),
360
+ ..Default::default()
361
+ }),
362
+ first_execution_run_id: FIRST_RUN_ID.to_string(),
363
+ attempt: 3,
364
+ workflow_task_timeout: Some(Duration::from_secs(5).try_into().unwrap()),
365
+ ..Default::default()
366
+ });
367
+ t.add_full_wf_task();
368
+ t.add_workflow_execution_completed();
369
+
370
+ let tasks = [{
371
+ let mut pr = hist_to_poll_resp(&t, WFID.to_owned(), ResponseType::ToTaskNum(1));
372
+ pr.queries = HashMap::from([(
373
+ "q1".to_string(),
374
+ WorkflowQuery {
375
+ query_type: "get_info".to_string(),
376
+ query_args: None,
377
+ header: None,
378
+ },
379
+ )]);
380
+ pr
381
+ }];
382
+
383
+ let mut mock_cfg = MockPollCfg::from_resp_batches(WFID, t, tasks, mock_worker_client());
384
+
385
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
386
+ asserts.then(|wft| {
387
+ assert_eq!(wft.query_responses.len(), 1);
388
+ let query_resp = &wft.query_responses[0];
389
+ assert_eq!(query_resp.query_id, "q1");
390
+
391
+ match &query_resp.variant {
392
+ Some(query_result::Variant::Succeeded(success)) => {
393
+ let payload = success
394
+ .response
395
+ .as_ref()
396
+ .expect("Expected response payload");
397
+ let info: WorkflowInfo =
398
+ serde_json::from_slice(&payload.data).expect("Expected WorkflowInfo");
399
+
400
+ assert_eq!(info.workflow_id, WFID);
401
+ assert_eq!(info.workflow_type, "context_view_wf");
402
+ // task_queue comes from worker config, not workflow history
403
+ assert_eq!(info.namespace, "default");
404
+ assert_eq!(info.attempt, 3);
405
+ assert_eq!(info.first_execution_run_id, FIRST_RUN_ID);
406
+ }
407
+ other => panic!("Expected successful query response, got {:?}", other),
408
+ }
409
+ });
410
+ });
411
+
412
+ let mut worker = build_fake_sdk(mock_cfg);
413
+ worker.register_workflow::<ContextViewWf>();
414
+ worker.run().await.unwrap();
415
+ }
@@ -3,17 +3,17 @@ use crate::{
3
3
  ActivationAssertionsInterceptor, build_fake_sdk, history_from_proto_binary,
4
4
  init_core_replay_preloaded, replay_sdk_worker, replay_sdk_worker_stream,
5
5
  },
6
- integ_tests::workflow_tests::patches::changes_wf,
6
+ integ_tests::workflow_tests::patches::ChangesWf,
7
7
  };
8
8
  use assert_matches::assert_matches;
9
9
  use parking_lot::Mutex;
10
10
  use std::{collections::HashSet, sync::Arc, time::Duration};
11
11
  use temporalio_common::{
12
- errors::PollError,
13
12
  prost_dur,
14
13
  protos::{
15
14
  DEFAULT_WORKFLOW_TYPE, TestHistoryBuilder, canned_histories,
16
15
  coresdk::{
16
+ AsJsonPayloadExt,
17
17
  workflow_activation::remove_from_cache::EvictionReason,
18
18
  workflow_commands::{ScheduleActivity, StartTimer},
19
19
  workflow_completion::WorkflowActivationCompletion,
@@ -21,34 +21,50 @@ use temporalio_common::{
21
21
  temporal::api::enums::v1::EventType,
22
22
  },
23
23
  };
24
- use temporalio_sdk::{WfContext, Worker, WorkflowFunction, interceptors::WorkerInterceptor};
24
+ use temporalio_macros::{workflow, workflow_methods};
25
+ use temporalio_sdk::{
26
+ Worker, WorkflowContext, WorkflowContextView, WorkflowResult, interceptors::WorkerInterceptor,
27
+ };
25
28
  use temporalio_sdk_core::{
29
+ PollError,
26
30
  replay::{HistoryFeeder, HistoryForReplay},
27
31
  test_help::{MockPollCfg, ResponseType, WorkerTestHelpers},
28
32
  };
29
33
  use tokio::join;
30
34
 
31
35
  fn test_hist_to_replay(t: TestHistoryBuilder) -> HistoryForReplay {
32
- let hi = t.get_full_history_info().unwrap().into();
36
+ let hi = t.get_full_history_info().unwrap();
33
37
  HistoryForReplay::new(hi, "fake".to_string())
34
38
  }
35
39
 
36
- fn timers_wf(num_timers: u32) -> WorkflowFunction {
37
- WorkflowFunction::new(move |ctx: WfContext| async move {
40
+ #[workflow]
41
+ struct TimersWf {
42
+ num_timers: u32,
43
+ }
44
+
45
+ #[workflow_methods]
46
+ impl TimersWf {
47
+ #[init]
48
+ fn new(_ctx: &WorkflowContextView, num_timers: u32) -> Self {
49
+ Self { num_timers }
50
+ }
51
+
52
+ #[run(name = DEFAULT_WORKFLOW_TYPE)]
53
+ async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
54
+ let num_timers = ctx.state(|wf| wf.num_timers);
38
55
  for _ in 1..=num_timers {
39
56
  ctx.timer(Duration::from_secs(1)).await;
40
57
  }
41
- Ok(().into())
42
- })
58
+ Ok(())
59
+ }
43
60
  }
44
61
 
45
62
  #[fixture(num_timers = 1)]
46
63
  fn fire_happy_hist(num_timers: u32) -> Worker {
47
- let func = timers_wf(num_timers);
48
- // Add 1 b/c history takes # wf tasks, not timers
49
- let t = canned_histories::long_sequential_timers(num_timers as usize);
64
+ let mut t = canned_histories::long_sequential_timers(num_timers as usize);
65
+ t.set_wf_input(num_timers.as_json_payload().unwrap());
50
66
  let mut worker = build_fake_sdk(MockPollCfg::from_resps(t, [ResponseType::AllHistory]));
51
- worker.register_wf(DEFAULT_WORKFLOW_TYPE, func);
67
+ worker.register_workflow::<TimersWf>();
52
68
  worker
53
69
  }
54
70
 
@@ -72,11 +88,10 @@ async fn replay_flag_is_correct(#[case] mut worker: Worker, #[case] num_timers:
72
88
 
73
89
  #[tokio::test(flavor = "multi_thread")]
74
90
  async fn replay_flag_is_correct_partial_history() {
75
- let func = timers_wf(1);
76
- // Add 1 b/c history takes # wf tasks, not timers
77
- let t = canned_histories::long_sequential_timers(2);
91
+ let mut t = canned_histories::long_sequential_timers(2);
92
+ t.set_wf_input(1u32.as_json_payload().unwrap());
78
93
  let mut worker = build_fake_sdk(MockPollCfg::from_resps(t, [1]));
79
- worker.register_wf(DEFAULT_WORKFLOW_TYPE, func);
94
+ worker.register_workflow::<TimersWf>();
80
95
 
81
96
  let mut aai = ActivationAssertionsInterceptor::default();
82
97
  aai.then(|a| assert!(!a.is_replaying));
@@ -184,11 +199,11 @@ async fn workflow_nondeterministic_replay() {
184
199
 
185
200
  #[tokio::test]
186
201
  async fn replay_using_wf_function() {
187
- let num_timers = 10;
188
- let t = canned_histories::long_sequential_timers(num_timers as usize);
189
- let func = timers_wf(num_timers);
202
+ let num_timers = 10u32;
203
+ let mut t = canned_histories::long_sequential_timers(num_timers as usize);
204
+ t.set_wf_input(num_timers.as_json_payload().unwrap());
190
205
  let mut worker = replay_sdk_worker([test_hist_to_replay(t)]);
191
- worker.register_wf(DEFAULT_WORKFLOW_TYPE, func);
206
+ worker.register_workflow::<TimersWf>();
192
207
  worker.run().await.unwrap();
193
208
  }
194
209
 
@@ -203,16 +218,16 @@ async fn replay_ending_wft_complete_with_commands_but_no_scheduled_started() {
203
218
  t.add_timer_fired(timer_started_event_id, i.to_string());
204
219
  t.add_full_wf_task();
205
220
  }
206
- let func = timers_wf(3);
221
+ t.set_wf_input(3u32.as_json_payload().unwrap());
207
222
  let mut worker = replay_sdk_worker([test_hist_to_replay(t)]);
208
- worker.register_wf(DEFAULT_WORKFLOW_TYPE, func);
223
+ worker.register_workflow::<TimersWf>();
209
224
  worker.run().await.unwrap();
210
225
  }
211
226
 
212
- async fn replay_abrupt_ending(t: TestHistoryBuilder) {
213
- let func = timers_wf(1);
227
+ async fn replay_abrupt_ending(mut t: TestHistoryBuilder) {
228
+ t.set_wf_input(1u32.as_json_payload().unwrap());
214
229
  let mut worker = replay_sdk_worker([test_hist_to_replay(t)]);
215
- worker.register_wf(DEFAULT_WORKFLOW_TYPE, func);
230
+ worker.register_workflow::<TimersWf>();
216
231
  worker.run().await.unwrap();
217
232
  }
218
233
  #[tokio::test]
@@ -230,10 +245,10 @@ async fn replay_ok_ending_with_timed_out() {
230
245
 
231
246
  #[tokio::test]
232
247
  async fn replay_shutdown_worker() {
233
- let t = canned_histories::single_timer("1");
234
- let func = timers_wf(1);
248
+ let mut t = canned_histories::single_timer("1");
249
+ t.set_wf_input(1u32.as_json_payload().unwrap());
235
250
  let mut worker = replay_sdk_worker([test_hist_to_replay(t)]);
236
- worker.register_wf(DEFAULT_WORKFLOW_TYPE, func);
251
+ worker.register_workflow::<TimersWf>();
237
252
  let shutdown_ctr_i = UniqueShutdownWorker::default();
238
253
  let shutdown_ctr = shutdown_ctr_i.runs.clone();
239
254
  worker.set_worker_interceptor(shutdown_ctr_i);
@@ -241,16 +256,60 @@ async fn replay_shutdown_worker() {
241
256
  assert_eq!(shutdown_ctr.lock().len(), 1);
242
257
  }
243
258
 
259
+ #[workflow]
260
+ struct OneTimerWf {
261
+ num_timers: u32,
262
+ }
263
+
264
+ #[workflow_methods]
265
+ impl OneTimerWf {
266
+ #[init]
267
+ fn new(_ctx: &WorkflowContextView, num_timers: u32) -> Self {
268
+ Self { num_timers }
269
+ }
270
+
271
+ #[run(name = "onetimer")]
272
+ async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
273
+ let num_timers = ctx.state(|wf| wf.num_timers);
274
+ for _ in 1..=num_timers {
275
+ ctx.timer(Duration::from_secs(1)).await;
276
+ }
277
+ Ok(())
278
+ }
279
+ }
280
+
281
+ #[workflow]
282
+ struct SeqTimerWf {
283
+ num_timers: u32,
284
+ }
285
+
286
+ #[workflow_methods]
287
+ impl SeqTimerWf {
288
+ #[init]
289
+ fn new(_ctx: &WorkflowContextView, num_timers: u32) -> Self {
290
+ Self { num_timers }
291
+ }
292
+
293
+ #[run(name = "seqtimer")]
294
+ async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
295
+ let num_timers = ctx.state(|wf| wf.num_timers);
296
+ for _ in 1..=num_timers {
297
+ ctx.timer(Duration::from_secs(1)).await;
298
+ }
299
+ Ok(())
300
+ }
301
+ }
302
+
244
303
  #[rstest::rstest]
245
304
  #[tokio::test]
246
305
  async fn multiple_histories_replay(#[values(false, true)] use_feeder: bool) {
247
- let num_timers = 10;
248
- let seq_timer_wf = timers_wf(num_timers);
249
- let one_timer_wf = timers_wf(1);
306
+ let num_timers = 10u32;
250
307
  let mut one_timer_hist = canned_histories::single_timer("1");
251
308
  one_timer_hist.set_wf_type("onetimer");
309
+ one_timer_hist.set_wf_input(1u32.as_json_payload().unwrap());
252
310
  let mut seq_timer_hist = canned_histories::long_sequential_timers(num_timers as usize);
253
311
  seq_timer_hist.set_wf_type("seqtimer");
312
+ seq_timer_hist.set_wf_input(num_timers.as_json_payload().unwrap());
254
313
  let (feeder, stream) = HistoryFeeder::new(1);
255
314
  let mut worker = if use_feeder {
256
315
  replay_sdk_worker_stream(stream)
@@ -263,8 +322,8 @@ async fn multiple_histories_replay(#[values(false, true)] use_feeder: bool) {
263
322
  let runs_ctr_i = UniqueRunsCounter::default();
264
323
  let runs_ctr = runs_ctr_i.runs.clone();
265
324
  worker.set_worker_interceptor(runs_ctr_i);
266
- worker.register_wf("onetimer", one_timer_wf);
267
- worker.register_wf("seqtimer", seq_timer_wf);
325
+ worker.register_workflow::<OneTimerWf>();
326
+ worker.register_workflow::<SeqTimerWf>();
268
327
 
269
328
  if use_feeder {
270
329
  let feed_fut = async move {
@@ -289,16 +348,17 @@ async fn multiple_histories_replay(#[values(false, true)] use_feeder: bool) {
289
348
  async fn multiple_histories_can_handle_dupe_run_ids() {
290
349
  let mut hist1 = canned_histories::single_timer("1");
291
350
  hist1.set_wf_type("onetimer");
351
+ hist1.set_wf_input(1u32.as_json_payload().unwrap());
292
352
  let mut worker = replay_sdk_worker([
293
353
  test_hist_to_replay(hist1.clone()),
294
354
  test_hist_to_replay(hist1.clone()),
295
355
  test_hist_to_replay(hist1),
296
356
  ]);
297
- worker.register_wf("onetimer", timers_wf(1));
357
+ worker.register_workflow::<OneTimerWf>();
298
358
  worker.run().await.unwrap();
299
359
  }
300
360
 
301
- // Verifies SDK can decode patch markers before changing them to use json encoding
361
+ // Verifies SDK can decode patch markers before changing them to use json encoding.
302
362
  #[tokio::test]
303
363
  async fn replay_old_patch_format() {
304
364
  let mut worker = replay_sdk_worker([HistoryForReplay::new(
@@ -307,7 +367,7 @@ async fn replay_old_patch_format() {
307
367
  .unwrap(),
308
368
  "fake".to_owned(),
309
369
  )]);
310
- worker.register_wf("writes_change_markers", changes_wf);
370
+ worker.register_workflow::<ChangesWf>();
311
371
  worker.run().await.unwrap();
312
372
  }
313
373