@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,7 +1,16 @@
1
+ use crate::common::{CoreWfStarter, build_fake_sdk};
1
2
  use temporal_client::WorkflowClientTrait;
2
3
  use temporal_sdk::{WfContext, WorkflowResult};
3
- use temporal_sdk_core_protos::coresdk::{AsJsonPayloadExt, FromJsonPayloadExt};
4
- use temporal_sdk_core_test_utils::CoreWfStarter;
4
+ use temporal_sdk_core::test_help::MockPollCfg;
5
+ use temporal_sdk_core_protos::{
6
+ DEFAULT_WORKFLOW_TYPE, TestHistoryBuilder,
7
+ coresdk::{AsJsonPayloadExt, FromJsonPayloadExt},
8
+ temporal::api::{
9
+ command::v1::{Command, command},
10
+ common::v1::Payload,
11
+ enums::v1::EventType,
12
+ },
13
+ };
5
14
  use uuid::Uuid;
6
15
 
7
16
  static FIELD_A: &str = "cat_name";
@@ -49,3 +58,57 @@ async fn sends_modify_wf_props() {
49
58
  assert_eq!("enchi", String::from_json_payload(catname).unwrap());
50
59
  assert_eq!(9001, usize::from_json_payload(cuteness).unwrap());
51
60
  }
61
+
62
+ #[tokio::test]
63
+ async fn workflow_modify_props() {
64
+ let mut t = TestHistoryBuilder::default();
65
+ t.add_by_type(EventType::WorkflowExecutionStarted);
66
+ t.add_full_wf_task();
67
+ t.add_workflow_execution_completed();
68
+
69
+ let (k1, k2) = ("foo", "bar");
70
+
71
+ let mut mock_cfg = MockPollCfg::from_hist_builder(t);
72
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
73
+ asserts.then(|wft| {
74
+ assert_matches!(
75
+ wft.commands.as_slice(),
76
+ [Command {
77
+ attributes: Some(
78
+ command::Attributes::ModifyWorkflowPropertiesCommandAttributes(msg)
79
+ ),
80
+ ..
81
+ }, ..] => {
82
+ let fields = &msg.upserted_memo.as_ref().unwrap().fields;
83
+ let payload1 = fields.get(k1).unwrap();
84
+ let payload2 = fields.get(k2).unwrap();
85
+ assert_eq!(payload1.data[0], 0x01);
86
+ assert_eq!(payload2.data[0], 0x02);
87
+ assert_eq!(fields.len(), 2);
88
+ }
89
+ );
90
+ });
91
+ });
92
+
93
+ let mut worker = build_fake_sdk(mock_cfg);
94
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, move |ctx: WfContext| async move {
95
+ ctx.upsert_memo([
96
+ (
97
+ String::from(k1),
98
+ Payload {
99
+ data: vec![0x01],
100
+ ..Default::default()
101
+ },
102
+ ),
103
+ (
104
+ String::from(k2),
105
+ Payload {
106
+ data: vec![0x02],
107
+ ..Default::default()
108
+ },
109
+ ),
110
+ ]);
111
+ Ok(().into())
112
+ });
113
+ worker.run().await.unwrap();
114
+ }
@@ -1,7 +1,16 @@
1
- use crate::integ_tests::mk_nexus_endpoint;
1
+ use crate::{
2
+ common::{CoreWfStarter, WorkflowHandleExt, rand_6_chars},
3
+ integ_tests::mk_nexus_endpoint,
4
+ };
2
5
  use anyhow::bail;
3
6
  use assert_matches::assert_matches;
4
- use std::time::Duration;
7
+ use std::{
8
+ sync::{
9
+ Arc,
10
+ atomic::{AtomicBool, Ordering},
11
+ },
12
+ time::Duration,
13
+ };
5
14
  use temporal_client::{WfClientExt, WorkflowClientTrait, WorkflowOptions};
6
15
  use temporal_sdk::{CancellableFuture, NexusOperationOptions, WfContext, WfExitValue};
7
16
  use temporal_sdk_core_api::errors::PollError;
@@ -24,11 +33,11 @@ use temporal_sdk_core_protos::{
24
33
  },
25
34
  },
26
35
  };
27
- use temporal_sdk_core_test_utils::{CoreWfStarter, WorkflowHandleExt, rand_6_chars};
28
36
  use tokio::{
29
37
  join,
30
38
  sync::{mpsc, watch},
31
39
  };
40
+ use tokio_stream::StreamExt;
32
41
 
33
42
  #[derive(Debug, PartialEq, Eq, Clone, Copy)]
34
43
  enum Outcome {
@@ -249,6 +258,16 @@ async fn nexus_async(
249
258
  let client = starter.get_client().await.get_client().clone();
250
259
  let nexus_task_handle = async {
251
260
  let mut nt = core_worker.poll_nexus_task().await.unwrap().unwrap_task();
261
+ // Verify request header key for timeout exists and is lowercase
262
+ if outcome == Outcome::Timeout {
263
+ assert!(
264
+ nt.request
265
+ .as_ref()
266
+ .unwrap()
267
+ .header
268
+ .contains_key("request-timeout")
269
+ );
270
+ }
252
271
  let start_req = assert_matches!(
253
272
  nt.request.unwrap().variant.unwrap(),
254
273
  request::Variant::StartOperation(sr) => sr
@@ -557,10 +576,10 @@ async fn nexus_cancellation_types(
557
576
  let endpoint = mk_nexus_endpoint(&mut starter).await;
558
577
  let schedule_to_close_timeout = Some(Duration::from_secs(5));
559
578
 
560
- let (cancel_call_completion_tx, cancel_call_completion_rx) = watch::channel(false);
579
+ let (caller_op_future_tx, caller_op_future_rx) = watch::channel(false);
561
580
  worker.register_wf(wf_name.to_owned(), move |ctx: WfContext| {
562
581
  let endpoint = endpoint.clone();
563
- let cancel_call_completion_tx = cancel_call_completion_tx.clone();
582
+ let caller_op_future_tx = caller_op_future_tx.clone();
564
583
  async move {
565
584
  let options = NexusOperationOptions {
566
585
  endpoint,
@@ -577,42 +596,62 @@ async fn nexus_cancellation_types(
577
596
  started.cancel(&ctx);
578
597
 
579
598
  let res = result.await;
580
- cancel_call_completion_tx.send(true).unwrap();
599
+ caller_op_future_tx.send(true).unwrap();
581
600
 
582
- // Make sure cancel after completion doesn't cause problems
601
+ // Make sure cancel after op completion doesn't cause problems
583
602
  started.cancel(&ctx);
584
603
 
585
604
  // We need to wait slightly so that the workflow is not complete at the same time
586
- // cancellation is invoked. If it does, the caller workflow will close and the server won't attempt to send the cancellation to the handler
605
+ // cancellation is invoked. If it does, the caller workflow will close and the server
606
+ // won't attempt to send the cancellation to the handler
587
607
  ctx.timer(Duration::from_millis(1)).await;
588
608
  Ok(res.into())
589
609
  }
590
610
  });
591
611
 
592
- let (cancellation_wait_tx, cancellation_wait_rx) = watch::channel(false);
612
+ let cancellation_wait_happened = Arc::new(AtomicBool::new(false));
613
+ let cancellation_wait_happened_clone = cancellation_wait_happened.clone();
593
614
  let (cancellation_tx, mut cancellation_rx) = watch::channel(false);
615
+ let (handler_exited_tx, mut handler_exited_rx) = watch::channel(false);
594
616
  worker.register_wf("async_completer".to_owned(), move |ctx: WfContext| {
595
617
  let cancellation_tx = cancellation_tx.clone();
596
- let mut cancellation_wait_rx = cancellation_wait_rx.clone();
618
+ let cancellation_wait_happened = cancellation_wait_happened_clone.clone();
619
+ let handler_exited_tx = handler_exited_tx.clone();
597
620
  async move {
621
+ // Wait for cancellation
598
622
  ctx.cancelled().await;
599
623
  cancellation_tx.send(true).unwrap();
624
+
600
625
  if cancellation_type == NexusOperationCancellationType::WaitCancellationCompleted {
601
- cancellation_wait_rx.changed().await.unwrap();
626
+ ctx.wait_condition(|| cancellation_wait_happened.load(Ordering::Relaxed))
627
+ .await;
628
+ } else if cancellation_type == NexusOperationCancellationType::WaitCancellationRequested
629
+ {
630
+ // For WAIT_REQUESTED, wait until the caller nexus op future has been resolved. This
631
+ // allows the test to verify that it resolved due to
632
+ // NexusOperationCancelRequestCompleted (written after cancel handler responds)
633
+ // rather than NexusOperationCanceled (written after handler workflow completes as
634
+ // cancelled).
635
+ let mut signal_chan = ctx.make_signal_channel("proceed-to-exit");
636
+ signal_chan.next().await;
602
637
  }
638
+
639
+ handler_exited_tx.send(true).unwrap();
603
640
  Ok(WfExitValue::<()>::Cancelled)
604
641
  }
605
642
  });
606
643
  let submitter = worker.get_submitter_handle();
607
644
  let wf_handle = starter.start_with_worker(wf_name, &mut worker).await;
608
645
  let client = starter.get_client().await.get_client().clone();
646
+ let (handler_wf_id_tx, mut handler_wf_id_rx) = tokio::sync::oneshot::channel();
647
+ let completer_id = &format!("completer-{}", rand_6_chars());
609
648
  let nexus_task_handle = async {
610
649
  let nt = core_worker.poll_nexus_task().await.unwrap().unwrap_task();
611
650
  let start_req = assert_matches!(
612
651
  nt.request.unwrap().variant.unwrap(),
613
652
  request::Variant::StartOperation(sr) => sr
614
653
  );
615
- let completer_id = format!("completer-{}", rand_6_chars());
654
+ let _ = handler_wf_id_tx.send(completer_id.clone());
616
655
  let links = start_req
617
656
  .links
618
657
  .iter()
@@ -668,16 +707,19 @@ async fn nexus_cancellation_types(
668
707
  match cancellation_type {
669
708
  NexusOperationCancellationType::WaitCancellationCompleted
670
709
  | NexusOperationCancellationType::WaitCancellationRequested => {
671
- assert!(!*cancel_call_completion_rx.borrow());
710
+ // The nexus op future should not have been resolved
711
+ assert!(!*caller_op_future_rx.borrow());
672
712
  }
673
713
  NexusOperationCancellationType::Abandon | NexusOperationCancellationType::TryCancel => {
674
714
  wf_handle
675
715
  .get_workflow_result(Default::default())
676
716
  .await
677
717
  .unwrap();
678
- assert!(*cancel_call_completion_rx.borrow())
718
+ // The nexus op future should have been resolved
719
+ assert!(*caller_op_future_rx.borrow())
679
720
  }
680
721
  }
722
+ let (cancel_handler_responded_tx, _cancel_handler_responded_rx) = watch::channel(false);
681
723
  if cancellation_type != NexusOperationCancellationType::Abandon {
682
724
  let nt = core_worker.poll_nexus_task().await.unwrap();
683
725
  let nt = nt.unwrap_task();
@@ -686,7 +728,12 @@ async fn nexus_cancellation_types(
686
728
  request::Variant::CancelOperation(_)
687
729
  );
688
730
  client
689
- .cancel_workflow_execution(completer_id, None, "nexus cancel".to_string(), None)
731
+ .cancel_workflow_execution(
732
+ completer_id.to_string(),
733
+ None,
734
+ "nexus cancel".to_string(),
735
+ None,
736
+ )
690
737
  .await
691
738
  .unwrap();
692
739
  core_worker
@@ -702,19 +749,31 @@ async fn nexus_cancellation_types(
702
749
  })
703
750
  .await
704
751
  .unwrap();
752
+ // Mark that the cancel handler has responded
753
+ cancel_handler_responded_tx.send(true).unwrap();
705
754
  }
706
755
 
707
- // Confirm the caller WF has not completed even after the handling of the cancel request
756
+ // Check that the nexus op future resolves only _after_ the handler WF completes
708
757
  if cancellation_type == NexusOperationCancellationType::WaitCancellationCompleted {
709
- assert!(!*cancel_call_completion_rx.borrow());
710
-
711
- // It only completes after the handler WF terminates
712
- cancellation_wait_tx.send(true).unwrap();
758
+ assert!(!*caller_op_future_rx.borrow());
759
+
760
+ cancellation_wait_happened.store(true, Ordering::Relaxed);
761
+ // Send a signal just to wake up the workflow so it'll check the condition
762
+ // (it may already have completed, so ignore the result)
763
+ let _ = client
764
+ .signal_workflow_execution(
765
+ completer_id.to_string(),
766
+ "".to_string(),
767
+ "wakeupdude".to_string(),
768
+ None,
769
+ None,
770
+ )
771
+ .await;
713
772
  wf_handle
714
773
  .get_workflow_result(Default::default())
715
774
  .await
716
775
  .unwrap();
717
- assert!(*cancel_call_completion_rx.borrow());
776
+ assert!(*caller_op_future_rx.borrow());
718
777
  }
719
778
 
720
779
  assert_matches!(
@@ -724,6 +783,38 @@ async fn nexus_cancellation_types(
724
783
  };
725
784
 
726
785
  let shutdown_handle = worker.inner_mut().shutdown_handle();
786
+
787
+ let check_caller_op_future_resolved_then_allow_handler_to_complete = async {
788
+ // The caller nexus op future has been resolved
789
+ assert!(*caller_op_future_rx.borrow());
790
+
791
+ // Verify the handler workflow has not exited yet. This proves that the caller op future
792
+ // was resolved as a result of NexusOperationCancelRequestCompleted (written after cancel
793
+ // handler responds), as opposed to NexusOperationCanceled (written after handler workflow
794
+ // exits).
795
+ assert!(
796
+ !*handler_exited_rx.borrow(),
797
+ "Handler should not have exited yet"
798
+ );
799
+
800
+ let handler_wf_id = handler_wf_id_rx
801
+ .try_recv()
802
+ .expect("Should have received handler workflow ID");
803
+ client
804
+ .signal_workflow_execution(
805
+ handler_wf_id,
806
+ "".to_string(),
807
+ "proceed-to-exit".to_string(),
808
+ None,
809
+ None,
810
+ )
811
+ .await
812
+ .unwrap();
813
+
814
+ handler_exited_rx.changed().await.unwrap();
815
+ assert!(*handler_exited_rx.borrow());
816
+ };
817
+
727
818
  join!(
728
819
  nexus_task_handle,
729
820
  async { worker.inner_mut().run().await.unwrap() },
@@ -735,6 +826,9 @@ async fn nexus_cancellation_types(
735
826
  if cancellation_type == NexusOperationCancellationType::TryCancel {
736
827
  cancellation_rx.changed().await.unwrap();
737
828
  }
829
+ if cancellation_type == NexusOperationCancellationType::WaitCancellationRequested {
830
+ check_caller_op_future_resolved_then_allow_handler_to_complete.await;
831
+ }
738
832
  shutdown_handle();
739
833
  }
740
834
  );
@@ -765,8 +859,14 @@ async fn nexus_cancellation_types(
765
859
  );
766
860
  assert_eq!(f.message, "Nexus operation cancelled after starting");
767
861
  }
768
- NexusOperationCancellationType::WaitCancellationRequested
769
- | NexusOperationCancellationType::WaitCancellationCompleted => {
862
+ NexusOperationCancellationType::WaitCancellationRequested => {
863
+ let f = assert_matches!(
864
+ res.status,
865
+ Some(nexus_operation_result::Status::Cancelled(f)) => f
866
+ );
867
+ assert_eq!(f.message, "Nexus operation cancellation request completed");
868
+ }
869
+ NexusOperationCancellationType::WaitCancellationCompleted => {
770
870
  let f = assert_matches!(
771
871
  res.status,
772
872
  Some(nexus_operation_result::Status::Cancelled(f)) => f