@temporalio/core-bridge 0.21.1 → 0.22.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 (45) hide show
  1. package/Cargo.lock +1 -0
  2. package/index.d.ts +6 -2
  3. package/package.json +3 -3
  4. package/releases/aarch64-apple-darwin/index.node +0 -0
  5. package/releases/x86_64-apple-darwin/index.node +0 -0
  6. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  7. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  8. package/sdk-core/.buildkite/docker/docker-compose.yaml +5 -4
  9. package/sdk-core/client/Cargo.toml +1 -0
  10. package/sdk-core/client/src/lib.rs +52 -9
  11. package/sdk-core/client/src/raw.rs +9 -1
  12. package/sdk-core/client/src/retry.rs +12 -1
  13. package/sdk-core/client/src/workflow_handle/mod.rs +183 -0
  14. package/sdk-core/core/src/core_tests/child_workflows.rs +7 -9
  15. package/sdk-core/core/src/core_tests/determinism.rs +8 -19
  16. package/sdk-core/core/src/core_tests/local_activities.rs +22 -32
  17. package/sdk-core/core/src/core_tests/queries.rs +127 -3
  18. package/sdk-core/core/src/core_tests/workers.rs +4 -34
  19. package/sdk-core/core/src/core_tests/workflow_tasks.rs +197 -9
  20. package/sdk-core/core/src/pending_activations.rs +11 -0
  21. package/sdk-core/core/src/telemetry/mod.rs +1 -1
  22. package/sdk-core/core/src/test_help/mod.rs +57 -7
  23. package/sdk-core/core/src/worker/mod.rs +60 -12
  24. package/sdk-core/core/src/workflow/machines/mod.rs +1 -1
  25. package/sdk-core/core/src/workflow/machines/timer_state_machine.rs +2 -2
  26. package/sdk-core/core/src/workflow/machines/workflow_machines.rs +5 -1
  27. package/sdk-core/core/src/workflow/workflow_tasks/cache_manager.rs +47 -2
  28. package/sdk-core/core/src/workflow/workflow_tasks/concurrency_manager.rs +16 -2
  29. package/sdk-core/core/src/workflow/workflow_tasks/mod.rs +185 -96
  30. package/sdk-core/core-api/src/worker.rs +9 -0
  31. package/sdk-core/sdk/Cargo.toml +1 -0
  32. package/sdk-core/sdk/src/activity_context.rs +223 -0
  33. package/sdk-core/sdk/src/interceptors.rs +8 -2
  34. package/sdk-core/sdk/src/lib.rs +167 -122
  35. package/sdk-core/test-utils/Cargo.toml +1 -0
  36. package/sdk-core/test-utils/src/lib.rs +78 -37
  37. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +11 -4
  38. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +0 -1
  39. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +0 -3
  40. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +33 -17
  41. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +10 -1
  42. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +0 -1
  43. package/sdk-core/tests/integ_tests/workflow_tests.rs +71 -3
  44. package/sdk-core/tests/load_tests.rs +80 -6
  45. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
package/Cargo.lock CHANGED
@@ -1568,6 +1568,7 @@ dependencies = [
1568
1568
  name = "temporal-client"
1569
1569
  version = "0.1.0"
1570
1570
  dependencies = [
1571
+ "anyhow",
1571
1572
  "async-trait",
1572
1573
  "backoff",
1573
1574
  "derive_builder",
package/index.d.ts CHANGED
@@ -66,9 +66,13 @@ export interface TelemetryOptions {
66
66
  * Which determines what tracing data is collected in the Core SDK
67
67
  */
68
68
  tracingFilter?: string;
69
- /** What level, if any, logs should be forwarded from core at */
69
+ /**
70
+ * What level, if any, logs should be forwarded from core at
71
+ *
72
+ * @default OFF
73
+ */
70
74
  // These strings should match the log::LevelFilter enum in rust
71
- logForwardingLevel: 'OFF' | LogLevel;
75
+ logForwardingLevel?: 'OFF' | LogLevel;
72
76
  /** If set, metrics will be exposed on an http server in this process for direct scraping by
73
77
  * prometheus. If used in conjunction with the OTel collector, metrics will *not* be exported
74
78
  * to the collector, but traces will be.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@temporalio/core-bridge",
3
- "version": "0.21.1",
3
+ "version": "0.22.0",
4
4
  "description": "Temporal.io SDK Core<>Node bridge",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -20,7 +20,7 @@
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
22
  "@opentelemetry/api": "^1.0.3",
23
- "@temporalio/internal-non-workflow-common": "^0.21.0",
23
+ "@temporalio/internal-non-workflow-common": "^0.22.0",
24
24
  "arg": "^5.0.1",
25
25
  "cargo-cp-artifact": "^0.1.4",
26
26
  "which": "^2.0.2"
@@ -43,5 +43,5 @@
43
43
  "publishConfig": {
44
44
  "access": "public"
45
45
  },
46
- "gitHead": "c241ab2f8e92966555dbd6d01dc072c8965ee6f4"
46
+ "gitHead": "3aa1f14982bd170d21b728cbf016dc4f1b595a76"
47
47
  }
@@ -9,7 +9,7 @@ services:
9
9
  # - '9042:9042'
10
10
 
11
11
  temporal:
12
- image: temporalio/auto-setup:1.15.0
12
+ image: temporalio/auto-setup:1.16.0
13
13
  ports:
14
14
  - "7233:7233"
15
15
  - "7234:7234"
@@ -26,12 +26,13 @@ services:
26
26
  - cassandra
27
27
 
28
28
  temporal-web:
29
- image: temporalio/web:1.13.0
29
+ image: temporalio/ui:0.10.2
30
30
  logging:
31
31
  driver: none
32
32
  ports:
33
- - "8088:8088"
33
+ - "8080:8080"
34
34
  environment:
35
- - "TEMPORAL_GRPC_ENDPOINT=temporal:7233"
35
+ - TEMPORAL_ADDRESS=temporal:7233
36
+ - TEMPORAL_CORS_ORIGINS=http://localhost:3000
36
37
  depends_on:
37
38
  - temporal
@@ -11,6 +11,7 @@ keywords = ["temporal", "workflow"]
11
11
  categories = ["development-tools"]
12
12
 
13
13
  [dependencies]
14
+ anyhow = "1.0"
14
15
  async-trait = "0.1"
15
16
  backoff = "0.4"
16
17
  derive_builder = "0.11"
@@ -10,13 +10,17 @@ extern crate tracing;
10
10
  mod metrics;
11
11
  mod raw;
12
12
  mod retry;
13
+ mod workflow_handle;
13
14
 
14
15
  pub use crate::retry::{CallType, RetryClient};
15
16
  pub use raw::WorkflowService;
17
+ pub use workflow_handle::{WorkflowExecutionInfo, WorkflowExecutionResult};
16
18
 
17
19
  use crate::{
18
20
  metrics::{GrpcMetricSvc, MetricsContext},
19
21
  raw::{sealed::RawClientLike, AttachMetricLabels},
22
+ sealed::{RawClientLikeUser, WfHandleClient},
23
+ workflow_handle::UntypedWorkflowHandle,
20
24
  };
21
25
  use backoff::{ExponentialBackoff, SystemClock};
22
26
  use http::uri::InvalidUri;
@@ -37,7 +41,7 @@ use temporal_sdk_core_protos::{
37
41
  enums::v1::{TaskQueueKind, WorkflowTaskFailedCause},
38
42
  failure::v1::Failure,
39
43
  query::v1::{WorkflowQuery, WorkflowQueryResult},
40
- taskqueue::v1::{StickyExecutionAttributes, TaskQueue},
44
+ taskqueue::v1::{StickyExecutionAttributes, TaskQueue, TaskQueueMetadata},
41
45
  workflowservice::v1::{workflow_service_client::WorkflowServiceClient, *},
42
46
  },
43
47
  TaskToken,
@@ -53,8 +57,6 @@ use tower::ServiceBuilder;
53
57
  use url::Url;
54
58
  use uuid::Uuid;
55
59
 
56
- use temporal_sdk_core_protos::temporal::api::taskqueue::v1::TaskQueueMetadata;
57
-
58
60
  static LONG_POLL_METHOD_NAMES: [&str; 2] = ["PollWorkflowTaskQueue", "PollActivityTaskQueue"];
59
61
  /// The server times out polls after 60 seconds. Set our timeout to be slightly beyond that.
60
62
  const LONG_POLL_TIMEOUT: Duration = Duration::from_secs(70);
@@ -431,7 +433,7 @@ pub type WorkflowServiceClientWithMetrics = WorkflowServiceClient<InterceptedMet
431
433
  type InterceptedMetricsSvc = InterceptedService<GrpcMetricSvc, ServiceCallInterceptor>;
432
434
 
433
435
  /// Contains an instance of a namespace-bound client for interacting with the Temporal server
434
- #[derive(Debug)]
436
+ #[derive(Debug, Clone)]
435
437
  pub struct Client {
436
438
  /// Client for interacting with workflow service
437
439
  inner: ConfiguredClient<WorkflowServiceClientWithMetrics>,
@@ -475,11 +477,6 @@ impl Client {
475
477
  pub fn options(&self) -> &ClientOptions {
476
478
  &self.inner.options
477
479
  }
478
-
479
- /// Used to access the client as a [WorkflowService] implementor rather than the raw struct
480
- fn wf_svc(&self) -> impl RawClientLike<SvcType = InterceptedMetricsSvc> {
481
- self.raw_client().clone()
482
- }
483
480
  }
484
481
 
485
482
  /// This trait provides higher-level friendlier interaction with the server.
@@ -1036,3 +1033,49 @@ impl WorkflowClientTrait for Client {
1036
1033
  &self.namespace
1037
1034
  }
1038
1035
  }
1036
+
1037
+ mod sealed {
1038
+ use crate::{InterceptedMetricsSvc, RawClientLike, WorkflowClientTrait};
1039
+
1040
+ pub trait RawClientLikeUser {
1041
+ type RawClientT: RawClientLike<SvcType = InterceptedMetricsSvc>;
1042
+ /// Used to access the client as a [WorkflowService] implementor rather than the raw struct
1043
+ fn wf_svc(&self) -> Self::RawClientT;
1044
+ }
1045
+
1046
+ pub trait WfHandleClient: WorkflowClientTrait + RawClientLikeUser {}
1047
+ impl<T> WfHandleClient for T where T: WorkflowClientTrait + RawClientLikeUser {}
1048
+ }
1049
+
1050
+ impl RawClientLikeUser for Client {
1051
+ type RawClientT = WorkflowServiceClientWithMetrics;
1052
+
1053
+ fn wf_svc(&self) -> Self::RawClientT {
1054
+ self.raw_client().clone()
1055
+ }
1056
+ }
1057
+
1058
+ /// Additional methods for workflow clients
1059
+ pub trait WfClientExt: WfHandleClient + Sized {
1060
+ /// Create an untyped handle for a workflow execution, which can be used to do things like
1061
+ /// wait for that workflow's result. `run_id` may be left blank to target the latest run.
1062
+ fn get_untyped_workflow_handle(
1063
+ &self,
1064
+ workflow_id: impl Into<String>,
1065
+ run_id: impl Into<String>,
1066
+ ) -> UntypedWorkflowHandle<Self::RawClientT>
1067
+ where
1068
+ Self::RawClientT: Clone,
1069
+ {
1070
+ let rid = run_id.into();
1071
+ UntypedWorkflowHandle::new(
1072
+ self.wf_svc(),
1073
+ WorkflowExecutionInfo {
1074
+ namespace: self.namespace().to_string(),
1075
+ workflow_id: workflow_id.into(),
1076
+ run_id: if rid.is_empty() { None } else { Some(rid) },
1077
+ },
1078
+ )
1079
+ }
1080
+ }
1081
+ impl<T> WfClientExt for T where T: WfHandleClient + Sized {}
@@ -15,7 +15,7 @@ use tonic::{body::BoxBody, client::GrpcService, metadata::KeyAndValueRef};
15
15
 
16
16
  pub(super) mod sealed {
17
17
  use super::*;
18
- use crate::{ConfiguredClient, RetryClient};
18
+ use crate::{Client, ConfiguredClient, InterceptedMetricsSvc, RetryClient};
19
19
  use futures::TryFutureExt;
20
20
  use tonic::{Request, Response, Status};
21
21
 
@@ -104,6 +104,14 @@ pub(super) mod sealed {
104
104
  &mut self.client
105
105
  }
106
106
  }
107
+
108
+ impl RawClientLike for Client {
109
+ type SvcType = InterceptedMetricsSvc;
110
+
111
+ fn client(&mut self) -> &mut WorkflowServiceClient<Self::SvcType> {
112
+ &mut self.inner
113
+ }
114
+ }
107
115
  }
108
116
 
109
117
  /// Helper for cloning a tonic request as long as the inner message may be cloned.
@@ -1,5 +1,5 @@
1
1
  use crate::{
2
- ClientOptions, Result, RetryConfig, WorkflowClientTrait, WorkflowOptions,
2
+ ClientOptions, RawClientLikeUser, Result, RetryConfig, WorkflowClientTrait, WorkflowOptions,
3
3
  WorkflowTaskCompletion,
4
4
  };
5
5
  use backoff::{backoff::Backoff, ExponentialBackoff};
@@ -592,3 +592,14 @@ mod tests {
592
592
  }
593
593
  }
594
594
  }
595
+
596
+ impl<C> RawClientLikeUser for RetryClient<C>
597
+ where
598
+ C: RawClientLikeUser,
599
+ {
600
+ type RawClientT = C::RawClientT;
601
+
602
+ fn wf_svc(&self) -> Self::RawClientT {
603
+ self.client.wf_svc()
604
+ }
605
+ }
@@ -0,0 +1,183 @@
1
+ use crate::{InterceptedMetricsSvc, RawClientLike};
2
+ use anyhow::{anyhow, bail};
3
+ use std::marker::PhantomData;
4
+ use temporal_sdk_core_protos::{
5
+ coresdk::{common::Payload, FromPayloadsExt},
6
+ temporal::api::{
7
+ common::v1::WorkflowExecution, enums::v1::HistoryEventFilterType, failure::v1::Failure,
8
+ history::v1::history_event::Attributes,
9
+ workflowservice::v1::GetWorkflowExecutionHistoryRequest,
10
+ },
11
+ };
12
+
13
+ /// Enumerates terminal states for a particular workflow execution
14
+ // TODO: Add non-proto failure types, flesh out details, etc.
15
+ #[derive(Debug)]
16
+ #[allow(clippy::large_enum_variant)]
17
+ pub enum WorkflowExecutionResult<T> {
18
+ /// The workflow finished successfully
19
+ Succeeded(T),
20
+ /// The workflow finished in failure
21
+ Failed(Failure),
22
+ /// The workflow was cancelled
23
+ Cancelled(Vec<Payload>),
24
+ /// The workflow was terminated
25
+ Terminated(Vec<Payload>),
26
+ /// The workflow timed out
27
+ TimedOut,
28
+ /// The workflow continued as new
29
+ ContinuedAsNew,
30
+ }
31
+
32
+ /// Options for fetching workflow results
33
+ #[derive(Debug, Clone, Copy)]
34
+ pub struct GetWorkflowResultOpts {
35
+ /// If true (the default), follows to the next workflow run in the execution chain while
36
+ /// retrieving results.
37
+ pub follow_runs: bool,
38
+ }
39
+ impl Default for GetWorkflowResultOpts {
40
+ fn default() -> Self {
41
+ Self { follow_runs: true }
42
+ }
43
+ }
44
+
45
+ /// A workflow handle which can refer to a specific workflow run, or a chain of workflow runs with
46
+ /// the same workflow id.
47
+ pub struct WorkflowHandle<ClientT, ResultT> {
48
+ client: ClientT,
49
+ info: WorkflowExecutionInfo,
50
+
51
+ _res_type: PhantomData<ResultT>,
52
+ }
53
+
54
+ /// Holds needed information to refer to a specific workflow run, or workflow execution chain
55
+ pub struct WorkflowExecutionInfo {
56
+ /// Namespace the workflow lives in
57
+ pub namespace: String,
58
+ /// The workflow's id
59
+ pub workflow_id: String,
60
+ /// If set, target this specific run of the workflow
61
+ pub run_id: Option<String>,
62
+ }
63
+
64
+ impl WorkflowExecutionInfo {
65
+ /// Bind the workflow info to a specific client, turning it into a workflow handle
66
+ pub fn bind_untyped<CT>(self, client: CT) -> UntypedWorkflowHandle<CT>
67
+ where
68
+ CT: RawClientLike<SvcType = InterceptedMetricsSvc> + Clone,
69
+ {
70
+ UntypedWorkflowHandle::new(client, self)
71
+ }
72
+ }
73
+
74
+ /// A workflow handle to a workflow with unknown types. Uses raw payloads.
75
+ pub type UntypedWorkflowHandle<CT> = WorkflowHandle<CT, Vec<Payload>>;
76
+
77
+ impl<CT, RT> WorkflowHandle<CT, RT>
78
+ where
79
+ CT: RawClientLike<SvcType = InterceptedMetricsSvc> + Clone,
80
+ // TODO: Make more generic, capable of (de)serialization w/ serde
81
+ RT: FromPayloadsExt,
82
+ {
83
+ pub(crate) fn new(client: CT, info: WorkflowExecutionInfo) -> Self {
84
+ Self {
85
+ client,
86
+ info,
87
+ _res_type: PhantomData::<RT>,
88
+ }
89
+ }
90
+
91
+ pub async fn get_workflow_result(
92
+ &self,
93
+ opts: GetWorkflowResultOpts,
94
+ ) -> Result<WorkflowExecutionResult<RT>, anyhow::Error> {
95
+ let mut next_page_tok = vec![];
96
+ let mut run_id = self.info.run_id.clone().unwrap_or_default();
97
+ loop {
98
+ let server_res = self
99
+ .client
100
+ .clone()
101
+ .client()
102
+ .get_workflow_execution_history(GetWorkflowExecutionHistoryRequest {
103
+ namespace: self.info.namespace.to_string(),
104
+ execution: Some(WorkflowExecution {
105
+ workflow_id: self.info.workflow_id.clone(),
106
+ run_id: run_id.clone(),
107
+ }),
108
+ skip_archival: true,
109
+ wait_new_event: true,
110
+ history_event_filter_type: HistoryEventFilterType::CloseEvent as i32,
111
+ next_page_token: next_page_tok.clone(),
112
+ ..Default::default()
113
+ })
114
+ .await?
115
+ .into_inner();
116
+
117
+ let mut history = server_res
118
+ .history
119
+ .ok_or_else(|| anyhow!("Server returned an empty history!"))?;
120
+
121
+ if history.events.is_empty() {
122
+ next_page_tok = server_res.next_page_token;
123
+ continue;
124
+ }
125
+ // If page token was previously set, clear it.
126
+ next_page_tok = vec![];
127
+
128
+ let event_attrs = history.events.pop().and_then(|ev| ev.attributes);
129
+
130
+ macro_rules! follow {
131
+ ($attrs:ident) => {
132
+ if opts.follow_runs && $attrs.new_execution_run_id != "" {
133
+ run_id = $attrs.new_execution_run_id;
134
+ continue;
135
+ }
136
+ };
137
+ }
138
+
139
+ break match event_attrs {
140
+ Some(Attributes::WorkflowExecutionCompletedEventAttributes(attrs)) => {
141
+ follow!(attrs);
142
+ Ok(WorkflowExecutionResult::Succeeded(RT::from_payloads(
143
+ attrs.result,
144
+ )))
145
+ }
146
+ Some(Attributes::WorkflowExecutionFailedEventAttributes(attrs)) => {
147
+ follow!(attrs);
148
+ Ok(WorkflowExecutionResult::Failed(
149
+ attrs.failure.unwrap_or_default(),
150
+ ))
151
+ }
152
+ Some(Attributes::WorkflowExecutionCanceledEventAttributes(attrs)) => Ok(
153
+ WorkflowExecutionResult::Cancelled(Vec::from_payloads(attrs.details)),
154
+ ),
155
+ Some(Attributes::WorkflowExecutionTimedOutEventAttributes(attrs)) => {
156
+ follow!(attrs);
157
+ Ok(WorkflowExecutionResult::TimedOut)
158
+ }
159
+ Some(Attributes::WorkflowExecutionTerminatedEventAttributes(attrs)) => Ok(
160
+ WorkflowExecutionResult::Terminated(Vec::from_payloads(attrs.details)),
161
+ ),
162
+ Some(Attributes::WorkflowExecutionContinuedAsNewEventAttributes(attrs)) => {
163
+ if opts.follow_runs {
164
+ if !attrs.new_execution_run_id.is_empty() {
165
+ run_id = attrs.new_execution_run_id;
166
+ continue;
167
+ } else {
168
+ bail!("New execution run id was empty in continue as new event!");
169
+ }
170
+ } else {
171
+ Ok(WorkflowExecutionResult::ContinuedAsNew)
172
+ }
173
+ }
174
+ o => Err(anyhow!(
175
+ "Server returned an event that didn't match the CloseEvent filter. \
176
+ This is either a server bug or a new event the SDK does not understand. \
177
+ Event details: {:?}",
178
+ o
179
+ )),
180
+ };
181
+ }
182
+ }
183
+ }
@@ -1,18 +1,14 @@
1
1
  use crate::{
2
2
  replay::DEFAULT_WORKFLOW_TYPE,
3
- test_help::{
4
- build_mock_pollers, canned_histories, mock_worker, MockPollCfg, ResponseType, TEST_Q,
5
- },
3
+ test_help::{canned_histories, mock_sdk, MockPollCfg, ResponseType},
6
4
  worker::client::mocks::mock_workflow_client,
7
5
  workflow::managed_wf::ManagedWFFunc,
8
6
  };
9
- use std::sync::Arc;
10
7
  use temporal_client::WorkflowOptions;
11
8
  use temporal_sdk::{ChildWorkflowOptions, Signal, WfContext, WorkflowFunction, WorkflowResult};
12
9
  use temporal_sdk_core_protos::coresdk::child_workflow::{
13
10
  child_workflow_result, ChildWorkflowCancellationType,
14
11
  };
15
- use temporal_sdk_core_test_utils::TestWorker;
16
12
  use tokio::join;
17
13
 
18
14
  const SIGNAME: &str = "SIGNAME";
@@ -26,10 +22,12 @@ async fn signal_child_workflow(#[case] serial: bool) {
26
22
  let wf_type = DEFAULT_WORKFLOW_TYPE;
27
23
  let t = canned_histories::single_child_workflow_signaled("child-id-1", SIGNAME);
28
24
  let mock = mock_workflow_client();
29
- let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::AllHistory], mock);
30
- let mock = build_mock_pollers(mh);
31
- let core = mock_worker(mock);
32
- let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
25
+ let mut worker = mock_sdk(MockPollCfg::from_resp_batches(
26
+ wf_id,
27
+ t,
28
+ [ResponseType::AllHistory],
29
+ mock,
30
+ ));
33
31
 
34
32
  let wf = move |ctx: WfContext| async move {
35
33
  let child = ctx.child_workflow(ChildWorkflowOptions {
@@ -1,21 +1,15 @@
1
1
  use crate::{
2
2
  replay::DEFAULT_WORKFLOW_TYPE,
3
- test_help::{
4
- build_mock_pollers, canned_histories, mock_worker, MockPollCfg, ResponseType, TEST_Q,
5
- },
3
+ test_help::{canned_histories, mock_sdk, mock_sdk_cfg, MockPollCfg, ResponseType},
6
4
  worker::client::mocks::mock_workflow_client,
7
5
  };
8
6
  use std::{
9
- sync::{
10
- atomic::{AtomicBool, AtomicUsize, Ordering},
11
- Arc,
12
- },
7
+ sync::atomic::{AtomicBool, AtomicUsize, Ordering},
13
8
  time::Duration,
14
9
  };
15
10
  use temporal_client::WorkflowOptions;
16
11
  use temporal_sdk::{WfContext, WorkflowResult};
17
12
  use temporal_sdk_core_protos::temporal::api::enums::v1::WorkflowTaskFailedCause;
18
- use temporal_sdk_core_test_utils::TestWorker;
19
13
 
20
14
  static DID_FAIL: AtomicBool = AtomicBool::new(false);
21
15
  pub async fn timer_wf_fails_once(ctx: WfContext) -> WorkflowResult<()> {
@@ -32,7 +26,7 @@ pub async fn timer_wf_fails_once(ctx: WfContext) -> WorkflowResult<()> {
32
26
  /// Verifies that workflow panics (which in this case the Rust SDK turns into workflow activation
33
27
  /// failures) are turned into unspecified WFT failures.
34
28
  #[tokio::test]
35
- async fn test_wf_task_rejected_properly() {
29
+ async fn test_panic_wf_task_rejected_properly() {
36
30
  let wf_id = "fakeid";
37
31
  let wf_type = DEFAULT_WORKFLOW_TYPE;
38
32
  let t = canned_histories::workflow_fails_with_failure_after_timer("1");
@@ -43,9 +37,7 @@ async fn test_wf_task_rejected_properly() {
43
37
  mh.num_expected_fails = Some(1);
44
38
  mh.expect_fail_wft_matcher =
45
39
  Box::new(|_, cause, _| matches!(cause, WorkflowTaskFailedCause::Unspecified));
46
- let mock = build_mock_pollers(mh);
47
- let core = mock_worker(mock);
48
- let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
40
+ let mut worker = mock_sdk(mh);
49
41
 
50
42
  worker.register_wf(wf_type.to_owned(), timer_wf_fails_once);
51
43
  worker
@@ -82,14 +74,11 @@ async fn test_wf_task_rejected_properly_due_to_nondeterminism(#[case] use_cache:
82
74
  mh.num_expected_fails = Some(1);
83
75
  mh.expect_fail_wft_matcher =
84
76
  Box::new(|_, cause, _| matches!(cause, WorkflowTaskFailedCause::NonDeterministicError));
85
- let mut mock = build_mock_pollers(mh);
86
- if use_cache {
87
- mock.worker_cfg(|cfg| {
77
+ let mut worker = mock_sdk_cfg(mh, |cfg| {
78
+ if use_cache {
88
79
  cfg.max_cached_workflows = 2;
89
- });
90
- }
91
- let core = mock_worker(mock);
92
- let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
80
+ }
81
+ });
93
82
 
94
83
  let started_count: &'static _ = Box::leak(Box::new(AtomicUsize::new(0)));
95
84
  worker.register_wf(wf_type.to_owned(), move |ctx: WfContext| async move {
@@ -1,27 +1,23 @@
1
1
  use crate::{
2
2
  replay::{default_wes_attribs, TestHistoryBuilder, DEFAULT_WORKFLOW_TYPE},
3
- test_help::{build_mock_pollers, mock_worker, MockPollCfg, ResponseType, TEST_Q},
3
+ test_help::{mock_sdk, mock_sdk_cfg, MockPollCfg, ResponseType},
4
4
  worker::client::mocks::mock_workflow_client,
5
5
  };
6
6
  use anyhow::anyhow;
7
7
  use futures::future::join_all;
8
8
  use std::{
9
- sync::{
10
- atomic::{AtomicUsize, Ordering},
11
- Arc,
12
- },
9
+ sync::atomic::{AtomicUsize, Ordering},
13
10
  time::Duration,
14
11
  };
15
12
  use temporal_client::WorkflowOptions;
16
- use temporal_sdk::{LocalActivityOptions, WfContext, WorkflowResult};
13
+ use temporal_sdk::{ActContext, LocalActivityOptions, WfContext, WorkflowResult};
17
14
  use temporal_sdk_core_protos::{
18
15
  coresdk::{common::RetryPolicy, AsJsonPayloadExt},
19
16
  temporal::api::{enums::v1::EventType, failure::v1::Failure},
20
17
  };
21
- use temporal_sdk_core_test_utils::TestWorker;
22
18
  use tokio::sync::Barrier;
23
19
 
24
- async fn echo(e: String) -> anyhow::Result<String> {
20
+ async fn echo(_ctx: ActContext, e: String) -> anyhow::Result<String> {
25
21
  Ok(e)
26
22
  }
27
23
 
@@ -52,12 +48,11 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
52
48
  vec![1.into(), 2.into(), ResponseType::AllHistory]
53
49
  };
54
50
  let mh = MockPollCfg::from_resp_batches(wf_id, t, resps, mock);
55
- let mut mock = build_mock_pollers(mh);
56
- if cached {
57
- mock.worker_cfg(|cfg| cfg.max_cached_workflows = 1);
58
- }
59
- let core = mock_worker(mock);
60
- let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
51
+ let mut worker = mock_sdk_cfg(mh, |cfg| {
52
+ if cached {
53
+ cfg.max_cached_workflows = 1;
54
+ }
55
+ });
61
56
 
62
57
  worker.register_wf(
63
58
  DEFAULT_WORKFLOW_TYPE.to_owned(),
@@ -119,12 +114,13 @@ async fn local_act_many_concurrent() {
119
114
  let wf_id = "fakeid";
120
115
  let mock = mock_workflow_client();
121
116
  let mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 3], mock);
122
- let mock = build_mock_pollers(mh);
123
- let core = mock_worker(mock);
124
- let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
117
+ let mut worker = mock_sdk(mh);
125
118
 
126
119
  worker.register_wf(DEFAULT_WORKFLOW_TYPE.to_owned(), local_act_fanout_wf);
127
- worker.register_activity("echo", |str: String| async move { Ok(str) });
120
+ worker.register_activity(
121
+ "echo",
122
+ |_ctx: ActContext, str: String| async move { Ok(str) },
123
+ );
128
124
  worker
129
125
  .submit_wf(
130
126
  wf_id.to_owned(),
@@ -167,10 +163,9 @@ async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
167
163
  // and might poll an extra time
168
164
  let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 2, 2, 2], mock);
169
165
  mh.enforce_correct_number_of_polls = false;
170
- let mut mock = build_mock_pollers(mh);
171
- mock.worker_cfg(|wc| wc.max_cached_workflows = 1);
172
- let core = Arc::new(mock_worker(mock));
173
- let mut worker = TestWorker::new(core.clone(), TEST_Q.to_string());
166
+ let mut worker = mock_sdk_cfg(mh, |wc| wc.max_cached_workflows = 1);
167
+ let core = worker.orig_core_worker.clone();
168
+
174
169
  let shutdown_barr: &'static Barrier = Box::leak(Box::new(Barrier::new(2)));
175
170
 
176
171
  worker.register_wf(
@@ -185,7 +180,7 @@ async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
185
180
  Ok(().into())
186
181
  },
187
182
  );
188
- worker.register_activity("echo", move |str: String| async move {
183
+ worker.register_activity("echo", move |_ctx: ActContext, str: String| async move {
189
184
  if shutdown_middle {
190
185
  shutdown_barr.wait().await;
191
186
  }
@@ -226,9 +221,7 @@ async fn local_act_fail_and_retry(#[case] eventually_pass: bool) {
226
221
  let wf_id = "fakeid";
227
222
  let mock = mock_workflow_client();
228
223
  let mh = MockPollCfg::from_resp_batches(wf_id, t, [1], mock);
229
- let mock = build_mock_pollers(mh);
230
- let core = mock_worker(mock);
231
- let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
224
+ let mut worker = mock_sdk(mh);
232
225
 
233
226
  worker.register_wf(
234
227
  DEFAULT_WORKFLOW_TYPE.to_owned(),
@@ -256,7 +249,7 @@ async fn local_act_fail_and_retry(#[case] eventually_pass: bool) {
256
249
  },
257
250
  );
258
251
  let attempts: &'static _ = Box::leak(Box::new(AtomicUsize::new(0)));
259
- worker.register_activity("echo", move |_: String| async move {
252
+ worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
260
253
  // Succeed on 3rd attempt (which is ==2 since fetch_add returns prev val)
261
254
  if 2 == attempts.fetch_add(1, Ordering::Relaxed) && eventually_pass {
262
255
  Ok(())
@@ -309,10 +302,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
309
302
  [1.into(), 2.into(), ResponseType::AllHistory],
310
303
  mock,
311
304
  );
312
- let mut mock = build_mock_pollers(mh);
313
- mock.worker_cfg(|w| w.max_cached_workflows = 1);
314
- let core = mock_worker(mock);
315
- let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
305
+ let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
316
306
 
317
307
  worker.register_wf(
318
308
  DEFAULT_WORKFLOW_TYPE.to_owned(),
@@ -338,7 +328,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
338
328
  Ok(().into())
339
329
  },
340
330
  );
341
- worker.register_activity("echo", move |_: String| async move {
331
+ worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
342
332
  Result::<(), _>::Err(anyhow!("Oh no I failed!"))
343
333
  });
344
334
  worker