@temporalio/core-bridge 1.13.0 → 1.13.2

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 (181) hide show
  1. package/Cargo.lock +239 -382
  2. package/Cargo.toml +11 -11
  3. package/lib/native.d.ts +10 -3
  4. package/package.json +3 -3
  5. package/releases/aarch64-apple-darwin/index.node +0 -0
  6. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  7. package/releases/x86_64-apple-darwin/index.node +0 -0
  8. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  9. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  10. package/sdk-core/.cargo/config.toml +71 -11
  11. package/sdk-core/.clippy.toml +1 -0
  12. package/sdk-core/.github/workflows/heavy.yml +2 -0
  13. package/sdk-core/.github/workflows/per-pr.yml +50 -18
  14. package/sdk-core/ARCHITECTURE.md +44 -48
  15. package/sdk-core/Cargo.toml +26 -7
  16. package/sdk-core/README.md +4 -0
  17. package/sdk-core/arch_docs/diagrams/TimerMachine_Coverage.puml +14 -0
  18. package/sdk-core/arch_docs/diagrams/initial_event_history.png +0 -0
  19. package/sdk-core/arch_docs/sdks_intro.md +299 -0
  20. package/sdk-core/client/Cargo.toml +8 -7
  21. package/sdk-core/client/src/callback_based.rs +1 -2
  22. package/sdk-core/client/src/lib.rs +485 -299
  23. package/sdk-core/client/src/metrics.rs +32 -8
  24. package/sdk-core/client/src/proxy.rs +124 -5
  25. package/sdk-core/client/src/raw.rs +598 -307
  26. package/sdk-core/client/src/replaceable.rs +253 -0
  27. package/sdk-core/client/src/retry.rs +9 -6
  28. package/sdk-core/client/src/worker_registry/mod.rs +19 -3
  29. package/sdk-core/client/src/workflow_handle/mod.rs +20 -17
  30. package/sdk-core/core/Cargo.toml +100 -31
  31. package/sdk-core/core/src/core_tests/activity_tasks.rs +55 -225
  32. package/sdk-core/core/src/core_tests/mod.rs +2 -8
  33. package/sdk-core/core/src/core_tests/queries.rs +3 -5
  34. package/sdk-core/core/src/core_tests/replay_flag.rs +3 -62
  35. package/sdk-core/core/src/core_tests/updates.rs +4 -5
  36. package/sdk-core/core/src/core_tests/workers.rs +4 -3
  37. package/sdk-core/core/src/core_tests/workflow_cancels.rs +10 -7
  38. package/sdk-core/core/src/core_tests/workflow_tasks.rs +28 -291
  39. package/sdk-core/core/src/ephemeral_server/mod.rs +15 -3
  40. package/sdk-core/core/src/internal_flags.rs +11 -1
  41. package/sdk-core/core/src/lib.rs +50 -36
  42. package/sdk-core/core/src/pollers/mod.rs +5 -5
  43. package/sdk-core/core/src/pollers/poll_buffer.rs +2 -2
  44. package/sdk-core/core/src/protosext/mod.rs +13 -5
  45. package/sdk-core/core/src/protosext/protocol_messages.rs +4 -11
  46. package/sdk-core/core/src/retry_logic.rs +256 -108
  47. package/sdk-core/core/src/telemetry/metrics.rs +1 -0
  48. package/sdk-core/core/src/telemetry/mod.rs +8 -2
  49. package/sdk-core/core/src/telemetry/prometheus_meter.rs +2 -2
  50. package/sdk-core/core/src/test_help/integ_helpers.rs +971 -0
  51. package/sdk-core/core/src/test_help/mod.rs +10 -1100
  52. package/sdk-core/core/src/test_help/unit_helpers.rs +218 -0
  53. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +42 -6
  54. package/sdk-core/core/src/worker/activities/local_activities.rs +19 -19
  55. package/sdk-core/core/src/worker/activities.rs +10 -3
  56. package/sdk-core/core/src/worker/client/mocks.rs +3 -3
  57. package/sdk-core/core/src/worker/client.rs +130 -93
  58. package/sdk-core/core/src/worker/heartbeat.rs +12 -13
  59. package/sdk-core/core/src/worker/mod.rs +31 -21
  60. package/sdk-core/core/src/worker/nexus.rs +14 -3
  61. package/sdk-core/core/src/worker/slot_provider.rs +9 -0
  62. package/sdk-core/core/src/worker/tuner.rs +159 -0
  63. package/sdk-core/core/src/worker/workflow/history_update.rs +3 -265
  64. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +1 -54
  65. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +0 -82
  66. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +0 -67
  67. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +1 -192
  68. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +0 -43
  69. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +6 -554
  70. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +0 -71
  71. package/sdk-core/core/src/worker/workflow/machines/nexus_operation_state_machine.rs +102 -3
  72. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +10 -539
  73. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +0 -139
  74. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +1 -119
  75. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +6 -63
  76. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +9 -4
  77. package/sdk-core/core/src/worker/workflow/mod.rs +5 -1
  78. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +8 -3
  79. package/sdk-core/core-api/Cargo.toml +4 -4
  80. package/sdk-core/core-api/src/envconfig.rs +153 -54
  81. package/sdk-core/core-api/src/lib.rs +68 -0
  82. package/sdk-core/core-api/src/telemetry/metrics.rs +2 -1
  83. package/sdk-core/core-api/src/telemetry.rs +13 -0
  84. package/sdk-core/core-c-bridge/Cargo.toml +13 -8
  85. package/sdk-core/core-c-bridge/include/temporal-sdk-core-c-bridge.h +184 -22
  86. package/sdk-core/core-c-bridge/src/client.rs +462 -184
  87. package/sdk-core/core-c-bridge/src/envconfig.rs +314 -0
  88. package/sdk-core/core-c-bridge/src/lib.rs +1 -0
  89. package/sdk-core/core-c-bridge/src/random.rs +4 -4
  90. package/sdk-core/core-c-bridge/src/runtime.rs +22 -23
  91. package/sdk-core/core-c-bridge/src/testing.rs +1 -4
  92. package/sdk-core/core-c-bridge/src/tests/context.rs +31 -31
  93. package/sdk-core/core-c-bridge/src/tests/mod.rs +32 -28
  94. package/sdk-core/core-c-bridge/src/tests/utils.rs +7 -7
  95. package/sdk-core/core-c-bridge/src/worker.rs +319 -66
  96. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +6 -1
  97. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.stderr +5 -5
  98. package/sdk-core/sdk/Cargo.toml +8 -2
  99. package/sdk-core/sdk/src/activity_context.rs +1 -1
  100. package/sdk-core/sdk/src/app_data.rs +1 -1
  101. package/sdk-core/sdk/src/interceptors.rs +1 -4
  102. package/sdk-core/sdk/src/lib.rs +1 -5
  103. package/sdk-core/sdk/src/workflow_context/options.rs +10 -1
  104. package/sdk-core/sdk/src/workflow_future.rs +1 -1
  105. package/sdk-core/sdk-core-protos/Cargo.toml +6 -6
  106. package/sdk-core/sdk-core-protos/build.rs +10 -23
  107. package/sdk-core/sdk-core-protos/protos/api_upstream/.github/workflows/create-release.yml +9 -1
  108. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +254 -5
  109. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +234 -5
  110. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +1 -1
  111. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/deployment/v1/message.proto +6 -0
  112. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -2
  113. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +60 -2
  114. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +30 -6
  115. package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +2 -0
  116. package/sdk-core/{test-utils → sdk-core-protos}/src/canned_histories.rs +5 -5
  117. package/sdk-core/sdk-core-protos/src/history_builder.rs +2 -2
  118. package/sdk-core/sdk-core-protos/src/lib.rs +25 -9
  119. package/sdk-core/sdk-core-protos/src/test_utils.rs +89 -0
  120. package/sdk-core/sdk-core-protos/src/utilities.rs +14 -5
  121. package/sdk-core/tests/c_bridge_smoke_test.c +10 -0
  122. package/sdk-core/tests/cloud_tests.rs +10 -8
  123. package/sdk-core/tests/common/http_proxy.rs +134 -0
  124. package/sdk-core/{test-utils/src/lib.rs → tests/common/mod.rs} +214 -281
  125. package/sdk-core/{test-utils/src → tests/common}/workflows.rs +4 -3
  126. package/sdk-core/tests/fuzzy_workflow.rs +1 -1
  127. package/sdk-core/tests/global_metric_tests.rs +8 -7
  128. package/sdk-core/tests/heavy_tests.rs +7 -3
  129. package/sdk-core/tests/integ_tests/client_tests.rs +111 -24
  130. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +14 -9
  131. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +4 -4
  132. package/sdk-core/tests/integ_tests/metrics_tests.rs +114 -14
  133. package/sdk-core/tests/integ_tests/pagination_tests.rs +273 -0
  134. package/sdk-core/tests/integ_tests/polling_tests.rs +311 -93
  135. package/sdk-core/tests/integ_tests/queries_tests.rs +4 -4
  136. package/sdk-core/tests/integ_tests/update_tests.rs +13 -7
  137. package/sdk-core/tests/integ_tests/visibility_tests.rs +26 -9
  138. package/sdk-core/tests/integ_tests/worker_tests.rs +668 -13
  139. package/sdk-core/tests/integ_tests/worker_versioning_tests.rs +40 -24
  140. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +244 -11
  141. package/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +1 -1
  142. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +78 -2
  143. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +61 -2
  144. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +465 -7
  145. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +41 -2
  146. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +315 -3
  147. package/sdk-core/tests/integ_tests/workflow_tests/eager.rs +1 -1
  148. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1990 -14
  149. package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +65 -2
  150. package/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +123 -23
  151. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +525 -3
  152. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +65 -16
  153. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +32 -23
  154. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +126 -5
  155. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +1 -2
  156. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +124 -8
  157. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +62 -2
  158. package/sdk-core/tests/integ_tests/workflow_tests.rs +67 -8
  159. package/sdk-core/tests/main.rs +26 -17
  160. package/sdk-core/tests/manual_tests.rs +5 -1
  161. package/sdk-core/tests/runner.rs +22 -40
  162. package/sdk-core/tests/shared_tests/mod.rs +1 -1
  163. package/sdk-core/tests/shared_tests/priority.rs +1 -1
  164. package/sdk-core/{core/benches/workflow_replay.rs → tests/workflow_replay_bench.rs} +10 -5
  165. package/src/client.rs +97 -20
  166. package/src/helpers/callbacks.rs +4 -4
  167. package/src/helpers/errors.rs +7 -1
  168. package/src/helpers/handles.rs +1 -0
  169. package/src/helpers/try_from_js.rs +4 -3
  170. package/src/lib.rs +3 -2
  171. package/src/metrics.rs +3 -0
  172. package/src/runtime.rs +5 -2
  173. package/src/worker.rs +9 -12
  174. package/ts/native.ts +13 -3
  175. package/sdk-core/arch_docs/diagrams/workflow_internals.svg +0 -1
  176. package/sdk-core/core/src/core_tests/child_workflows.rs +0 -281
  177. package/sdk-core/core/src/core_tests/determinism.rs +0 -318
  178. package/sdk-core/core/src/core_tests/local_activities.rs +0 -1442
  179. package/sdk-core/test-utils/Cargo.toml +0 -38
  180. package/sdk-core/test-utils/src/histfetch.rs +0 -28
  181. package/sdk-core/test-utils/src/interceptors.rs +0 -46
@@ -1,20 +1,48 @@
1
+ use crate::common::{CoreWfStarter, build_fake_sdk, mock_sdk, mock_sdk_cfg};
1
2
  use anyhow::anyhow;
2
3
  use assert_matches::assert_matches;
3
4
  use std::time::Duration;
4
5
  use temporal_client::{WorkflowClientTrait, WorkflowOptions};
5
- use temporal_sdk::{ChildWorkflowOptions, WfContext, WfExitValue, WorkflowResult};
6
+ use temporal_sdk::{
7
+ CancellableFuture, ChildWorkflowOptions, Signal, WfContext, WfExitValue, WorkflowResult,
8
+ };
9
+ use temporal_sdk_core::{
10
+ replay::DEFAULT_WORKFLOW_TYPE,
11
+ test_help::{MockPollCfg, ResponseType, mock_worker, mock_worker_client, single_hist_mock_sg},
12
+ };
13
+ use temporal_sdk_core_api::Worker;
6
14
  use temporal_sdk_core_protos::{
15
+ TestHistoryBuilder, canned_histories,
7
16
  coresdk::{
8
17
  AsJsonPayloadExt,
9
- child_workflow::{ChildWorkflowCancellationType, Success, child_workflow_result},
18
+ child_workflow::{
19
+ ChildWorkflowCancellationType, StartChildWorkflowExecutionFailedCause, Success,
20
+ child_workflow_result,
21
+ },
22
+ workflow_activation::{
23
+ WorkflowActivationJob, resolve_child_workflow_execution_start::Status as StartStatus,
24
+ workflow_activation_job,
25
+ },
26
+ workflow_commands::{
27
+ CancelChildWorkflowExecution, CompleteWorkflowExecution, StartChildWorkflowExecution,
28
+ },
29
+ workflow_completion::WorkflowActivationCompletion,
30
+ },
31
+ temporal::api::{
32
+ common::v1::Payload,
33
+ enums::v1::{CommandType, EventType, ParentClosePolicy},
34
+ history::v1::{
35
+ StartChildWorkflowExecutionFailedEventAttributes,
36
+ StartChildWorkflowExecutionInitiatedEventAttributes,
37
+ },
38
+ sdk::v1::UserMetadata,
10
39
  },
11
- temporal::api::enums::v1::ParentClosePolicy,
12
40
  };
13
- use temporal_sdk_core_test_utils::CoreWfStarter;
14
- use tokio::sync::Barrier;
41
+ use tokio::{join, sync::Barrier};
15
42
 
16
43
  static PARENT_WF_TYPE: &str = "parent_wf";
17
44
  static CHILD_WF_TYPE: &str = "child_wf";
45
+ const SIGNAME: &str = "SIGNAME";
18
46
 
19
47
  async fn child_wf(ctx: WfContext) -> WorkflowResult<()> {
20
48
  assert_eq!(
@@ -32,7 +60,7 @@ async fn child_wf(ctx: WfContext) -> WorkflowResult<()> {
32
60
  Ok(().into())
33
61
  }
34
62
 
35
- async fn parent_wf(ctx: WfContext) -> WorkflowResult<()> {
63
+ async fn happy_parent(ctx: WfContext) -> WorkflowResult<()> {
36
64
  let child = ctx.child_workflow(ChildWorkflowOptions {
37
65
  workflow_id: "child-1".to_owned(),
38
66
  workflow_type: CHILD_WF_TYPE.to_owned(),
@@ -56,7 +84,7 @@ async fn child_workflow_happy_path() {
56
84
  starter.worker_config.no_remote_activities(true);
57
85
  let mut worker = starter.worker().await;
58
86
 
59
- worker.register_wf(PARENT_WF_TYPE.to_string(), parent_wf);
87
+ worker.register_wf(PARENT_WF_TYPE.to_string(), happy_parent);
60
88
  worker.register_wf(CHILD_WF_TYPE.to_string(), child_wf);
61
89
 
62
90
  worker
@@ -244,3 +272,433 @@ async fn cancelled_child_gets_reason() {
244
272
  starter.start_with_worker(wf_name, &mut worker).await;
245
273
  worker.run_until_done().await.unwrap();
246
274
  }
275
+
276
+ #[rstest::rstest]
277
+ #[case::signal_then_result(true)]
278
+ #[case::signal_and_result_concurrent(false)]
279
+ #[tokio::test]
280
+ async fn signal_child_workflow(#[case] serial: bool) {
281
+ let wf_id = "fakeid";
282
+ let wf_type = DEFAULT_WORKFLOW_TYPE;
283
+ let t = canned_histories::single_child_workflow_signaled("child-id-1", SIGNAME);
284
+ let mock = mock_worker_client();
285
+ let mut worker = mock_sdk(MockPollCfg::from_resp_batches(
286
+ wf_id,
287
+ t,
288
+ [ResponseType::AllHistory],
289
+ mock,
290
+ ));
291
+
292
+ let wf = move |ctx: WfContext| async move {
293
+ let child = ctx.child_workflow(ChildWorkflowOptions {
294
+ workflow_id: "child-id-1".to_string(),
295
+ workflow_type: "child".to_string(),
296
+ ..Default::default()
297
+ });
298
+
299
+ let start_res = child
300
+ .start(&ctx)
301
+ .await
302
+ .into_started()
303
+ .expect("Child should get started");
304
+ let (sigres, res) = if serial {
305
+ let sigres = start_res.signal(&ctx, Signal::new(SIGNAME, [b"Hi!"])).await;
306
+ let res = start_res.result().await;
307
+ (sigres, res)
308
+ } else {
309
+ let sigfut = start_res.signal(&ctx, Signal::new(SIGNAME, [b"Hi!"]));
310
+ let resfut = start_res.result();
311
+ join!(sigfut, resfut)
312
+ };
313
+ sigres.expect("signal result is ok");
314
+ res.status.expect("child wf result is ok");
315
+ Ok(().into())
316
+ };
317
+
318
+ worker.register_wf(wf_type.to_owned(), wf);
319
+ worker
320
+ .submit_wf(
321
+ wf_id.to_owned(),
322
+ wf_type.to_owned(),
323
+ vec![],
324
+ WorkflowOptions::default(),
325
+ )
326
+ .await
327
+ .unwrap();
328
+ worker.run_until_done().await.unwrap();
329
+ }
330
+
331
+ async fn parent_cancels_child_wf(ctx: WfContext) -> WorkflowResult<()> {
332
+ let child = ctx.child_workflow(ChildWorkflowOptions {
333
+ workflow_id: "child-id-1".to_string(),
334
+ workflow_type: "child".to_string(),
335
+ cancel_type: ChildWorkflowCancellationType::WaitCancellationCompleted,
336
+ ..Default::default()
337
+ });
338
+
339
+ let start_res = child
340
+ .start(&ctx)
341
+ .await
342
+ .into_started()
343
+ .expect("Child should get started");
344
+ start_res.cancel(&ctx, "cancel reason".to_string());
345
+ let stat = start_res
346
+ .result()
347
+ .await
348
+ .status
349
+ .expect("child wf result is ok");
350
+ assert_matches!(stat, child_workflow_result::Status::Cancelled(_));
351
+ Ok(().into())
352
+ }
353
+
354
+ #[tokio::test]
355
+ async fn cancel_child_workflow() {
356
+ let t = canned_histories::single_child_workflow_cancelled("child-id-1");
357
+ let mut worker = build_fake_sdk(MockPollCfg::from_resps(t, [ResponseType::AllHistory]));
358
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, parent_cancels_child_wf);
359
+ worker.run().await.unwrap();
360
+ }
361
+
362
+ #[rstest::rstest]
363
+ #[case::abandon(ChildWorkflowCancellationType::Abandon)]
364
+ #[case::try_cancel(ChildWorkflowCancellationType::TryCancel)]
365
+ #[case::wait_cancel_completed(ChildWorkflowCancellationType::WaitCancellationCompleted)]
366
+ #[tokio::test]
367
+ async fn cancel_child_workflow_lang_thinks_not_started_but_is(
368
+ #[case] cancellation_type: ChildWorkflowCancellationType,
369
+ ) {
370
+ // Since signal handlers always run first, it's possible lang might try to cancel
371
+ // a child workflow it thinks isn't started, but we've told it is in the same activation.
372
+ // It would be annoying for lang to have to peek ahead at jobs to be consistent in that case.
373
+ let t = match cancellation_type {
374
+ ChildWorkflowCancellationType::Abandon => {
375
+ canned_histories::single_child_workflow_abandon_cancelled("child-id-1")
376
+ }
377
+ ChildWorkflowCancellationType::TryCancel => {
378
+ canned_histories::single_child_workflow_try_cancelled("child-id-1")
379
+ }
380
+ _ => canned_histories::single_child_workflow_cancelled("child-id-1"),
381
+ };
382
+ let mock = mock_worker_client();
383
+ let mock = single_hist_mock_sg("fakeid", t, [ResponseType::AllHistory], mock, true);
384
+ let core = mock_worker(mock);
385
+ let act = core.poll_workflow_activation().await.unwrap();
386
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
387
+ act.run_id,
388
+ StartChildWorkflowExecution {
389
+ seq: 1,
390
+ cancellation_type: cancellation_type as i32,
391
+ ..Default::default()
392
+ }
393
+ .into(),
394
+ ))
395
+ .await
396
+ .unwrap();
397
+ let act = core.poll_workflow_activation().await.unwrap();
398
+ assert_matches!(
399
+ act.jobs.as_slice(),
400
+ [WorkflowActivationJob {
401
+ variant: Some(workflow_activation_job::Variant::ResolveChildWorkflowExecutionStart(_)),
402
+ }]
403
+ );
404
+ // Issue the cancel command
405
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
406
+ act.run_id,
407
+ CancelChildWorkflowExecution {
408
+ child_workflow_seq: 1,
409
+ reason: "dieee".to_string(),
410
+ }
411
+ .into(),
412
+ ))
413
+ .await
414
+ .unwrap();
415
+ let act = core.poll_workflow_activation().await.unwrap();
416
+ // Make sure that a resolve for the "request cancel external workflow" command does *not* appear
417
+ // since lang didn't actually issue one. The only job should be resolving the child workflow.
418
+ assert_matches!(
419
+ act.jobs.as_slice(),
420
+ [WorkflowActivationJob {
421
+ variant: Some(workflow_activation_job::Variant::ResolveChildWorkflowExecution(_)),
422
+ }]
423
+ );
424
+ // Request cancel external is technically fallible, but the only reasons relate to targeting
425
+ // a not-found workflow, which couldn't happen in this case.
426
+ }
427
+
428
+ #[tokio::test]
429
+ async fn cancel_already_complete_child_ignored() {
430
+ let t = canned_histories::single_child_workflow("child-id-1");
431
+ let mock = mock_worker_client();
432
+ let mock = single_hist_mock_sg("fakeid", t, [ResponseType::AllHistory], mock, true);
433
+ let core = mock_worker(mock);
434
+ let act = core.poll_workflow_activation().await.unwrap();
435
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
436
+ act.run_id,
437
+ StartChildWorkflowExecution {
438
+ seq: 1,
439
+ ..Default::default()
440
+ }
441
+ .into(),
442
+ ))
443
+ .await
444
+ .unwrap();
445
+ let act = core.poll_workflow_activation().await.unwrap();
446
+ assert_matches!(
447
+ act.jobs.as_slice(),
448
+ [WorkflowActivationJob {
449
+ variant: Some(workflow_activation_job::Variant::ResolveChildWorkflowExecutionStart(_)),
450
+ }]
451
+ );
452
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(act.run_id))
453
+ .await
454
+ .unwrap();
455
+ let act = core.poll_workflow_activation().await.unwrap();
456
+ assert_matches!(
457
+ act.jobs.as_slice(),
458
+ [WorkflowActivationJob {
459
+ variant: Some(workflow_activation_job::Variant::ResolveChildWorkflowExecution(_)),
460
+ }]
461
+ );
462
+ // Try to cancel post-completion, it should be ignored. Also complete the wf.
463
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
464
+ act.run_id,
465
+ vec![
466
+ CancelChildWorkflowExecution {
467
+ child_workflow_seq: 1,
468
+ reason: "go away!".to_string(),
469
+ }
470
+ .into(),
471
+ CompleteWorkflowExecution { result: None }.into(),
472
+ ],
473
+ ))
474
+ .await
475
+ .unwrap();
476
+ }
477
+
478
+ #[tokio::test]
479
+ async fn pass_child_workflow_summary_to_metadata() {
480
+ let wf_id = "1";
481
+ let wf_type = DEFAULT_WORKFLOW_TYPE;
482
+ let t = canned_histories::single_child_workflow(wf_id);
483
+ let mut mock_cfg = MockPollCfg::from_hist_builder(t);
484
+ let expected_user_metadata = Some(UserMetadata {
485
+ summary: Some(b"child summary".into()),
486
+ details: Some(b"child details".into()),
487
+ });
488
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
489
+ asserts
490
+ .then(move |wft| {
491
+ assert_eq!(wft.commands.len(), 1);
492
+ assert_eq!(
493
+ wft.commands[0].command_type(),
494
+ CommandType::StartChildWorkflowExecution
495
+ );
496
+ assert_eq!(wft.commands[0].user_metadata, expected_user_metadata)
497
+ })
498
+ .then(move |wft| {
499
+ assert_eq!(wft.commands.len(), 1);
500
+ assert_eq!(
501
+ wft.commands[0].command_type(),
502
+ CommandType::CompleteWorkflowExecution
503
+ );
504
+ });
505
+ });
506
+
507
+ let mut worker = mock_sdk_cfg(mock_cfg, |_| {});
508
+ worker.register_wf(wf_type, move |ctx: WfContext| async move {
509
+ ctx.child_workflow(ChildWorkflowOptions {
510
+ workflow_id: wf_id.to_string(),
511
+ workflow_type: "child".to_string(),
512
+ static_summary: Some("child summary".to_string()),
513
+ static_details: Some("child details".to_string()),
514
+ ..Default::default()
515
+ })
516
+ .start(&ctx)
517
+ .await;
518
+ Ok(().into())
519
+ });
520
+ worker
521
+ .submit_wf(
522
+ wf_id.to_owned(),
523
+ wf_type.to_owned(),
524
+ vec![],
525
+ WorkflowOptions::default(),
526
+ )
527
+ .await
528
+ .unwrap();
529
+ worker.run_until_done().await.unwrap();
530
+ }
531
+
532
+ #[derive(Clone, Copy)]
533
+ enum Expectation {
534
+ Success,
535
+ Failure,
536
+ StartFailure,
537
+ }
538
+
539
+ impl Expectation {
540
+ const fn try_from_u8(x: u8) -> Option<Self> {
541
+ Some(match x {
542
+ 0 => Self::Success,
543
+ 1 => Self::Failure,
544
+ 2 => Self::StartFailure,
545
+ _ => return None,
546
+ })
547
+ }
548
+ }
549
+
550
+ #[fixture]
551
+ fn child_workflow_happy_hist() -> MockPollCfg {
552
+ let mut t = canned_histories::single_child_workflow("child-id-1");
553
+ t.set_wf_input(Payload::from([Expectation::Success as u8]));
554
+ MockPollCfg::from_hist_builder(t)
555
+ }
556
+
557
+ #[fixture]
558
+ fn child_workflow_fail_hist() -> MockPollCfg {
559
+ let mut t = canned_histories::single_child_workflow_fail("child-id-1");
560
+ t.set_wf_input(Payload::from([Expectation::Failure as u8]));
561
+ MockPollCfg::from_hist_builder(t)
562
+ }
563
+
564
+ async fn parent_wf(ctx: WfContext) -> WorkflowResult<()> {
565
+ let expectation = Expectation::try_from_u8(ctx.get_args()[0].data[0]).unwrap();
566
+ let child = ctx.child_workflow(ChildWorkflowOptions {
567
+ workflow_id: "child-id-1".to_string(),
568
+ workflow_type: "child".to_string(),
569
+ ..Default::default()
570
+ });
571
+
572
+ let start_res = child.start(&ctx).await;
573
+ match (expectation, &start_res.status) {
574
+ (Expectation::Success | Expectation::Failure, StartStatus::Succeeded(_)) => {}
575
+ (Expectation::StartFailure, StartStatus::Failed(_)) => return Ok(().into()),
576
+ _ => return Err(anyhow!("Unexpected start status")),
577
+ };
578
+ match (
579
+ expectation,
580
+ start_res.into_started().unwrap().result().await.status,
581
+ ) {
582
+ (Expectation::Success, Some(child_workflow_result::Status::Completed(_))) => Ok(().into()),
583
+ (Expectation::Failure, _) => Ok(().into()),
584
+ _ => Err(anyhow!("Unexpected child WF status")),
585
+ }
586
+ }
587
+
588
+ #[rstest(
589
+ mock_cfg,
590
+ case::success(child_workflow_happy_hist()),
591
+ case::failure(child_workflow_fail_hist())
592
+ )]
593
+ #[tokio::test]
594
+ async fn single_child_workflow_until_completion(mut mock_cfg: MockPollCfg) {
595
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
596
+ asserts
597
+ .then(|wft| {
598
+ assert_eq!(wft.commands.len(), 1);
599
+ assert_matches!(
600
+ wft.commands[0].command_type(),
601
+ CommandType::StartChildWorkflowExecution
602
+ );
603
+ })
604
+ .then(move |wft| {
605
+ assert_eq!(wft.commands.len(), 0);
606
+ })
607
+ .then(move |wft| {
608
+ assert_eq!(wft.commands.len(), 1);
609
+ assert_matches!(
610
+ wft.commands[0].command_type(),
611
+ CommandType::CompleteWorkflowExecution
612
+ );
613
+ });
614
+ });
615
+
616
+ let mut worker = build_fake_sdk(mock_cfg);
617
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, parent_wf);
618
+ worker.run().await.unwrap();
619
+ }
620
+
621
+ #[tokio::test]
622
+ async fn single_child_workflow_start_fail() {
623
+ let child_wf_id = "child-id-1";
624
+ let mut t = TestHistoryBuilder::default();
625
+ t.add_by_type(EventType::WorkflowExecutionStarted);
626
+ t.set_wf_input(Payload::from([Expectation::StartFailure as u8]));
627
+ t.add_full_wf_task();
628
+ let initiated_event_id = t.add(StartChildWorkflowExecutionInitiatedEventAttributes {
629
+ workflow_id: child_wf_id.to_owned(),
630
+ workflow_type: Some("child".into()),
631
+ ..Default::default()
632
+ });
633
+ t.add(StartChildWorkflowExecutionFailedEventAttributes {
634
+ workflow_id: child_wf_id.to_owned(),
635
+ initiated_event_id,
636
+ cause: StartChildWorkflowExecutionFailedCause::WorkflowAlreadyExists as i32,
637
+ ..Default::default()
638
+ });
639
+ t.add_full_wf_task();
640
+ t.add_workflow_execution_completed();
641
+
642
+ let mut mock_cfg = MockPollCfg::from_hist_builder(t);
643
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
644
+ asserts
645
+ .then(|wft| {
646
+ assert_eq!(wft.commands.len(), 1);
647
+ assert_matches!(
648
+ wft.commands[0].command_type(),
649
+ CommandType::StartChildWorkflowExecution
650
+ );
651
+ })
652
+ .then(move |wft| {
653
+ assert_eq!(wft.commands.len(), 1);
654
+ assert_matches!(
655
+ wft.commands[0].command_type(),
656
+ CommandType::CompleteWorkflowExecution
657
+ );
658
+ });
659
+ });
660
+
661
+ let mut worker = build_fake_sdk(mock_cfg);
662
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, parent_wf);
663
+ worker.run().await.unwrap();
664
+ }
665
+
666
+ async fn cancel_before_send_wf(ctx: WfContext) -> WorkflowResult<()> {
667
+ let workflow_id = "child-id-1";
668
+ let child = ctx.child_workflow(ChildWorkflowOptions {
669
+ workflow_id: workflow_id.to_string(),
670
+ workflow_type: "child".to_string(),
671
+ ..Default::default()
672
+ });
673
+ let start = child.start(&ctx);
674
+ start.cancel(&ctx);
675
+ match start.await.status {
676
+ StartStatus::Cancelled(_) => Ok(().into()),
677
+ _ => Err(anyhow!("Unexpected start status")),
678
+ }
679
+ }
680
+
681
+ #[tokio::test]
682
+ async fn single_child_workflow_cancel_before_sent() {
683
+ let mut t = TestHistoryBuilder::default();
684
+ t.add_by_type(EventType::WorkflowExecutionStarted);
685
+ t.add_full_wf_task();
686
+ t.add_workflow_execution_completed();
687
+
688
+ let mut mock_cfg = MockPollCfg::from_hist_builder(t);
689
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
690
+ asserts.then(move |wft| {
691
+ // Workflow starts and cancels the child workflow, no commands should be sent besides
692
+ // workflow completion
693
+ assert_eq!(wft.commands.len(), 1);
694
+ assert_matches!(
695
+ wft.commands[0].command_type(),
696
+ CommandType::CompleteWorkflowExecution
697
+ );
698
+ });
699
+ });
700
+
701
+ let mut worker = build_fake_sdk(mock_cfg);
702
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, cancel_before_send_wf);
703
+ worker.run().await.unwrap();
704
+ }
@@ -1,8 +1,13 @@
1
+ use crate::common::{CoreWfStarter, build_fake_sdk};
1
2
  use std::time::Duration;
2
3
  use temporal_client::WorkflowOptions;
3
4
  use temporal_sdk::{WfContext, WfExitValue, WorkflowResult};
4
- use temporal_sdk_core_protos::coresdk::workflow_commands::ContinueAsNewWorkflowExecution;
5
- use temporal_sdk_core_test_utils::CoreWfStarter;
5
+ use temporal_sdk_core::test_help::MockPollCfg;
6
+ use temporal_sdk_core_protos::{
7
+ DEFAULT_WORKFLOW_TYPE, canned_histories,
8
+ coresdk::workflow_commands::ContinueAsNewWorkflowExecution,
9
+ temporal::api::enums::v1::CommandType,
10
+ };
6
11
 
7
12
  async fn continue_as_new_wf(ctx: WfContext) -> WorkflowResult<()> {
8
13
  let run_ct = ctx.get_args()[0].data[0];
@@ -63,3 +68,37 @@ async fn continue_as_new_multiple_concurrent() {
63
68
  }
64
69
  worker.run_until_done().await.unwrap();
65
70
  }
71
+
72
+ async fn wf_with_timer(ctx: WfContext) -> WorkflowResult<()> {
73
+ ctx.timer(Duration::from_millis(500)).await;
74
+ Ok(WfExitValue::continue_as_new(
75
+ ContinueAsNewWorkflowExecution {
76
+ arguments: vec![[1].into()],
77
+ ..Default::default()
78
+ },
79
+ ))
80
+ }
81
+
82
+ #[tokio::test]
83
+ async fn wf_completing_with_continue_as_new() {
84
+ let t = canned_histories::timer_then_continue_as_new("1");
85
+ let mut mock_cfg = MockPollCfg::from_hist_builder(t);
86
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
87
+ asserts
88
+ .then(|wft| {
89
+ assert_eq!(wft.commands.len(), 1);
90
+ assert_matches!(wft.commands[0].command_type(), CommandType::StartTimer);
91
+ })
92
+ .then(move |wft| {
93
+ assert_eq!(wft.commands.len(), 1);
94
+ assert_matches!(
95
+ wft.commands[0].command_type(),
96
+ CommandType::ContinueAsNewWorkflowExecution
97
+ );
98
+ });
99
+ });
100
+
101
+ let mut worker = build_fake_sdk(mock_cfg);
102
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, wf_with_timer);
103
+ worker.run().await.unwrap();
104
+ }