temporalio 0.0.2 → 0.1.0

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 (202) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +25 -23
  3. data/bridge/Cargo.lock +168 -59
  4. data/bridge/Cargo.toml +4 -2
  5. data/bridge/sdk-core/README.md +19 -6
  6. data/bridge/sdk-core/client/src/lib.rs +215 -39
  7. data/bridge/sdk-core/client/src/metrics.rs +17 -8
  8. data/bridge/sdk-core/client/src/raw.rs +4 -4
  9. data/bridge/sdk-core/client/src/retry.rs +32 -20
  10. data/bridge/sdk-core/core/Cargo.toml +22 -9
  11. data/bridge/sdk-core/core/src/abstractions.rs +203 -14
  12. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +76 -41
  13. data/bridge/sdk-core/core/src/core_tests/determinism.rs +165 -2
  14. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +204 -83
  15. data/bridge/sdk-core/core/src/core_tests/queries.rs +3 -4
  16. data/bridge/sdk-core/core/src/core_tests/workers.rs +1 -3
  17. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +397 -54
  18. data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +106 -12
  19. data/bridge/sdk-core/core/src/internal_flags.rs +136 -0
  20. data/bridge/sdk-core/core/src/lib.rs +16 -9
  21. data/bridge/sdk-core/core/src/telemetry/log_export.rs +1 -1
  22. data/bridge/sdk-core/core/src/telemetry/metrics.rs +69 -35
  23. data/bridge/sdk-core/core/src/telemetry/mod.rs +29 -13
  24. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +17 -12
  25. data/bridge/sdk-core/core/src/test_help/mod.rs +62 -12
  26. data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +112 -156
  27. data/bridge/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  28. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +352 -122
  29. data/bridge/sdk-core/core/src/worker/activities.rs +233 -157
  30. data/bridge/sdk-core/core/src/worker/client/mocks.rs +22 -2
  31. data/bridge/sdk-core/core/src/worker/client.rs +18 -2
  32. data/bridge/sdk-core/core/src/worker/mod.rs +165 -58
  33. data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
  34. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -5
  35. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +856 -277
  36. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +100 -43
  37. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +7 -7
  38. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +5 -4
  39. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +87 -27
  40. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +5 -4
  41. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +5 -4
  42. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +5 -4
  43. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +137 -62
  44. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +25 -17
  45. data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +7 -6
  46. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +103 -152
  47. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +7 -7
  48. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +9 -9
  49. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
  50. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +14 -7
  51. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +5 -16
  52. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +201 -121
  53. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +11 -14
  54. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +30 -15
  55. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +1026 -376
  56. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +460 -384
  57. data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
  58. data/bridge/sdk-core/core/src/worker/workflow/wft_extraction.rs +125 -0
  59. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +1 -4
  60. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
  61. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
  62. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +448 -718
  63. data/bridge/sdk-core/core-api/Cargo.toml +2 -1
  64. data/bridge/sdk-core/core-api/src/errors.rs +1 -34
  65. data/bridge/sdk-core/core-api/src/lib.rs +6 -2
  66. data/bridge/sdk-core/core-api/src/telemetry.rs +0 -6
  67. data/bridge/sdk-core/core-api/src/worker.rs +14 -1
  68. data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +18 -15
  69. data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +8 -3
  70. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
  71. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +5 -17
  72. data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +11 -0
  73. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -6
  74. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -6
  75. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +5 -0
  76. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +22 -6
  77. data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +48 -19
  78. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +2 -0
  79. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +3 -0
  80. data/bridge/sdk-core/protos/api_upstream/temporal/api/{enums/v1/interaction_type.proto → protocol/v1/message.proto} +29 -11
  81. data/bridge/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  82. data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +111 -0
  83. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +59 -28
  84. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +2 -2
  85. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -0
  86. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -0
  87. data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -0
  88. data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  89. data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  90. data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -0
  91. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +7 -0
  92. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +1 -0
  93. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +6 -0
  94. data/bridge/sdk-core/sdk/Cargo.toml +3 -2
  95. data/bridge/sdk-core/sdk/src/lib.rs +87 -20
  96. data/bridge/sdk-core/sdk/src/workflow_future.rs +9 -8
  97. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +5 -2
  98. data/bridge/sdk-core/sdk-core-protos/build.rs +36 -1
  99. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +100 -87
  100. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +5 -1
  101. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +175 -57
  102. data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
  103. data/bridge/sdk-core/test-utils/Cargo.toml +3 -1
  104. data/bridge/sdk-core/test-utils/src/canned_histories.rs +106 -296
  105. data/bridge/sdk-core/test-utils/src/histfetch.rs +1 -1
  106. data/bridge/sdk-core/test-utils/src/lib.rs +82 -23
  107. data/bridge/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
  108. data/bridge/sdk-core/test-utils/src/workflows.rs +29 -0
  109. data/bridge/sdk-core/tests/fuzzy_workflow.rs +130 -0
  110. data/bridge/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
  111. data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  112. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +5 -3
  113. data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
  114. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +4 -47
  115. data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
  116. data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
  117. data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -69
  118. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  119. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
  120. data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +1 -0
  121. data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
  122. data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
  123. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +72 -191
  124. data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +1 -0
  125. data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +7 -28
  126. data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +12 -7
  127. data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  128. data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
  129. data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
  130. data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
  131. data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -4
  132. data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +10 -11
  133. data/bridge/sdk-core/tests/main.rs +3 -13
  134. data/bridge/sdk-core/tests/runner.rs +75 -36
  135. data/bridge/sdk-core/tests/wf_input_replay.rs +32 -0
  136. data/bridge/src/connection.rs +41 -25
  137. data/bridge/src/lib.rs +269 -14
  138. data/bridge/src/runtime.rs +1 -1
  139. data/bridge/src/test_server.rs +153 -0
  140. data/bridge/src/worker.rs +89 -16
  141. data/lib/gen/temporal/api/command/v1/message_pb.rb +4 -18
  142. data/lib/gen/temporal/api/common/v1/message_pb.rb +4 -0
  143. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +1 -3
  144. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +3 -3
  145. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +2 -0
  146. data/lib/gen/temporal/api/enums/v1/update_pb.rb +6 -4
  147. data/lib/gen/temporal/api/history/v1/message_pb.rb +27 -19
  148. data/lib/gen/temporal/api/namespace/v1/message_pb.rb +1 -0
  149. data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +3 -0
  150. data/lib/gen/temporal/api/protocol/v1/message_pb.rb +30 -0
  151. data/lib/gen/temporal/api/sdk/v1/task_complete_metadata_pb.rb +23 -0
  152. data/lib/gen/temporal/api/testservice/v1/request_response_pb.rb +49 -0
  153. data/lib/gen/temporal/api/testservice/v1/service_pb.rb +21 -0
  154. data/lib/gen/temporal/api/update/v1/message_pb.rb +72 -0
  155. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +26 -16
  156. data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +13 -9
  157. data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +10 -6
  158. data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +13 -9
  159. data/lib/gen/temporal/sdk/core/common/common_pb.rb +7 -3
  160. data/lib/gen/temporal/sdk/core/core_interface_pb.rb +9 -3
  161. data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +7 -3
  162. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +27 -21
  163. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +28 -24
  164. data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +12 -5
  165. data/lib/temporalio/activity/context.rb +13 -8
  166. data/lib/temporalio/activity/info.rb +1 -1
  167. data/lib/temporalio/bridge/connect_options.rb +15 -0
  168. data/lib/temporalio/bridge/retry_config.rb +24 -0
  169. data/lib/temporalio/bridge/tls_options.rb +19 -0
  170. data/lib/temporalio/client/implementation.rb +8 -8
  171. data/lib/temporalio/connection/retry_config.rb +44 -0
  172. data/lib/temporalio/connection/service.rb +20 -0
  173. data/lib/temporalio/connection/test_service.rb +92 -0
  174. data/lib/temporalio/connection/tls_options.rb +51 -0
  175. data/lib/temporalio/connection/workflow_service.rb +731 -0
  176. data/lib/temporalio/connection.rb +55 -720
  177. data/lib/temporalio/interceptor/activity_inbound.rb +22 -0
  178. data/lib/temporalio/interceptor/activity_outbound.rb +24 -0
  179. data/lib/temporalio/interceptor/chain.rb +5 -5
  180. data/lib/temporalio/interceptor/client.rb +8 -4
  181. data/lib/temporalio/interceptor.rb +22 -0
  182. data/lib/temporalio/retry_policy.rb +13 -3
  183. data/lib/temporalio/testing/time_skipping_handle.rb +32 -0
  184. data/lib/temporalio/testing/time_skipping_interceptor.rb +23 -0
  185. data/lib/temporalio/testing/workflow_environment.rb +112 -0
  186. data/lib/temporalio/testing.rb +175 -0
  187. data/lib/temporalio/version.rb +1 -1
  188. data/lib/temporalio/worker/activity_runner.rb +26 -4
  189. data/lib/temporalio/worker/activity_worker.rb +44 -18
  190. data/lib/temporalio/worker/sync_worker.rb +47 -11
  191. data/lib/temporalio/worker.rb +27 -21
  192. data/lib/temporalio/workflow/async.rb +46 -0
  193. data/lib/temporalio/workflow/future.rb +138 -0
  194. data/lib/temporalio/workflow/info.rb +76 -0
  195. data/temporalio.gemspec +4 -3
  196. metadata +67 -17
  197. data/bridge/sdk-core/Cargo.lock +0 -2606
  198. data/bridge/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +0 -87
  199. data/lib/bridge.so +0 -0
  200. data/lib/gen/temporal/api/enums/v1/interaction_type_pb.rb +0 -25
  201. data/lib/gen/temporal/api/interaction/v1/message_pb.rb +0 -49
  202. data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +0 -222
@@ -1,4 +1,5 @@
1
1
  use crate::{
2
+ internal_flags::CoreInternalFlags,
2
3
  replay::DEFAULT_WORKFLOW_TYPE,
3
4
  test_help::{canned_histories, mock_sdk, mock_sdk_cfg, MockPollCfg, ResponseType},
4
5
  worker::client::mocks::mock_workflow_client,
@@ -8,8 +9,13 @@ use std::{
8
9
  time::Duration,
9
10
  };
10
11
  use temporal_client::WorkflowOptions;
11
- use temporal_sdk::{WfContext, WorkflowResult};
12
- use temporal_sdk_core_protos::temporal::api::enums::v1::WorkflowTaskFailedCause;
12
+ use temporal_sdk::{
13
+ ActivityOptions, ChildWorkflowOptions, LocalActivityOptions, WfContext, WorkflowResult,
14
+ };
15
+ use temporal_sdk_core_protos::{
16
+ temporal::api::{enums::v1::WorkflowTaskFailedCause, failure::v1::Failure},
17
+ DEFAULT_ACTIVITY_TYPE,
18
+ };
13
19
 
14
20
  static DID_FAIL: AtomicBool = AtomicBool::new(false);
15
21
  pub async fn timer_wf_fails_once(ctx: WfContext) -> WorkflowResult<()> {
@@ -105,3 +111,160 @@ async fn test_wf_task_rejected_properly_due_to_nondeterminism(#[case] use_cache:
105
111
  // timer and proceed without restarting
106
112
  assert_eq!(2, started_count.load(Ordering::Relaxed));
107
113
  }
114
+
115
+ #[rstest::rstest]
116
+ #[tokio::test]
117
+ async fn activity_id_or_type_change_is_nondeterministic(
118
+ #[values(true, false)] use_cache: bool,
119
+ #[values(true, false)] id_change: bool,
120
+ #[values(true, false)] local_act: bool,
121
+ ) {
122
+ let wf_id = "fakeid";
123
+ let wf_type = DEFAULT_WORKFLOW_TYPE;
124
+ let mut t = if local_act {
125
+ canned_histories::single_local_activity("1")
126
+ } else {
127
+ canned_histories::single_activity("1")
128
+ };
129
+ t.set_flags_first_wft(&[CoreInternalFlags::IdAndTypeDeterminismChecks as u32], &[]);
130
+ let mock = mock_workflow_client();
131
+ let mut mh = MockPollCfg::from_resp_batches(
132
+ wf_id,
133
+ t,
134
+ // Two polls are needed, since the first will fail
135
+ [ResponseType::AllHistory, ResponseType::AllHistory],
136
+ mock,
137
+ );
138
+ // We should see one wft failure which has nondeterminism cause
139
+ mh.num_expected_fails = 1;
140
+ mh.expect_fail_wft_matcher = Box::new(move |_, cause, f| {
141
+ let should_contain = if id_change {
142
+ "does not match activity id"
143
+ } else {
144
+ "does not match activity type"
145
+ };
146
+ matches!(cause, WorkflowTaskFailedCause::NonDeterministicError)
147
+ && matches!(f, Some(Failure {
148
+ message,
149
+ ..
150
+ }) if message.contains(should_contain))
151
+ });
152
+ let mut worker = mock_sdk_cfg(mh, |cfg| {
153
+ if use_cache {
154
+ cfg.max_cached_workflows = 2;
155
+ }
156
+ });
157
+
158
+ worker.register_wf(wf_type.to_owned(), move |ctx: WfContext| async move {
159
+ if local_act {
160
+ ctx.local_activity(if id_change {
161
+ LocalActivityOptions {
162
+ activity_id: Some("I'm bad and wrong!".to_string()),
163
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
164
+ ..Default::default()
165
+ }
166
+ } else {
167
+ LocalActivityOptions {
168
+ activity_type: "not the default act type".to_string(),
169
+ ..Default::default()
170
+ }
171
+ })
172
+ .await;
173
+ } else {
174
+ ctx.activity(if id_change {
175
+ ActivityOptions {
176
+ activity_id: Some("I'm bad and wrong!".to_string()),
177
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
178
+ ..Default::default()
179
+ }
180
+ } else {
181
+ ActivityOptions {
182
+ activity_type: "not the default act type".to_string(),
183
+ ..Default::default()
184
+ }
185
+ })
186
+ .await;
187
+ }
188
+ Ok(().into())
189
+ });
190
+
191
+ worker
192
+ .submit_wf(
193
+ wf_id.to_owned(),
194
+ wf_type.to_owned(),
195
+ vec![],
196
+ WorkflowOptions::default(),
197
+ )
198
+ .await
199
+ .unwrap();
200
+ worker.run_until_done().await.unwrap();
201
+ }
202
+
203
+ #[rstest::rstest]
204
+ #[tokio::test]
205
+ async fn child_wf_id_or_type_change_is_nondeterministic(
206
+ #[values(true, false)] use_cache: bool,
207
+ #[values(true, false)] id_change: bool,
208
+ ) {
209
+ let wf_id = "fakeid";
210
+ let wf_type = DEFAULT_WORKFLOW_TYPE;
211
+ let mut t = canned_histories::single_child_workflow("1");
212
+ t.set_flags_first_wft(&[CoreInternalFlags::IdAndTypeDeterminismChecks as u32], &[]);
213
+ let mock = mock_workflow_client();
214
+ let mut mh = MockPollCfg::from_resp_batches(
215
+ wf_id,
216
+ t,
217
+ // Two polls are needed, since the first will fail
218
+ [ResponseType::AllHistory, ResponseType::AllHistory],
219
+ mock,
220
+ );
221
+ // We should see one wft failure which has nondeterminism cause
222
+ mh.num_expected_fails = 1;
223
+ mh.expect_fail_wft_matcher = Box::new(move |_, cause, f| {
224
+ let should_contain = if id_change {
225
+ "does not match child workflow id"
226
+ } else {
227
+ "does not match child workflow type"
228
+ };
229
+ matches!(cause, WorkflowTaskFailedCause::NonDeterministicError)
230
+ && matches!(f, Some(Failure {
231
+ message,
232
+ ..
233
+ }) if message.contains(should_contain))
234
+ });
235
+ let mut worker = mock_sdk_cfg(mh, |cfg| {
236
+ if use_cache {
237
+ cfg.max_cached_workflows = 2;
238
+ }
239
+ });
240
+
241
+ worker.register_wf(wf_type.to_owned(), move |ctx: WfContext| async move {
242
+ ctx.child_workflow(if id_change {
243
+ ChildWorkflowOptions {
244
+ workflow_id: "I'm bad and wrong!".to_string(),
245
+ workflow_type: DEFAULT_ACTIVITY_TYPE.to_string(),
246
+ ..Default::default()
247
+ }
248
+ } else {
249
+ ChildWorkflowOptions {
250
+ workflow_id: "1".to_string(),
251
+ workflow_type: "not the child wf type".to_string(),
252
+ ..Default::default()
253
+ }
254
+ })
255
+ .start(&ctx)
256
+ .await;
257
+ Ok(().into())
258
+ });
259
+
260
+ worker
261
+ .submit_wf(
262
+ wf_id.to_owned(),
263
+ wf_type.to_owned(),
264
+ vec![],
265
+ WorkflowOptions::default(),
266
+ )
267
+ .await
268
+ .unwrap();
269
+ worker.run_until_done().await.unwrap();
270
+ }
@@ -8,6 +8,7 @@ use crate::{
8
8
  worker::{client::mocks::mock_workflow_client, LEGACY_QUERY_ID},
9
9
  };
10
10
  use anyhow::anyhow;
11
+ use crossbeam::queue::SegQueue;
11
12
  use futures::{future::join_all, FutureExt};
12
13
  use std::{
13
14
  collections::HashMap,
@@ -19,7 +20,9 @@ use std::{
19
20
  time::{Duration, SystemTime},
20
21
  };
21
22
  use temporal_client::WorkflowOptions;
22
- use temporal_sdk::{ActContext, LocalActivityOptions, WfContext, WorkflowResult};
23
+ use temporal_sdk::{
24
+ ActContext, ActivityCancelledError, LocalActivityOptions, WfContext, WorkflowResult,
25
+ };
23
26
  use temporal_sdk_core_api::Worker;
24
27
  use temporal_sdk_core_protos::{
25
28
  coresdk::{
@@ -31,11 +34,11 @@ use temporal_sdk_core_protos::{
31
34
  },
32
35
  temporal::api::{
33
36
  common::v1::RetryPolicy,
34
- enums::v1::{EventType, TimeoutType},
37
+ enums::v1::{EventType, TimeoutType, WorkflowTaskFailedCause},
35
38
  failure::v1::Failure,
36
- history::v1::History,
37
39
  query::v1::WorkflowQuery,
38
40
  },
41
+ DEFAULT_ACTIVITY_TYPE,
39
42
  };
40
43
  use temporal_sdk_core_test_utils::{
41
44
  schedule_local_activity_cmd, start_timer_cmd, WorkerTestHelpers,
@@ -58,7 +61,7 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
58
61
  let mut t = TestHistoryBuilder::default();
59
62
  t.add_by_type(EventType::WorkflowExecutionStarted);
60
63
  t.add_full_wf_task();
61
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
64
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
62
65
  t.add_full_wf_task();
63
66
  t.add_local_activity_result_marker(1, "1", b"echo".into());
64
67
  t.add_timer_fired(timer_started_event_id, "1".to_string());
@@ -83,7 +86,7 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
83
86
  DEFAULT_WORKFLOW_TYPE.to_owned(),
84
87
  |ctx: WfContext| async move {
85
88
  let la = ctx.local_activity(LocalActivityOptions {
86
- activity_type: "echo".to_string(),
89
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
87
90
  input: "hi".as_json_payload().expect("serializes fine"),
88
91
  ..Default::default()
89
92
  });
@@ -92,7 +95,7 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
92
95
  Ok(().into())
93
96
  },
94
97
  );
95
- worker.register_activity("echo", echo);
98
+ worker.register_activity(DEFAULT_ACTIVITY_TYPE, echo);
96
99
  worker
97
100
  .submit_wf(
98
101
  wf_id.to_owned(),
@@ -110,7 +113,7 @@ pub async fn local_act_fanout_wf(ctx: WfContext) -> WorkflowResult<()> {
110
113
  .map(|i| {
111
114
  ctx.local_activity(LocalActivityOptions {
112
115
  activity_type: "echo".to_string(),
113
- input: format!("Hi {}", i)
116
+ input: format!("Hi {i}")
114
117
  .as_json_payload()
115
118
  .expect("serializes fine"),
116
119
  ..Default::default()
@@ -127,7 +130,7 @@ async fn local_act_many_concurrent() {
127
130
  let mut t = TestHistoryBuilder::default();
128
131
  t.add_by_type(EventType::WorkflowExecutionStarted);
129
132
  t.add_full_wf_task();
130
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
133
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
131
134
  t.add_full_wf_task();
132
135
  for i in 1..=50 {
133
136
  t.add_local_activity_result_marker(i, &i.to_string(), b"echo".into());
@@ -168,12 +171,7 @@ async fn local_act_many_concurrent() {
168
171
  async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
169
172
  let mut t = TestHistoryBuilder::default();
170
173
  let wft_timeout = Duration::from_millis(200);
171
- let mut wes_short_wft_timeout = default_wes_attribs();
172
- wes_short_wft_timeout.workflow_task_timeout = Some(wft_timeout.try_into().unwrap());
173
- t.add(
174
- EventType::WorkflowExecutionStarted,
175
- wes_short_wft_timeout.into(),
176
- );
174
+ t.add_wfe_started_with_wft_timeout(wft_timeout);
177
175
  t.add_full_wf_task();
178
176
  // Task created by WFT heartbeat
179
177
  t.add_full_wf_task();
@@ -304,7 +302,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
304
302
  "1",
305
303
  Failure::application_failure("la failed".to_string(), false),
306
304
  );
307
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
305
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
308
306
  t.add_timer_fired(timer_started_event_id, "1".to_string());
309
307
  t.add_full_wf_task();
310
308
  t.add_local_activity_fail_marker(
@@ -312,7 +310,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
312
310
  "2",
313
311
  Failure::application_failure("la failed".to_string(), false),
314
312
  );
315
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
313
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
316
314
  t.add_timer_fired(timer_started_event_id, "2".to_string());
317
315
  t.add_full_wf_task();
318
316
  t.add_workflow_execution_completed();
@@ -332,7 +330,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
332
330
  |ctx: WfContext| async move {
333
331
  let la_res = ctx
334
332
  .local_activity(LocalActivityOptions {
335
- activity_type: "echo".to_string(),
333
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
336
334
  input: "hi".as_json_payload().expect("serializes fine"),
337
335
  retry_policy: RetryPolicy {
338
336
  initial_interval: Some(prost_dur!(from_millis(65))),
@@ -351,9 +349,12 @@ async fn local_act_retry_long_backoff_uses_timer() {
351
349
  Ok(().into())
352
350
  },
353
351
  );
354
- worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
355
- Result::<(), _>::Err(anyhow!("Oh no I failed!"))
356
- });
352
+ worker.register_activity(
353
+ DEFAULT_ACTIVITY_TYPE,
354
+ move |_ctx: ActContext, _: String| async move {
355
+ Result::<(), _>::Err(anyhow!("Oh no I failed!"))
356
+ },
357
+ );
357
358
  worker
358
359
  .submit_wf(
359
360
  wf_id.to_owned(),
@@ -413,7 +414,7 @@ async fn local_act_command_immediately_follows_la_marker() {
413
414
  t.add_full_wf_task();
414
415
  t.add_full_wf_task();
415
416
  t.add_local_activity_result_marker(1, "1", "done".into());
416
- t.add_get_event_id(EventType::TimerStarted, None);
417
+ t.add_by_type(EventType::TimerStarted);
417
418
  t.add_full_wf_task();
418
419
 
419
420
  let wf_id = "fakeid";
@@ -452,12 +453,7 @@ async fn local_act_command_immediately_follows_la_marker() {
452
453
  async fn query_during_wft_heartbeat_doesnt_accidentally_fail_to_continue_heartbeat() {
453
454
  let wfid = "fake_wf_id";
454
455
  let mut t = TestHistoryBuilder::default();
455
- let mut wes_short_wft_timeout = default_wes_attribs();
456
- wes_short_wft_timeout.workflow_task_timeout = Some(prost_dur!(from_millis(200)));
457
- t.add(
458
- EventType::WorkflowExecutionStarted,
459
- wes_short_wft_timeout.into(),
460
- );
456
+ t.add_wfe_started_with_wft_timeout(Duration::from_millis(200));
461
457
  t.add_full_wf_task();
462
458
  // get query here
463
459
  t.add_full_wf_task();
@@ -506,7 +502,7 @@ async fn query_during_wft_heartbeat_doesnt_accidentally_fail_to_continue_heartbe
506
502
  task.run_id,
507
503
  schedule_local_activity_cmd(
508
504
  1,
509
- "act-id",
505
+ "1",
510
506
  ActivityCancellationType::TryCancel,
511
507
  Duration::from_secs(60),
512
508
  ),
@@ -572,15 +568,11 @@ async fn la_resolve_during_legacy_query_does_not_combine(#[case] impossible_quer
572
568
  // never happen, but there was an issue where an LA resolving could trigger that.
573
569
  let wfid = "fake_wf_id";
574
570
  let mut t = TestHistoryBuilder::default();
575
- let wes_short_wft_timeout = default_wes_attribs();
576
- t.add(
577
- EventType::WorkflowExecutionStarted,
578
- wes_short_wft_timeout.into(),
579
- );
571
+ t.add(default_wes_attribs());
580
572
  // Since we don't send queries with start workflow, need one workflow task of something else
581
573
  // b/c we want to get an activation with a job and a nonlegacy query
582
574
  t.add_full_wf_task();
583
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
575
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
584
576
  t.add_timer_fired(timer_started_event_id, "1".to_string());
585
577
 
586
578
  // nonlegacy query got here & LA started here
@@ -622,8 +614,12 @@ async fn la_resolve_during_legacy_query_does_not_combine(#[case] impossible_quer
622
614
  2,
623
615
  ),
624
616
  );
625
- // Strip history, we need to look like we hit the cache
626
- pr.history = Some(History { events: vec![] });
617
+ // Strip beginning of history so the only events are WFT sched/started, we need to look
618
+ // like we hit the cache
619
+ {
620
+ let h = pr.history.as_mut().unwrap();
621
+ h.events = h.events.split_off(6);
622
+ }
627
623
  // In the nonsense server response case, we attach a legacy query, otherwise this
628
624
  // response looks like a normal response to a forced WFT heartbeat.
629
625
  if impossible_query_in_task {
@@ -783,12 +779,19 @@ async fn test_schedule_to_start_timeout() {
783
779
  worker.run_until_done().await.unwrap();
784
780
  }
785
781
 
782
+ #[rstest::rstest]
783
+ #[case::sched_to_start(true)]
784
+ #[case::sched_to_close(false)]
786
785
  #[tokio::test]
787
- async fn test_schedule_to_start_timeout_not_based_on_original_time() {
786
+ async fn test_schedule_to_start_timeout_not_based_on_original_time(
787
+ #[case] is_sched_to_start: bool,
788
+ ) {
788
789
  // We used to carry over the schedule time of LAs from the "original" schedule time if these LAs
789
790
  // created newly after backing off across a timer. That was a mistake, since schedule-to-start
790
- // timeouts should apply to when the new attempt was scheduled. This test verifies we don't
791
- // time out on s-t-s timeouts because of that.
791
+ // timeouts should apply to when the new attempt was scheduled. This test verifies:
792
+ // * we don't time out on s-t-s timeouts because of that, when the param is true.
793
+ // * we do properly time out on s-t-c timeouts when the param is false
794
+
792
795
  let mut t = TestHistoryBuilder::default();
793
796
  t.add_by_type(EventType::WorkflowExecutionStarted);
794
797
  t.add_full_wf_task();
@@ -805,7 +808,7 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time() {
805
808
  deets.backoff = Some(prost_dur!(from_secs(100)));
806
809
  },
807
810
  );
808
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
811
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
809
812
  t.add_timer_fired(timer_started_event_id, "1".to_string());
810
813
  t.add_workflow_task_scheduled_and_started();
811
814
 
@@ -814,9 +817,18 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time() {
814
817
  let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::AllHistory], mock);
815
818
  let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
816
819
 
820
+ let schedule_to_close_timeout = Some(if is_sched_to_start {
821
+ // This 60 minute timeout will not have elapsed according to the original
822
+ // schedule time in the history.
823
+ Duration::from_secs(60 * 60)
824
+ } else {
825
+ // This 10 minute timeout will have already elapsed
826
+ Duration::from_secs(10 * 60)
827
+ });
828
+
817
829
  worker.register_wf(
818
830
  DEFAULT_WORKFLOW_TYPE.to_owned(),
819
- |ctx: WfContext| async move {
831
+ move |ctx: WfContext| async move {
820
832
  let la_res = ctx
821
833
  .local_activity(LocalActivityOptions {
822
834
  activity_type: "echo".to_string(),
@@ -829,11 +841,15 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time() {
829
841
  non_retryable_error_types: vec![],
830
842
  },
831
843
  schedule_to_start_timeout: Some(Duration::from_secs(60)),
832
- schedule_to_close_timeout: Some(Duration::from_secs(60 * 60)),
844
+ schedule_to_close_timeout,
833
845
  ..Default::default()
834
846
  })
835
847
  .await;
836
- assert!(la_res.completed_ok());
848
+ if is_sched_to_start {
849
+ assert!(la_res.completed_ok());
850
+ } else {
851
+ assert_eq!(la_res.timed_out(), Some(TimeoutType::ScheduleToClose));
852
+ }
837
853
  Ok(().into())
838
854
  },
839
855
  );
@@ -854,64 +870,162 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time() {
854
870
  }
855
871
 
856
872
  #[tokio::test]
857
- async fn test_schedule_to_close_timeout_based_on_original_time() {
858
- // We used to carry over the schedule time of LAs from the "original" schedule time if these LAs
859
- // created newly after backing off across a timer. That was a mistake, since schedule-to-start
860
- // timeouts should apply to when the new attempt was scheduled. This test verifies we don't
861
- // time out on s-t-s timeouts because of that.
873
+ async fn wft_failure_cancels_running_las() {
862
874
  let mut t = TestHistoryBuilder::default();
863
- t.add_by_type(EventType::WorkflowExecutionStarted);
875
+ t.add_wfe_started_with_wft_timeout(Duration::from_millis(200));
864
876
  t.add_full_wf_task();
865
- let orig_sched = SystemTime::now().sub(Duration::from_secs(60 * 20));
866
- t.add_local_activity_marker(
867
- 1,
868
- "1",
869
- None,
870
- Some(Failure::application_failure("la failed".to_string(), false)),
871
- |deets| {
872
- // Really old schedule time, which should _not_ count against schedule_to_start
873
- deets.original_schedule_time = Some(orig_sched.into());
874
- // Backoff value must be present since we're simulating timer backoff
875
- deets.backoff = Some(prost_dur!(from_secs(100)));
876
- },
877
- );
878
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
877
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
879
878
  t.add_timer_fired(timer_started_event_id, "1".to_string());
880
879
  t.add_workflow_task_scheduled_and_started();
881
880
 
882
881
  let wf_id = "fakeid";
883
882
  let mock = mock_workflow_client();
884
- let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::AllHistory], mock);
883
+ let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2], mock);
884
+ mh.num_expected_fails = 1;
885
885
  let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
886
886
 
887
887
  worker.register_wf(
888
888
  DEFAULT_WORKFLOW_TYPE.to_owned(),
889
889
  |ctx: WfContext| async move {
890
- let la_res = ctx
891
- .local_activity(LocalActivityOptions {
892
- activity_type: "echo".to_string(),
893
- input: "hi".as_json_payload().expect("serializes fine"),
894
- retry_policy: RetryPolicy {
895
- initial_interval: Some(prost_dur!(from_millis(50))),
896
- backoff_coefficient: 1.2,
897
- maximum_interval: None,
898
- maximum_attempts: 5,
899
- non_retryable_error_types: vec![],
900
- },
901
- // This 10 minute timeout will have already elapsed according to the original
902
- // schedule time in the history.
903
- schedule_to_close_timeout: Some(Duration::from_secs(10 * 60)),
904
- ..Default::default()
905
- })
906
- .await;
907
- assert_eq!(la_res.timed_out(), Some(TimeoutType::ScheduleToClose));
890
+ let la_handle = ctx.local_activity(LocalActivityOptions {
891
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
892
+ input: "hi".as_json_payload().expect("serializes fine"),
893
+ ..Default::default()
894
+ });
895
+ tokio::join!(
896
+ async {
897
+ ctx.timer(Duration::from_secs(1)).await;
898
+ panic!("ahhh I'm failing wft")
899
+ },
900
+ la_handle
901
+ );
908
902
  Ok(().into())
909
903
  },
910
904
  );
905
+ worker.register_activity(
906
+ DEFAULT_ACTIVITY_TYPE,
907
+ move |ctx: ActContext, _: String| async move {
908
+ let res = tokio::time::timeout(Duration::from_millis(500), ctx.cancelled()).await;
909
+ if res.is_err() {
910
+ panic!("Activity must be cancelled!!!!");
911
+ }
912
+ Result::<(), _>::Err(ActivityCancelledError::default().into())
913
+ },
914
+ );
915
+ worker
916
+ .submit_wf(
917
+ wf_id.to_owned(),
918
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
919
+ vec![],
920
+ WorkflowOptions::default(),
921
+ )
922
+ .await
923
+ .unwrap();
924
+ worker.run_until_done().await.unwrap();
925
+ }
926
+
927
+ #[tokio::test]
928
+ async fn resolved_las_not_recorded_if_wft_fails_many_times() {
929
+ // We shouldn't record any LA results if the workflow activation is repeatedly failing. There
930
+ // was an issue that, because we stop reporting WFT failures after 2 tries, this meant the WFT
931
+ // was not marked as "completed" and the WFT could accidentally be replied to with LA results.
932
+ let mut t = TestHistoryBuilder::default();
933
+ t.add_by_type(EventType::WorkflowExecutionStarted);
934
+ t.add_workflow_task_scheduled_and_started();
935
+ t.add_workflow_task_failed_with_failure(
936
+ WorkflowTaskFailedCause::Unspecified,
937
+ Default::default(),
938
+ );
939
+ t.add_workflow_task_scheduled_and_started();
940
+
941
+ let wf_id = "fakeid";
942
+ let mock = mock_workflow_client();
943
+ let mut mh = MockPollCfg::from_resp_batches(
944
+ wf_id,
945
+ t,
946
+ [1.into(), ResponseType::AllHistory, ResponseType::AllHistory],
947
+ mock,
948
+ );
949
+ mh.num_expected_fails = 2;
950
+ mh.num_expected_completions = Some(0.into());
951
+ let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
952
+
953
+ worker.register_wf(
954
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
955
+ |ctx: WfContext| async move {
956
+ ctx.local_activity(LocalActivityOptions {
957
+ activity_type: "echo".to_string(),
958
+ input: "hi".as_json_payload().expect("serializes fine"),
959
+ ..Default::default()
960
+ })
961
+ .await;
962
+ panic!("Oh nooooo")
963
+ },
964
+ );
911
965
  worker.register_activity(
912
966
  "echo",
913
- move |_ctx: ActContext, _: String| async move { Ok(()) },
967
+ move |_: ActContext, _: String| async move { Ok(()) },
968
+ );
969
+ worker
970
+ .submit_wf(
971
+ wf_id.to_owned(),
972
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
973
+ vec![],
974
+ WorkflowOptions::default(),
975
+ )
976
+ .await
977
+ .unwrap();
978
+ worker.run_until_done().await.unwrap();
979
+ }
980
+
981
+ #[tokio::test]
982
+ async fn local_act_records_nonfirst_attempts_ok() {
983
+ let mut t = TestHistoryBuilder::default();
984
+ let wft_timeout = Duration::from_millis(200);
985
+ t.add_wfe_started_with_wft_timeout(wft_timeout);
986
+ t.add_full_wf_task();
987
+ t.add_full_wf_task();
988
+ t.add_full_wf_task();
989
+ t.add_workflow_task_scheduled_and_started();
990
+
991
+ let wf_id = "fakeid";
992
+ let mock = mock_workflow_client();
993
+ let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 3], mock);
994
+ let nonfirst_counts = Arc::new(SegQueue::new());
995
+ let nfc_c = nonfirst_counts.clone();
996
+ mh.completion_asserts = Some(Box::new(move |c| {
997
+ nfc_c.push(
998
+ c.metering_metadata
999
+ .nonfirst_local_activity_execution_attempts,
1000
+ );
1001
+ }));
1002
+ let mut worker = mock_sdk_cfg(mh, |wc| {
1003
+ wc.max_cached_workflows = 1;
1004
+ wc.max_outstanding_workflow_tasks = 1;
1005
+ });
1006
+
1007
+ worker.register_wf(
1008
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
1009
+ |ctx: WfContext| async move {
1010
+ ctx.local_activity(LocalActivityOptions {
1011
+ activity_type: "echo".to_string(),
1012
+ input: "hi".as_json_payload().expect("serializes fine"),
1013
+ retry_policy: RetryPolicy {
1014
+ initial_interval: Some(prost_dur!(from_millis(10))),
1015
+ backoff_coefficient: 1.0,
1016
+ maximum_interval: None,
1017
+ maximum_attempts: 0,
1018
+ non_retryable_error_types: vec![],
1019
+ },
1020
+ ..Default::default()
1021
+ })
1022
+ .await;
1023
+ Ok(().into())
1024
+ },
914
1025
  );
1026
+ worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
1027
+ Result::<(), _>::Err(anyhow!("I fail"))
1028
+ });
915
1029
  worker
916
1030
  .submit_wf(
917
1031
  wf_id.to_owned(),
@@ -922,4 +1036,11 @@ async fn test_schedule_to_close_timeout_based_on_original_time() {
922
1036
  .await
923
1037
  .unwrap();
924
1038
  worker.run_until_done().await.unwrap();
1039
+ // 3 workflow tasks
1040
+ assert_eq!(nonfirst_counts.len(), 3);
1041
+ // First task's non-first count should, of course, be 0
1042
+ assert_eq!(nonfirst_counts.pop().unwrap(), 0);
1043
+ // Next two, some nonzero amount which could vary based on test load
1044
+ assert!(nonfirst_counts.pop().unwrap() > 0);
1045
+ assert!(nonfirst_counts.pop().unwrap() > 0);
925
1046
  }
@@ -162,7 +162,7 @@ async fn new_queries(#[case] num_queries: usize) {
162
162
  pr.queries = HashMap::new();
163
163
  for i in 1..=num_queries {
164
164
  pr.queries.insert(
165
- format!("q{}", i),
165
+ format!("q{i}"),
166
166
  WorkflowQuery {
167
167
  query_type: "query-type".to_string(),
168
168
  query_args: Some(b"hi".into()),
@@ -206,7 +206,7 @@ async fn new_queries(#[case] num_queries: usize) {
206
206
  let mut qresults: Vec<_> = (1..=num_queries)
207
207
  .map(|i| {
208
208
  QueryResult {
209
- query_id: format!("q{}", i),
209
+ query_id: format!("q{i}"),
210
210
  variant: Some(
211
211
  QuerySuccess {
212
212
  response: Some(query_resp.into()),
@@ -769,11 +769,10 @@ async fn legacy_query_combined_with_timer_fire_repro() {
769
769
  t.add_by_type(EventType::WorkflowExecutionStarted);
770
770
  t.add_full_wf_task();
771
771
  let scheduled_event_id = t.add_activity_task_scheduled("1");
772
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
772
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
773
773
  t.add_timer_fired(timer_started_event_id, "1".to_string());
774
774
  t.add_full_wf_task();
775
775
  t.add(
776
- EventType::ActivityTaskCancelRequested,
777
776
  history_event::Attributes::ActivityTaskCancelRequestedEventAttributes(
778
777
  ActivityTaskCancelRequestedEventAttributes {
779
778
  scheduled_event_id,