@temporalio/core-bridge 1.1.0 → 1.4.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 (124) hide show
  1. package/Cargo.lock +765 -128
  2. package/Cargo.toml +2 -2
  3. package/common.js +7 -3
  4. package/index.d.ts +118 -5
  5. package/index.js +2 -6
  6. package/package.json +2 -3
  7. package/releases/aarch64-apple-darwin/index.node +0 -0
  8. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  9. package/releases/x86_64-apple-darwin/index.node +0 -0
  10. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  11. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  12. package/scripts/build.js +4 -3
  13. package/sdk-core/.buildkite/docker/Dockerfile +2 -1
  14. package/sdk-core/.buildkite/pipeline.yml +2 -0
  15. package/sdk-core/.cargo/config.toml +1 -1
  16. package/sdk-core/ARCHITECTURE.md +2 -2
  17. package/sdk-core/README.md +12 -0
  18. package/sdk-core/bridge-ffi/Cargo.toml +2 -2
  19. package/sdk-core/bridge-ffi/src/lib.rs +2 -2
  20. package/sdk-core/client/Cargo.toml +7 -5
  21. package/sdk-core/client/src/lib.rs +354 -226
  22. package/sdk-core/client/src/metrics.rs +13 -11
  23. package/sdk-core/client/src/raw.rs +352 -107
  24. package/sdk-core/client/src/retry.rs +188 -147
  25. package/sdk-core/client/src/workflow_handle/mod.rs +1 -1
  26. package/sdk-core/core/Cargo.toml +28 -15
  27. package/sdk-core/core/src/core_tests/activity_tasks.rs +98 -33
  28. package/sdk-core/core/src/core_tests/child_workflows.rs +125 -3
  29. package/sdk-core/core/src/core_tests/local_activities.rs +6 -6
  30. package/sdk-core/core/src/core_tests/workers.rs +3 -2
  31. package/sdk-core/core/src/core_tests/workflow_tasks.rs +70 -2
  32. package/sdk-core/core/src/ephemeral_server/mod.rs +515 -0
  33. package/sdk-core/core/src/lib.rs +62 -28
  34. package/sdk-core/core/src/pollers/mod.rs +2 -0
  35. package/sdk-core/core/src/pollers/poll_buffer.rs +4 -4
  36. package/sdk-core/core/src/replay/mod.rs +3 -3
  37. package/sdk-core/core/src/retry_logic.rs +10 -9
  38. package/sdk-core/core/src/telemetry/metrics.rs +48 -39
  39. package/sdk-core/core/src/telemetry/mod.rs +46 -12
  40. package/sdk-core/core/src/telemetry/prometheus_server.rs +17 -13
  41. package/sdk-core/core/src/test_help/mod.rs +18 -8
  42. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +10 -10
  43. package/sdk-core/core/src/worker/activities/local_activities.rs +13 -13
  44. package/sdk-core/core/src/worker/activities.rs +6 -12
  45. package/sdk-core/core/src/worker/client/mocks.rs +1 -0
  46. package/sdk-core/core/src/worker/client.rs +193 -64
  47. package/sdk-core/core/src/worker/mod.rs +14 -19
  48. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -0
  49. package/sdk-core/core/src/worker/workflow/history_update.rs +5 -5
  50. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +133 -85
  51. package/sdk-core/core/src/worker/workflow/machines/mod.rs +3 -2
  52. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +160 -105
  53. package/sdk-core/core/src/worker/workflow/managed_run.rs +2 -1
  54. package/sdk-core/core/src/worker/workflow/mod.rs +62 -58
  55. package/sdk-core/core/src/worker/workflow/run_cache.rs +5 -3
  56. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +7 -5
  57. package/sdk-core/core-api/Cargo.toml +3 -3
  58. package/sdk-core/core-api/src/errors.rs +3 -11
  59. package/sdk-core/core-api/src/worker.rs +7 -0
  60. package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +1 -1
  61. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +1 -1
  62. package/sdk-core/protos/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +2 -6
  63. package/sdk-core/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +29 -0
  64. package/sdk-core/protos/api_upstream/Makefile +2 -2
  65. package/sdk-core/protos/api_upstream/buf.yaml +1 -0
  66. package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +86 -0
  67. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
  68. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +46 -0
  69. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +7 -0
  70. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +14 -0
  71. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +51 -0
  72. package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +18 -0
  73. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +57 -1
  74. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +1 -3
  75. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -2
  76. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +11 -0
  77. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +23 -0
  78. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +46 -0
  79. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +1 -0
  80. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +172 -0
  81. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +30 -0
  82. package/sdk-core/protos/grpc/health/v1/health.proto +63 -0
  83. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +18 -15
  84. package/sdk-core/protos/testsrv_upstream/Makefile +80 -0
  85. package/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
  86. package/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
  87. package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
  88. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
  89. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
  90. package/sdk-core/sdk/Cargo.toml +2 -2
  91. package/sdk-core/sdk/src/lib.rs +2 -2
  92. package/sdk-core/sdk/src/workflow_context/options.rs +36 -8
  93. package/sdk-core/sdk/src/workflow_context.rs +30 -6
  94. package/sdk-core/sdk/src/workflow_future.rs +4 -4
  95. package/sdk-core/sdk-core-protos/Cargo.toml +5 -5
  96. package/sdk-core/sdk-core-protos/build.rs +9 -1
  97. package/sdk-core/sdk-core-protos/src/history_builder.rs +6 -1
  98. package/sdk-core/sdk-core-protos/src/lib.rs +93 -32
  99. package/sdk-core/test-utils/Cargo.toml +3 -3
  100. package/sdk-core/test-utils/src/canned_histories.rs +58 -0
  101. package/sdk-core/test-utils/src/lib.rs +35 -12
  102. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +128 -0
  103. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +55 -5
  104. package/sdk-core/tests/integ_tests/polling_tests.rs +2 -1
  105. package/sdk-core/tests/integ_tests/queries_tests.rs +5 -5
  106. package/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
  107. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -10
  108. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  109. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +14 -14
  110. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +2 -6
  111. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +12 -12
  112. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +12 -1
  113. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +3 -3
  114. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +8 -2
  115. package/sdk-core/tests/integ_tests/workflow_tests.rs +19 -4
  116. package/sdk-core/tests/load_tests.rs +2 -1
  117. package/sdk-core/tests/main.rs +17 -0
  118. package/sdk-core/tests/runner.rs +93 -0
  119. package/src/conversions.rs +157 -94
  120. package/src/helpers.rs +190 -0
  121. package/src/lib.rs +10 -912
  122. package/src/runtime.rs +436 -0
  123. package/src/testing.rs +67 -0
  124. package/src/worker.rs +465 -0
@@ -20,6 +20,7 @@ use temporal_client::{
20
20
  };
21
21
  use temporal_sdk::{interceptors::WorkerInterceptor, IntoActivityFunc, Worker, WorkflowFunction};
22
22
  use temporal_sdk_core::{
23
+ ephemeral_server::{EphemeralExe, EphemeralExeVersion},
23
24
  init_replay_worker, init_worker, telemetry_init, ClientOptions, ClientOptionsBuilder, Logger,
24
25
  MetricsExporter, OtelCollectorOptions, TelemetryOptions, TelemetryOptionsBuilder,
25
26
  TraceExporter, WorkerConfig, WorkerConfigBuilder,
@@ -40,11 +41,17 @@ use url::Url;
40
41
 
41
42
  pub const NAMESPACE: &str = "default";
42
43
  pub const TEST_Q: &str = "q";
44
+ /// The env var used to specify where the integ tests should point
45
+ pub const INTEG_SERVER_TARGET_ENV_VAR: &str = "TEMPORAL_SERVICE_ADDRESS";
46
+ /// This env var is set (to any value) if temporalite is in use
47
+ pub const INTEG_TEMPORALITE_USED_ENV_VAR: &str = "INTEG_TEMPORALITE_ON";
48
+ /// This env var is set (to any value) if the test server is in use
49
+ pub const INTEG_TEST_SERVER_USED_ENV_VAR: &str = "INTEG_TEST_SERVER_ON";
50
+
43
51
  /// If set, turn export traces and metrics to the OTel collector at the given URL
44
52
  const OTEL_URL_ENV_VAR: &str = "TEMPORAL_INTEG_OTEL_URL";
45
53
  /// If set, enable direct scraping of prom metrics on the specified port
46
54
  const PROM_ENABLE_ENV_VAR: &str = "TEMPORAL_INTEG_PROM_PORT";
47
-
48
55
  /// Create a worker instance which will use the provided test name to base the task queue and wf id
49
56
  /// upon. Returns the instance and the task queue name (which is also the workflow id).
50
57
  pub async fn init_core_and_create_wf(test_name: &str) -> CoreWfStarter {
@@ -160,6 +167,7 @@ impl CoreWfStarter {
160
167
  self.worker_config.task_queue.clone(),
161
168
  workflow_id,
162
169
  self.task_queue_name.clone(),
170
+ None,
163
171
  opts,
164
172
  )
165
173
  .await
@@ -319,6 +327,7 @@ impl TestWorker {
319
327
  self.inner.task_queue().to_string(),
320
328
  wfid.clone(),
321
329
  workflow_type.into(),
330
+ None,
322
331
  options,
323
332
  )
324
333
  .await?;
@@ -362,6 +371,8 @@ impl TestWorker {
362
371
  }
363
372
  }
364
373
 
374
+ pub type BoxDynActivationHook = Box<dyn Fn(&WorkflowActivationCompletion)>;
375
+
365
376
  pub enum TestWorkerShutdownCond {
366
377
  GetResults(Vec<WorkflowExecutionInfo>, Arc<RetryClient<Client>>),
367
378
  NoAutoShutdown,
@@ -370,7 +381,7 @@ pub enum TestWorkerShutdownCond {
370
381
  pub struct TestWorkerCompletionIceptor {
371
382
  condition: TestWorkerShutdownCond,
372
383
  shutdown_handle: Arc<dyn Fn()>,
373
- every_activation: Option<Box<dyn Fn(&WorkflowActivationCompletion)>>,
384
+ every_activation: Option<BoxDynActivationHook>,
374
385
  next: Option<Box<dyn WorkerInterceptor>>,
375
386
  }
376
387
  impl TestWorkerCompletionIceptor {
@@ -429,8 +440,10 @@ impl WorkerInterceptor for TestWorkerCompletionIceptor {
429
440
  }
430
441
  }
431
442
 
443
+ /// Returns the client options used to connect to the server used for integration tests.
432
444
  pub fn get_integ_server_options() -> ClientOptions {
433
- let temporal_server_address = match env::var("TEMPORAL_SERVICE_ADDRESS") {
445
+ telemetry_init(&get_integ_telem_options()).expect("Telemetry inits cleanly");
446
+ let temporal_server_address = match env::var(INTEG_SERVER_TARGET_ENV_VAR) {
434
447
  Ok(addr) => addr,
435
448
  Err(_) => "http://localhost:7233".to_owned(),
436
449
  };
@@ -469,6 +482,16 @@ pub fn get_integ_telem_options() -> TelemetryOptions {
469
482
  .unwrap()
470
483
  }
471
484
 
485
+ pub fn default_cached_download() -> EphemeralExe {
486
+ EphemeralExe::CachedDownload {
487
+ version: EphemeralExeVersion::Default {
488
+ sdk_name: "sdk-rust".to_string(),
489
+ sdk_version: "0.1.0".to_string(),
490
+ },
491
+ dest_dir: None,
492
+ }
493
+ }
494
+
472
495
  pub fn schedule_activity_cmd(
473
496
  seq: u32,
474
497
  task_q: &str,
@@ -483,10 +506,10 @@ pub fn schedule_activity_cmd(
483
506
  activity_type: "test_activity".to_string(),
484
507
  namespace: NAMESPACE.to_owned(),
485
508
  task_queue: task_q.to_owned(),
486
- schedule_to_start_timeout: Some(activity_timeout.into()),
487
- start_to_close_timeout: Some(activity_timeout.into()),
488
- schedule_to_close_timeout: Some(activity_timeout.into()),
489
- heartbeat_timeout: Some(heartbeat_timeout.into()),
509
+ schedule_to_start_timeout: Some(activity_timeout.try_into().expect("duration fits")),
510
+ start_to_close_timeout: Some(activity_timeout.try_into().expect("duration fits")),
511
+ schedule_to_close_timeout: Some(activity_timeout.try_into().expect("duration fits")),
512
+ heartbeat_timeout: Some(heartbeat_timeout.try_into().expect("duration fits")),
490
513
  cancellation_type: cancellation_type as i32,
491
514
  ..Default::default()
492
515
  }
@@ -503,9 +526,9 @@ pub fn schedule_local_activity_cmd(
503
526
  seq,
504
527
  activity_id: activity_id.to_string(),
505
528
  activity_type: "test_activity".to_string(),
506
- schedule_to_start_timeout: Some(activity_timeout.into()),
507
- start_to_close_timeout: Some(activity_timeout.into()),
508
- schedule_to_close_timeout: Some(activity_timeout.into()),
529
+ schedule_to_start_timeout: Some(activity_timeout.try_into().expect("duration fits")),
530
+ start_to_close_timeout: Some(activity_timeout.try_into().expect("duration fits")),
531
+ schedule_to_close_timeout: Some(activity_timeout.try_into().expect("duration fits")),
509
532
  cancellation_type: cancellation_type as i32,
510
533
  ..Default::default()
511
534
  }
@@ -515,7 +538,7 @@ pub fn schedule_local_activity_cmd(
515
538
  pub fn start_timer_cmd(seq: u32, duration: Duration) -> workflow_command::Variant {
516
539
  StartTimer {
517
540
  seq,
518
- start_to_fire_timeout: Some(duration.into()),
541
+ start_to_fire_timeout: Some(duration.try_into().expect("duration fits")),
519
542
  }
520
543
  .into()
521
544
  }
@@ -565,7 +588,7 @@ where
565
588
  run_id.to_string(),
566
589
  vec![StartTimer {
567
590
  seq,
568
- start_to_fire_timeout: Some(duration.into()),
591
+ start_to_fire_timeout: Some(duration.try_into().expect("duration fits")),
569
592
  }
570
593
  .into()],
571
594
  ))
@@ -0,0 +1,128 @@
1
+ use std::time::{SystemTime, UNIX_EPOCH};
2
+ use temporal_client::{ClientOptionsBuilder, TestService, WorkflowService};
3
+ use temporal_sdk_core::ephemeral_server::{
4
+ EphemeralExe, EphemeralExeVersion, EphemeralServer, TemporaliteConfigBuilder,
5
+ TestServerConfigBuilder,
6
+ };
7
+ use temporal_sdk_core_protos::temporal::api::workflowservice::v1::DescribeNamespaceRequest;
8
+ use temporal_sdk_core_test_utils::{default_cached_download, NAMESPACE};
9
+ use url::Url;
10
+
11
+ #[tokio::test]
12
+ async fn temporalite_default() {
13
+ let config = TemporaliteConfigBuilder::default()
14
+ .exe(default_cached_download())
15
+ .build()
16
+ .unwrap();
17
+ let mut server = config.start_server().await.unwrap();
18
+ assert_ephemeral_server(&server).await;
19
+ server.shutdown().await.unwrap();
20
+ }
21
+
22
+ #[tokio::test]
23
+ async fn temporalite_fixed() {
24
+ let config = TemporaliteConfigBuilder::default()
25
+ .exe(fixed_cached_download("v0.1.1"))
26
+ .build()
27
+ .unwrap();
28
+ let mut server = config.start_server().await.unwrap();
29
+ assert_ephemeral_server(&server).await;
30
+ server.shutdown().await.unwrap();
31
+ }
32
+
33
+ #[tokio::test]
34
+ async fn temporalite_shutdown_port_reuse() {
35
+ // Start, test shutdown, do again immediately on same port to ensure we can
36
+ // reuse after shutdown
37
+ let config = TemporaliteConfigBuilder::default()
38
+ .exe(default_cached_download())
39
+ .port(Some(10123))
40
+ .build()
41
+ .unwrap();
42
+ let mut server = config.start_server().await.unwrap();
43
+ assert_ephemeral_server(&server).await;
44
+ server.shutdown().await.unwrap();
45
+ let mut server = config.start_server().await.unwrap();
46
+ assert_ephemeral_server(&server).await;
47
+ server.shutdown().await.unwrap();
48
+ }
49
+
50
+ #[tokio::test]
51
+ async fn test_server_default() {
52
+ let config = TestServerConfigBuilder::default()
53
+ .exe(default_cached_download())
54
+ .build()
55
+ .unwrap();
56
+ let mut server = config.start_server().await.unwrap();
57
+ assert_ephemeral_server(&server).await;
58
+ server.shutdown().await.unwrap();
59
+ }
60
+
61
+ #[tokio::test]
62
+ async fn test_server_fixed() {
63
+ let config = TestServerConfigBuilder::default()
64
+ .exe(fixed_cached_download("v1.16.0"))
65
+ .build()
66
+ .unwrap();
67
+ let mut server = config.start_server().await.unwrap();
68
+ assert_ephemeral_server(&server).await;
69
+ server.shutdown().await.unwrap();
70
+ }
71
+
72
+ #[tokio::test]
73
+ async fn test_server_shutdown_port_reuse() {
74
+ // Start, test shutdown, do again immediately on same port to ensure we can
75
+ // reuse after shutdown
76
+ let config = TestServerConfigBuilder::default()
77
+ .exe(default_cached_download())
78
+ .port(Some(10124))
79
+ .build()
80
+ .unwrap();
81
+ let mut server = config.start_server().await.unwrap();
82
+ assert_ephemeral_server(&server).await;
83
+ server.shutdown().await.unwrap();
84
+ let mut server = config.start_server().await.unwrap();
85
+ assert_ephemeral_server(&server).await;
86
+ server.shutdown().await.unwrap();
87
+ }
88
+
89
+ fn fixed_cached_download(version: &str) -> EphemeralExe {
90
+ EphemeralExe::CachedDownload {
91
+ version: EphemeralExeVersion::Fixed(version.to_string()),
92
+ dest_dir: None,
93
+ }
94
+ }
95
+
96
+ async fn assert_ephemeral_server(server: &EphemeralServer) {
97
+ // Connect and describe namespace
98
+ let mut client = ClientOptionsBuilder::default()
99
+ .identity("integ_tester".to_string())
100
+ .target_url(Url::try_from(&*format!("http://{}", server.target)).unwrap())
101
+ .client_name("temporal-core".to_string())
102
+ .client_version("0.1.0".to_string())
103
+ .build()
104
+ .unwrap()
105
+ .connect_no_namespace(None, None)
106
+ .await
107
+ .unwrap();
108
+ let resp = client
109
+ .describe_namespace(DescribeNamespaceRequest {
110
+ namespace: NAMESPACE.to_string(),
111
+ ..Default::default()
112
+ })
113
+ .await
114
+ .unwrap();
115
+ assert!(resp.into_inner().namespace_info.unwrap().name == "default");
116
+
117
+ // If it has test service, make sure we can use it too
118
+ if server.has_test_service {
119
+ let resp = client.get_current_time(()).await.unwrap();
120
+ // Make sure it's within 5 mins of now
121
+ let resp_seconds = resp.get_ref().time.as_ref().unwrap().seconds as u64;
122
+ let curr_seconds = SystemTime::now()
123
+ .duration_since(UNIX_EPOCH)
124
+ .unwrap()
125
+ .as_secs();
126
+ assert!(curr_seconds - 300 < resp_seconds && curr_seconds + 300 > resp_seconds);
127
+ }
128
+ }
@@ -1,5 +1,7 @@
1
1
  use assert_matches::assert_matches;
2
2
  use std::time::Duration;
3
+ use temporal_client::{WfClientExt, WorkflowOptions};
4
+ use temporal_sdk::{ActContext, ActivityOptions, WfContext};
3
5
  use temporal_sdk_core_protos::{
4
6
  coresdk::{
5
7
  activity_result::{
@@ -9,12 +11,12 @@ use temporal_sdk_core_protos::{
9
11
  workflow_activation::{workflow_activation_job, ResolveActivity, WorkflowActivationJob},
10
12
  workflow_commands::{ActivityCancellationType, ScheduleActivity},
11
13
  workflow_completion::WorkflowActivationCompletion,
12
- ActivityHeartbeat, ActivityTaskCompletion, IntoCompletion,
14
+ ActivityHeartbeat, ActivityTaskCompletion, AsJsonPayloadExt, IntoCompletion,
13
15
  },
14
16
  temporal::api::common::v1::{Payload, RetryPolicy},
15
17
  };
16
18
  use temporal_sdk_core_test_utils::{
17
- init_core_and_create_wf, schedule_activity_cmd, WorkerTestHelpers,
19
+ init_core_and_create_wf, schedule_activity_cmd, CoreWfStarter, WorkerTestHelpers,
18
20
  };
19
21
  use tokio::time::sleep;
20
22
 
@@ -103,14 +105,14 @@ async fn many_act_fails_with_heartbeats() {
103
105
  activity_id: activity_id.to_string(),
104
106
  activity_type: "test_act".to_string(),
105
107
  task_queue: starter.get_task_queue().to_string(),
106
- start_to_close_timeout: Some(Duration::from_secs(30).into()),
108
+ start_to_close_timeout: Some(prost_dur!(from_secs(30))),
107
109
  retry_policy: Some(RetryPolicy {
108
- initial_interval: Some(Duration::from_millis(10).into()),
110
+ initial_interval: Some(prost_dur!(from_millis(10))),
109
111
  backoff_coefficient: 1.0,
110
112
  maximum_attempts: 4,
111
113
  ..Default::default()
112
114
  }),
113
- heartbeat_timeout: Some(Duration::from_secs(1).into()),
115
+ heartbeat_timeout: Some(prost_dur!(from_secs(1))),
114
116
  ..Default::default()
115
117
  }
116
118
  .into(),
@@ -166,3 +168,51 @@ async fn many_act_fails_with_heartbeats() {
166
168
  core.complete_execution(&task.run_id).await;
167
169
  core.shutdown().await;
168
170
  }
171
+
172
+ #[tokio::test]
173
+ async fn activity_doesnt_heartbeat_hits_timeout_then_completes() {
174
+ let wf_name = "activity_doesnt_heartbeat_hits_timeout_then_completes";
175
+ let mut starter = CoreWfStarter::new(wf_name);
176
+ let mut worker = starter.worker().await;
177
+ let client = starter.get_client().await;
178
+ worker.register_activity(
179
+ "echo_activity",
180
+ |_ctx: ActContext, echo_me: String| async move {
181
+ sleep(Duration::from_secs(4)).await;
182
+ Ok(echo_me)
183
+ },
184
+ );
185
+ worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
186
+ let res = ctx
187
+ .activity(ActivityOptions {
188
+ activity_type: "echo_activity".to_string(),
189
+ input: "hi!".as_json_payload().expect("serializes fine"),
190
+ start_to_close_timeout: Some(Duration::from_secs(10)),
191
+ heartbeat_timeout: Some(Duration::from_secs(2)),
192
+ retry_policy: Some(RetryPolicy {
193
+ maximum_attempts: 1,
194
+ ..Default::default()
195
+ }),
196
+ ..Default::default()
197
+ })
198
+ .await;
199
+ assert!(res.timed_out());
200
+ Ok(().into())
201
+ });
202
+
203
+ let run_id = worker
204
+ .submit_wf(
205
+ wf_name.to_owned(),
206
+ wf_name.to_owned(),
207
+ vec![],
208
+ WorkflowOptions::default(),
209
+ )
210
+ .await
211
+ .unwrap();
212
+ worker.run_until_done().await.unwrap();
213
+ let handle = client.get_untyped_workflow_handle(wf_name, run_id);
214
+ handle
215
+ .get_workflow_result(Default::default())
216
+ .await
217
+ .unwrap();
218
+ }
@@ -36,7 +36,7 @@ async fn out_of_order_completion_doesnt_hang() {
36
36
  ),
37
37
  StartTimer {
38
38
  seq: 1,
39
- start_to_fire_timeout: Some(Duration::from_millis(50).into()),
39
+ start_to_fire_timeout: Some(prost_dur!(from_millis(50))),
40
40
  }
41
41
  .into(),
42
42
  ]
@@ -133,6 +133,7 @@ async fn can_paginate_long_history() {
133
133
  worker.run_until_done().await.unwrap();
134
134
  }
135
135
 
136
+ // TODO: Takes ages now, fix somehow
136
137
  #[tokio::test]
137
138
  async fn poll_of_nonexistent_namespace_is_fatal() {
138
139
  let mut starter = CoreWfStarter::new("whatever_yo");
@@ -24,12 +24,12 @@ async fn simple_query_legacy() {
24
24
  vec![
25
25
  StartTimer {
26
26
  seq: 0,
27
- start_to_fire_timeout: Some(Duration::from_millis(500).into()),
27
+ start_to_fire_timeout: Some(prost_dur!(from_millis(500))),
28
28
  }
29
29
  .into(),
30
30
  StartTimer {
31
31
  seq: 1,
32
- start_to_fire_timeout: Some(Duration::from_secs(3).into()),
32
+ start_to_fire_timeout: Some(prost_dur!(from_secs(3))),
33
33
  }
34
34
  .into(),
35
35
  ],
@@ -105,7 +105,7 @@ async fn simple_query_legacy() {
105
105
  assert_eq!(&q_resp.unwrap()[0].data, query_resp);
106
106
  }
107
107
 
108
- #[rstest::rstest]
108
+ #[rstest]
109
109
  #[case::no_eviction(false)]
110
110
  #[case::with_eviction(true)]
111
111
  #[tokio::test]
@@ -343,12 +343,12 @@ async fn fail_legacy_query() {
343
343
  let t1_resp = vec![
344
344
  StartTimer {
345
345
  seq: 1,
346
- start_to_fire_timeout: Some(Duration::from_millis(500).into()),
346
+ start_to_fire_timeout: Some(prost_dur!(from_millis(500))),
347
347
  }
348
348
  .into(),
349
349
  StartTimer {
350
350
  seq: 2,
351
- start_to_fire_timeout: Some(Duration::from_secs(3).into()),
351
+ start_to_fire_timeout: Some(prost_dur!(from_secs(3))),
352
352
  }
353
353
  .into(),
354
354
  ];
@@ -0,0 +1,93 @@
1
+ use assert_matches::assert_matches;
2
+ use std::{sync::Arc, time::Duration};
3
+ use temporal_client::{
4
+ ListClosedFilters, ListOpenFilters, Namespace, StartTimeFilter, WorkflowClientTrait,
5
+ WorkflowExecutionFilter, WorkflowOptions,
6
+ };
7
+ use temporal_sdk_core_protos::coresdk::workflow_activation::{
8
+ workflow_activation_job, WorkflowActivationJob,
9
+ };
10
+ use temporal_sdk_core_test_utils::{
11
+ get_integ_server_options, CoreWfStarter, WorkerTestHelpers, NAMESPACE,
12
+ };
13
+
14
+ #[tokio::test]
15
+ async fn client_list_open_closed_workflow_executions() {
16
+ let wf_name = "client_list_open_closed_workflow_executions".to_owned();
17
+ let mut starter = CoreWfStarter::new(&wf_name);
18
+ let core = starter.get_worker().await;
19
+ let client = starter.get_client().await;
20
+
21
+ let earliest = std::time::SystemTime::now();
22
+ let latest = earliest + Duration::from_secs(60);
23
+
24
+ // start workflow
25
+ let run_id = starter
26
+ .start_wf_with_id(wf_name.to_owned(), WorkflowOptions::default())
27
+ .await;
28
+ let task = core.poll_workflow_activation().await.unwrap();
29
+ assert_matches!(
30
+ task.jobs.as_slice(),
31
+ [WorkflowActivationJob {
32
+ variant: Some(workflow_activation_job::Variant::StartWorkflow(_)),
33
+ }]
34
+ );
35
+
36
+ // List above OPEN workflow
37
+ let start_time_filter = StartTimeFilter {
38
+ earliest_time: Some(earliest).and_then(|t| t.try_into().ok()),
39
+ latest_time: Some(latest).and_then(|t| t.try_into().ok()),
40
+ };
41
+ let filter = ListOpenFilters::ExecutionFilter(WorkflowExecutionFilter {
42
+ workflow_id: wf_name.clone(),
43
+ run_id: "".to_owned(),
44
+ });
45
+ let open_workflows = client
46
+ .list_open_workflow_executions(1, Default::default(), Some(start_time_filter), Some(filter))
47
+ .await
48
+ .unwrap();
49
+ assert_eq!(open_workflows.executions.len(), 1);
50
+ let workflow = open_workflows.executions[0].clone();
51
+ assert_eq!(workflow.execution.as_ref().unwrap().workflow_id, wf_name);
52
+
53
+ // Complete workflow
54
+ core.complete_execution(&task.run_id).await;
55
+
56
+ // List above CLOSED workflow
57
+ let start_time_filter = StartTimeFilter {
58
+ earliest_time: Some(earliest).and_then(|t| t.try_into().ok()),
59
+ latest_time: Some(latest).and_then(|t| t.try_into().ok()),
60
+ };
61
+ let filter = ListClosedFilters::ExecutionFilter(WorkflowExecutionFilter {
62
+ workflow_id: wf_name.clone(),
63
+ run_id,
64
+ });
65
+ let closed_workflows = client
66
+ .list_closed_workflow_executions(
67
+ 1,
68
+ Default::default(),
69
+ Some(start_time_filter),
70
+ Some(filter),
71
+ )
72
+ .await
73
+ .unwrap();
74
+ assert_eq!(closed_workflows.executions.len(), 1);
75
+ let workflow = closed_workflows.executions[0].clone();
76
+ assert_eq!(workflow.execution.as_ref().unwrap().workflow_id, wf_name);
77
+ }
78
+
79
+ #[tokio::test]
80
+ async fn client_describe_namespace() {
81
+ let client = Arc::new(
82
+ get_integ_server_options()
83
+ .connect(NAMESPACE.to_owned(), None, None)
84
+ .await
85
+ .expect("Must connect"),
86
+ );
87
+
88
+ let namespace_result = client
89
+ .describe_namespace(Namespace::Name(NAMESPACE.to_owned()))
90
+ .await
91
+ .unwrap();
92
+ assert_eq!(namespace_result.namespace_info.unwrap().name, NAMESPACE);
93
+ }
@@ -1,12 +1,13 @@
1
1
  use assert_matches::assert_matches;
2
2
  use std::time::Duration;
3
3
  use temporal_client::{WfClientExt, WorkflowClientTrait, WorkflowExecutionResult, WorkflowOptions};
4
- use temporal_sdk::{ActContext, ActivityOptions, CancellableFuture, WfContext, WorkflowResult};
4
+ use temporal_sdk::{
5
+ ActContext, ActExitValue, ActivityOptions, CancellableFuture, WfContext, WorkflowResult,
6
+ };
5
7
  use temporal_sdk_core_protos::{
6
8
  coresdk::{
7
9
  activity_result::{
8
- self, activity_resolution, activity_resolution as act_res, ActivityExecutionResult,
9
- ActivityResolution,
10
+ self, activity_resolution as act_res, ActivityExecutionResult, ActivityResolution,
10
11
  },
11
12
  activity_task::activity_task as act_task,
12
13
  workflow_activation::{
@@ -14,13 +15,15 @@ use temporal_sdk_core_protos::{
14
15
  },
15
16
  workflow_commands::{ActivityCancellationType, RequestCancelActivity, StartTimer},
16
17
  workflow_completion::WorkflowActivationCompletion,
17
- ActivityHeartbeat, ActivityTaskCompletion, AsJsonPayloadExt, IntoCompletion,
18
+ ActivityHeartbeat, ActivityTaskCompletion, AsJsonPayloadExt, FromJsonPayloadExt,
19
+ IntoCompletion,
18
20
  },
19
21
  temporal::api::{
20
22
  common::v1::{ActivityType, Payload, Payloads},
21
23
  enums::v1::RetryState,
22
24
  failure::v1::{failure::FailureInfo, ActivityFailureInfo, Failure},
23
25
  },
26
+ TaskToken,
24
27
  };
25
28
  use temporal_sdk_core_test_utils::{
26
29
  init_core_and_create_wf, schedule_activity_cmd, CoreWfStarter, WorkerTestHelpers,
@@ -297,7 +300,7 @@ async fn activity_cancellation_try_cancel() {
297
300
  ),
298
301
  StartTimer {
299
302
  seq: 1,
300
- start_to_fire_timeout: Some(Duration::from_millis(50).into()),
303
+ start_to_fire_timeout: Some(prost_dur!(from_millis(50))),
301
304
  }
302
305
  .into(),
303
306
  ]
@@ -359,7 +362,7 @@ async fn activity_cancellation_plus_complete_doesnt_double_resolve() {
359
362
  ),
360
363
  StartTimer {
361
364
  seq: 1,
362
- start_to_fire_timeout: Some(Duration::from_millis(50).into()),
365
+ start_to_fire_timeout: Some(prost_dur!(from_millis(50))),
363
366
  }
364
367
  .into(),
365
368
  ]
@@ -390,7 +393,7 @@ async fn activity_cancellation_plus_complete_doesnt_double_resolve() {
390
393
  variant: Some(workflow_activation_job::Variant::ResolveActivity(
391
394
  ResolveActivity {
392
395
  result: Some(ActivityResolution {
393
- status: Some(activity_resolution::Status::Cancelled(_))
396
+ status: Some(act_res::Status::Cancelled(_))
394
397
  }),
395
398
  ..
396
399
  }
@@ -403,7 +406,7 @@ async fn activity_cancellation_plus_complete_doesnt_double_resolve() {
403
406
  task.run_id,
404
407
  vec![StartTimer {
405
408
  seq: 2,
406
- start_to_fire_timeout: Some(Duration::from_millis(100).into()),
409
+ start_to_fire_timeout: Some(prost_dur!(from_millis(100))),
407
410
  }
408
411
  .into()],
409
412
  ))
@@ -506,7 +509,7 @@ async fn activity_cancellation_wait_cancellation_completed() {
506
509
  ),
507
510
  StartTimer {
508
511
  seq: 1,
509
- start_to_fire_timeout: Some(Duration::from_millis(50).into()),
512
+ start_to_fire_timeout: Some(prost_dur!(from_millis(50))),
510
513
  }
511
514
  .into(),
512
515
  ]
@@ -573,7 +576,7 @@ async fn activity_cancellation_abandon() {
573
576
  ),
574
577
  StartTimer {
575
578
  seq: 1,
576
- start_to_fire_timeout: Some(Duration::from_millis(50).into()),
579
+ start_to_fire_timeout: Some(prost_dur!(from_millis(50))),
577
580
  }
578
581
  .into(),
579
582
  ]
@@ -793,3 +796,83 @@ async fn one_activity_abandon_cancelled_after_complete() {
793
796
  .unwrap();
794
797
  assert_matches!(res, WorkflowExecutionResult::Succeeded(_));
795
798
  }
799
+
800
+ #[tokio::test]
801
+ async fn it_can_complete_async() {
802
+ use std::sync::Arc;
803
+ use tokio::sync::Mutex;
804
+
805
+ let wf_name = "it_can_complete_async".to_owned();
806
+ let mut starter = CoreWfStarter::new(&wf_name);
807
+ let mut worker = starter.worker().await;
808
+ let client = starter.get_client().await;
809
+ let async_response = "agence";
810
+ let shared_token: Arc<Mutex<Option<Vec<u8>>>> = Arc::new(Mutex::new(None));
811
+ worker.register_wf(wf_name.clone(), move |ctx: WfContext| async move {
812
+ let activity_resolution = ctx
813
+ .activity(ActivityOptions {
814
+ activity_type: "complete_async_activity".to_string(),
815
+ input: "hi".as_json_payload().expect("serializes fine"),
816
+ start_to_close_timeout: Some(Duration::from_secs(30)),
817
+ ..Default::default()
818
+ })
819
+ .await;
820
+
821
+ let res = match activity_resolution.status {
822
+ Some(act_res::Status::Completed(activity_result::Success { result })) => result
823
+ .map(|p| String::from_json_payload(&p).unwrap())
824
+ .unwrap(),
825
+ _ => panic!("activity task failed {:?}", activity_resolution),
826
+ };
827
+
828
+ assert_eq!(&res, async_response);
829
+ Ok(().into())
830
+ });
831
+
832
+ let shared_token_ref = shared_token.clone();
833
+ worker.register_activity(
834
+ "complete_async_activity",
835
+ move |ctx: ActContext, _: String| {
836
+ let shared_token_ref = shared_token_ref.clone();
837
+ async move {
838
+ // set the `activity_task_token`
839
+ let activity_info = ctx.get_info();
840
+ let task_token = &activity_info.task_token;
841
+ let mut shared = shared_token_ref.lock().await;
842
+ *shared = Some(task_token.clone());
843
+ Ok::<ActExitValue<()>, _>(ActExitValue::WillCompleteAsync)
844
+ }
845
+ },
846
+ );
847
+
848
+ let shared_token_ref2 = shared_token.clone();
849
+ tokio::spawn(async move {
850
+ loop {
851
+ let mut shared = shared_token_ref2.lock().await;
852
+ let maybe_token = shared.take();
853
+
854
+ if let Some(task_token) = maybe_token {
855
+ client
856
+ .complete_activity_task(
857
+ TaskToken(task_token),
858
+ Some(async_response.as_json_payload().unwrap().into()),
859
+ )
860
+ .await
861
+ .unwrap();
862
+ return;
863
+ }
864
+ }
865
+ });
866
+
867
+ let _run_id = worker
868
+ .submit_wf(
869
+ wf_name.to_owned(),
870
+ wf_name.to_owned(),
871
+ vec![],
872
+ WorkflowOptions::default(),
873
+ )
874
+ .await
875
+ .unwrap();
876
+
877
+ worker.run_until_done().await.unwrap();
878
+ }
@@ -39,7 +39,7 @@ async fn cancel_during_timer() {
39
39
  tokio::time::sleep(Duration::from_millis(500)).await;
40
40
  // Cancel the workflow externally
41
41
  client
42
- .cancel_workflow_execution(wf_name.to_string(), None, "Dieee".to_string())
42
+ .cancel_workflow_execution(wf_name.to_string(), None, "Dieee".to_string(), None)
43
43
  .await
44
44
  .unwrap();
45
45
  };