@temporalio/core-bridge 1.8.6 → 1.9.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 (213) hide show
  1. package/Cargo.lock +670 -594
  2. package/Cargo.toml +2 -1
  3. package/lib/errors.js +6 -6
  4. package/lib/errors.js.map +1 -1
  5. package/lib/index.d.ts +17 -44
  6. package/lib/index.js.map +1 -1
  7. package/package.json +5 -6
  8. package/releases/aarch64-apple-darwin/index.node +0 -0
  9. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  10. package/releases/x86_64-apple-darwin/index.node +0 -0
  11. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  12. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  13. package/sdk-core/.github/workflows/heavy.yml +4 -0
  14. package/sdk-core/.github/workflows/per-pr.yml +96 -0
  15. package/sdk-core/ARCHITECTURE.md +1 -1
  16. package/sdk-core/Cargo.toml +10 -0
  17. package/sdk-core/LICENSE.txt +0 -2
  18. package/sdk-core/README.md +37 -21
  19. package/sdk-core/client/Cargo.toml +7 -4
  20. package/sdk-core/client/src/lib.rs +274 -142
  21. package/sdk-core/client/src/metrics.rs +68 -57
  22. package/sdk-core/client/src/raw.rs +191 -45
  23. package/sdk-core/client/src/retry.rs +20 -0
  24. package/sdk-core/client/src/worker_registry/mod.rs +264 -0
  25. package/sdk-core/client/src/workflow_handle/mod.rs +2 -1
  26. package/sdk-core/core/Cargo.toml +17 -19
  27. package/sdk-core/core/src/core_tests/activity_tasks.rs +4 -2
  28. package/sdk-core/core/src/core_tests/child_workflows.rs +7 -7
  29. package/sdk-core/core/src/core_tests/mod.rs +1 -0
  30. package/sdk-core/core/src/core_tests/queries.rs +42 -1
  31. package/sdk-core/core/src/core_tests/replay_flag.rs +29 -39
  32. package/sdk-core/core/src/core_tests/updates.rs +73 -0
  33. package/sdk-core/core/src/core_tests/workflow_tasks.rs +52 -1
  34. package/sdk-core/core/src/ephemeral_server/mod.rs +34 -11
  35. package/sdk-core/core/src/internal_flags.rs +7 -1
  36. package/sdk-core/core/src/lib.rs +19 -36
  37. package/sdk-core/core/src/protosext/mod.rs +12 -4
  38. package/sdk-core/core/src/protosext/protocol_messages.rs +102 -0
  39. package/sdk-core/core/src/replay/mod.rs +99 -48
  40. package/sdk-core/core/src/telemetry/log_export.rs +161 -28
  41. package/sdk-core/core/src/telemetry/metrics.rs +869 -248
  42. package/sdk-core/core/src/telemetry/mod.rs +153 -257
  43. package/sdk-core/core/src/telemetry/prometheus_server.rs +36 -31
  44. package/sdk-core/core/src/test_help/mod.rs +64 -5
  45. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +12 -2
  46. package/sdk-core/core/src/worker/activities.rs +276 -10
  47. package/sdk-core/core/src/worker/client/mocks.rs +18 -0
  48. package/sdk-core/core/src/worker/client.rs +16 -3
  49. package/sdk-core/core/src/worker/mod.rs +45 -28
  50. package/sdk-core/core/src/worker/slot_provider.rs +175 -0
  51. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +27 -34
  52. package/sdk-core/core/src/worker/workflow/history_update.rs +5 -2
  53. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +71 -95
  54. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +34 -22
  55. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +50 -34
  56. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +106 -92
  57. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +22 -21
  58. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +386 -499
  59. package/sdk-core/core/src/worker/workflow/machines/mod.rs +12 -2
  60. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +33 -26
  61. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +198 -215
  62. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +67 -63
  63. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +88 -119
  64. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +3 -1
  65. package/sdk-core/core/src/worker/workflow/machines/update_state_machine.rs +411 -0
  66. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +27 -26
  67. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +319 -94
  68. package/sdk-core/core/src/worker/workflow/managed_run.rs +179 -132
  69. package/sdk-core/core/src/worker/workflow/mod.rs +129 -58
  70. package/sdk-core/core/src/worker/workflow/run_cache.rs +16 -26
  71. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +2 -2
  72. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +48 -43
  73. package/sdk-core/core-api/Cargo.toml +8 -7
  74. package/sdk-core/core-api/src/lib.rs +4 -12
  75. package/sdk-core/core-api/src/telemetry/metrics.rs +334 -0
  76. package/sdk-core/core-api/src/telemetry.rs +53 -42
  77. package/sdk-core/core-api/src/worker.rs +7 -0
  78. package/sdk-core/{.buildkite/docker → docker}/docker-compose.yaml +1 -1
  79. package/sdk-core/etc/dynamic-config.yaml +11 -1
  80. package/sdk-core/fsm/LICENSE.txt +0 -2
  81. package/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +1 -1
  82. package/sdk-core/fsm/rustfsm_procmacro/LICENSE.txt +0 -2
  83. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +1 -3
  84. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +2 -2
  85. package/sdk-core/fsm/rustfsm_trait/LICENSE.txt +0 -2
  86. package/sdk-core/sdk/Cargo.toml +2 -2
  87. package/sdk-core/sdk/src/lib.rs +85 -7
  88. package/sdk-core/sdk/src/workflow_context/options.rs +4 -0
  89. package/sdk-core/sdk/src/workflow_context.rs +43 -15
  90. package/sdk-core/sdk/src/workflow_future.rs +334 -204
  91. package/sdk-core/sdk-core-protos/Cargo.toml +3 -3
  92. package/sdk-core/sdk-core-protos/build.rs +14 -14
  93. package/sdk-core/sdk-core-protos/protos/api_upstream/.buildkite/Dockerfile +2 -0
  94. package/sdk-core/sdk-core-protos/protos/api_upstream/Makefile +99 -0
  95. package/sdk-core/sdk-core-protos/protos/api_upstream/api-linter.yaml +56 -0
  96. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.gen.yaml +20 -0
  97. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.lock +11 -0
  98. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.yaml +18 -0
  99. package/sdk-core/sdk-core-protos/protos/api_upstream/google/api/annotations.proto +31 -0
  100. package/sdk-core/sdk-core-protos/protos/api_upstream/google/api/http.proto +379 -0
  101. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/any.proto +162 -0
  102. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/descriptor.proto +1212 -0
  103. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/duration.proto +115 -0
  104. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/empty.proto +51 -0
  105. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/timestamp.proto +144 -0
  106. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/wrappers.proto +123 -0
  107. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/batch/v1/message.proto +12 -9
  108. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/command/v1/message.proto +11 -13
  109. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/common/v1/message.proto +33 -4
  110. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
  111. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/reset.proto +4 -4
  112. package/sdk-core/{protos/api_upstream/build/tools.go → sdk-core-protos/protos/api_upstream/temporal/api/export/v1/message.proto} +22 -6
  113. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/filter/v1/message.proto +2 -4
  114. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/history/v1/message.proto +21 -23
  115. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/namespace/v1/message.proto +2 -4
  116. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/operatorservice/v1/request_response.proto +2 -0
  117. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -0
  118. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/replication/v1/message.proto +1 -3
  119. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/schedule/v1/message.proto +36 -20
  120. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +13 -0
  121. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +66 -0
  122. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -4
  123. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/update/v1/message.proto +1 -1
  124. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/version/v1/message.proto +2 -3
  125. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/workflow/v1/message.proto +24 -22
  126. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/workflowservice/v1/request_response.proto +84 -32
  127. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/workflowservice/v1/service.proto +205 -47
  128. package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +57 -0
  129. package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +27 -0
  130. package/sdk-core/sdk-core-protos/src/history_builder.rs +67 -2
  131. package/sdk-core/sdk-core-protos/src/history_info.rs +1 -1
  132. package/sdk-core/sdk-core-protos/src/lib.rs +76 -3
  133. package/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  134. package/sdk-core/test-utils/Cargo.toml +6 -1
  135. package/sdk-core/test-utils/src/canned_histories.rs +3 -57
  136. package/sdk-core/test-utils/src/interceptors.rs +46 -0
  137. package/sdk-core/test-utils/src/lib.rs +106 -38
  138. package/sdk-core/tests/integ_tests/metrics_tests.rs +110 -15
  139. package/sdk-core/tests/integ_tests/queries_tests.rs +174 -3
  140. package/sdk-core/tests/integ_tests/update_tests.rs +908 -0
  141. package/sdk-core/tests/integ_tests/visibility_tests.rs +4 -4
  142. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +44 -1
  143. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -1
  144. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  145. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -4
  146. package/sdk-core/tests/integ_tests/workflow_tests/eager.rs +61 -0
  147. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +27 -2
  148. package/sdk-core/tests/integ_tests/workflow_tests.rs +142 -3
  149. package/sdk-core/tests/main.rs +2 -1
  150. package/sdk-core/tests/runner.rs +15 -2
  151. package/src/conversions.rs +107 -96
  152. package/src/helpers.rs +74 -0
  153. package/src/runtime.rs +29 -15
  154. package/src/worker.rs +14 -61
  155. package/ts/index.ts +23 -54
  156. package/sdk-core/.buildkite/docker/Dockerfile +0 -9
  157. package/sdk-core/.buildkite/docker/build.sh +0 -5
  158. package/sdk-core/.buildkite/docker/docker-compose-ci.yaml +0 -27
  159. package/sdk-core/.buildkite/pipeline.yml +0 -57
  160. package/sdk-core/.github/workflows/semgrep.yml +0 -25
  161. package/sdk-core/client/LICENSE.txt +0 -23
  162. package/sdk-core/core/LICENSE.txt +0 -23
  163. package/sdk-core/core/src/worker/workflow/bridge.rs +0 -35
  164. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +0 -215
  165. package/sdk-core/core-api/LICENSE.txt +0 -23
  166. package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +0 -2
  167. package/sdk-core/protos/api_upstream/Makefile +0 -80
  168. package/sdk-core/protos/api_upstream/api-linter.yaml +0 -40
  169. package/sdk-core/protos/api_upstream/buf.yaml +0 -9
  170. package/sdk-core/protos/api_upstream/build/go.mod +0 -7
  171. package/sdk-core/protos/api_upstream/build/go.sum +0 -5
  172. package/sdk-core/protos/api_upstream/go.mod +0 -6
  173. package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +0 -141
  174. package/sdk-core/sdk/LICENSE.txt +0 -23
  175. package/sdk-core/sdk-core-protos/LICENSE.txt +0 -23
  176. /package/sdk-core/{.buildkite/docker → docker}/docker-compose-telem.yaml +0 -0
  177. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.buildkite/docker-compose.yml +0 -0
  178. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.buildkite/pipeline.yml +0 -0
  179. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/CODEOWNERS +0 -0
  180. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  181. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/workflows/publish-docs.yml +0 -0
  182. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/workflows/trigger-api-go-update.yml +0 -0
  183. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/LICENSE +0 -0
  184. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/README.md +0 -0
  185. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/batch_operation.proto +0 -0
  186. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/command_type.proto +0 -0
  187. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/common.proto +0 -0
  188. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/event_type.proto +0 -0
  189. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/namespace.proto +0 -0
  190. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/query.proto +0 -0
  191. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/schedule.proto +0 -0
  192. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/task_queue.proto +0 -0
  193. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/update.proto +0 -0
  194. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/workflow.proto +0 -0
  195. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/errordetails/v1/message.proto +0 -0
  196. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/failure/v1/message.proto +0 -0
  197. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/protocol/v1/message.proto +0 -0
  198. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/query/v1/message.proto +0 -0
  199. /package/sdk-core/{protos → sdk-core-protos/protos}/google/rpc/status.proto +0 -0
  200. /package/sdk-core/{protos → sdk-core-protos/protos}/grpc/health/v1/health.proto +0 -0
  201. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/activity_result/activity_result.proto +0 -0
  202. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/activity_task/activity_task.proto +0 -0
  203. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/child_workflow/child_workflow.proto +0 -0
  204. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/common/common.proto +0 -0
  205. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/core_interface.proto +0 -0
  206. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/external_data/external_data.proto +0 -0
  207. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +0 -0
  208. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/Makefile +0 -0
  209. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/api-linter.yaml +0 -0
  210. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/buf.yaml +0 -0
  211. /package/sdk-core/{protos/api_upstream → sdk-core-protos/protos/testsrv_upstream}/dependencies/gogoproto/gogo.proto +0 -0
  212. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +0 -0
  213. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/temporal/api/testservice/v1/service.proto +0 -0
@@ -13,6 +13,7 @@ use crate::{
13
13
  LocalActivityExecutionResult,
14
14
  },
15
15
  };
16
+ use itertools::Itertools;
16
17
  use rustfsm::{fsm, MachineError, StateMachine, TransitionResult};
17
18
  use std::{
18
19
  convert::TryFrom,
@@ -30,7 +31,7 @@ use temporal_sdk_core_protos::{
30
31
  workflow_commands::ActivityCancellationType,
31
32
  },
32
33
  temporal::api::{
33
- command::v1::{Command, RecordMarkerCommandAttributes},
34
+ command::v1::{command, RecordMarkerCommandAttributes},
34
35
  enums::v1::{CommandType, EventType, RetryState},
35
36
  failure::v1::{failure::FailureInfo, Failure},
36
37
  history::v1::HistoryEvent,
@@ -617,10 +618,8 @@ impl Cancellable for LocalActivityMachine {
617
618
  let mach_resps = cmds
618
619
  .into_iter()
619
620
  .map(|mc| self.adapt_response(mc, None))
620
- .collect::<Result<Vec<_>, _>>()?
621
- .into_iter()
622
- .flatten()
623
- .collect();
621
+ .flatten_ok()
622
+ .try_collect()?;
624
623
  Ok(mach_resps)
625
624
  }
626
625
 
@@ -781,10 +780,9 @@ impl WFMachinesAdapter for LocalActivityMachine {
781
780
  header: None,
782
781
  failure: maybe_failure,
783
782
  };
784
- responses.push(MachineResponse::IssueNewCommand(Command {
785
- command_type: CommandType::RecordMarker as i32,
786
- attributes: Some(marker_data.into()),
787
- }));
783
+ responses.push(MachineResponse::IssueNewCommand(
784
+ command::Attributes::RecordMarkerCommandAttributes(marker_data).into(),
785
+ ));
788
786
  }
789
787
  Ok(responses)
790
788
  }
@@ -879,27 +877,40 @@ fn verify_marker_data_matches(
879
877
  mod tests {
880
878
  use super::*;
881
879
  use crate::{
882
- replay::TestHistoryBuilder, test_help::canned_histories, worker::workflow::ManagedWFFunc,
880
+ replay::TestHistoryBuilder,
881
+ test_help::{build_fake_sdk, canned_histories, MockPollCfg, ResponseType},
883
882
  };
883
+ use anyhow::anyhow;
884
884
  use rstest::rstest;
885
- use std::time::Duration;
885
+ use std::{
886
+ sync::atomic::{AtomicI64, Ordering},
887
+ time::Duration,
888
+ };
886
889
  use temporal_sdk::{
887
- CancellableFuture, LocalActivityOptions, WfContext, WorkflowFunction, WorkflowResult,
890
+ ActContext, ActivityCancelledError, CancellableFuture, LocalActivityOptions, WfContext,
891
+ WorkflowResult,
888
892
  };
889
893
  use temporal_sdk_core_protos::{
890
894
  coresdk::{
891
- activity_result::ActivityExecutionResult,
892
895
  workflow_activation::{workflow_activation_job, WorkflowActivationJob},
896
+ AsJsonPayloadExt,
893
897
  },
894
898
  temporal::api::{
895
- command::v1::command, enums::v1::WorkflowTaskFailedCause, failure::v1::Failure,
899
+ common::v1::RetryPolicy, enums::v1::WorkflowTaskFailedCause, failure::v1::Failure,
896
900
  },
897
- DEFAULT_ACTIVITY_TYPE,
901
+ DEFAULT_ACTIVITY_TYPE, DEFAULT_WORKFLOW_TYPE,
898
902
  };
903
+ use temporal_sdk_core_test_utils::interceptors::ActivationAssertionsInterceptor;
904
+ use tokio_util::sync::CancellationToken;
899
905
 
900
906
  async fn la_wf(ctx: WfContext) -> WorkflowResult<()> {
901
907
  ctx.local_activity(LocalActivityOptions {
902
908
  activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
909
+ input: ().as_json_payload().unwrap(),
910
+ retry_policy: RetryPolicy {
911
+ maximum_attempts: 1,
912
+ ..Default::default()
913
+ },
903
914
  ..Default::default()
904
915
  })
905
916
  .await;
@@ -913,7 +924,6 @@ mod tests {
913
924
  #[case::replay_fail(true, false)]
914
925
  #[tokio::test]
915
926
  async fn one_la_success(#[case] replay: bool, #[case] completes_ok: bool) {
916
- let func = WorkflowFunction::new(la_wf);
917
927
  let activity_id = "1";
918
928
  let mut t = TestHistoryBuilder::default();
919
929
  t.add_by_type(EventType::WorkflowExecutionStarted);
@@ -927,202 +937,95 @@ mod tests {
927
937
  Failure::application_failure("I failed".to_string(), false),
928
938
  );
929
939
  }
930
- t.add_workflow_execution_completed();
940
+ t.add_workflow_task_scheduled_and_started();
931
941
 
932
- let histinfo = if replay {
933
- t.get_full_history_info().unwrap().into()
942
+ let mut mock_cfg = if replay {
943
+ MockPollCfg::from_resps(t, [ResponseType::AllHistory])
934
944
  } else {
935
- t.get_history_info(1).unwrap().into()
945
+ MockPollCfg::from_hist_builder(t)
936
946
  };
937
- let mut wfm = ManagedWFFunc::new_from_update(histinfo, func, vec![]);
938
-
939
- // First activation will have no server commands. Activity will be put into the activity
940
- // queue locally
941
- wfm.get_next_activation().await.unwrap();
942
- let commands = wfm.get_server_commands().commands;
943
- assert_eq!(commands.len(), 0);
944
-
945
- let ready_to_execute_las = wfm.drain_queued_local_activities();
946
- if !replay {
947
- assert_eq!(ready_to_execute_las.len(), 1);
948
- } else {
949
- assert_eq!(ready_to_execute_las.len(), 0);
950
- }
951
-
952
- if !replay {
953
- if completes_ok {
954
- wfm.complete_local_activity(1, ActivityExecutionResult::ok(b"hi".into()))
955
- .unwrap();
956
- } else {
957
- wfm.complete_local_activity(
958
- 1,
959
- ActivityExecutionResult::fail(Failure {
960
- message: "I failed".to_string(),
961
- ..Default::default()
962
- }),
963
- )
964
- .unwrap();
965
- }
966
- }
967
-
968
- // Now the next activation will unblock the local activity
969
- wfm.get_next_activation().await.unwrap();
970
- let commands = wfm.get_server_commands().commands;
971
- if replay {
972
- assert_eq!(commands.len(), 1);
973
- assert_eq!(
974
- commands[0].command_type,
975
- CommandType::CompleteWorkflowExecution as i32
976
- );
977
- } else {
978
- assert_eq!(commands.len(), 2);
979
- assert_eq!(commands[0].command_type, CommandType::RecordMarker as i32);
980
- if completes_ok {
981
- assert_matches!(
982
- commands[0].attributes.as_ref().unwrap(),
983
- command::Attributes::RecordMarkerCommandAttributes(
984
- RecordMarkerCommandAttributes { failure: None, .. }
985
- )
986
- );
987
- } else {
988
- assert_matches!(
989
- commands[0].attributes.as_ref().unwrap(),
990
- command::Attributes::RecordMarkerCommandAttributes(
991
- RecordMarkerCommandAttributes {
992
- failure: Some(_),
993
- ..
994
- }
995
- )
996
- );
997
- }
998
- assert_eq!(
999
- commands[1].command_type,
1000
- CommandType::CompleteWorkflowExecution as i32
1001
- );
1002
- }
1003
-
1004
- if !replay {
1005
- wfm.new_history(t.get_full_history_info().unwrap().into())
1006
- .await
1007
- .unwrap();
1008
- }
1009
- assert_eq!(wfm.drain_queued_local_activities().len(), 0);
1010
- assert_eq!(wfm.get_next_activation().await.unwrap().jobs.len(), 0);
1011
- let commands = wfm.get_server_commands().commands;
1012
- assert_eq!(commands.len(), 0);
947
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
948
+ asserts.then(move |wft| {
949
+ let commands = &wft.commands;
950
+ if !replay {
951
+ assert_eq!(commands.len(), 2);
952
+ assert_eq!(commands[0].command_type(), CommandType::RecordMarker);
953
+ if completes_ok {
954
+ assert_matches!(
955
+ commands[0].attributes.as_ref().unwrap(),
956
+ command::Attributes::RecordMarkerCommandAttributes(
957
+ RecordMarkerCommandAttributes { failure: None, .. }
958
+ )
959
+ );
960
+ } else {
961
+ assert_matches!(
962
+ commands[0].attributes.as_ref().unwrap(),
963
+ command::Attributes::RecordMarkerCommandAttributes(
964
+ RecordMarkerCommandAttributes {
965
+ failure: Some(_),
966
+ ..
967
+ }
968
+ )
969
+ );
970
+ }
971
+ assert_eq!(
972
+ commands[1].command_type(),
973
+ CommandType::CompleteWorkflowExecution
974
+ );
975
+ } else {
976
+ assert_eq!(commands.len(), 1);
977
+ assert_matches!(
978
+ commands[0].command_type(),
979
+ CommandType::CompleteWorkflowExecution
980
+ );
981
+ }
982
+ });
983
+ });
1013
984
 
1014
- wfm.shutdown().await.unwrap();
985
+ let mut worker = build_fake_sdk(mock_cfg);
986
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, la_wf);
987
+ worker.register_activity(
988
+ DEFAULT_ACTIVITY_TYPE,
989
+ move |_ctx: ActContext, _: ()| async move {
990
+ if replay {
991
+ panic!("Should not be invoked on replay");
992
+ }
993
+ if completes_ok {
994
+ Ok("hi")
995
+ } else {
996
+ Err(anyhow!("Oh no I failed!"))
997
+ }
998
+ },
999
+ );
1000
+ worker.run().await.unwrap();
1015
1001
  }
1016
1002
 
1017
1003
  async fn two_la_wf(ctx: WfContext) -> WorkflowResult<()> {
1018
1004
  ctx.local_activity(LocalActivityOptions {
1019
1005
  activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
1006
+ input: ().as_json_payload().unwrap(),
1020
1007
  ..Default::default()
1021
1008
  })
1022
1009
  .await;
1023
1010
  ctx.local_activity(LocalActivityOptions {
1024
1011
  activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
1012
+ input: ().as_json_payload().unwrap(),
1025
1013
  ..Default::default()
1026
1014
  })
1027
1015
  .await;
1028
1016
  Ok(().into())
1029
1017
  }
1030
1018
 
1031
- #[rstest]
1032
- #[case::incremental(false)]
1033
- #[case::replay(true)]
1034
- #[tokio::test]
1035
- async fn two_sequential_las(#[case] replay: bool) {
1036
- let func = WorkflowFunction::new(two_la_wf);
1037
- let t = canned_histories::two_local_activities_one_wft(false);
1038
- let histinfo = if replay {
1039
- t.get_full_history_info().unwrap().into()
1040
- } else {
1041
- t.get_history_info(1).unwrap().into()
1042
- };
1043
- let mut wfm = ManagedWFFunc::new_from_update(histinfo, func, vec![]);
1044
-
1045
- // First activation will have no server commands. Activity will be put into the activity
1046
- // queue locally
1047
- let act = wfm.get_next_activation().await.unwrap();
1048
- let first_act_ts = act.timestamp.unwrap();
1049
- let commands = wfm.get_server_commands().commands;
1050
- assert_eq!(commands.len(), 0);
1051
- let ready_to_execute_las = wfm.drain_queued_local_activities();
1052
- let num_queued = usize::from(!replay);
1053
- assert_eq!(ready_to_execute_las.len(), num_queued);
1054
-
1055
- if !replay {
1056
- wfm.complete_local_activity(1, ActivityExecutionResult::ok(b"Resolved".into()))
1057
- .unwrap();
1058
- }
1059
-
1060
- let act = wfm.get_next_activation().await.unwrap();
1061
- // Verify LAs advance time (they take 1s in this test)
1062
- assert_eq!(act.timestamp.unwrap().seconds, first_act_ts.seconds + 1);
1063
- assert_matches!(
1064
- act.jobs.as_slice(),
1065
- [WorkflowActivationJob {
1066
- variant: Some(workflow_activation_job::Variant::ResolveActivity(ra))
1067
- }] => assert_eq!(ra.seq, 1)
1068
- );
1069
- let ready_to_execute_las = wfm.drain_queued_local_activities();
1070
- if !replay {
1071
- assert_eq!(ready_to_execute_las.len(), 1);
1072
- } else {
1073
- assert_eq!(ready_to_execute_las.len(), 0);
1074
- }
1075
-
1076
- if !replay {
1077
- wfm.complete_local_activity(2, ActivityExecutionResult::ok(b"Resolved".into()))
1078
- .unwrap();
1079
- }
1080
-
1081
- let act = wfm.get_next_activation().await.unwrap();
1082
- assert_eq!(act.timestamp.unwrap().seconds, first_act_ts.seconds + 2);
1083
- assert_matches!(
1084
- act.jobs.as_slice(),
1085
- [WorkflowActivationJob {
1086
- variant: Some(workflow_activation_job::Variant::ResolveActivity(ra))
1087
- }] => assert_eq!(ra.seq, 2)
1088
- );
1089
- let commands = wfm.get_server_commands().commands;
1090
- if replay {
1091
- assert_eq!(commands.len(), 1);
1092
- assert_eq!(
1093
- commands[0].command_type,
1094
- CommandType::CompleteWorkflowExecution as i32
1095
- );
1096
- } else {
1097
- assert_eq!(commands.len(), 3);
1098
- assert_eq!(commands[0].command_type, CommandType::RecordMarker as i32);
1099
- assert_eq!(commands[1].command_type, CommandType::RecordMarker as i32);
1100
- assert_eq!(
1101
- commands[2].command_type,
1102
- CommandType::CompleteWorkflowExecution as i32
1103
- );
1104
- }
1105
-
1106
- if !replay {
1107
- wfm.new_history(t.get_full_history_info().unwrap().into())
1108
- .await
1109
- .unwrap();
1110
- }
1111
- assert_eq!(wfm.get_next_activation().await.unwrap().jobs.len(), 0);
1112
- let commands = wfm.get_server_commands().commands;
1113
- assert_eq!(commands.len(), 0);
1114
-
1115
- wfm.shutdown().await.unwrap();
1116
- }
1117
-
1118
1019
  async fn two_la_wf_parallel(ctx: WfContext) -> WorkflowResult<()> {
1119
1020
  tokio::join!(
1120
1021
  ctx.local_activity(LocalActivityOptions {
1121
1022
  activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
1023
+ input: ().as_json_payload().unwrap(),
1122
1024
  ..Default::default()
1123
1025
  }),
1124
1026
  ctx.local_activity(LocalActivityOptions {
1125
1027
  activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
1028
+ input: ().as_json_payload().unwrap(),
1126
1029
  ..Default::default()
1127
1030
  })
1128
1031
  );
@@ -1130,91 +1033,112 @@ mod tests {
1130
1033
  }
1131
1034
 
1132
1035
  #[rstest]
1133
- #[case::incremental(false)]
1134
- #[case::replay(true)]
1135
1036
  #[tokio::test]
1136
- async fn two_parallel_las(#[case] replay: bool) {
1137
- let func = WorkflowFunction::new(two_la_wf_parallel);
1138
- let t = canned_histories::two_local_activities_one_wft(true);
1139
- let histinfo = if replay {
1140
- t.get_full_history_info().unwrap().into()
1037
+ async fn two_sequential_las(
1038
+ #[values(true, false)] replay: bool,
1039
+ #[values(true, false)] parallel: bool,
1040
+ ) {
1041
+ let t = canned_histories::two_local_activities_one_wft(parallel);
1042
+ let mut mock_cfg = if replay {
1043
+ MockPollCfg::from_resps(t, [ResponseType::AllHistory])
1141
1044
  } else {
1142
- t.get_history_info(1).unwrap().into()
1045
+ MockPollCfg::from_hist_builder(t)
1143
1046
  };
1144
- let mut wfm = ManagedWFFunc::new_from_update(histinfo, func, vec![]);
1145
-
1146
- // First activation will have no server commands. Activity(ies) will be put into the queue
1147
- // for execution
1148
- let act = wfm.get_next_activation().await.unwrap();
1149
- let first_act_ts = act.timestamp.unwrap();
1150
- let commands = wfm.get_server_commands().commands;
1151
- assert_eq!(commands.len(), 0);
1152
- let ready_to_execute_las = wfm.drain_queued_local_activities();
1153
- let num_queued = if !replay { 2 } else { 0 };
1154
- assert_eq!(ready_to_execute_las.len(), num_queued);
1155
1047
 
1156
- if !replay {
1157
- wfm.complete_local_activity(1, ActivityExecutionResult::ok(b"Resolved".into()))
1158
- .unwrap();
1159
- wfm.complete_local_activity(2, ActivityExecutionResult::ok(b"Resolved".into()))
1160
- .unwrap();
1048
+ let mut aai = ActivationAssertionsInterceptor::default();
1049
+ let first_act_ts_seconds: &'static _ = Box::leak(Box::new(AtomicI64::new(-1)));
1050
+ aai.then(|a| {
1051
+ first_act_ts_seconds.store(a.timestamp.as_ref().unwrap().seconds, Ordering::Relaxed)
1052
+ });
1053
+ // Verify LAs advance time (they take 1s as defined in the canned history)
1054
+ aai.then(move |a| {
1055
+ if !parallel {
1056
+ assert_matches!(
1057
+ a.jobs.as_slice(),
1058
+ [WorkflowActivationJob {
1059
+ variant: Some(workflow_activation_job::Variant::ResolveActivity(ra))
1060
+ }] => assert_eq!(ra.seq, 1)
1061
+ );
1062
+ } else {
1063
+ assert_matches!(
1064
+ a.jobs.as_slice(),
1065
+ [WorkflowActivationJob {
1066
+ variant: Some(workflow_activation_job::Variant::ResolveActivity(ra))
1067
+ }, WorkflowActivationJob {
1068
+ variant: Some(workflow_activation_job::Variant::ResolveActivity(ra2))
1069
+ }] => {assert_eq!(ra.seq, 1); assert_eq!(ra2.seq, 2)}
1070
+ );
1071
+ }
1072
+ if replay {
1073
+ assert!(
1074
+ a.timestamp.as_ref().unwrap().seconds
1075
+ > first_act_ts_seconds.load(Ordering::Relaxed)
1076
+ )
1077
+ }
1078
+ });
1079
+ if !parallel {
1080
+ aai.then(move |a| {
1081
+ assert_matches!(
1082
+ a.jobs.as_slice(),
1083
+ [WorkflowActivationJob {
1084
+ variant: Some(workflow_activation_job::Variant::ResolveActivity(ra))
1085
+ }] => assert_eq!(ra.seq, 2)
1086
+ );
1087
+ if replay {
1088
+ assert!(
1089
+ a.timestamp.as_ref().unwrap().seconds
1090
+ >= first_act_ts_seconds.load(Ordering::Relaxed) + 2
1091
+ )
1092
+ }
1093
+ });
1161
1094
  }
1162
1095
 
1163
- let act = wfm.get_next_activation().await.unwrap();
1164
- assert_eq!(act.timestamp.unwrap().seconds, first_act_ts.seconds + 1);
1165
- assert_matches!(
1166
- act.jobs.as_slice(),
1167
- [WorkflowActivationJob {
1168
- variant: Some(workflow_activation_job::Variant::ResolveActivity(ra))
1169
- },
1170
- WorkflowActivationJob {
1171
- variant: Some(workflow_activation_job::Variant::ResolveActivity(ra2))
1172
- }] => {assert_eq!(ra.seq, 1); assert_eq!(ra2.seq, 2)}
1173
- );
1174
- let ready_to_execute_las = wfm.drain_queued_local_activities();
1175
- assert_eq!(ready_to_execute_las.len(), 0);
1176
-
1177
- let commands = wfm.get_server_commands().commands;
1178
- if replay {
1179
- assert_eq!(commands.len(), 1);
1180
- assert_eq!(
1181
- commands[0].command_type,
1182
- CommandType::CompleteWorkflowExecution as i32
1183
- );
1184
- } else {
1185
- assert_eq!(commands.len(), 3);
1186
- assert_eq!(commands[0].command_type, CommandType::RecordMarker as i32);
1187
- assert_eq!(commands[1].command_type, CommandType::RecordMarker as i32);
1188
- assert_eq!(
1189
- commands[2].command_type,
1190
- CommandType::CompleteWorkflowExecution as i32
1191
- );
1192
- }
1096
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
1097
+ asserts.then(move |wft| {
1098
+ let commands = &wft.commands;
1099
+ if !replay {
1100
+ assert_eq!(commands.len(), 3);
1101
+ assert_eq!(commands[0].command_type(), CommandType::RecordMarker);
1102
+ assert_eq!(commands[1].command_type(), CommandType::RecordMarker);
1103
+ assert_matches!(
1104
+ commands[2].command_type(),
1105
+ CommandType::CompleteWorkflowExecution
1106
+ );
1107
+ } else {
1108
+ assert_eq!(commands.len(), 1);
1109
+ assert_matches!(
1110
+ commands[0].command_type(),
1111
+ CommandType::CompleteWorkflowExecution
1112
+ );
1113
+ }
1114
+ });
1115
+ });
1193
1116
 
1194
- if !replay {
1195
- wfm.new_history(t.get_full_history_info().unwrap().into())
1196
- .await
1197
- .unwrap();
1117
+ let mut worker = build_fake_sdk(mock_cfg);
1118
+ worker.set_worker_interceptor(aai);
1119
+ if parallel {
1120
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, two_la_wf_parallel);
1121
+ } else {
1122
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, two_la_wf);
1198
1123
  }
1199
- let act = wfm.get_next_activation().await.unwrap();
1200
- // Still only 1s ahead b/c parallel
1201
- assert_eq!(act.timestamp.unwrap().seconds, first_act_ts.seconds + 1);
1202
- assert_eq!(act.jobs.len(), 0);
1203
- let commands = wfm.get_server_commands().commands;
1204
- assert_eq!(commands.len(), 0);
1205
-
1206
- wfm.shutdown().await.unwrap();
1124
+ worker.register_activity(
1125
+ DEFAULT_ACTIVITY_TYPE,
1126
+ move |_ctx: ActContext, _: ()| async move { Ok("Resolved") },
1127
+ );
1128
+ worker.run().await.unwrap();
1207
1129
  }
1208
1130
 
1209
1131
  async fn la_timer_la(ctx: WfContext) -> WorkflowResult<()> {
1210
1132
  ctx.local_activity(LocalActivityOptions {
1211
1133
  activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
1134
+ input: ().as_json_payload().unwrap(),
1212
1135
  ..Default::default()
1213
1136
  })
1214
1137
  .await;
1215
1138
  ctx.timer(Duration::from_secs(5)).await;
1216
1139
  ctx.local_activity(LocalActivityOptions {
1217
1140
  activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
1141
+ input: ().as_json_payload().unwrap(),
1218
1142
  ..Default::default()
1219
1143
  })
1220
1144
  .await;
@@ -1226,98 +1150,81 @@ mod tests {
1226
1150
  #[case::replay(true)]
1227
1151
  #[tokio::test]
1228
1152
  async fn las_separated_by_timer(#[case] replay: bool) {
1229
- let func = WorkflowFunction::new(la_timer_la);
1230
- let t = canned_histories::two_local_activities_separated_by_timer();
1231
- let histinfo = if replay {
1232
- t.get_full_history_info().unwrap().into()
1153
+ let mut t = TestHistoryBuilder::default();
1154
+ t.add_by_type(EventType::WorkflowExecutionStarted);
1155
+ t.add_full_wf_task();
1156
+ t.add_local_activity_result_marker(1, "1", b"hi".into());
1157
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
1158
+ t.add_timer_fired(timer_started_event_id, "1".to_string());
1159
+ t.add_full_wf_task();
1160
+ t.add_local_activity_result_marker(2, "2", b"hi2".into());
1161
+ t.add_workflow_task_scheduled_and_started();
1162
+ let mut mock_cfg = if replay {
1163
+ MockPollCfg::from_resps(t, [ResponseType::AllHistory])
1233
1164
  } else {
1234
- t.get_history_info(1).unwrap().into()
1165
+ MockPollCfg::from_hist_builder(t)
1235
1166
  };
1236
- let mut wfm = ManagedWFFunc::new_from_update(histinfo, func, vec![]);
1237
-
1238
- wfm.get_next_activation().await.unwrap();
1239
- let commands = wfm.get_server_commands().commands;
1240
- assert_eq!(commands.len(), 0);
1241
- let ready_to_execute_las = wfm.drain_queued_local_activities();
1242
- let num_queued = usize::from(!replay);
1243
- assert_eq!(ready_to_execute_las.len(), num_queued);
1244
-
1245
- if !replay {
1246
- wfm.complete_local_activity(1, ActivityExecutionResult::ok(b"Resolved".into()))
1247
- .unwrap();
1248
- }
1249
-
1250
- let act = wfm.get_next_activation().await.unwrap();
1251
- assert_matches!(
1252
- act.jobs.as_slice(),
1253
- [WorkflowActivationJob {
1254
- variant: Some(workflow_activation_job::Variant::ResolveActivity(ra))
1255
- }] => assert_eq!(ra.seq, 1)
1256
- );
1257
- let ready_to_execute_las = wfm.drain_queued_local_activities();
1258
- assert_eq!(ready_to_execute_las.len(), 0);
1259
1167
 
1260
- let commands = wfm.get_server_commands().commands;
1261
- if replay {
1262
- assert_eq!(commands.len(), 1);
1263
- assert_eq!(commands[0].command_type, CommandType::StartTimer as i32);
1264
- } else {
1265
- assert_eq!(commands.len(), 2);
1266
- assert_eq!(commands[0].command_type, CommandType::RecordMarker as i32);
1267
- assert_eq!(commands[1].command_type, CommandType::StartTimer as i32);
1268
- }
1168
+ let mut aai = ActivationAssertionsInterceptor::default();
1169
+ aai.skip_one()
1170
+ .then(|a| {
1171
+ assert_matches!(
1172
+ a.jobs.as_slice(),
1173
+ [WorkflowActivationJob {
1174
+ variant: Some(workflow_activation_job::Variant::ResolveActivity(ra))
1175
+ }] => assert_eq!(ra.seq, 1)
1176
+ );
1177
+ })
1178
+ .then(|a| {
1179
+ assert_matches!(
1180
+ a.jobs.as_slice(),
1181
+ [WorkflowActivationJob {
1182
+ variant: Some(workflow_activation_job::Variant::FireTimer(_))
1183
+ }]
1184
+ );
1185
+ });
1269
1186
 
1270
- let act = if !replay {
1271
- wfm.new_history(t.get_history_info(2).unwrap().into())
1272
- .await
1273
- .unwrap()
1274
- } else {
1275
- wfm.get_next_activation().await.unwrap()
1276
- };
1277
- assert_matches!(
1278
- act.jobs.as_slice(),
1279
- [WorkflowActivationJob {
1280
- variant: Some(workflow_activation_job::Variant::FireTimer(_))
1281
- }]
1282
- );
1283
- let ready_to_execute_las = wfm.drain_queued_local_activities();
1284
- let num_queued = usize::from(!replay);
1285
- assert_eq!(ready_to_execute_las.len(), num_queued);
1286
- if !replay {
1287
- wfm.complete_local_activity(2, ActivityExecutionResult::ok(b"Resolved".into()))
1288
- .unwrap();
1289
- }
1187
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
1188
+ if replay {
1189
+ asserts.then(|wft| {
1190
+ assert_eq!(wft.commands.len(), 1);
1191
+ assert_eq!(
1192
+ wft.commands[0].command_type,
1193
+ CommandType::CompleteWorkflowExecution as i32
1194
+ );
1195
+ });
1196
+ } else {
1197
+ asserts
1198
+ .then(|wft| {
1199
+ let commands = &wft.commands;
1200
+ assert_eq!(commands.len(), 2);
1201
+ assert_eq!(commands[0].command_type, CommandType::RecordMarker as i32);
1202
+ assert_eq!(commands[1].command_type, CommandType::StartTimer as i32);
1203
+ })
1204
+ .then(|wft| {
1205
+ let commands = &wft.commands;
1206
+ assert_eq!(commands.len(), 2);
1207
+ assert_eq!(commands[0].command_type, CommandType::RecordMarker as i32);
1208
+ assert_eq!(
1209
+ commands[1].command_type,
1210
+ CommandType::CompleteWorkflowExecution as i32
1211
+ );
1212
+ });
1213
+ }
1214
+ });
1290
1215
 
1291
- let act = wfm.get_next_activation().await.unwrap();
1292
- assert_matches!(
1293
- act.jobs.as_slice(),
1294
- [WorkflowActivationJob {
1295
- variant: Some(workflow_activation_job::Variant::ResolveActivity(ra))
1296
- }] => assert_eq!(ra.seq, 2)
1216
+ let mut worker = build_fake_sdk(mock_cfg);
1217
+ worker.set_worker_interceptor(aai);
1218
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, la_timer_la);
1219
+ worker.register_activity(
1220
+ DEFAULT_ACTIVITY_TYPE,
1221
+ move |_ctx: ActContext, _: ()| async move { Ok("Resolved") },
1297
1222
  );
1298
-
1299
- let commands = wfm.get_server_commands().commands;
1300
- if replay {
1301
- assert_eq!(commands.len(), 1);
1302
- assert_eq!(
1303
- commands[0].command_type,
1304
- CommandType::CompleteWorkflowExecution as i32
1305
- );
1306
- } else {
1307
- assert_eq!(commands.len(), 2);
1308
- assert_eq!(commands[0].command_type, CommandType::RecordMarker as i32);
1309
- assert_eq!(
1310
- commands[1].command_type,
1311
- CommandType::CompleteWorkflowExecution as i32
1312
- );
1313
- }
1314
-
1315
- wfm.shutdown().await.unwrap();
1223
+ worker.run().await.unwrap();
1316
1224
  }
1317
1225
 
1318
1226
  #[tokio::test]
1319
1227
  async fn one_la_heartbeating_wft_failure_still_executes() {
1320
- let func = WorkflowFunction::new(la_wf);
1321
1228
  let mut t = TestHistoryBuilder::default();
1322
1229
  t.add_by_type(EventType::WorkflowExecutionStarted);
1323
1230
  // Heartbeats
@@ -1330,21 +1237,26 @@ mod tests {
1330
1237
  );
1331
1238
  t.add_workflow_task_scheduled_and_started();
1332
1239
 
1333
- let histinfo = t.get_full_history_info().unwrap().into();
1334
- let mut wfm = ManagedWFFunc::new_from_update(histinfo, func, vec![]);
1335
-
1336
- // First activation will request to run the LA since the heartbeat & wft failure are skipped
1337
- // over
1338
- wfm.get_next_activation().await.unwrap();
1339
- let commands = wfm.get_server_commands().commands;
1340
- assert_eq!(commands.len(), 0);
1341
- let ready_to_execute_las = wfm.drain_queued_local_activities();
1342
- assert_eq!(ready_to_execute_las.len(), 1);
1343
- // We can happily complete it now
1344
- wfm.complete_local_activity(1, ActivityExecutionResult::ok(b"hi".into()))
1345
- .unwrap();
1346
-
1347
- wfm.shutdown().await.unwrap();
1240
+ let mut mock_cfg = MockPollCfg::from_hist_builder(t);
1241
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
1242
+ asserts.then(move |wft| {
1243
+ assert_eq!(wft.commands.len(), 2);
1244
+ assert_eq!(wft.commands[0].command_type(), CommandType::RecordMarker);
1245
+ assert_matches!(
1246
+ wft.commands[1].command_type(),
1247
+ CommandType::CompleteWorkflowExecution
1248
+ );
1249
+ });
1250
+ });
1251
+
1252
+ let mut worker = build_fake_sdk(mock_cfg);
1253
+ dbg!("Past thing");
1254
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, la_wf);
1255
+ worker.register_activity(
1256
+ DEFAULT_ACTIVITY_TYPE,
1257
+ move |_ctx: ActContext, _: ()| async move { Ok("Resolved") },
1258
+ );
1259
+ worker.run().await.unwrap();
1348
1260
  }
1349
1261
 
1350
1262
  /// This test verifies something that technically shouldn't really be possible but is worth
@@ -1352,7 +1264,6 @@ mod tests {
1352
1264
  /// chunk comes back with it failing? We should fail with a mismatch.
1353
1265
  #[tokio::test]
1354
1266
  async fn exec_passes_but_history_has_fail() {
1355
- let func = WorkflowFunction::new(la_wf);
1356
1267
  let mut t = TestHistoryBuilder::default();
1357
1268
  t.add_by_type(EventType::WorkflowExecutionStarted);
1358
1269
  t.add_full_wf_task();
@@ -1361,36 +1272,30 @@ mod tests {
1361
1272
  "1",
1362
1273
  Failure::application_failure("I failed".to_string(), false),
1363
1274
  );
1364
- t.add_workflow_execution_completed();
1275
+ t.add_workflow_task_scheduled_and_started();
1365
1276
 
1366
- let histinfo = t.get_history_info(1).unwrap().into();
1367
- let mut wfm = ManagedWFFunc::new_from_update(histinfo, func, vec![]);
1368
-
1369
- wfm.get_next_activation().await.unwrap();
1370
- let commands = wfm.get_server_commands().commands;
1371
- assert_eq!(commands.len(), 0);
1372
- let ready_to_execute_las = wfm.drain_queued_local_activities();
1373
- assert_eq!(ready_to_execute_las.len(), 1);
1374
- // Completes OK
1375
- wfm.complete_local_activity(1, ActivityExecutionResult::ok(b"hi".into()))
1376
- .unwrap();
1377
-
1378
- // next activation unblocks LA
1379
- wfm.get_next_activation().await.unwrap();
1380
- let commands = wfm.get_server_commands().commands;
1381
- assert_eq!(commands.len(), 2);
1382
- assert_eq!(commands[0].command_type, CommandType::RecordMarker as i32);
1383
- assert_eq!(
1384
- commands[1].command_type,
1385
- CommandType::CompleteWorkflowExecution as i32
1386
- );
1277
+ let mut mock_cfg = MockPollCfg::from_hist_builder(t);
1278
+ // We expect to see a nondeterminism failure (after we respond with record marker, and then
1279
+ // apply the failed marker from history).
1280
+ mock_cfg.num_expected_fails = 1;
1281
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
1282
+ asserts.then(|wft| {
1283
+ assert_eq!(wft.commands.len(), 2);
1284
+ assert_eq!(wft.commands[0].command_type(), CommandType::RecordMarker);
1285
+ assert_matches!(
1286
+ wft.commands[1].command_type(),
1287
+ CommandType::CompleteWorkflowExecution
1288
+ );
1289
+ });
1290
+ });
1387
1291
 
1388
- let err = wfm
1389
- .new_history(t.get_full_history_info().unwrap().into())
1390
- .await
1391
- .unwrap_err();
1392
- assert!(err.to_string().contains("Nondeterminism"));
1393
- wfm.shutdown().await.unwrap();
1292
+ let mut worker = build_fake_sdk(mock_cfg);
1293
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, la_wf);
1294
+ worker.register_activity(
1295
+ DEFAULT_ACTIVITY_TYPE,
1296
+ move |_ctx: ActContext, _: ()| async move { Ok("Resolved") },
1297
+ );
1298
+ worker.run().await.unwrap();
1394
1299
  }
1395
1300
 
1396
1301
  #[rstest]
@@ -1403,7 +1308,26 @@ mod tests {
1403
1308
  )]
1404
1309
  cancel_type: ActivityCancellationType,
1405
1310
  ) {
1406
- let func = WorkflowFunction::new(move |ctx| async move {
1311
+ let mut t = TestHistoryBuilder::default();
1312
+ t.add_by_type(EventType::WorkflowExecutionStarted);
1313
+ t.add_full_wf_task();
1314
+ t.add_workflow_execution_completed();
1315
+
1316
+ let mut mock_cfg = MockPollCfg::from_hist_builder(t);
1317
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
1318
+ asserts.then(|wft| {
1319
+ assert_eq!(wft.commands.len(), 2);
1320
+ // We record the cancel marker
1321
+ assert_eq!(wft.commands[0].command_type(), CommandType::RecordMarker);
1322
+ assert_matches!(
1323
+ wft.commands[1].command_type(),
1324
+ CommandType::CompleteWorkflowExecution
1325
+ );
1326
+ });
1327
+ });
1328
+
1329
+ let mut worker = build_fake_sdk(mock_cfg);
1330
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, move |ctx: WfContext| async move {
1407
1331
  let la = ctx.local_activity(LocalActivityOptions {
1408
1332
  cancel_type,
1409
1333
  ..Default::default()
@@ -1412,34 +1336,8 @@ mod tests {
1412
1336
  la.await;
1413
1337
  Ok(().into())
1414
1338
  });
1415
- let mut t = TestHistoryBuilder::default();
1416
- t.add_by_type(EventType::WorkflowExecutionStarted);
1417
- t.add_full_wf_task();
1418
- t.add_workflow_execution_completed();
1419
-
1420
- let histinfo = t.get_history_info(1).unwrap().into();
1421
- let mut wfm = ManagedWFFunc::new_from_update(histinfo, func, vec![]);
1422
-
1423
- wfm.get_next_activation().await.unwrap();
1424
- let commands = wfm.get_server_commands().commands;
1425
- assert_eq!(commands.len(), 1);
1426
- // We record the cancel marker
1427
- assert_eq!(commands[0].command_type, CommandType::RecordMarker as i32);
1428
- // Importantly, the activity shouldn't get executed since it was insta-cancelled
1429
- let ready_to_execute_las = wfm.drain_queued_local_activities();
1430
- assert_eq!(ready_to_execute_las.len(), 0);
1431
-
1432
- // next activation unblocks LA, which is cancelled now.
1433
- wfm.get_next_activation().await.unwrap();
1434
- let commands = wfm.get_server_commands().commands;
1435
- assert_eq!(commands.len(), 2);
1436
- assert_eq!(commands[0].command_type, CommandType::RecordMarker as i32);
1437
- assert_eq!(
1438
- commands[1].command_type,
1439
- CommandType::CompleteWorkflowExecution as i32
1440
- );
1441
-
1442
- wfm.shutdown().await.unwrap();
1339
+ // Explicitly don't register an activity, since we shouldn't need to run one.
1340
+ worker.run().await.unwrap();
1443
1341
  }
1444
1342
 
1445
1343
  #[rstest]
@@ -1455,37 +1353,19 @@ mod tests {
1455
1353
  )]
1456
1354
  cancel_type: ActivityCancellationType,
1457
1355
  ) {
1458
- let func = WorkflowFunction::new(move |ctx| async move {
1459
- let la = ctx.local_activity(LocalActivityOptions {
1460
- cancel_type,
1461
- activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
1462
- ..Default::default()
1463
- });
1464
- ctx.timer(Duration::from_secs(1)).await;
1465
- la.cancel(&ctx);
1466
- // This extra timer is here to ensure the presence of another WF task doesn't mess up
1467
- // resolving the LA with cancel on replay
1468
- ctx.timer(Duration::from_secs(1)).await;
1469
- let resolution = la.await;
1470
- assert!(resolution.cancelled());
1471
- let rfail = resolution.unwrap_failure();
1472
- assert_matches!(
1473
- rfail.failure_info,
1474
- Some(FailureInfo::ActivityFailureInfo(_))
1475
- );
1476
- assert_matches!(
1477
- rfail.cause.unwrap().failure_info,
1478
- Some(FailureInfo::CanceledFailureInfo(_))
1479
- );
1480
- Ok(().into())
1481
- });
1482
-
1483
1356
  let mut t = TestHistoryBuilder::default();
1484
- t.add_by_type(EventType::WorkflowExecutionStarted);
1357
+ t.add_wfe_started_with_wft_timeout(Duration::from_millis(100));
1485
1358
  t.add_full_wf_task();
1486
1359
  let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
1487
1360
  t.add_timer_fired(timer_started_event_id, "1".to_string());
1488
1361
  t.add_full_wf_task();
1362
+ // This extra workflow task serves to prevent looking ahead and pre-resolving during
1363
+ // wait-cancel.
1364
+ // TODO: including this on non wait-cancel seems to cause double-send of
1365
+ // marker recorded cmd
1366
+ if cancel_type == ActivityCancellationType::WaitCancellationCompleted {
1367
+ t.add_full_wf_task();
1368
+ }
1489
1369
  if cancel_type != ActivityCancellationType::WaitCancellationCompleted {
1490
1370
  // With non-wait cancels, the cancel is immediate
1491
1371
  t.add_local_activity_cancel_marker(1, "1");
@@ -1499,79 +1379,86 @@ mod tests {
1499
1379
  t.add_full_wf_task();
1500
1380
  t.add_workflow_execution_completed();
1501
1381
 
1502
- let histinfo = if replay {
1503
- t.get_full_history_info().unwrap().into()
1382
+ let mut mock_cfg = if replay {
1383
+ MockPollCfg::from_resps(t, [ResponseType::AllHistory])
1504
1384
  } else {
1505
- t.get_history_info(1).unwrap().into()
1506
- };
1507
- let mut wfm = ManagedWFFunc::new_from_update(histinfo, func, vec![]);
1508
-
1509
- wfm.get_next_activation().await.unwrap();
1510
- let commands = wfm.get_server_commands().commands;
1511
- assert_eq!(commands.len(), 1);
1512
- assert_eq!(commands[0].command_type, CommandType::StartTimer as i32);
1513
- let ready_to_execute_las = wfm.drain_queued_local_activities();
1514
- let num_queued = usize::from(!replay);
1515
- assert_eq!(ready_to_execute_las.len(), num_queued);
1516
-
1517
- // Next activation timer fires and activity cancel will be requested
1518
- if replay {
1519
- wfm.get_next_activation().await.unwrap()
1520
- } else {
1521
- wfm.new_history(t.get_history_info(2).unwrap().into())
1522
- .await
1523
- .unwrap()
1385
+ MockPollCfg::from_hist_builder(t)
1524
1386
  };
1387
+ let allow_cancel_barr = CancellationToken::new();
1388
+ let allow_cancel_barr_clone = allow_cancel_barr.clone();
1525
1389
 
1526
- let commands = wfm.get_server_commands().commands;
1527
- if cancel_type == ActivityCancellationType::WaitCancellationCompleted || replay {
1528
- assert_eq!(commands.len(), 1);
1529
- assert_eq!(commands[0].command_type, CommandType::StartTimer as i32);
1530
- } else {
1531
- // Try-cancel/abandon will immediately record marker (when not replaying)
1532
- assert_eq!(commands.len(), 2);
1533
- assert_eq!(commands[0].command_type, CommandType::RecordMarker as i32);
1534
- assert_eq!(commands[1].command_type, CommandType::StartTimer as i32);
1390
+ if !replay {
1391
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
1392
+ asserts
1393
+ .then(move |wft| {
1394
+ assert_eq!(wft.commands.len(), 1);
1395
+ assert_eq!(wft.commands[0].command_type, CommandType::StartTimer as i32);
1396
+ })
1397
+ .then(move |wft| {
1398
+ let commands = &wft.commands;
1399
+ if cancel_type == ActivityCancellationType::WaitCancellationCompleted {
1400
+ assert_eq!(commands.len(), 1);
1401
+ assert_eq!(commands[0].command_type, CommandType::StartTimer as i32);
1402
+ } else {
1403
+ // Try-cancel/abandon immediately recordsmarker (when not replaying)
1404
+ assert_eq!(commands.len(), 2);
1405
+ assert_eq!(commands[0].command_type, CommandType::RecordMarker as i32);
1406
+ assert_eq!(commands[1].command_type, CommandType::StartTimer as i32);
1407
+ }
1408
+ // Allow the wait-cancel to actually cancel
1409
+ allow_cancel_barr.cancel();
1410
+ })
1411
+ .then(move |wft| {
1412
+ let commands = &wft.commands;
1413
+ if cancel_type == ActivityCancellationType::WaitCancellationCompleted {
1414
+ assert_eq!(commands[0].command_type, CommandType::StartTimer as i32);
1415
+ assert_eq!(commands[1].command_type, CommandType::RecordMarker as i32);
1416
+ } else {
1417
+ assert_eq!(
1418
+ commands[0].command_type,
1419
+ CommandType::CompleteWorkflowExecution as i32
1420
+ );
1421
+ }
1422
+ });
1423
+ });
1535
1424
  }
1536
1425
 
1537
- let commands = if replay {
1538
- wfm.get_next_activation().await.unwrap();
1539
- wfm.get_server_commands().commands
1540
- } else {
1541
- // On non replay, there's an additional activation, because completing with the cancel
1542
- // wants to wake up the workflow to see if resolving the LA as cancelled did anything.
1543
- // In this case, it doesn't really, because we just hit the next timer which is also
1544
- // what would have happened if we woke up with new history -- but it does mean we
1545
- // generate the commands at this point. This matters b/c we want to make sure the record
1546
- // marker command is sent as soon as cancel happens.
1547
- if cancel_type == ActivityCancellationType::WaitCancellationCompleted {
1548
- wfm.complete_local_activity(1, ActivityExecutionResult::cancel_from_details(None))
1549
- .unwrap();
1550
- }
1551
- wfm.get_next_activation().await.unwrap();
1552
- let commands = wfm.get_server_commands().commands;
1553
- assert_eq!(commands.len(), 2);
1554
- if cancel_type == ActivityCancellationType::WaitCancellationCompleted {
1555
- assert_eq!(commands[0].command_type, CommandType::StartTimer as i32);
1556
- assert_eq!(commands[1].command_type, CommandType::RecordMarker as i32);
1557
- } else {
1558
- assert_eq!(commands[0].command_type, CommandType::RecordMarker as i32);
1559
- assert_eq!(commands[1].command_type, CommandType::StartTimer as i32);
1426
+ let mut worker = build_fake_sdk(mock_cfg);
1427
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, move |ctx: WfContext| async move {
1428
+ let la = ctx.local_activity(LocalActivityOptions {
1429
+ cancel_type,
1430
+ input: ().as_json_payload().unwrap(),
1431
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
1432
+ ..Default::default()
1433
+ });
1434
+ ctx.timer(Duration::from_secs(1)).await;
1435
+ la.cancel(&ctx);
1436
+ // This extra timer is here to ensure the presence of another WF task doesn't mess up
1437
+ // resolving the LA with cancel on replay
1438
+ ctx.timer(Duration::from_secs(1)).await;
1439
+ let resolution = la.await;
1440
+ assert!(resolution.cancelled());
1441
+ let rfail = resolution.unwrap_failure();
1442
+ assert_matches!(
1443
+ rfail.failure_info,
1444
+ Some(FailureInfo::ActivityFailureInfo(_))
1445
+ );
1446
+ assert_matches!(
1447
+ rfail.cause.unwrap().failure_info,
1448
+ Some(FailureInfo::CanceledFailureInfo(_))
1449
+ );
1450
+ Ok(().into())
1451
+ });
1452
+ worker.register_activity(DEFAULT_ACTIVITY_TYPE, move |ctx: ActContext, _: ()| {
1453
+ let allow_cancel_barr_clone = allow_cancel_barr_clone.clone();
1454
+ async move {
1455
+ if cancel_type == ActivityCancellationType::WaitCancellationCompleted {
1456
+ ctx.cancelled().await;
1457
+ }
1458
+ allow_cancel_barr_clone.cancelled().await;
1459
+ Result::<(), _>::Err(anyhow!(ActivityCancelledError::default()))
1560
1460
  }
1561
-
1562
- wfm.new_history(t.get_history_info(3).unwrap().into())
1563
- .await
1564
- .unwrap();
1565
- wfm.get_next_activation().await.unwrap();
1566
- wfm.get_server_commands().commands
1567
- };
1568
-
1569
- assert_eq!(commands.len(), 1);
1570
- assert_eq!(
1571
- commands[0].command_type,
1572
- CommandType::CompleteWorkflowExecution as i32
1573
- );
1574
-
1575
- wfm.shutdown().await.unwrap();
1461
+ });
1462
+ worker.run().await.unwrap();
1576
1463
  }
1577
1464
  }