@temporalio/core-bridge 0.19.2 → 0.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/Cargo.lock +90 -157
  2. package/Cargo.toml +1 -0
  3. package/index.d.ts +11 -27
  4. package/package.json +3 -3
  5. package/releases/aarch64-apple-darwin/index.node +0 -0
  6. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  7. package/releases/x86_64-apple-darwin/index.node +0 -0
  8. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  9. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  10. package/sdk-core/.buildkite/docker/Dockerfile +1 -1
  11. package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
  12. package/sdk-core/.cargo/config.toml +1 -0
  13. package/sdk-core/CODEOWNERS +1 -1
  14. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +119 -86
  15. package/sdk-core/bridge-ffi/src/lib.rs +311 -315
  16. package/sdk-core/bridge-ffi/src/wrappers.rs +108 -113
  17. package/sdk-core/client/Cargo.toml +13 -9
  18. package/sdk-core/client/LICENSE.txt +23 -0
  19. package/sdk-core/client/src/lib.rs +286 -174
  20. package/sdk-core/client/src/metrics.rs +86 -12
  21. package/sdk-core/client/src/raw.rs +566 -0
  22. package/sdk-core/client/src/retry.rs +137 -99
  23. package/sdk-core/core/Cargo.toml +15 -10
  24. package/sdk-core/core/LICENSE.txt +23 -0
  25. package/sdk-core/core/benches/workflow_replay.rs +79 -0
  26. package/sdk-core/core/src/abstractions.rs +38 -0
  27. package/sdk-core/core/src/core_tests/activity_tasks.rs +108 -182
  28. package/sdk-core/core/src/core_tests/child_workflows.rs +16 -11
  29. package/sdk-core/core/src/core_tests/determinism.rs +24 -12
  30. package/sdk-core/core/src/core_tests/local_activities.rs +53 -27
  31. package/sdk-core/core/src/core_tests/mod.rs +30 -43
  32. package/sdk-core/core/src/core_tests/queries.rs +82 -81
  33. package/sdk-core/core/src/core_tests/workers.rs +111 -296
  34. package/sdk-core/core/src/core_tests/workflow_cancels.rs +4 -4
  35. package/sdk-core/core/src/core_tests/workflow_tasks.rs +257 -242
  36. package/sdk-core/core/src/lib.rs +73 -318
  37. package/sdk-core/core/src/pollers/mod.rs +4 -6
  38. package/sdk-core/core/src/pollers/poll_buffer.rs +20 -14
  39. package/sdk-core/core/src/protosext/mod.rs +7 -10
  40. package/sdk-core/core/src/replay/mod.rs +11 -150
  41. package/sdk-core/core/src/telemetry/metrics.rs +35 -2
  42. package/sdk-core/core/src/telemetry/mod.rs +49 -16
  43. package/sdk-core/core/src/telemetry/prometheus_server.rs +14 -35
  44. package/sdk-core/core/src/test_help/mod.rs +104 -170
  45. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +57 -34
  46. package/sdk-core/core/src/worker/activities/local_activities.rs +95 -23
  47. package/sdk-core/core/src/worker/activities.rs +23 -16
  48. package/sdk-core/core/src/worker/client/mocks.rs +86 -0
  49. package/sdk-core/core/src/worker/client.rs +209 -0
  50. package/sdk-core/core/src/worker/mod.rs +207 -108
  51. package/sdk-core/core/src/workflow/driven_workflow.rs +21 -6
  52. package/sdk-core/core/src/workflow/history_update.rs +107 -24
  53. package/sdk-core/core/src/workflow/machines/activity_state_machine.rs +2 -3
  54. package/sdk-core/core/src/workflow/machines/child_workflow_state_machine.rs +2 -3
  55. package/sdk-core/core/src/workflow/machines/mod.rs +20 -17
  56. package/sdk-core/core/src/workflow/machines/signal_external_state_machine.rs +56 -19
  57. package/sdk-core/core/src/workflow/machines/transition_coverage.rs +5 -0
  58. package/sdk-core/core/src/workflow/machines/upsert_search_attributes_state_machine.rs +230 -22
  59. package/sdk-core/core/src/workflow/machines/workflow_machines.rs +81 -115
  60. package/sdk-core/core/src/workflow/machines/workflow_task_state_machine.rs +4 -4
  61. package/sdk-core/core/src/workflow/mod.rs +13 -1
  62. package/sdk-core/core/src/workflow/workflow_tasks/concurrency_manager.rs +70 -11
  63. package/sdk-core/core/src/workflow/workflow_tasks/mod.rs +65 -41
  64. package/sdk-core/core-api/Cargo.toml +9 -1
  65. package/sdk-core/core-api/LICENSE.txt +23 -0
  66. package/sdk-core/core-api/src/errors.rs +7 -38
  67. package/sdk-core/core-api/src/lib.rs +44 -52
  68. package/sdk-core/core-api/src/worker.rs +10 -2
  69. package/sdk-core/etc/deps.svg +127 -96
  70. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +11 -7
  71. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +10 -0
  72. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +6 -1
  73. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +6 -0
  74. package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +6 -0
  75. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +2 -1
  76. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +3 -0
  77. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +12 -0
  78. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +25 -0
  79. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +4 -0
  80. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +19 -35
  81. package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +2 -6
  82. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +53 -11
  83. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +14 -7
  84. package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +3 -5
  85. package/sdk-core/sdk/Cargo.toml +16 -2
  86. package/sdk-core/sdk/LICENSE.txt +23 -0
  87. package/sdk-core/sdk/src/interceptors.rs +11 -0
  88. package/sdk-core/sdk/src/lib.rs +139 -151
  89. package/sdk-core/sdk/src/workflow_context/options.rs +86 -1
  90. package/sdk-core/sdk/src/workflow_context.rs +36 -17
  91. package/sdk-core/sdk/src/workflow_future.rs +19 -25
  92. package/sdk-core/sdk-core-protos/Cargo.toml +1 -1
  93. package/sdk-core/sdk-core-protos/build.rs +1 -0
  94. package/sdk-core/sdk-core-protos/src/history_info.rs +17 -4
  95. package/sdk-core/sdk-core-protos/src/lib.rs +251 -47
  96. package/sdk-core/test-utils/Cargo.toml +3 -1
  97. package/sdk-core/test-utils/src/canned_histories.rs +27 -0
  98. package/sdk-core/test-utils/src/histfetch.rs +3 -3
  99. package/sdk-core/test-utils/src/lib.rs +223 -68
  100. package/sdk-core/tests/integ_tests/client_tests.rs +27 -4
  101. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +93 -14
  102. package/sdk-core/tests/integ_tests/polling_tests.rs +18 -12
  103. package/sdk-core/tests/integ_tests/queries_tests.rs +50 -53
  104. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +117 -103
  105. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +8 -1
  106. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +10 -5
  107. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +7 -1
  108. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +32 -9
  109. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +7 -1
  110. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +76 -15
  111. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +19 -3
  112. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +39 -42
  113. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +84 -0
  114. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +30 -8
  115. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +21 -6
  116. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +26 -16
  117. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +66 -0
  118. package/sdk-core/tests/integ_tests/workflow_tests.rs +78 -74
  119. package/sdk-core/tests/load_tests.rs +9 -6
  120. package/sdk-core/tests/main.rs +43 -10
  121. package/src/conversions.rs +7 -12
  122. package/src/lib.rs +322 -357
  123. package/sdk-core/client/src/mocks.rs +0 -167
  124. package/sdk-core/core/src/worker/dispatcher.rs +0 -171
  125. package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +0 -61
@@ -1,5 +1,8 @@
1
1
  use futures::StreamExt;
2
- use temporal_sdk::{ChildWorkflowOptions, WfContext, WorkflowResult};
2
+ use temporal_client::WorkflowOptions;
3
+ use temporal_sdk::{
4
+ ChildWorkflowOptions, Signal, SignalWorkflowOptions, WfContext, WorkflowResult,
5
+ };
3
6
  use temporal_sdk_core_test_utils::CoreWfStarter;
4
7
  use uuid::Uuid;
5
8
 
@@ -10,9 +13,9 @@ async fn signal_sender(ctx: WfContext) -> WorkflowResult<()> {
10
13
  let run_id = std::str::from_utf8(&ctx.get_args()[0].data)
11
14
  .unwrap()
12
15
  .to_owned();
13
- let sigres = ctx
14
- .signal_workflow(RECEIVER_WFID, run_id, SIGNAME, b"hi!")
15
- .await;
16
+ let mut dat = SignalWorkflowOptions::new(RECEIVER_WFID, run_id, SIGNAME, [b"hi!"]);
17
+ dat.with_header("tupac", b"shakur");
18
+ let sigres = ctx.signal_workflow(dat).await;
16
19
  if ctx.get_args().get(1).is_some() {
17
20
  // We expect failure
18
21
  assert!(sigres.is_err());
@@ -34,6 +37,7 @@ async fn sends_signal_to_missing_wf() {
34
37
  wf_name,
35
38
  wf_name,
36
39
  vec![Uuid::new_v4().to_string().into(), [1].into()],
40
+ WorkflowOptions::default(),
37
41
  )
38
42
  .await
39
43
  .unwrap();
@@ -41,7 +45,12 @@ async fn sends_signal_to_missing_wf() {
41
45
  }
42
46
 
43
47
  async fn signal_receiver(ctx: WfContext) -> WorkflowResult<()> {
44
- ctx.make_signal_channel(SIGNAME).next().await.unwrap();
48
+ let res = ctx.make_signal_channel(SIGNAME).next().await.unwrap();
49
+ assert_eq!(&res.input, &[b"hi!".into()]);
50
+ assert_eq!(
51
+ *res.headers.get("tupac").expect("tupac header exists"),
52
+ b"shakur".into()
53
+ );
45
54
  Ok(().into())
46
55
  }
47
56
 
@@ -53,7 +62,12 @@ async fn sends_signal_to_other_wf() {
53
62
  worker.register_wf("receiver", signal_receiver);
54
63
 
55
64
  let receiver_run_id = worker
56
- .submit_wf(RECEIVER_WFID, "receiver", vec![])
65
+ .submit_wf(
66
+ RECEIVER_WFID,
67
+ "receiver",
68
+ vec![],
69
+ WorkflowOptions::default(),
70
+ )
57
71
  .await
58
72
  .unwrap();
59
73
  worker
@@ -61,6 +75,7 @@ async fn sends_signal_to_other_wf() {
61
75
  "sends-signal-sender",
62
76
  "sender",
63
77
  vec![receiver_run_id.into()],
78
+ WorkflowOptions::default(),
64
79
  )
65
80
  .await
66
81
  .unwrap();
@@ -78,7 +93,9 @@ async fn signals_child(ctx: WfContext) -> WorkflowResult<()> {
78
93
  .await
79
94
  .into_started()
80
95
  .expect("Must start ok");
81
- started_child.signal(&ctx, SIGNAME, b"hiya!").await.unwrap();
96
+ let mut sig = Signal::new(SIGNAME, [b"hi!"]);
97
+ sig.data.with_header("tupac", b"shakur");
98
+ started_child.signal(&ctx, sig).await.unwrap();
82
99
  started_child.result().await.status.unwrap();
83
100
  Ok(().into())
84
101
  }
@@ -92,7 +109,12 @@ async fn sends_signal_to_child() {
92
109
 
93
110
  worker.incr_expected_run_count(1); // Expect another WF to be run as child
94
111
  worker
95
- .submit_wf("sends-signal-to-child", "child_signaler", vec![])
112
+ .submit_wf(
113
+ "sends-signal-to-child",
114
+ "child_signaler",
115
+ vec![],
116
+ WorkflowOptions::default(),
117
+ )
96
118
  .await
97
119
  .unwrap();
98
120
  worker.run_until_done().await.unwrap();
@@ -3,6 +3,7 @@ use std::{
3
3
  sync::atomic::{AtomicBool, AtomicUsize, Ordering},
4
4
  time::Duration,
5
5
  };
6
+ use temporal_client::WorkflowOptions;
6
7
  use temporal_sdk::{WfContext, WorkflowResult};
7
8
  use temporal_sdk_core_test_utils::CoreWfStarter;
8
9
  use tokio::sync::Barrier;
@@ -16,7 +17,12 @@ async fn timer_workflow_not_sticky() {
16
17
  worker.register_wf(wf_name.to_owned(), timer_wf);
17
18
 
18
19
  worker
19
- .submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
20
+ .submit_wf(
21
+ wf_name.to_owned(),
22
+ wf_name.to_owned(),
23
+ vec![],
24
+ WorkflowOptions::default(),
25
+ )
20
26
  .await
21
27
  .unwrap();
22
28
  worker.run_until_done().await.unwrap();
@@ -46,7 +52,12 @@ async fn timer_workflow_timeout_on_sticky() {
46
52
  worker.register_wf(wf_name.to_owned(), timer_timeout_wf);
47
53
 
48
54
  worker
49
- .submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
55
+ .submit_wf(
56
+ wf_name.to_owned(),
57
+ wf_name.to_owned(),
58
+ vec![],
59
+ WorkflowOptions::default(),
60
+ )
50
61
  .await
51
62
  .unwrap();
52
63
  worker.run_until_done().await.unwrap();
@@ -69,14 +80,18 @@ async fn cache_miss_ok() {
69
80
  });
70
81
 
71
82
  let run_id = worker
72
- .submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
83
+ .submit_wf(
84
+ wf_name.to_owned(),
85
+ wf_name.to_owned(),
86
+ vec![],
87
+ WorkflowOptions::default(),
88
+ )
73
89
  .await
74
90
  .unwrap();
75
- let core = starter.get_core().await;
76
- let tq = starter.get_task_queue();
91
+ let core = starter.get_worker().await;
77
92
  let (r1, _) = tokio::join!(worker.run_until_done(), async move {
78
93
  barr.wait().await;
79
- core.request_workflow_eviction(tq, &run_id);
94
+ core.request_workflow_eviction(&run_id);
80
95
  // We need to signal the barrier again since the wf gets evicted and will hit it again
81
96
  barr.wait().await;
82
97
  });
@@ -1,11 +1,12 @@
1
1
  use std::time::Duration;
2
+ use temporal_client::WorkflowOptions;
2
3
  use temporal_sdk::{WfContext, WorkflowResult};
3
4
  use temporal_sdk_core_protos::coresdk::{
4
5
  workflow_commands::{CancelTimer, CompleteWorkflowExecution, StartTimer},
5
6
  workflow_completion::WorkflowActivationCompletion,
6
7
  };
7
8
  use temporal_sdk_core_test_utils::{
8
- init_core_and_create_wf, start_timer_cmd, CoreTestHelpers, CoreWfStarter,
9
+ init_core_and_create_wf, start_timer_cmd, CoreWfStarter, WorkerTestHelpers,
9
10
  };
10
11
 
11
12
  pub async fn timer_wf(command_sink: WfContext) -> WorkflowResult<()> {
@@ -21,7 +22,12 @@ async fn timer_workflow_workflow_driver() {
21
22
  worker.register_wf(wf_name.to_owned(), timer_wf);
22
23
 
23
24
  worker
24
- .submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
25
+ .submit_wf(
26
+ wf_name.to_owned(),
27
+ wf_name.to_owned(),
28
+ vec![],
29
+ WorkflowOptions::default(),
30
+ )
25
31
  .await
26
32
  .unwrap();
27
33
  worker.run_until_done().await.unwrap();
@@ -29,10 +35,10 @@ async fn timer_workflow_workflow_driver() {
29
35
 
30
36
  #[tokio::test]
31
37
  async fn timer_workflow_manual() {
32
- let (core, task_q) = init_core_and_create_wf("timer_workflow").await;
33
- let task = core.poll_workflow_activation(&task_q).await.unwrap();
38
+ let mut starter = init_core_and_create_wf("timer_workflow").await;
39
+ let core = starter.get_worker().await;
40
+ let task = core.poll_workflow_activation().await.unwrap();
34
41
  core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
35
- &task_q,
36
42
  task.run_id,
37
43
  vec![StartTimer {
38
44
  seq: 0,
@@ -42,17 +48,17 @@ async fn timer_workflow_manual() {
42
48
  ))
43
49
  .await
44
50
  .unwrap();
45
- let task = core.poll_workflow_activation(&task_q).await.unwrap();
46
- core.complete_execution(&task_q, &task.run_id).await;
51
+ let task = core.poll_workflow_activation().await.unwrap();
52
+ core.complete_execution(&task.run_id).await;
47
53
  core.shutdown().await;
48
54
  }
49
55
 
50
56
  #[tokio::test]
51
57
  async fn timer_cancel_workflow() {
52
- let (core, task_q) = init_core_and_create_wf("timer_cancel_workflow").await;
53
- let task = core.poll_workflow_activation(&task_q).await.unwrap();
58
+ let mut starter = init_core_and_create_wf("timer_cancel_workflow").await;
59
+ let core = starter.get_worker().await;
60
+ let task = core.poll_workflow_activation().await.unwrap();
54
61
  core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
55
- &task_q,
56
62
  task.run_id,
57
63
  vec![
58
64
  StartTimer {
@@ -69,9 +75,8 @@ async fn timer_cancel_workflow() {
69
75
  ))
70
76
  .await
71
77
  .unwrap();
72
- let task = core.poll_workflow_activation(&task_q).await.unwrap();
78
+ let task = core.poll_workflow_activation().await.unwrap();
73
79
  core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
74
- &task_q,
75
80
  task.run_id,
76
81
  vec![
77
82
  CancelTimer { seq: 1 }.into(),
@@ -84,10 +89,10 @@ async fn timer_cancel_workflow() {
84
89
 
85
90
  #[tokio::test]
86
91
  async fn timer_immediate_cancel_workflow() {
87
- let (core, task_q) = init_core_and_create_wf("timer_immediate_cancel_workflow").await;
88
- let task = core.poll_workflow_activation(&task_q).await.unwrap();
92
+ let mut starter = init_core_and_create_wf("timer_immediate_cancel_workflow").await;
93
+ let core = starter.get_worker().await;
94
+ let task = core.poll_workflow_activation().await.unwrap();
89
95
  core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
90
- &task_q,
91
96
  task.run_id,
92
97
  vec![
93
98
  start_timer_cmd(0, Duration::from_secs(1)),
@@ -114,7 +119,12 @@ async fn parallel_timers() {
114
119
  worker.register_wf(wf_name.to_owned(), parallel_timer_wf);
115
120
 
116
121
  worker
117
- .submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
122
+ .submit_wf(
123
+ wf_name.to_owned(),
124
+ wf_name.to_owned(),
125
+ vec![],
126
+ WorkflowOptions::default(),
127
+ )
118
128
  .await
119
129
  .unwrap();
120
130
  worker.run_until_done().await.unwrap();
@@ -0,0 +1,66 @@
1
+ use std::collections::HashMap;
2
+ use temporal_client::{WorkflowClientTrait, WorkflowOptions};
3
+ use temporal_sdk::{WfContext, WorkflowResult};
4
+ use temporal_sdk_core_protos::coresdk::AsJsonPayloadExt;
5
+ use temporal_sdk_core_test_utils::CoreWfStarter;
6
+ use uuid::Uuid;
7
+
8
+ // These are initialized on the server as part of the autosetup container which we
9
+ // use for integration tests.
10
+ static TXT_ATTR: &str = "CustomTextField";
11
+ static INT_ATTR: &str = "CustomIntField";
12
+
13
+ async fn search_attr_updater(ctx: WfContext) -> WorkflowResult<()> {
14
+ ctx.upsert_search_attributes([
15
+ (TXT_ATTR.to_string(), "goodbye".as_json_payload().unwrap()),
16
+ (INT_ATTR.to_string(), 98.as_json_payload().unwrap()),
17
+ ]);
18
+ Ok(().into())
19
+ }
20
+
21
+ #[tokio::test]
22
+ async fn sends_upsert() {
23
+ let wf_name = "sends_upsert_search_attrs";
24
+ let wf_id = Uuid::new_v4();
25
+ let mut starter = CoreWfStarter::new(wf_name);
26
+ let mut worker = starter.worker().await;
27
+ worker.register_wf(wf_name, search_attr_updater);
28
+ let run_id = worker
29
+ .submit_wf(
30
+ wf_id.to_string(),
31
+ wf_name,
32
+ vec![],
33
+ WorkflowOptions {
34
+ search_attributes: Some(HashMap::from([
35
+ (TXT_ATTR.to_string(), "hello".as_json_payload().unwrap()),
36
+ (INT_ATTR.to_string(), 1.as_json_payload().unwrap()),
37
+ ])),
38
+ ..Default::default()
39
+ },
40
+ )
41
+ .await
42
+ .unwrap();
43
+ worker.run_until_done().await.unwrap();
44
+
45
+ let search_attrs = starter
46
+ .get_client()
47
+ .await
48
+ .describe_workflow_execution(wf_id.to_string(), Some(run_id))
49
+ .await
50
+ .unwrap()
51
+ .workflow_execution_info
52
+ .unwrap()
53
+ .search_attributes
54
+ .unwrap()
55
+ .indexed_fields;
56
+ let txt_attr_payload = search_attrs.get(TXT_ATTR).unwrap();
57
+ let int_attr_payload = search_attrs.get(INT_ATTR).unwrap();
58
+ for payload in [txt_attr_payload, int_attr_payload] {
59
+ assert_eq!(
60
+ &b"json/plain".to_vec(),
61
+ payload.metadata.get("encoding").unwrap()
62
+ );
63
+ }
64
+ assert_eq!("\"goodbye\"", txt_attr_payload.to_string());
65
+ assert_eq!("98", int_attr_payload.to_string());
66
+ }